]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devsdp.c
pc kernel: fix wrong simd exception mask (fixes go bootstrap)
[plan9front.git] / sys / src / 9 / port / devsdp.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "../port/netif.h"
7 #include "../port/error.h"
8
9 #include        <libsec.h>
10 #include "../port/thwack.h"
11
12 /*
13  * sdp - secure datagram protocol
14  */
15
16 typedef struct Sdp Sdp;
17 typedef struct Conv Conv;
18 typedef struct OneWay OneWay;
19 typedef struct Stats Stats;
20 typedef struct AckPkt AckPkt;
21 typedef struct Algorithm Algorithm;
22 typedef struct CipherRc4 CipherRc4;
23
24 enum
25 {
26         Qtopdir=        1,              /* top level directory */
27
28         Qsdpdir,                        /* sdp directory */
29         Qclone,
30         Qlog,
31
32         Qconvdir,                       /* directory per conversation */
33         Qctl,
34         Qdata,                          /* unreliable packet channel */
35         Qcontrol,                       /* reliable control channel */
36         Qstatus,
37         Qstats,
38         Qrstats,
39
40         MaxQ,
41
42         Maxconv= 256,           // power of 2
43         Nfs= 4,                 // number of file systems
44         MaxRetries=     12,
45         KeepAlive = 300,        // keep alive in seconds - should probably be about 60 but is higher to avoid linksys bug
46         SecretLength= 32,       // a secret per direction
47         SeqMax = (1<<24),
48         SeqWindow = 32,
49         NCompStats = 8,
50 };
51
52 #define TYPE(x)         (((ulong)(x).path) & 0xff)
53 #define CONV(x)         ((((ulong)(x).path) >> 8)&(Maxconv-1))
54 #define QID(x, y)       (((x)<<8) | (y))
55
56 struct Stats
57 {
58         ulong   outPackets;
59         ulong   outDataPackets;
60         ulong   outDataBytes;
61         ulong   outCompDataBytes;
62         ulong   outCompBytes;
63         ulong   outCompStats[NCompStats];
64         ulong   inPackets;
65         ulong   inDataPackets;
66         ulong   inDataBytes;
67         ulong   inCompDataBytes;
68         ulong   inMissing;
69         ulong   inDup;
70         ulong   inReorder;
71         ulong   inBadComp;
72         ulong   inBadAuth;
73         ulong   inBadSeq;
74         ulong   inBadOther;
75 };
76
77 struct OneWay
78 {
79         Rendez  statsready;
80
81         ulong   seqwrap;        // number of wraps of the sequence number
82         ulong   seq;
83         ulong   window;
84
85         uchar   secret[SecretLength];
86
87         QLock   controllk;
88         Rendez  controlready;
89         Block   *controlpkt;            // control channel
90         ulong   controlseq;
91
92         void    *cipherstate;   // state cipher
93         int             cipherivlen;    // initial vector length
94         int             cipherblklen;   // block length
95         int             (*cipher)(OneWay*, uchar *buf, int len);
96
97         void    *authstate;             // auth state
98         int             authlen;                // auth data length in bytes
99         int             (*auth)(OneWay*, uchar *buf, int len);
100
101         void    *compstate;
102         int             (*comp)(Conv*, int subtype, ulong seq, Block **);
103 };
104
105 // conv states
106 enum {
107         CFree,
108         CInit,
109         CDial,
110         CAccept,
111         COpen,
112         CLocalClose,
113         CRemoteClose,
114         CClosed,
115 };
116
117 struct Conv {
118         QLock;
119         Sdp     *sdp;
120         int     id;
121
122         int ref;        // holds conv up
123
124         int state;
125
126         int dataopen;   // ref count of opens on Qdata
127         int controlopen;        // ref count of opens on Qcontrol
128         int reader;             // reader proc has been started
129
130         Stats   lstats;
131         Stats   rstats;
132         
133         ulong   lastrecv;       // time last packet was received 
134         ulong   timeout;
135         int             retries;
136
137         // the following pair uniquely define conversation on this port
138         ulong dialid;
139         ulong acceptid;
140
141         QLock readlk;           // protects readproc
142         Proc *readproc;
143
144         Chan *chan;             // packet channel
145         char *channame;
146
147         char owner[KNAMELEN];           /* protections */
148         int     perm;
149
150         Algorithm *auth;
151         Algorithm *cipher;
152         Algorithm *comp;
153
154         int drop;
155
156         OneWay  in;
157         OneWay  out;
158 };
159
160 struct Sdp {
161         QLock;
162         Log;
163         int     nconv;
164         Conv *conv[Maxconv];
165         int ackproc;
166 };
167
168 enum {
169         TConnect,
170         TControl,
171         TData,
172         TCompData,
173 };
174
175 enum {
176         ControlMesg,
177         ControlAck,
178 };
179
180 enum {
181         ThwackU,
182         ThwackC,
183 };
184
185 enum {
186         ConOpenRequest,
187         ConOpenAck,
188         ConOpenAckAck,
189         ConClose,
190         ConCloseAck,
191         ConReset,
192 };
193
194 struct AckPkt
195 {
196         uchar   cseq[4];
197         uchar   outPackets[4];
198         uchar   outDataPackets[4];
199         uchar   outDataBytes[4];
200         uchar   outCompDataBytes[4];
201         uchar   outCompStats[4*NCompStats];
202         uchar   inPackets[4];
203         uchar   inDataPackets[4];
204         uchar   inDataBytes[4];
205         uchar   inCompDataBytes[4];
206         uchar   inMissing[4];
207         uchar   inDup[4];
208         uchar   inReorder[4];
209         uchar   inBadComp[4];
210         uchar   inBadAuth[4];
211         uchar   inBadSeq[4];
212         uchar   inBadOther[4];
213 };
214
215 struct Algorithm
216 {
217         char    *name;
218         int             keylen;         // in bytes
219         void    (*init)(Conv*);
220 };
221
222 enum {
223         RC4forward      = 10*1024*1024, // maximum skip forward
224         RC4back = 100*1024,             // maximum look back
225 };
226
227 struct CipherRc4
228 {
229         ulong cseq;     // current byte sequence number
230         RC4state current;
231
232         int ovalid;     // old is valid
233         ulong lgseq; // last good sequence
234         ulong oseq;     // old byte sequence number
235         RC4state old;
236 };
237
238 static Dirtab sdpdirtab[]={
239         "log",          {Qlog},         0,      0666,
240         "clone",        {Qclone},               0,      0666,
241 };
242
243 static Dirtab convdirtab[]={
244         "ctl",          {Qctl}, 0,      0666,
245         "data",         {Qdata},        0,      0666,
246         "control",      {Qcontrol},     0,      0666,
247         "status",       {Qstatus},      0,      0444,
248         "stats",        {Qstats},       0,      0444,
249         "rstats",       {Qrstats},      0,      0444,
250 };
251
252 static int m2p[] = {
253         [OREAD]         4,
254         [OWRITE]        2,
255         [ORDWR]         6
256 };
257
258 enum {
259         Logcompress=    (1<<0),
260         Logauth=        (1<<1),
261         Loghmac=        (1<<2),
262 };
263
264 static Logflag logflags[] =
265 {
266         { "compress",   Logcompress, },
267         { "auth",       Logauth, },
268         { "hmac",       Loghmac, },
269         { nil,          0, },
270 };
271
272 static Dirtab   *dirtab[MaxQ];
273 static Sdp sdptab[Nfs];
274 static char *convstatename[] = {
275         [CFree]         "Free",
276         [CInit]         "Init",
277         [CDial]         "Dial",
278         [CAccept]       "Accept",
279         [COpen]         "Open",
280         [CLocalClose] "LocalClose",
281         [CRemoteClose] "RemoteClose",
282         [CClosed]       "Closed",
283 };
284
285 static int sdpgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp);
286 static Conv *sdpclone(Sdp *sdp);
287 static void sdpackproc(void *a);
288 static void onewaycleanup(OneWay *ow);
289 static int readready(void *a);
290 static int controlread();
291 static void convsetstate(Conv *c, int state);
292 static Block *readcontrol(Conv *c, int n);
293 static void writecontrol(Conv *c, void *p, int n, int wait);
294 static Block *readdata(Conv *c, int n);
295 static long writedata(Conv *c, Block *b);
296 static void convderef(Conv *c);
297 static Block *conviput(Conv *c, Block *b, int control);
298 static void conviconnect(Conv *c, int op, Block *b);
299 static void convicontrol(Conv *c, int op, Block *b);
300 static Block *convicomp(Conv *c, int op, ulong, Block *b);
301 static void convoput(Conv *c, int type, int subtype, Block *b);
302 static void convoconnect(Conv *c, int op, ulong dialid, ulong acceptid);
303 static void convopenchan(Conv *c, char *path);
304 static void convstats(Conv *c, int local, char *buf, int n);
305 static void convreader(void *a);
306
307 static void setalg(Conv *c, char *name, Algorithm *tab, Algorithm **);
308 static void setsecret(OneWay *cc, char *secret);
309
310 static void nullcipherinit(Conv*c);
311 static void descipherinit(Conv*c);
312 static void rc4cipherinit(Conv*c);
313 static void nullauthinit(Conv*c);
314 static void shaauthinit(Conv*c);
315 static void md5authinit(Conv*c);
316 static void nullcompinit(Conv*c);
317 static void thwackcompinit(Conv*c);
318
319 static Algorithm cipheralg[] =
320 {
321         "null",                 0,      nullcipherinit,
322         "des_56_cbc",   7,      descipherinit,
323         "rc4_128",              16,     rc4cipherinit,
324         "rc4_256",              32,     rc4cipherinit,
325         nil,                    0,      nil,
326 };
327
328 static Algorithm authalg[] =
329 {
330         "null",                 0,      nullauthinit,
331         "hmac_sha1_96", 16,     shaauthinit,
332         "hmac_md5_96",  16,     md5authinit,
333         nil,                    0,      nil,
334 };
335
336 static Algorithm compalg[] =
337 {
338         "null",                 0,      nullcompinit,
339         "thwack",               0,      thwackcompinit,
340         nil,                    0,      nil,
341 };
342
343
344 static void
345 sdpinit(void)
346 {
347         int i;
348         Dirtab *dt;
349         
350         // setup dirtab with non directory entries
351         for(i=0; i<nelem(sdpdirtab); i++) {
352                 dt = sdpdirtab + i;
353                 dirtab[TYPE(dt->qid)] = dt;
354         }
355
356         for(i=0; i<nelem(convdirtab); i++) {
357                 dt = convdirtab + i;
358                 dirtab[TYPE(dt->qid)] = dt;
359         }
360
361 }
362
363 static Chan*
364 sdpattach(char* spec)
365 {
366         Chan *c;
367         int dev;
368         char buf[100];
369         Sdp *sdp;
370         int start;
371
372         dev = atoi(spec);
373         if(dev<0 || dev >= Nfs)
374                 error("bad specification");
375
376         c = devattach('E', spec);
377         c->qid = (Qid){QID(0, Qtopdir), 0, QTDIR};
378         c->dev = dev;
379
380         sdp = sdptab + dev;
381         qlock(sdp);
382         start = sdp->ackproc == 0;
383         sdp->ackproc = 1;
384         qunlock(sdp);
385
386         if(start) {
387                 snprint(buf, sizeof(buf), "sdpackproc%d", dev);
388                 kproc(buf, sdpackproc, sdp);
389         }
390         
391         return c;
392 }
393
394 static Walkqid*
395 sdpwalk(Chan *c, Chan *nc, char **name, int nname)
396 {
397         return devwalk(c, nc, name, nname, 0, 0, sdpgen);
398 }
399
400 static int
401 sdpstat(Chan* c, uchar* db, int n)
402 {
403         return devstat(c, db, n, nil, 0, sdpgen);
404 }
405
406 static Chan*
407 sdpopen(Chan* ch, int omode)
408 {
409         int perm;
410         Sdp *sdp;
411         Conv *c;
412
413         omode &= 3;
414         perm = m2p[omode];
415         USED(perm);
416
417         sdp = sdptab + ch->dev;
418
419         switch(TYPE(ch->qid)) {
420         default:
421                 break;
422         case Qtopdir:
423         case Qsdpdir:
424         case Qconvdir:
425                 if(omode != OREAD)
426                         error(Eperm);
427                 break;
428         case Qlog:
429                 logopen(sdp);
430                 break;
431         case Qclone:
432                 c = sdpclone(sdp);
433                 if(c == nil)
434                         error(Enodev);
435                 ch->qid.path = QID(c->id, Qctl);
436                 break;
437         case Qdata:
438         case Qctl:
439         case Qstatus:
440         case Qcontrol:
441         case Qstats:
442         case Qrstats:
443                 c = sdp->conv[CONV(ch->qid)];
444                 qlock(c);
445                 if(waserror()) {
446                         qunlock(c);
447                         nexterror();
448                 }
449                 if((perm & (c->perm>>6)) != perm)
450                 if(strcmp(up->user, c->owner) != 0 || (perm & c->perm) != perm)
451                                 error(Eperm);
452
453                 c->ref++;
454                 if(TYPE(ch->qid) == Qdata) {
455                         c->dataopen++;
456                         // kill reader if Qdata is opened for the first time
457                         if(c->dataopen == 1)
458                         if(c->readproc != nil)
459                                 postnote(c->readproc, 1, "interrupt", 0);
460                 } else if(TYPE(ch->qid) == Qcontrol) {  
461                         c->controlopen++;
462                 }
463                 qunlock(c);
464                 poperror();
465                 break;
466         }
467         ch->mode = openmode(omode);
468         ch->flag |= COPEN;
469         ch->offset = 0;
470         return ch;
471 }
472
473 static void
474 sdpclose(Chan* ch)
475 {
476         Sdp *sdp  = sdptab + ch->dev;
477         Conv *c;
478
479         if(!(ch->flag & COPEN))
480                 return;
481         switch(TYPE(ch->qid)) {
482         case Qlog:
483                 logclose(sdp);
484                 break;
485         case Qctl:
486         case Qstatus:
487         case Qstats:
488         case Qrstats:
489                 c = sdp->conv[CONV(ch->qid)];
490                 qlock(c);
491                 convderef(c);
492                 qunlock(c);
493                 break;
494
495         case Qdata:
496                 c = sdp->conv[CONV(ch->qid)];
497                 qlock(c);
498                 c->dataopen--;
499                 convderef(c);
500                 if(c->dataopen == 0)
501                 if(c->reader == 0)
502                 if(c->chan != nil)
503                 if(!waserror()) {
504                         kproc("convreader", convreader, c);
505                         c->reader = 1;
506                         c->ref++;
507                         poperror();
508                 }
509                 qunlock(c);
510                 break;
511
512         case Qcontrol:
513                 c = sdp->conv[CONV(ch->qid)];
514                 qlock(c);
515                 c->controlopen--;
516                 convderef(c);
517                 if(c->controlopen == 0 && c->ref != 0) {
518                         switch(c->state) {
519                         default:
520                                 convsetstate(c, CClosed);
521                                 break;
522                         case CAccept:
523                         case COpen:
524                                 convsetstate(c, CLocalClose);
525                                 break;
526                         }
527                 }
528                 qunlock(c);
529                 break;
530         }
531 }
532
533 static long
534 sdpread(Chan *ch, void *a, long n, vlong off)
535 {
536         char buf[256];
537         char *s;
538         Sdp *sdp = sdptab + ch->dev;
539         Conv *c;
540         Block *b;
541         int rv;
542
543         USED(off);
544         switch(TYPE(ch->qid)) {
545         default:
546                 error(Eperm);
547         case Qtopdir:
548         case Qsdpdir:
549         case Qconvdir:
550                 return devdirread(ch, a, n, 0, 0, sdpgen);
551         case Qlog:
552                 return logread(sdp, a, off, n);
553         case Qstatus:
554                 c = sdp->conv[CONV(ch->qid)];
555                 qlock(c);
556                 n = readstr(off, a, n, convstatename[c->state]);
557                 qunlock(c);
558                 return n;
559         case Qctl:
560                 sprint(buf, "%lud", CONV(ch->qid));
561                 return readstr(off, a, n, buf);
562         case Qcontrol:
563                 b = readcontrol(sdp->conv[CONV(ch->qid)], n);
564                 if(b == nil)
565                         return 0;
566                 if(BLEN(b) < n)
567                         n = BLEN(b);
568                 memmove(a, b->rp, n);
569                 freeb(b);
570                 return n;
571         case Qdata:
572                 b = readdata(sdp->conv[CONV(ch->qid)], n);
573                 if(b == nil)
574                         return 0;
575                 if(BLEN(b) < n)
576                         n = BLEN(b);
577                 memmove(a, b->rp, n);
578                 freeb(b);
579                 return n;
580         case Qstats:
581         case Qrstats:
582                 c = sdp->conv[CONV(ch->qid)];
583                 s = smalloc(1000);
584                 convstats(c, TYPE(ch->qid) == Qstats, s, 1000);
585                 rv = readstr(off, a, n, s);
586                 free(s);
587                 return rv;
588         }
589 }
590
591 static Block*
592 sdpbread(Chan* ch, long n, ulong offset)
593 {
594         Sdp *sdp = sdptab + ch->dev;
595
596         if(TYPE(ch->qid) != Qdata)
597                 return devbread(ch, n, offset);
598         return readdata(sdp->conv[CONV(ch->qid)], n);
599 }
600
601
602 static long
603 sdpwrite(Chan *ch, void *a, long n, vlong off)
604 {
605         Sdp *sdp = sdptab + ch->dev;
606         Cmdbuf *cb;
607         char *arg0;
608         char *p;
609         Conv *c;
610         Block *b;
611         
612         USED(off);
613         switch(TYPE(ch->qid)) {
614         default:
615                 error(Eperm);
616         case Qctl:
617                 c = sdp->conv[CONV(ch->qid)];
618                 cb = parsecmd(a, n);
619                 qlock(c);
620                 if(waserror()) {
621                         qunlock(c);
622                         free(cb);
623                         nexterror();
624                 }
625                 if(cb->nf == 0)
626                         error("short write");
627                 arg0 = cb->f[0];
628                 if(strcmp(arg0, "accept") == 0) {
629                         if(cb->nf != 2)
630                                 error("usage: accept file");
631                         convopenchan(c, cb->f[1]);
632                 } else if(strcmp(arg0, "dial") == 0) {
633                         if(cb->nf != 2)
634                                 error("usage: dial file");
635                         convopenchan(c, cb->f[1]);
636                         convsetstate(c, CDial);
637                 } else if(strcmp(arg0, "drop") == 0) {
638                         if(cb->nf != 2)
639                                 error("usage: drop permil");
640                         c->drop = atoi(cb->f[1]);
641                 } else if(strcmp(arg0, "cipher") == 0) {
642                         if(cb->nf != 2)
643                                 error("usage: cipher alg");
644                         setalg(c, cb->f[1], cipheralg, &c->cipher);
645                 } else if(strcmp(arg0, "auth") == 0) {
646                         if(cb->nf != 2)
647                                 error("usage: auth alg");
648                         setalg(c, cb->f[1], authalg, &c->auth);
649                 } else if(strcmp(arg0, "comp") == 0) {
650                         if(cb->nf != 2)
651                                 error("usage: comp alg");
652                         setalg(c, cb->f[1], compalg, &c->comp);
653                 } else if(strcmp(arg0, "insecret") == 0) {
654                         if(cb->nf != 2)
655                                 error("usage: insecret secret");
656                         setsecret(&c->in, cb->f[1]);
657                         if(c->cipher)
658                                 c->cipher->init(c);
659                         if(c->auth)
660                                 c->auth->init(c);
661                 } else if(strcmp(arg0, "outsecret") == 0) {
662                         if(cb->nf != 2)
663                                 error("usage: outsecret secret");
664                         setsecret(&c->out, cb->f[1]);
665                         if(c->cipher)
666                                 c->cipher->init(c);
667                         if(c->auth)
668                                 c->auth->init(c);
669                 } else
670                         error("unknown control request");
671                 poperror();
672                 qunlock(c);
673                 free(cb);
674                 return n;
675         case Qlog:
676                 cb = parsecmd(a, n);
677                 p = logctl(sdp, cb->nf, cb->f, logflags);
678                 free(cb);
679                 if(p != nil)
680                         error(p);
681                 return n;
682         case Qcontrol:
683                 writecontrol(sdp->conv[CONV(ch->qid)], a, n, 0);
684                 return n;
685         case Qdata:
686                 b = allocb(n);
687                 memmove(b->wp, a, n);
688                 b->wp += n;
689                 return writedata(sdp->conv[CONV(ch->qid)], b);
690         }
691 }
692
693 long
694 sdpbwrite(Chan *ch, Block *bp, ulong offset)
695 {
696         Sdp *sdp = sdptab + ch->dev;
697
698         if(TYPE(ch->qid) != Qdata)
699                 return devbwrite(ch, bp, offset);
700         return writedata(sdp->conv[CONV(ch->qid)], bp);
701 }
702
703 static int
704 sdpgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
705 {
706         Sdp *sdp = sdptab + c->dev;
707         int type = TYPE(c->qid);
708         Dirtab *dt;
709         Qid qid;
710
711         if(s == DEVDOTDOT){
712                 switch(TYPE(c->qid)){
713                 case Qtopdir:
714                 case Qsdpdir:
715                         snprint(up->genbuf, sizeof(up->genbuf), "#E%ld", c->dev);
716                         mkqid(&qid, Qtopdir, 0, QTDIR);
717                         devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
718                         break;
719                 case Qconvdir:
720                         snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
721                         mkqid(&qid, Qsdpdir, 0, QTDIR);
722                         devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
723                         break;
724                 default:
725                         panic("sdpwalk %llux", c->qid.path);
726                 }
727                 return 1;
728         }
729
730         switch(type) {
731         default:
732                 // non directory entries end up here
733                 if(c->qid.type & QTDIR)
734                         panic("sdpgen: unexpected directory");  
735                 if(s != 0)
736                         return -1;
737                 dt = dirtab[TYPE(c->qid)];
738                 if(dt == nil)
739                         panic("sdpgen: unknown type: %lud", TYPE(c->qid));
740                 devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
741                 return 1;
742         case Qtopdir:
743                 if(s != 0)
744                         return -1;
745                 mkqid(&qid, QID(0, Qsdpdir), 0, QTDIR);
746                 devdir(c, qid, "sdp", 0, eve, 0555, dp);
747                 return 1;
748         case Qsdpdir:
749                 if(s<nelem(sdpdirtab)) {
750                         dt = sdpdirtab+s;
751                         devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp);
752                         return 1;
753                 }
754                 s -= nelem(sdpdirtab);
755                 if(s >= sdp->nconv)
756                         return -1;
757                 mkqid(&qid, QID(s, Qconvdir), 0, QTDIR);
758                 snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
759                 devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
760                 return 1;
761         case Qconvdir:
762                 if(s>=nelem(convdirtab))
763                         return -1;
764                 dt = convdirtab+s;
765                 mkqid(&qid, QID(CONV(c->qid),TYPE(dt->qid)), 0, QTFILE);
766                 devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);
767                 return 1;
768         }
769 }
770
771 static Conv*
772 sdpclone(Sdp *sdp)
773 {
774         Conv *c, **pp, **ep;
775
776         c = nil;
777         ep = sdp->conv + nelem(sdp->conv);
778         qlock(sdp);
779         if(waserror()) {
780                 qunlock(sdp);
781                 nexterror();
782         }
783         for(pp = sdp->conv; pp < ep; pp++) {
784                 c = *pp;
785                 if(c == nil){
786                         c = malloc(sizeof(Conv));
787                         if(c == nil)
788                                 error(Enomem);
789                         memset(c, 0, sizeof(Conv));
790                         qlock(c);
791                         c->sdp = sdp;
792                         c->id = pp - sdp->conv;
793                         *pp = c;
794                         sdp->nconv++;
795                         break;
796                 }
797                 if(c->ref == 0 && canqlock(c)){
798                         if(c->ref == 0)
799                                 break;
800                         qunlock(c);
801                 }
802         }
803         poperror();
804         qunlock(sdp);
805
806         if(pp >= ep)
807                 return nil;
808
809         assert(c->state == CFree);
810         // set ref to 2 - 1 ref for open - 1 ref for channel state
811         c->ref = 2;
812         c->state = CInit;
813         c->in.window = ~0;
814         strncpy(c->owner, up->user, sizeof(c->owner)-1);
815         c->owner[sizeof(c->owner)-1] = 0;
816         c->perm = 0660;
817         qunlock(c);
818
819         return c;
820 }
821
822 // assume c is locked
823 static void
824 convretryinit(Conv *c)
825 {
826         c->retries = 0;
827         // +2 to avoid rounding effects.
828         c->timeout = TK2SEC(m->ticks) + 2;
829 }
830
831 // assume c is locked
832 static int
833 convretry(Conv *c, int reset)
834 {
835         c->retries++;
836         if(c->retries > MaxRetries) {
837                 if(reset)
838                         convoconnect(c, ConReset, c->dialid, c->acceptid);
839                 convsetstate(c, CClosed);
840                 return 0;
841         }
842         c->timeout = TK2SEC(m->ticks) + (c->retries+1);
843         return 1;
844 }
845
846 // assumes c is locked
847 static void
848 convtimer(Conv *c, ulong sec)
849 {
850         Block *b;
851
852         if(c->timeout > sec)
853                 return;
854
855         switch(c->state) {
856         case CInit:
857                 break;
858         case CDial:
859                 if(convretry(c, 1))
860                         convoconnect(c, ConOpenRequest, c->dialid, 0);
861                 break;
862         case CAccept:
863                 if(convretry(c, 1))
864                         convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
865                 break;
866         case COpen:
867                 b = c->out.controlpkt;
868                 if(b != nil) {
869                         if(convretry(c, 1))
870                                 convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
871                         break;
872                 }
873
874                 c->timeout = c->lastrecv + KeepAlive;
875                 if(c->timeout > sec)
876                         break;
877                 // keepalive - randomly spaced between KeepAlive and 2*KeepAlive
878                 if(c->timeout + KeepAlive > sec && nrand(c->lastrecv + 2*KeepAlive - sec) > 0)
879                         break;
880                 // can not use writecontrol
881                 b = allocb(4);
882                 c->out.controlseq++;
883                 hnputl(b->wp, c->out.controlseq);
884                 b->wp += 4;
885                 c->out.controlpkt = b;
886                 convretryinit(c);
887                 if(!waserror()) {
888                         convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
889                         poperror();
890                 }
891                 break;
892         case CLocalClose:
893                 if(convretry(c, 0))
894                         convoconnect(c, ConClose, c->dialid, c->acceptid);
895                 break;
896         case CRemoteClose:
897         case CClosed:
898                 break;
899         }
900 }
901
902
903 static void
904 sdpackproc(void *a)
905 {
906         Sdp *sdp = a;
907         ulong sec;
908         int i;
909         Conv *c;
910
911         while(waserror())
912                 ;
913         for(;;) {
914                 tsleep(&up->sleep, return0, 0, 1000);
915                 sec = TK2SEC(m->ticks);
916                 qlock(sdp);
917                 for(i=0; i<sdp->nconv; i++) {
918                         c = sdp->conv[i];
919                         if(c->ref == 0)
920                                 continue;
921                         qunlock(sdp);
922                         qlock(c);
923                         if(c->ref > 0 && !waserror()) {
924                                 convtimer(c, sec);
925                                 poperror();
926                         }
927                         qunlock(c);
928                         qlock(sdp);
929                 }
930                 qunlock(sdp);
931         }
932 }
933
934 Dev sdpdevtab = {
935         'E',
936         "sdp",
937
938         devreset,
939         sdpinit,
940         devshutdown,
941         sdpattach,
942         sdpwalk,
943         sdpstat,
944         sdpopen,
945         devcreate,
946         sdpclose,
947         sdpread,
948         devbread,
949         sdpwrite,
950         devbwrite,
951         devremove,
952         devwstat,
953 };
954
955 // assume hold lock on c
956 static void
957 convsetstate(Conv *c, int state)
958 {
959
960 if(0)print("convsetstate %d: %s -> %s\n", c->id, convstatename[c->state], convstatename[state]);
961
962         switch(state) {
963         default:
964                 panic("setstate: bad state: %d", state);
965         case CDial:
966                 assert(c->state == CInit);
967                 c->dialid = (rand()<<16) + rand();
968                 convretryinit(c);
969                 convoconnect(c, ConOpenRequest, c->dialid, 0);
970                 break;
971         case CAccept:
972                 assert(c->state == CInit);
973                 c->acceptid = (rand()<<16) + rand();
974                 convretryinit(c);
975                 convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
976                 break;
977         case COpen:
978                 assert(c->state == CDial || c->state == CAccept);
979                 c->lastrecv = TK2SEC(m->ticks);
980                 if(c->state == CDial) {
981                         convretryinit(c);
982                         convoconnect(c, ConOpenAckAck, c->dialid, c->acceptid);
983                         hnputl(c->in.secret, c->acceptid);
984                         hnputl(c->in.secret+4, c->dialid);
985                         hnputl(c->out.secret, c->dialid);
986                         hnputl(c->out.secret+4, c->acceptid);
987                 } else {
988                         hnputl(c->in.secret, c->dialid);
989                         hnputl(c->in.secret+4, c->acceptid);
990                         hnputl(c->out.secret, c->acceptid);
991                         hnputl(c->out.secret+4, c->dialid);
992                 }
993                 setalg(c, "hmac_md5_96", authalg, &c->auth);
994                 break;
995         case CLocalClose:
996                 assert(c->state == CAccept || c->state == COpen);
997                 convretryinit(c);
998                 convoconnect(c, ConClose, c->dialid, c->acceptid);
999                 break;
1000         case CRemoteClose:
1001                 wakeup(&c->in.controlready);
1002                 wakeup(&c->out.controlready);
1003                 break;
1004         case CClosed:
1005                 wakeup(&c->in.controlready);
1006                 wakeup(&c->out.controlready);
1007                 if(c->readproc)
1008                         postnote(c->readproc, 1, "interrupt", 0);
1009                 if(c->state != CClosed)
1010                         convderef(c);
1011                 break;
1012         }
1013         c->state = state;
1014 }
1015
1016
1017 //assumes c is locked
1018 static void
1019 convderef(Conv *c)
1020 {
1021         c->ref--;
1022         if(c->ref > 0) {
1023                 return;
1024         }
1025         assert(c->ref == 0);
1026         assert(c->dataopen == 0);
1027         assert(c->controlopen == 0);
1028 if(0)print("convderef: %d: ref == 0!\n", c->id);
1029         c->state = CFree;
1030         if(c->chan) {   
1031                 cclose(c->chan);
1032                 c->chan = nil;
1033         }
1034         if(c->channame) {
1035                 free(c->channame);
1036                 c->channame = nil;
1037         }
1038         c->cipher = nil;
1039         c->auth = nil;
1040         c->comp = nil;
1041         strcpy(c->owner, "network");
1042         c->perm = 0660;
1043         c->dialid = 0;
1044         c->acceptid = 0;
1045         c->timeout = 0;
1046         c->retries = 0;
1047         c->drop = 0;
1048         onewaycleanup(&c->in);
1049         onewaycleanup(&c->out);
1050         memset(&c->lstats, 0, sizeof(Stats));
1051         memset(&c->rstats, 0, sizeof(Stats));
1052 }
1053
1054 static void
1055 onewaycleanup(OneWay *ow)
1056 {
1057         if(ow->controlpkt)
1058                 freeb(ow->controlpkt);
1059         secfree(ow->authstate);
1060         secfree(ow->cipherstate);
1061         if(ow->compstate)
1062                 free(ow->compstate);
1063         memset(ow, 0, sizeof(OneWay));
1064 }
1065
1066
1067 // assumes conv is locked
1068 static void
1069 convopenchan(Conv *c, char *path)
1070 {
1071         if(c->state != CInit || c->chan != nil)
1072                 error("already connected");
1073         c->chan = namec(path, Aopen, ORDWR, 0);
1074         c->channame = smalloc(strlen(path)+1);
1075         strcpy(c->channame, path);
1076         if(waserror()) {
1077                 cclose(c->chan);
1078                 c->chan = nil;
1079                 free(c->channame);
1080                 c->channame = nil;
1081                 nexterror();
1082         }
1083         kproc("convreader", convreader, c);
1084
1085         assert(c->reader == 0 && c->ref > 0);
1086         // after kproc in case it fails
1087         c->reader = 1;
1088         c->ref++;
1089
1090         poperror();
1091 }
1092
1093 static void
1094 convstats(Conv *c, int local, char *buf, int n)
1095 {
1096         Stats *stats;
1097         char *p, *ep;
1098         int i;
1099
1100         if(local) {
1101                 stats = &c->lstats;
1102         } else {
1103                 if(!waserror()) {
1104                         writecontrol(c, 0, 0, 1);
1105                         poperror();
1106                 }
1107                 stats = &c->rstats;
1108         }
1109
1110         qlock(c);
1111         p = buf;
1112         ep = buf + n;
1113         p += snprint(p, ep-p, "outPackets: %lud\n", stats->outPackets);
1114         p += snprint(p, ep-p, "outDataPackets: %lud\n", stats->outDataPackets);
1115         p += snprint(p, ep-p, "outDataBytes: %lud\n", stats->outDataBytes);
1116         p += snprint(p, ep-p, "outCompDataBytes: %lud\n", stats->outCompDataBytes);
1117         for(i=0; i<NCompStats; i++) {
1118                 if(stats->outCompStats[i] == 0)
1119                         continue;
1120                 p += snprint(p, ep-p, "outCompStats[%d]: %lud\n", i, stats->outCompStats[i]);
1121         }
1122         p += snprint(p, ep-p, "inPackets: %lud\n", stats->inPackets);
1123         p += snprint(p, ep-p, "inDataPackets: %lud\n", stats->inDataPackets);
1124         p += snprint(p, ep-p, "inDataBytes: %lud\n", stats->inDataBytes);
1125         p += snprint(p, ep-p, "inCompDataBytes: %lud\n", stats->inCompDataBytes);
1126         p += snprint(p, ep-p, "inMissing: %lud\n", stats->inMissing);
1127         p += snprint(p, ep-p, "inDup: %lud\n", stats->inDup);
1128         p += snprint(p, ep-p, "inReorder: %lud\n", stats->inReorder);
1129         p += snprint(p, ep-p, "inBadComp: %lud\n", stats->inBadComp);
1130         p += snprint(p, ep-p, "inBadAuth: %lud\n", stats->inBadAuth);
1131         p += snprint(p, ep-p, "inBadSeq: %lud\n", stats->inBadSeq);
1132         p += snprint(p, ep-p, "inBadOther: %lud\n", stats->inBadOther);
1133         USED(p);
1134         qunlock(c);
1135 }
1136
1137 // c is locked
1138 static void
1139 convack(Conv *c)
1140 {
1141         Block *b;
1142         AckPkt *ack;
1143         Stats *s;
1144         int i;
1145
1146         b = allocb(sizeof(AckPkt));
1147         ack = (AckPkt*)b->wp;
1148         b->wp += sizeof(AckPkt);
1149         s = &c->lstats;
1150         hnputl(ack->cseq, c->in.controlseq);
1151         hnputl(ack->outPackets, s->outPackets);
1152         hnputl(ack->outDataPackets, s->outDataPackets);
1153         hnputl(ack->outDataBytes, s->outDataBytes);
1154         hnputl(ack->outCompDataBytes, s->outCompDataBytes);
1155         for(i=0; i<NCompStats; i++)
1156                 hnputl(ack->outCompStats+i*4, s->outCompStats[i]);
1157         hnputl(ack->inPackets, s->inPackets);
1158         hnputl(ack->inDataPackets, s->inDataPackets);
1159         hnputl(ack->inDataBytes, s->inDataBytes);
1160         hnputl(ack->inCompDataBytes, s->inCompDataBytes);
1161         hnputl(ack->inMissing, s->inMissing);
1162         hnputl(ack->inDup, s->inDup);
1163         hnputl(ack->inReorder, s->inReorder);
1164         hnputl(ack->inBadComp, s->inBadComp);
1165         hnputl(ack->inBadAuth, s->inBadAuth);
1166         hnputl(ack->inBadSeq, s->inBadSeq);
1167         hnputl(ack->inBadOther, s->inBadOther);
1168         convoput(c, TControl, ControlAck, b);
1169 }
1170
1171
1172 // assume we hold lock for c
1173 static Block *
1174 conviput(Conv *c, Block *b, int control)
1175 {
1176         int type, subtype;
1177         ulong seq, seqwrap;
1178         long seqdiff;
1179         int pad;
1180
1181         c->lstats.inPackets++;
1182
1183         if(BLEN(b) < 4) {
1184                 c->lstats.inBadOther++;
1185                 freeb(b);
1186                 return nil;
1187         }
1188         
1189         type = b->rp[0] >> 4;
1190         subtype = b->rp[0] & 0xf;
1191         b->rp += 1;
1192         if(type == TConnect) {
1193                 conviconnect(c, subtype, b);
1194                 return nil;
1195         }
1196
1197         switch(c->state) {
1198         case CInit:
1199         case CDial:
1200                 c->lstats.inBadOther++;
1201                 convoconnect(c, ConReset, c->dialid, c->acceptid);
1202                 convsetstate(c, CClosed);
1203                 break;
1204         case CAccept:
1205         case CRemoteClose:
1206         case CLocalClose:
1207                 c->lstats.inBadOther++;
1208                 freeb(b);
1209                 return nil;
1210         }
1211
1212         seq = (b->rp[0]<<16) + (b->rp[1]<<8) + b->rp[2];
1213         b->rp += 3;
1214
1215         seqwrap = c->in.seqwrap;
1216         seqdiff = seq - c->in.seq;
1217         if(seqdiff < -(SeqMax*3/4)) {
1218                 seqwrap++;
1219                 seqdiff += SeqMax;
1220         } else if(seqdiff > SeqMax*3/4) {
1221                 seqwrap--;
1222                 seqdiff -= SeqMax;
1223         }
1224
1225         if(seqdiff <= 0) {
1226                 if(seqdiff <= -SeqWindow) {
1227 if(0)print("old sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff);
1228                         c->lstats.inBadSeq++;
1229                         freeb(b);
1230                         return nil;
1231                 }
1232
1233                 if(c->in.window & (1<<-seqdiff)) {
1234 if(0)print("dup sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff);
1235                         c->lstats.inDup++;
1236                         freeb(b);
1237                         return nil;
1238                 }
1239
1240                 c->lstats.inReorder++;
1241         }
1242
1243         // ok the sequence number looks ok
1244 if(0) print("coniput seq=%ulx\n", seq);
1245         if(c->in.auth != 0) {
1246                 if(!(*c->in.auth)(&c->in, b->rp-4, BLEN(b)+4)) {
1247 if(0)print("bad auth %ld\n", BLEN(b)+4);
1248                         c->lstats.inBadAuth++;
1249                         freeb(b);
1250                         return nil;
1251                 }
1252                 b->wp -= c->in.authlen;
1253         }
1254
1255         if(c->in.cipher != 0) {
1256                 if(!(*c->in.cipher)(&c->in, b->rp, BLEN(b))) {
1257 if(0)print("bad cipher\n");
1258                         c->lstats.inBadOther++;
1259                         freeb(b);
1260                         return nil;
1261                 }
1262                 b->rp += c->in.cipherivlen;
1263                 if(c->in.cipherblklen > 1) {
1264                         pad = b->wp[-1];
1265                         if(pad > BLEN(b)) {
1266 if(0)print("pad too big\n");
1267                                 c->lstats.inBadOther++;
1268                                 freeb(b);
1269                                 return nil;
1270                         }
1271                         b->wp -= pad;
1272                 }
1273         }
1274
1275         // ok the packet is good
1276         if(seqdiff > 0) {
1277                 while(seqdiff > 0 && c->in.window != 0) {
1278                         if((c->in.window & (1<<(SeqWindow-1))) == 0) {
1279                                 c->lstats.inMissing++;
1280                         }
1281                         c->in.window <<= 1;
1282                         seqdiff--;
1283                 }
1284                 if(seqdiff > 0) {
1285                         c->lstats.inMissing += seqdiff;
1286                         seqdiff = 0;
1287                 }
1288                 c->in.seq = seq;
1289                 c->in.seqwrap = seqwrap;
1290         }
1291         c->in.window |= 1<<-seqdiff;
1292         c->lastrecv = TK2SEC(m->ticks);
1293
1294         switch(type) {
1295         case TControl:
1296                 convicontrol(c, subtype, b);
1297                 return nil;
1298         case TData:
1299                 c->lstats.inDataPackets++;
1300                 c->lstats.inDataBytes += BLEN(b);
1301                 if(control)
1302                         break;
1303                 return b;
1304         case TCompData:
1305                 c->lstats.inDataPackets++;
1306                 c->lstats.inCompDataBytes += BLEN(b);
1307                 b = convicomp(c, subtype, seq, b);
1308                 if(b == nil) {
1309                         c->lstats.inBadComp++;
1310                         return nil;
1311                 }
1312                 c->lstats.inDataBytes += BLEN(b);
1313                 if(control)
1314                         break;
1315                 return b;
1316         }
1317 if(0)print("dropping packet id=%d: type=%d n=%ld control=%d\n", c->id, type, BLEN(b), control);
1318         c->lstats.inBadOther++;
1319         freeb(b);
1320         return nil;
1321 }
1322
1323 // assume hold conv lock
1324 static void
1325 conviconnect(Conv *c, int subtype, Block *b)
1326 {
1327         ulong dialid;
1328         ulong acceptid;
1329
1330         if(BLEN(b) != 8) {
1331                 freeb(b);
1332                 return;
1333         }
1334         dialid = nhgetl(b->rp);
1335         acceptid = nhgetl(b->rp + 4);
1336         freeb(b);
1337
1338 if(0)print("sdp: conviconnect: %s: %d %uld %uld\n", convstatename[c->state], subtype, dialid, acceptid);
1339
1340         if(subtype == ConReset) {
1341                 convsetstate(c, CClosed);
1342                 return;
1343         }
1344
1345         switch(c->state) {
1346         default:
1347                 panic("unknown state: %d", c->state);
1348         case CInit:
1349                 break;
1350         case CDial:
1351                 if(dialid != c->dialid)
1352                         goto Reset;
1353                 break;
1354         case CAccept:
1355         case COpen:
1356         case CLocalClose:
1357         case CRemoteClose:
1358                 if(dialid != c->dialid
1359                 || subtype != ConOpenRequest && acceptid != c->acceptid)
1360                         goto Reset;
1361                 break;
1362         case CClosed:
1363                 goto Reset;
1364         }
1365
1366         switch(subtype) {
1367         case ConOpenRequest:
1368                 switch(c->state) {
1369                 case CInit:
1370                         c->dialid = dialid;
1371                         convsetstate(c, CAccept);
1372                         return;
1373                 case CAccept:
1374                 case COpen:
1375                         // duplicate ConOpenRequest that we ignore
1376                         return;
1377                 }
1378                 break;
1379         case ConOpenAck:
1380                 switch(c->state) {
1381                 case CDial:
1382                         c->acceptid = acceptid;
1383                         convsetstate(c, COpen);
1384                         return;
1385                 case COpen:
1386                         // duplicate that we have to ack
1387                         convoconnect(c, ConOpenAckAck, acceptid, dialid);
1388                         return;
1389                 }
1390                 break;
1391         case ConOpenAckAck:
1392                 switch(c->state) {
1393                 case CAccept:
1394                         convsetstate(c, COpen);
1395                         return;
1396                 case COpen:
1397                 case CLocalClose:
1398                 case CRemoteClose:
1399                         // duplicate that we ignore
1400                         return;
1401                 }
1402                 break;
1403         case ConClose:
1404                 switch(c->state) {
1405                 case COpen:
1406                         convoconnect(c, ConCloseAck, dialid, acceptid);
1407                         convsetstate(c, CRemoteClose);
1408                         return;
1409                 case CRemoteClose:
1410                         // duplicate ConClose
1411                         convoconnect(c, ConCloseAck, dialid, acceptid);
1412                         return;
1413                 }
1414                 break;
1415         case ConCloseAck:
1416                 switch(c->state) {
1417                 case CLocalClose:
1418                         convsetstate(c, CClosed);
1419                         return;
1420                 }
1421                 break;
1422         }
1423 Reset:
1424         // invalid connection message - reset to sender
1425 if(1)print("sdp: invalid conviconnect - sending reset\n");
1426         convoconnect(c, ConReset, dialid, acceptid);
1427         convsetstate(c, CClosed);
1428 }
1429
1430 static void
1431 convicontrol(Conv *c, int subtype, Block *b)
1432 {
1433         ulong cseq;
1434         AckPkt *ack;
1435         int i;
1436
1437         if(BLEN(b) < 4)
1438                 return;
1439         cseq = nhgetl(b->rp);
1440         
1441         switch(subtype){
1442         case ControlMesg:
1443                 if(cseq == c->in.controlseq) {
1444 if(0)print("duplicate control packet: %ulx\n", cseq);
1445                         // duplicate control packet
1446                         freeb(b);
1447                         if(c->in.controlpkt == nil)
1448                                 convack(c);
1449                         return;
1450                 }
1451
1452                 if(cseq != c->in.controlseq+1)
1453                         return;
1454                 c->in.controlseq = cseq;
1455                 b->rp += 4;
1456                 if(BLEN(b) == 0) {
1457                         // just a ping
1458                         freeb(b);
1459                         convack(c);
1460                 } else {
1461                         c->in.controlpkt = b;
1462 if(0) print("recv %ld size=%ld\n", cseq, BLEN(b));
1463                         wakeup(&c->in.controlready);
1464                 }
1465                 return;
1466         case ControlAck:
1467                 if(cseq != c->out.controlseq)
1468                         return;
1469                 if(BLEN(b) < sizeof(AckPkt))
1470                         return;
1471                 ack = (AckPkt*)(b->rp);
1472                 c->rstats.outPackets = nhgetl(ack->outPackets);
1473                 c->rstats.outDataPackets = nhgetl(ack->outDataPackets);
1474                 c->rstats.outDataBytes = nhgetl(ack->outDataBytes);
1475                 c->rstats.outCompDataBytes = nhgetl(ack->outCompDataBytes);
1476                 for(i=0; i<NCompStats; i++)
1477                         c->rstats.outCompStats[i] = nhgetl(ack->outCompStats + 4*i);
1478                 c->rstats.inPackets = nhgetl(ack->inPackets);
1479                 c->rstats.inDataPackets = nhgetl(ack->inDataPackets);
1480                 c->rstats.inDataBytes = nhgetl(ack->inDataBytes);
1481                 c->rstats.inCompDataBytes = nhgetl(ack->inCompDataBytes);
1482                 c->rstats.inMissing = nhgetl(ack->inMissing);
1483                 c->rstats.inDup = nhgetl(ack->inDup);
1484                 c->rstats.inReorder = nhgetl(ack->inReorder);
1485                 c->rstats.inBadComp = nhgetl(ack->inBadComp);
1486                 c->rstats.inBadAuth = nhgetl(ack->inBadAuth);
1487                 c->rstats.inBadSeq = nhgetl(ack->inBadSeq);
1488                 c->rstats.inBadOther = nhgetl(ack->inBadOther);
1489                 freeb(b);
1490                 freeb(c->out.controlpkt);
1491                 c->out.controlpkt = nil;
1492                 c->timeout = c->lastrecv + KeepAlive;
1493                 wakeup(&c->out.controlready);
1494                 return;
1495         }
1496 }
1497
1498 static Block*
1499 convicomp(Conv *c, int subtype, ulong seq, Block *b)
1500 {
1501         if(c->in.comp == nil) {
1502                 freeb(b);
1503                 return nil;
1504         }
1505         if(!(*c->in.comp)(c, subtype, seq, &b))
1506                 return nil;
1507         return b;
1508 }
1509
1510 // c is locked
1511 static void
1512 convwriteblock(Conv *c, Block *b)
1513 {
1514         // simulated errors
1515         if(c->drop && nrand(c->drop) == 0)
1516                 return;
1517
1518         if(waserror()) {
1519                 convsetstate(c, CClosed);
1520                 nexterror();
1521         }
1522         devtab[c->chan->type]->bwrite(c->chan, b, 0);
1523         poperror();
1524 }
1525
1526
1527 // assume hold conv lock
1528 static void
1529 convoput(Conv *c, int type, int subtype, Block *b)
1530 {
1531         int pad;
1532         
1533         c->lstats.outPackets++;
1534         /* Make room for sdp trailer */
1535         if(c->out.cipherblklen > 1)
1536                 pad = c->out.cipherblklen - (BLEN(b) + c->out.cipherivlen) % c->out.cipherblklen;
1537         else
1538                 pad = 0;
1539
1540         b = padblock(b, -(pad+c->out.authlen));
1541
1542         if(pad) {
1543                 memset(b->wp, 0, pad-1);
1544                 b->wp[pad-1] = pad;
1545                 b->wp += pad;
1546         }
1547
1548         /* Make space to fit sdp header */
1549         b = padblock(b, 4 + c->out.cipherivlen);
1550         b->rp[0] = (type << 4) | subtype;
1551         c->out.seq++;
1552         if(c->out.seq == (1<<24)) {
1553                 c->out.seq = 0;
1554                 c->out.seqwrap++;
1555         }
1556         b->rp[1] = c->out.seq>>16;
1557         b->rp[2] = c->out.seq>>8;
1558         b->rp[3] = c->out.seq;
1559         
1560         if(c->out.cipher)
1561                 (*c->out.cipher)(&c->out, b->rp+4, BLEN(b)-4);
1562
1563         // auth
1564         if(c->out.auth) {
1565                 b->wp += c->out.authlen;
1566                 (*c->out.auth)(&c->out, b->rp, BLEN(b));
1567         }
1568         
1569         convwriteblock(c, b);
1570 }
1571
1572 // assume hold conv lock
1573 static void
1574 convoconnect(Conv *c, int op, ulong dialid, ulong acceptid)
1575 {
1576         Block *b;
1577
1578         c->lstats.outPackets++;
1579         assert(c->chan != nil);
1580         b = allocb(9);
1581         b->wp[0] = (TConnect << 4) | op;
1582         hnputl(b->wp+1, dialid);
1583         hnputl(b->wp+5, acceptid);
1584         b->wp += 9;
1585
1586         if(!waserror()) {
1587                 convwriteblock(c, b);
1588                 poperror();
1589         }
1590 }
1591
1592 static Block *
1593 convreadblock(Conv *c, int n)
1594 {
1595         Block *b;
1596         Chan *ch;
1597
1598         qlock(&c->readlk);
1599         if(waserror()) {
1600                 c->readproc = nil;
1601                 qunlock(&c->readlk);
1602                 nexterror();
1603         }
1604         qlock(c);
1605         if(c->state == CClosed) {
1606                 qunlock(c);
1607                 error("closed");
1608         }
1609         c->readproc = up;
1610         ch = c->chan;
1611         assert(c->ref > 0);
1612         qunlock(c);
1613
1614         b = devtab[ch->type]->bread(ch, n, 0);
1615         c->readproc = nil;
1616         poperror();
1617         qunlock(&c->readlk);
1618
1619         return b;
1620 }
1621
1622 static int
1623 readready(void *a)
1624 {
1625         Conv *c = a;
1626
1627         return c->in.controlpkt != nil || (c->state == CClosed) || (c->state == CRemoteClose);
1628 }
1629
1630 static Block *
1631 readcontrol(Conv *c, int n)
1632 {
1633         Block *b;
1634
1635         USED(n);
1636
1637         qlock(&c->in.controllk);
1638         if(waserror()) {
1639                 qunlock(&c->in.controllk);
1640                 nexterror();
1641         }
1642         qlock(c);       // this lock is not held during the sleep below
1643
1644         for(;;) {
1645                 if(c->chan == nil || c->state == CClosed) {
1646                         qunlock(c);
1647 if(0)print("readcontrol: return error - state = %s\n", convstatename[c->state]);
1648                         error("conversation closed");
1649                 }
1650
1651                 if(c->in.controlpkt != nil)
1652                         break;
1653
1654                 if(c->state == CRemoteClose) {
1655                         qunlock(c);
1656 if(0)print("readcontrol: return nil - state = %s\n", convstatename[c->state]);
1657                         poperror();
1658                         return nil;
1659                 }
1660                 qunlock(c);
1661                 sleep(&c->in.controlready, readready, c);
1662                 qlock(c);
1663         }
1664
1665         convack(c);
1666
1667         b = c->in.controlpkt;
1668         c->in.controlpkt = nil;
1669         qunlock(c);
1670         poperror();
1671         qunlock(&c->in.controllk);
1672         return b;
1673 }
1674
1675
1676 static int
1677 writeready(void *a)
1678 {
1679         Conv *c = a;
1680
1681         return c->out.controlpkt == nil || (c->state == CClosed) || (c->state == CRemoteClose);
1682 }
1683
1684 // c is locked
1685 static void
1686 writewait(Conv *c)
1687 {
1688         for(;;) {
1689                 if(c->state == CFree || c->state == CInit ||
1690                    c->state == CClosed || c->state == CRemoteClose)
1691                         error("conversation closed");
1692
1693                 if(c->state == COpen && c->out.controlpkt == nil)
1694                         break;
1695
1696                 qunlock(c);
1697                 if(waserror()) {
1698                         qlock(c);
1699                         nexterror();
1700                 }
1701                 sleep(&c->out.controlready, writeready, c);
1702                 poperror();
1703                 qlock(c);
1704         }
1705 }
1706
1707 static void
1708 writecontrol(Conv *c, void *p, int n, int wait)
1709 {
1710         Block *b;
1711
1712         qlock(&c->out.controllk);
1713         qlock(c);
1714         if(waserror()) {
1715                 qunlock(c);
1716                 qunlock(&c->out.controllk);
1717                 nexterror();
1718         }
1719         writewait(c);
1720         b = allocb(4+n);
1721         c->out.controlseq++;
1722         hnputl(b->wp, c->out.controlseq);
1723         memmove(b->wp+4, p, n);
1724         b->wp += 4+n;
1725         c->out.controlpkt = b;
1726         convretryinit(c);
1727         convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
1728         if(wait)
1729                 writewait(c);
1730         poperror();
1731         qunlock(c);
1732         qunlock(&c->out.controllk);
1733 }
1734
1735 static Block *
1736 readdata(Conv *c, int n)
1737 {
1738         Block *b;
1739         int nn;
1740
1741         for(;;) {
1742
1743                 // some slack for tunneling overhead
1744                 nn = n + 100;
1745
1746                 // make sure size is big enough for control messages
1747                 if(nn < 1000)
1748                         nn = 1000;
1749                 b = convreadblock(c, nn);
1750                 if(b == nil)
1751                         return nil;
1752                 qlock(c);
1753                 if(waserror()) {
1754                         qunlock(c);
1755                         return nil;
1756                 }
1757                 b = conviput(c, b, 0);
1758                 poperror();
1759                 qunlock(c);
1760                 if(b != nil) {
1761                         if(BLEN(b) > n)
1762                                 b->wp = b->rp + n;
1763                         return b;
1764                 }
1765         }
1766 }
1767
1768 static long
1769 writedata(Conv *c, Block *b)
1770 {
1771         int n;
1772         ulong seq;
1773         int subtype;
1774
1775         qlock(c);
1776         if(waserror()) {
1777                 qunlock(c);
1778                 nexterror();
1779         }
1780
1781         if(c->state != COpen) {
1782                 freeb(b);
1783                 error("conversation not open");
1784         }
1785
1786         n = BLEN(b);
1787         c->lstats.outDataPackets++;
1788         c->lstats.outDataBytes += n;
1789
1790         if(c->out.comp != nil) {
1791                 // must generate same value as convoput
1792                 seq = (c->out.seq + 1) & (SeqMax-1);
1793
1794                 subtype = (*c->out.comp)(c, 0, seq, &b);
1795                 c->lstats.outCompDataBytes += BLEN(b);
1796                 convoput(c, TCompData, subtype, b);
1797         } else
1798                 convoput(c, TData, 0, b);
1799
1800         poperror();
1801         qunlock(c);
1802         return n;
1803 }
1804
1805 static void
1806 convreader(void *a)
1807 {
1808         Conv *c = a;
1809         Block *b;
1810
1811         qlock(c);
1812         assert(c->reader == 1);
1813         while(c->dataopen == 0 && c->state != CClosed) {
1814                 qunlock(c);
1815                 b = nil;
1816                 if(!waserror()) {
1817                         b = convreadblock(c, 2000);
1818                         poperror();
1819                 }
1820                 qlock(c);
1821                 if(b == nil) {
1822                         if(strcmp(up->errstr, Eintr) != 0) {
1823                                 convsetstate(c, CClosed);
1824                                 break;
1825                         }
1826                 } else if(!waserror()) {
1827                         conviput(c, b, 1);
1828                         poperror();
1829                 }
1830         }
1831         c->reader = 0;
1832         convderef(c);
1833         qunlock(c);
1834         pexit("hangup", 1);
1835 }
1836
1837
1838 /* ciphers, authenticators, and compressors  */
1839
1840 static void
1841 setalg(Conv *c, char *name, Algorithm *alg, Algorithm **p)
1842 {
1843         for(; alg->name; alg++)
1844                 if(strcmp(name, alg->name) == 0)
1845                         break;
1846         if(alg->name == nil)
1847                 error("unknown algorithm");
1848
1849         *p = alg;
1850         alg->init(c);
1851 }
1852
1853 static void
1854 setsecret(OneWay *ow, char *secret)
1855 {
1856         char *p;
1857         int i, c;
1858         
1859         i = 0;
1860         memset(ow->secret, 0, sizeof(ow->secret));
1861         for(p=secret; *p; p++) {
1862                 if(i >= sizeof(ow->secret)*2)
1863                         break;
1864                 c = *p;
1865                 if(c >= '0' && c <= '9')
1866                         c -= '0';
1867                 else if(c >= 'a' && c <= 'f')
1868                         c -= 'a'-10;
1869                 else if(c >= 'A' && c <= 'F')
1870                         c -= 'A'-10;
1871                 else
1872                         error("bad character in secret");
1873                 if((i&1) == 0)
1874                         c <<= 4;
1875                 ow->secret[i>>1] |= c;
1876                 i++;
1877         }
1878 }
1879
1880 static void
1881 setkey(uchar *key, int n, OneWay *ow, char *prefix)
1882 {
1883         uchar ibuf[SHA1dlen], obuf[MD5dlen], salt[10];
1884         int i, round = 0;
1885
1886         while(n > 0){
1887                 for(i=0; i<round+1; i++)
1888                         salt[i] = 'A'+round;
1889                 sha1((uchar*)prefix, strlen(prefix), ibuf, sha1(salt, round+1, nil, nil));
1890                 md5(ibuf, SHA1dlen, obuf, md5(ow->secret, sizeof(ow->secret), nil, nil));
1891                 i = (n<MD5dlen) ? n : MD5dlen;
1892                 memmove(key, obuf, i);
1893                 key += i;
1894                 n -= i;
1895                 if(++round > sizeof salt)
1896                         panic("setkey: you ask too much");
1897         }
1898 }
1899
1900 static void
1901 cipherfree(Conv *c)
1902 {
1903         if(c->in.cipherstate) {
1904                 free(c->in.cipherstate);
1905                 c->in.cipherstate = nil;
1906         }
1907         if(c->out.cipherstate) {
1908                 free(c->out.cipherstate);
1909                 c->out.cipherstate = nil;
1910         }
1911         c->in.cipher = nil;
1912         c->in.cipherblklen = 0;
1913         c->out.cipherblklen = 0;
1914         c->in.cipherivlen = 0;
1915         c->out.cipherivlen = 0;
1916 }
1917
1918 static void
1919 authfree(Conv *c)
1920 {
1921         secfree(c->in.authstate);
1922         secfree(c->out.authstate);
1923         c->in.authstate = nil;
1924         c->out.authstate = nil;
1925         c->in.auth = nil;
1926         c->in.authlen = 0;
1927         c->out.authlen = 0;
1928 }
1929
1930 static void
1931 compfree(Conv *c)
1932 {
1933         if(c->in.compstate) {
1934                 free(c->in.compstate);
1935                 c->in.compstate = nil;
1936         }
1937         if(c->out.compstate) {
1938                 free(c->out.compstate);
1939                 c->out.compstate = nil;
1940         }
1941         c->in.comp = nil;
1942 }
1943
1944 static void
1945 nullcipherinit(Conv *c)
1946 {
1947         cipherfree(c);
1948 }
1949
1950 static int
1951 desencrypt(OneWay *ow, uchar *p, int n)
1952 {
1953         uchar *pp, *ip, *eip, *ep;
1954         DESstate *ds = ow->cipherstate;
1955
1956         if(n < 8 || (n & 0x7 != 0))
1957                 return 0;
1958         ep = p + n;
1959         memmove(p, ds->ivec, 8);
1960         for(p += 8; p < ep; p += 8){
1961                 pp = p;
1962                 ip = ds->ivec;
1963                 for(eip = ip+8; ip < eip; )
1964                         *pp++ ^= *ip++;
1965                 block_cipher(ds->expanded, p, 0);
1966                 memmove(ds->ivec, p, 8);
1967         }
1968         return 1;
1969 }
1970
1971 static int
1972 desdecrypt(OneWay *ow, uchar *p, int n)
1973 {
1974         uchar tmp[8];
1975         uchar *tp, *ip, *eip, *ep;
1976         DESstate *ds = ow->cipherstate;
1977
1978         if(n < 8 || (n & 0x7 != 0))
1979                 return 0;
1980         ep = p + n;
1981         memmove(ds->ivec, p, 8);
1982         p += 8;
1983         while(p < ep){
1984                 memmove(tmp, p, 8);
1985                 block_cipher(ds->expanded, p, 1);
1986                 tp = tmp;
1987                 ip = ds->ivec;
1988                 for(eip = ip+8; ip < eip; ){
1989                         *p++ ^= *ip;
1990                         *ip++ = *tp++;
1991                 }
1992         }
1993         return 1;
1994 }
1995
1996 static void
1997 descipherinit(Conv *c)
1998 {
1999         uchar key[8];
2000         uchar ivec[8];
2001         int n = c->cipher->keylen;
2002
2003         cipherfree(c);
2004         
2005         if(n > sizeof(key))
2006                 n = sizeof(key);
2007
2008         /* in */
2009         memset(key, 0, sizeof(key));
2010         setkey(key, n, &c->in, "cipher");
2011         memset(ivec, 0, sizeof(ivec));
2012         c->in.cipherblklen = 8;
2013         c->in.cipherivlen = 8;
2014         c->in.cipher = desdecrypt;
2015         c->in.cipherstate = secalloc(sizeof(DESstate));
2016         setupDESstate(c->in.cipherstate, key, ivec);
2017         
2018         /* out */
2019         memset(key, 0, sizeof(key));
2020         setkey(key, n, &c->out, "cipher");
2021         prng(ivec, 8);
2022         c->out.cipherblklen = 8;
2023         c->out.cipherivlen = 8;
2024         c->out.cipher = desencrypt;
2025         c->out.cipherstate = secalloc(sizeof(DESstate));
2026         setupDESstate(c->out.cipherstate, key, ivec);
2027 }
2028
2029 static int
2030 rc4encrypt(OneWay *ow, uchar *p, int n)
2031 {
2032         CipherRc4 *cr = ow->cipherstate;
2033
2034         if(n < 4)
2035                 return 0;
2036
2037         hnputl(p, cr->cseq);
2038         p += 4;
2039         n -= 4;
2040         rc4(&cr->current, p, n);
2041         cr->cseq += n;
2042         return 1;
2043 }
2044
2045 static int
2046 rc4decrypt(OneWay *ow, uchar *p, int n)
2047 {
2048         CipherRc4 *cr = ow->cipherstate;
2049         RC4state tmpstate;
2050         ulong seq;
2051         long d, dd;
2052
2053         if(n < 4)
2054                 return 0;
2055
2056         seq = nhgetl(p);
2057         p += 4;
2058         n -= 4;
2059         d = seq-cr->cseq;
2060         if(d == 0) {
2061                 rc4(&cr->current, p, n);
2062                 cr->cseq += n;
2063                 if(cr->ovalid) {
2064                         dd = cr->cseq - cr->lgseq;
2065                         if(dd > RC4back)
2066                                 cr->ovalid = 0;
2067                 }
2068         } else if(d > 0) {
2069 //print("missing packet: %uld %ld\n", seq, d);
2070                 // this link is hosed 
2071                 if(d > RC4forward)
2072                         return 0;
2073                 cr->lgseq = seq;
2074                 if(!cr->ovalid) {
2075                         cr->ovalid = 1;
2076                         cr->oseq = cr->cseq;
2077                         memmove(&cr->old, &cr->current, sizeof(RC4state));
2078                 }
2079                 rc4skip(&cr->current, d);
2080                 rc4(&cr->current, p, n);
2081                 cr->cseq = seq+n;
2082         } else {
2083 //print("reordered packet: %uld %ld\n", seq, d);
2084                 dd = seq - cr->oseq;
2085                 if(!cr->ovalid || -d > RC4back || dd < 0)
2086                         return 0;
2087                 memmove(&tmpstate, &cr->old, sizeof(RC4state));
2088                 rc4skip(&tmpstate, dd);
2089                 rc4(&tmpstate, p, n);
2090                 return 1;
2091         }
2092
2093         // move old state up
2094         if(cr->ovalid) {
2095                 dd = cr->cseq - RC4back - cr->oseq;
2096                 if(dd > 0) {
2097                         rc4skip(&cr->old, dd);
2098                         cr->oseq += dd;
2099                 }
2100         }
2101
2102         return 1;
2103 }
2104
2105 static void
2106 rc4cipherinit(Conv *c)
2107 {
2108         uchar key[32];
2109         CipherRc4 *cr;
2110         int n;
2111
2112         cipherfree(c);
2113
2114         n = c->cipher->keylen;
2115         if(n > sizeof(key))
2116                 n = sizeof(key);
2117
2118         /* in */
2119         memset(key, 0, sizeof(key));
2120         setkey(key, n, &c->in, "cipher");
2121         c->in.cipherblklen = 1;
2122         c->in.cipherivlen = 4;
2123         c->in.cipher = rc4decrypt;
2124         cr = secalloc(sizeof(CipherRc4));
2125         memset(cr, 0, sizeof(*cr));
2126         setupRC4state(&cr->current, key, n);
2127         c->in.cipherstate = cr;
2128
2129         /* out */
2130         memset(key, 0, sizeof(key));
2131         setkey(key, n, &c->out, "cipher");
2132         c->out.cipherblklen = 1;
2133         c->out.cipherivlen = 4;
2134         c->out.cipher = rc4encrypt;
2135         cr = secalloc(sizeof(CipherRc4));
2136         memset(cr, 0, sizeof(*cr));
2137         setupRC4state(&cr->current, key, n);
2138         c->out.cipherstate = cr;
2139 }
2140
2141 static void
2142 nullauthinit(Conv *c)
2143 {
2144         authfree(c);
2145 }
2146
2147 static void
2148 shaauthinit(Conv *c)
2149 {
2150         authfree(c);
2151 }
2152
2153 static void
2154 seanq_hmac_md5(uchar hash[MD5dlen], ulong wrap, uchar *t, long tlen, uchar *key, long klen)
2155 {
2156         uchar ipad[65], opad[65], wbuf[4];
2157         int i;
2158         DigestState *digest;
2159         uchar innerhash[MD5dlen];
2160
2161         for(i=0; i<64; i++){
2162                 ipad[i] = 0x36;
2163                 opad[i] = 0x5c;
2164         }
2165         ipad[64] = opad[64] = 0;
2166         for(i=0; i<klen; i++){
2167                 ipad[i] ^= key[i];
2168                 opad[i] ^= key[i];
2169         }
2170         hnputl(wbuf, wrap);
2171         digest = md5(ipad, 64, nil, nil);
2172         digest = md5(wbuf, sizeof(wbuf), nil, digest);
2173         md5(t, tlen, innerhash, digest);
2174         digest = md5(opad, 64, nil, nil);
2175         md5(innerhash, MD5dlen, hash, digest);
2176 }
2177
2178 static int
2179 md5auth(OneWay *ow, uchar *t, int tlen)
2180 {
2181         uchar hash[MD5dlen];
2182         int r;
2183
2184         if(tlen < ow->authlen)
2185                 return 0;
2186         tlen -= ow->authlen;
2187
2188         memset(hash, 0, MD5dlen);
2189         seanq_hmac_md5(hash, ow->seqwrap, t, tlen, (uchar*)ow->authstate, 16);
2190         r = tsmemcmp(t+tlen, hash, ow->authlen) == 0;
2191         memmove(t+tlen, hash, ow->authlen);
2192         return r;
2193 }
2194
2195 static void
2196 md5authinit(Conv *c)
2197 {
2198         int keylen;
2199
2200         authfree(c);
2201
2202         keylen = c->auth->keylen;
2203         if(keylen > 16)
2204                 keylen = 16;
2205
2206         /* in */
2207         c->in.authstate = secalloc(16);
2208         memset(c->in.authstate, 0, 16);
2209         setkey(c->in.authstate, keylen, &c->in, "auth");
2210         c->in.authlen = 12;
2211         c->in.auth = md5auth;
2212         
2213         /* out */
2214         c->out.authstate = secalloc(16);
2215         memset(c->out.authstate, 0, 16);
2216         setkey(c->out.authstate, keylen, &c->out, "auth");
2217         c->out.authlen = 12;
2218         c->out.auth = md5auth;
2219 }
2220
2221 static void
2222 nullcompinit(Conv *c)
2223 {
2224         compfree(c);
2225 }
2226
2227 static int
2228 thwackcomp(Conv *c, int, ulong seq, Block **bp)
2229 {
2230         Block *b, *bb;
2231         int nn;
2232         ulong ackseq;
2233         uchar mask;
2234
2235         // add ack info
2236         b = padblock(*bp, 4);
2237
2238         ackseq = unthwackstate(c->in.compstate, &mask);
2239         b->rp[0] = mask;
2240         b->rp[1] = ackseq>>16;
2241         b->rp[2] = ackseq>>8;
2242         b->rp[3] = ackseq;
2243
2244         bb = allocb(BLEN(b));
2245         nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq, c->lstats.outCompStats);
2246         if(nn < 0) {
2247                 freeb(bb);
2248                 *bp = b;
2249                 return ThwackU;
2250         } else {
2251                 bb->wp += nn;
2252                 freeb(b);
2253                 *bp = bb;
2254                 return ThwackC;
2255         }
2256 }
2257
2258 static int
2259 thwackuncomp(Conv *c, int subtype, ulong seq, Block **bp)
2260 {
2261         Block *b, *bb;
2262         ulong mask;
2263         ulong mseq;
2264         int n;
2265
2266         switch(subtype) {
2267         default:
2268                 return 0;
2269         case ThwackU:
2270                 b = *bp;
2271                 mask = b->rp[0];
2272                 mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2273                 b->rp += 4;
2274                 thwackack(c->out.compstate, mseq, mask);
2275                 return 1;
2276         case ThwackC:
2277                 bb = *bp;
2278                 b = allocb(ThwMaxBlock);
2279                 n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq);
2280                 freeb(bb);
2281                 *bp = nil;
2282                 if(n < 0) {
2283 if(0)print("unthwack failed: %d\n", n);
2284                         freeb(b);
2285                         return 0;
2286                 }
2287                 b->wp += n;
2288                 mask = b->rp[0];
2289                 mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2290                 thwackack(c->out.compstate, mseq, mask);
2291                 b->rp += 4;
2292                 *bp = b;
2293                 return 1;
2294         }
2295 }
2296
2297 static void
2298 thwackcompinit(Conv *c)
2299 {
2300         compfree(c);
2301
2302         c->in.compstate = malloc(sizeof(Unthwack));
2303         if(c->in.compstate == nil)
2304                 error(Enomem);
2305         unthwackinit(c->in.compstate);
2306         c->out.compstate = malloc(sizeof(Thwack));
2307         if(c->out.compstate == nil)
2308                 error(Enomem);
2309         thwackinit(c->out.compstate);
2310         c->in.comp = thwackuncomp;
2311         c->out.comp = thwackcomp;
2312 }