]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devsdp.c
devshr: fixed crash
[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));
815         c->perm = 0660;
816         qunlock(c);
817
818         return c;
819 }
820
821 // assume c is locked
822 static void
823 convretryinit(Conv *c)
824 {
825         c->retries = 0;
826         // +2 to avoid rounding effects.
827         c->timeout = TK2SEC(m->ticks) + 2;
828 }
829
830 // assume c is locked
831 static int
832 convretry(Conv *c, int reset)
833 {
834         c->retries++;
835         if(c->retries > MaxRetries) {
836                 if(reset)
837                         convoconnect(c, ConReset, c->dialid, c->acceptid);
838                 convsetstate(c, CClosed);
839                 return 0;
840         }
841         c->timeout = TK2SEC(m->ticks) + (c->retries+1);
842         return 1;
843 }
844
845 // assumes c is locked
846 static void
847 convtimer(Conv *c, ulong sec)
848 {
849         Block *b;
850
851         if(c->timeout > sec)
852                 return;
853
854         switch(c->state) {
855         case CInit:
856                 break;
857         case CDial:
858                 if(convretry(c, 1))
859                         convoconnect(c, ConOpenRequest, c->dialid, 0);
860                 break;
861         case CAccept:
862                 if(convretry(c, 1))
863                         convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
864                 break;
865         case COpen:
866                 b = c->out.controlpkt;
867                 if(b != nil) {
868                         if(convretry(c, 1))
869                                 convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
870                         break;
871                 }
872
873                 c->timeout = c->lastrecv + KeepAlive;
874                 if(c->timeout > sec)
875                         break;
876                 // keepalive - randomly spaced between KeepAlive and 2*KeepAlive
877                 if(c->timeout + KeepAlive > sec && nrand(c->lastrecv + 2*KeepAlive - sec) > 0)
878                         break;
879                 // can not use writecontrol
880                 b = allocb(4);
881                 c->out.controlseq++;
882                 hnputl(b->wp, c->out.controlseq);
883                 b->wp += 4;
884                 c->out.controlpkt = b;
885                 convretryinit(c);
886                 if(!waserror()) {
887                         convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
888                         poperror();
889                 }
890                 break;
891         case CLocalClose:
892                 if(convretry(c, 0))
893                         convoconnect(c, ConClose, c->dialid, c->acceptid);
894                 break;
895         case CRemoteClose:
896         case CClosed:
897                 break;
898         }
899 }
900
901
902 static void
903 sdpackproc(void *a)
904 {
905         Sdp *sdp = a;
906         ulong sec;
907         int i;
908         Conv *c;
909
910         for(;;) {
911                 tsleep(&up->sleep, return0, 0, 1000);
912                 sec = TK2SEC(m->ticks);
913                 qlock(sdp);
914                 for(i=0; i<sdp->nconv; i++) {
915                         c = sdp->conv[i];
916                         if(c->ref == 0)
917                                 continue;
918                         qunlock(sdp);
919                         qlock(c);
920                         if(c->ref > 0 && !waserror()) {
921                                 convtimer(c, sec);
922                                 poperror();
923                         }
924                         qunlock(c);
925                         qlock(sdp);
926                 }
927                 qunlock(sdp);
928         }
929 }
930
931 Dev sdpdevtab = {
932         'E',
933         "sdp",
934
935         devreset,
936         sdpinit,
937         devshutdown,
938         sdpattach,
939         sdpwalk,
940         sdpstat,
941         sdpopen,
942         devcreate,
943         sdpclose,
944         sdpread,
945         devbread,
946         sdpwrite,
947         devbwrite,
948         devremove,
949         devwstat,
950 };
951
952 // assume hold lock on c
953 static void
954 convsetstate(Conv *c, int state)
955 {
956
957 if(0)print("convsetstate %d: %s -> %s\n", c->id, convstatename[c->state], convstatename[state]);
958
959         switch(state) {
960         default:
961                 panic("setstate: bad state: %d", state);
962         case CDial:
963                 assert(c->state == CInit);
964                 c->dialid = (rand()<<16) + rand();
965                 convretryinit(c);
966                 convoconnect(c, ConOpenRequest, c->dialid, 0);
967                 break;
968         case CAccept:
969                 assert(c->state == CInit);
970                 c->acceptid = (rand()<<16) + rand();
971                 convretryinit(c);
972                 convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
973                 break;
974         case COpen:
975                 assert(c->state == CDial || c->state == CAccept);
976                 c->lastrecv = TK2SEC(m->ticks);
977                 if(c->state == CDial) {
978                         convretryinit(c);
979                         convoconnect(c, ConOpenAckAck, c->dialid, c->acceptid);
980                         hnputl(c->in.secret, c->acceptid);
981                         hnputl(c->in.secret+4, c->dialid);
982                         hnputl(c->out.secret, c->dialid);
983                         hnputl(c->out.secret+4, c->acceptid);
984                 } else {
985                         hnputl(c->in.secret, c->dialid);
986                         hnputl(c->in.secret+4, c->acceptid);
987                         hnputl(c->out.secret, c->acceptid);
988                         hnputl(c->out.secret+4, c->dialid);
989                 }
990                 setalg(c, "hmac_md5_96", authalg, &c->auth);
991                 break;
992         case CLocalClose:
993                 assert(c->state == CAccept || c->state == COpen);
994                 convretryinit(c);
995                 convoconnect(c, ConClose, c->dialid, c->acceptid);
996                 break;
997         case CRemoteClose:
998                 wakeup(&c->in.controlready);
999                 wakeup(&c->out.controlready);
1000                 break;
1001         case CClosed:
1002                 wakeup(&c->in.controlready);
1003                 wakeup(&c->out.controlready);
1004                 if(c->readproc)
1005                         postnote(c->readproc, 1, "interrupt", 0);
1006                 if(c->state != CClosed)
1007                         convderef(c);
1008                 break;
1009         }
1010         c->state = state;
1011 }
1012
1013
1014 //assumes c is locked
1015 static void
1016 convderef(Conv *c)
1017 {
1018         c->ref--;
1019         if(c->ref > 0) {
1020                 return;
1021         }
1022         assert(c->ref == 0);
1023         assert(c->dataopen == 0);
1024         assert(c->controlopen == 0);
1025 if(0)print("convderef: %d: ref == 0!\n", c->id);
1026         c->state = CFree;
1027         if(c->chan) {   
1028                 cclose(c->chan);
1029                 c->chan = nil;
1030         }
1031         if(c->channame) {
1032                 free(c->channame);
1033                 c->channame = nil;
1034         }
1035         c->cipher = nil;
1036         c->auth = nil;
1037         c->comp = nil;
1038         strcpy(c->owner, "network");
1039         c->perm = 0660;
1040         c->dialid = 0;
1041         c->acceptid = 0;
1042         c->timeout = 0;
1043         c->retries = 0;
1044         c->drop = 0;
1045         onewaycleanup(&c->in);
1046         onewaycleanup(&c->out);
1047         memset(&c->lstats, 0, sizeof(Stats));
1048         memset(&c->rstats, 0, sizeof(Stats));
1049 }
1050
1051 static void
1052 onewaycleanup(OneWay *ow)
1053 {
1054         if(ow->controlpkt)
1055                 freeb(ow->controlpkt);
1056         if(ow->authstate)
1057                 free(ow->authstate);
1058         if(ow->cipherstate)
1059                 free(ow->cipherstate);
1060         if(ow->compstate)
1061                 free(ow->compstate);
1062         memset(ow, 0, sizeof(OneWay));
1063 }
1064
1065
1066 // assumes conv is locked
1067 static void
1068 convopenchan(Conv *c, char *path)
1069 {
1070         if(c->state != CInit || c->chan != nil)
1071                 error("already connected");
1072         c->chan = namec(path, Aopen, ORDWR, 0);
1073         c->channame = smalloc(strlen(path)+1);
1074         strcpy(c->channame, path);
1075         if(waserror()) {
1076                 cclose(c->chan);
1077                 c->chan = nil;
1078                 free(c->channame);
1079                 c->channame = nil;
1080                 nexterror();
1081         }
1082         kproc("convreader", convreader, c);
1083
1084         assert(c->reader == 0 && c->ref > 0);
1085         // after kproc in case it fails
1086         c->reader = 1;
1087         c->ref++;
1088
1089         poperror();
1090 }
1091
1092 static void
1093 convstats(Conv *c, int local, char *buf, int n)
1094 {
1095         Stats *stats;
1096         char *p, *ep;
1097         int i;
1098
1099         if(local) {
1100                 stats = &c->lstats;
1101         } else {
1102                 if(!waserror()) {
1103                         writecontrol(c, 0, 0, 1);
1104                         poperror();
1105                 }
1106                 stats = &c->rstats;
1107         }
1108
1109         qlock(c);
1110         p = buf;
1111         ep = buf + n;
1112         p += snprint(p, ep-p, "outPackets: %lud\n", stats->outPackets);
1113         p += snprint(p, ep-p, "outDataPackets: %lud\n", stats->outDataPackets);
1114         p += snprint(p, ep-p, "outDataBytes: %lud\n", stats->outDataBytes);
1115         p += snprint(p, ep-p, "outCompDataBytes: %lud\n", stats->outCompDataBytes);
1116         for(i=0; i<NCompStats; i++) {
1117                 if(stats->outCompStats[i] == 0)
1118                         continue;
1119                 p += snprint(p, ep-p, "outCompStats[%d]: %lud\n", i, stats->outCompStats[i]);
1120         }
1121         p += snprint(p, ep-p, "inPackets: %lud\n", stats->inPackets);
1122         p += snprint(p, ep-p, "inDataPackets: %lud\n", stats->inDataPackets);
1123         p += snprint(p, ep-p, "inDataBytes: %lud\n", stats->inDataBytes);
1124         p += snprint(p, ep-p, "inCompDataBytes: %lud\n", stats->inCompDataBytes);
1125         p += snprint(p, ep-p, "inMissing: %lud\n", stats->inMissing);
1126         p += snprint(p, ep-p, "inDup: %lud\n", stats->inDup);
1127         p += snprint(p, ep-p, "inReorder: %lud\n", stats->inReorder);
1128         p += snprint(p, ep-p, "inBadComp: %lud\n", stats->inBadComp);
1129         p += snprint(p, ep-p, "inBadAuth: %lud\n", stats->inBadAuth);
1130         p += snprint(p, ep-p, "inBadSeq: %lud\n", stats->inBadSeq);
1131         p += snprint(p, ep-p, "inBadOther: %lud\n", stats->inBadOther);
1132         USED(p);
1133         qunlock(c);
1134 }
1135
1136 // c is locked
1137 static void
1138 convack(Conv *c)
1139 {
1140         Block *b;
1141         AckPkt *ack;
1142         Stats *s;
1143         int i;
1144
1145         b = allocb(sizeof(AckPkt));
1146         ack = (AckPkt*)b->wp;
1147         b->wp += sizeof(AckPkt);
1148         s = &c->lstats;
1149         hnputl(ack->cseq, c->in.controlseq);
1150         hnputl(ack->outPackets, s->outPackets);
1151         hnputl(ack->outDataPackets, s->outDataPackets);
1152         hnputl(ack->outDataBytes, s->outDataBytes);
1153         hnputl(ack->outCompDataBytes, s->outCompDataBytes);
1154         for(i=0; i<NCompStats; i++)
1155                 hnputl(ack->outCompStats+i*4, s->outCompStats[i]);
1156         hnputl(ack->inPackets, s->inPackets);
1157         hnputl(ack->inDataPackets, s->inDataPackets);
1158         hnputl(ack->inDataBytes, s->inDataBytes);
1159         hnputl(ack->inCompDataBytes, s->inCompDataBytes);
1160         hnputl(ack->inMissing, s->inMissing);
1161         hnputl(ack->inDup, s->inDup);
1162         hnputl(ack->inReorder, s->inReorder);
1163         hnputl(ack->inBadComp, s->inBadComp);
1164         hnputl(ack->inBadAuth, s->inBadAuth);
1165         hnputl(ack->inBadSeq, s->inBadSeq);
1166         hnputl(ack->inBadOther, s->inBadOther);
1167         convoput(c, TControl, ControlAck, b);
1168 }
1169
1170
1171 // assume we hold lock for c
1172 static Block *
1173 conviput(Conv *c, Block *b, int control)
1174 {
1175         int type, subtype;
1176         ulong seq, seqwrap;
1177         long seqdiff;
1178         int pad;
1179
1180         c->lstats.inPackets++;
1181
1182         if(BLEN(b) < 4) {
1183                 c->lstats.inBadOther++;
1184                 freeb(b);
1185                 return nil;
1186         }
1187         
1188         type = b->rp[0] >> 4;
1189         subtype = b->rp[0] & 0xf;
1190         b->rp += 1;
1191         if(type == TConnect) {
1192                 conviconnect(c, subtype, b);
1193                 return nil;
1194         }
1195
1196         switch(c->state) {
1197         case CInit:
1198         case CDial:
1199                 c->lstats.inBadOther++;
1200                 convoconnect(c, ConReset, c->dialid, c->acceptid);
1201                 convsetstate(c, CClosed);
1202                 break;
1203         case CAccept:
1204         case CRemoteClose:
1205         case CLocalClose:
1206                 c->lstats.inBadOther++;
1207                 freeb(b);
1208                 return nil;
1209         }
1210
1211         seq = (b->rp[0]<<16) + (b->rp[1]<<8) + b->rp[2];
1212         b->rp += 3;
1213
1214         seqwrap = c->in.seqwrap;
1215         seqdiff = seq - c->in.seq;
1216         if(seqdiff < -(SeqMax*3/4)) {
1217                 seqwrap++;
1218                 seqdiff += SeqMax;
1219         } else if(seqdiff > SeqMax*3/4) {
1220                 seqwrap--;
1221                 seqdiff -= SeqMax;
1222         }
1223
1224         if(seqdiff <= 0) {
1225                 if(seqdiff <= -SeqWindow) {
1226 if(0)print("old sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff);
1227                         c->lstats.inBadSeq++;
1228                         freeb(b);
1229                         return nil;
1230                 }
1231
1232                 if(c->in.window & (1<<-seqdiff)) {
1233 if(0)print("dup sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff);
1234                         c->lstats.inDup++;
1235                         freeb(b);
1236                         return nil;
1237                 }
1238
1239                 c->lstats.inReorder++;
1240         }
1241
1242         // ok the sequence number looks ok
1243 if(0) print("coniput seq=%ulx\n", seq);
1244         if(c->in.auth != 0) {
1245                 if(!(*c->in.auth)(&c->in, b->rp-4, BLEN(b)+4)) {
1246 if(0)print("bad auth %ld\n", BLEN(b)+4);
1247                         c->lstats.inBadAuth++;
1248                         freeb(b);
1249                         return nil;
1250                 }
1251                 b->wp -= c->in.authlen;
1252         }
1253
1254         if(c->in.cipher != 0) {
1255                 if(!(*c->in.cipher)(&c->in, b->rp, BLEN(b))) {
1256 if(0)print("bad cipher\n");
1257                         c->lstats.inBadOther++;
1258                         freeb(b);
1259                         return nil;
1260                 }
1261                 b->rp += c->in.cipherivlen;
1262                 if(c->in.cipherblklen > 1) {
1263                         pad = b->wp[-1];
1264                         if(pad > BLEN(b)) {
1265 if(0)print("pad too big\n");
1266                                 c->lstats.inBadOther++;
1267                                 freeb(b);
1268                                 return nil;
1269                         }
1270                         b->wp -= pad;
1271                 }
1272         }
1273
1274         // ok the packet is good
1275         if(seqdiff > 0) {
1276                 while(seqdiff > 0 && c->in.window != 0) {
1277                         if((c->in.window & (1<<(SeqWindow-1))) == 0) {
1278                                 c->lstats.inMissing++;
1279                         }
1280                         c->in.window <<= 1;
1281                         seqdiff--;
1282                 }
1283                 if(seqdiff > 0) {
1284                         c->lstats.inMissing += seqdiff;
1285                         seqdiff = 0;
1286                 }
1287                 c->in.seq = seq;
1288                 c->in.seqwrap = seqwrap;
1289         }
1290         c->in.window |= 1<<-seqdiff;
1291         c->lastrecv = TK2SEC(m->ticks);
1292
1293         switch(type) {
1294         case TControl:
1295                 convicontrol(c, subtype, b);
1296                 return nil;
1297         case TData:
1298                 c->lstats.inDataPackets++;
1299                 c->lstats.inDataBytes += BLEN(b);
1300                 if(control)
1301                         break;
1302                 return b;
1303         case TCompData:
1304                 c->lstats.inDataPackets++;
1305                 c->lstats.inCompDataBytes += BLEN(b);
1306                 b = convicomp(c, subtype, seq, b);
1307                 if(b == nil) {
1308                         c->lstats.inBadComp++;
1309                         return nil;
1310                 }
1311                 c->lstats.inDataBytes += BLEN(b);
1312                 if(control)
1313                         break;
1314                 return b;
1315         }
1316 if(0)print("dropping packet id=%d: type=%d n=%ld control=%d\n", c->id, type, BLEN(b), control);
1317         c->lstats.inBadOther++;
1318         freeb(b);
1319         return nil;
1320 }
1321
1322 // assume hold conv lock
1323 static void
1324 conviconnect(Conv *c, int subtype, Block *b)
1325 {
1326         ulong dialid;
1327         ulong acceptid;
1328
1329         if(BLEN(b) != 8) {
1330                 freeb(b);
1331                 return;
1332         }
1333         dialid = nhgetl(b->rp);
1334         acceptid = nhgetl(b->rp + 4);
1335         freeb(b);
1336
1337 if(0)print("sdp: conviconnect: %s: %d %uld %uld\n", convstatename[c->state], subtype, dialid, acceptid);
1338
1339         if(subtype == ConReset) {
1340                 convsetstate(c, CClosed);
1341                 return;
1342         }
1343
1344         switch(c->state) {
1345         default:
1346                 panic("unknown state: %d", c->state);
1347         case CInit:
1348                 break;
1349         case CDial:
1350                 if(dialid != c->dialid)
1351                         goto Reset;
1352                 break;
1353         case CAccept:
1354         case COpen:
1355         case CLocalClose:
1356         case CRemoteClose:
1357                 if(dialid != c->dialid
1358                 || subtype != ConOpenRequest && acceptid != c->acceptid)
1359                         goto Reset;
1360                 break;
1361         case CClosed:
1362                 goto Reset;
1363         }
1364
1365         switch(subtype) {
1366         case ConOpenRequest:
1367                 switch(c->state) {
1368                 case CInit:
1369                         c->dialid = dialid;
1370                         convsetstate(c, CAccept);
1371                         return;
1372                 case CAccept:
1373                 case COpen:
1374                         // duplicate ConOpenRequest that we ignore
1375                         return;
1376                 }
1377                 break;
1378         case ConOpenAck:
1379                 switch(c->state) {
1380                 case CDial:
1381                         c->acceptid = acceptid;
1382                         convsetstate(c, COpen);
1383                         return;
1384                 case COpen:
1385                         // duplicate that we have to ack
1386                         convoconnect(c, ConOpenAckAck, acceptid, dialid);
1387                         return;
1388                 }
1389                 break;
1390         case ConOpenAckAck:
1391                 switch(c->state) {
1392                 case CAccept:
1393                         convsetstate(c, COpen);
1394                         return;
1395                 case COpen:
1396                 case CLocalClose:
1397                 case CRemoteClose:
1398                         // duplicate that we ignore
1399                         return;
1400                 }
1401                 break;
1402         case ConClose:
1403                 switch(c->state) {
1404                 case COpen:
1405                         convoconnect(c, ConCloseAck, dialid, acceptid);
1406                         convsetstate(c, CRemoteClose);
1407                         return;
1408                 case CRemoteClose:
1409                         // duplicate ConClose
1410                         convoconnect(c, ConCloseAck, dialid, acceptid);
1411                         return;
1412                 }
1413                 break;
1414         case ConCloseAck:
1415                 switch(c->state) {
1416                 case CLocalClose:
1417                         convsetstate(c, CClosed);
1418                         return;
1419                 }
1420                 break;
1421         }
1422 Reset:
1423         // invalid connection message - reset to sender
1424 if(1)print("sdp: invalid conviconnect - sending reset\n");
1425         convoconnect(c, ConReset, dialid, acceptid);
1426         convsetstate(c, CClosed);
1427 }
1428
1429 static void
1430 convicontrol(Conv *c, int subtype, Block *b)
1431 {
1432         ulong cseq;
1433         AckPkt *ack;
1434         int i;
1435
1436         if(BLEN(b) < 4)
1437                 return;
1438         cseq = nhgetl(b->rp);
1439         
1440         switch(subtype){
1441         case ControlMesg:
1442                 if(cseq == c->in.controlseq) {
1443 if(0)print("duplicate control packet: %ulx\n", cseq);
1444                         // duplicate control packet
1445                         freeb(b);
1446                         if(c->in.controlpkt == nil)
1447                                 convack(c);
1448                         return;
1449                 }
1450
1451                 if(cseq != c->in.controlseq+1)
1452                         return;
1453                 c->in.controlseq = cseq;
1454                 b->rp += 4;
1455                 if(BLEN(b) == 0) {
1456                         // just a ping
1457                         freeb(b);
1458                         convack(c);
1459                 } else {
1460                         c->in.controlpkt = b;
1461 if(0) print("recv %ld size=%ld\n", cseq, BLEN(b));
1462                         wakeup(&c->in.controlready);
1463                 }
1464                 return;
1465         case ControlAck:
1466                 if(cseq != c->out.controlseq)
1467                         return;
1468                 if(BLEN(b) < sizeof(AckPkt))
1469                         return;
1470                 ack = (AckPkt*)(b->rp);
1471                 c->rstats.outPackets = nhgetl(ack->outPackets);
1472                 c->rstats.outDataPackets = nhgetl(ack->outDataPackets);
1473                 c->rstats.outDataBytes = nhgetl(ack->outDataBytes);
1474                 c->rstats.outCompDataBytes = nhgetl(ack->outCompDataBytes);
1475                 for(i=0; i<NCompStats; i++)
1476                         c->rstats.outCompStats[i] = nhgetl(ack->outCompStats + 4*i);
1477                 c->rstats.inPackets = nhgetl(ack->inPackets);
1478                 c->rstats.inDataPackets = nhgetl(ack->inDataPackets);
1479                 c->rstats.inDataBytes = nhgetl(ack->inDataBytes);
1480                 c->rstats.inCompDataBytes = nhgetl(ack->inCompDataBytes);
1481                 c->rstats.inMissing = nhgetl(ack->inMissing);
1482                 c->rstats.inDup = nhgetl(ack->inDup);
1483                 c->rstats.inReorder = nhgetl(ack->inReorder);
1484                 c->rstats.inBadComp = nhgetl(ack->inBadComp);
1485                 c->rstats.inBadAuth = nhgetl(ack->inBadAuth);
1486                 c->rstats.inBadSeq = nhgetl(ack->inBadSeq);
1487                 c->rstats.inBadOther = nhgetl(ack->inBadOther);
1488                 freeb(b);
1489                 freeb(c->out.controlpkt);
1490                 c->out.controlpkt = nil;
1491                 c->timeout = c->lastrecv + KeepAlive;
1492                 wakeup(&c->out.controlready);
1493                 return;
1494         }
1495 }
1496
1497 static Block*
1498 convicomp(Conv *c, int subtype, ulong seq, Block *b)
1499 {
1500         if(c->in.comp == nil) {
1501                 freeb(b);
1502                 return nil;
1503         }
1504         if(!(*c->in.comp)(c, subtype, seq, &b))
1505                 return nil;
1506         return b;
1507 }
1508
1509 // c is locked
1510 static void
1511 convwriteblock(Conv *c, Block *b)
1512 {
1513         // simulated errors
1514         if(c->drop && nrand(c->drop) == 0)
1515                 return;
1516
1517         if(waserror()) {
1518                 convsetstate(c, CClosed);
1519                 nexterror();
1520         }
1521         devtab[c->chan->type]->bwrite(c->chan, b, 0);
1522         poperror();
1523 }
1524
1525
1526 // assume hold conv lock
1527 static void
1528 convoput(Conv *c, int type, int subtype, Block *b)
1529 {
1530         int pad;
1531         
1532         c->lstats.outPackets++;
1533         /* Make room for sdp trailer */
1534         if(c->out.cipherblklen > 1)
1535                 pad = c->out.cipherblklen - (BLEN(b) + c->out.cipherivlen) % c->out.cipherblklen;
1536         else
1537                 pad = 0;
1538
1539         b = padblock(b, -(pad+c->out.authlen));
1540
1541         if(pad) {
1542                 memset(b->wp, 0, pad-1);
1543                 b->wp[pad-1] = pad;
1544                 b->wp += pad;
1545         }
1546
1547         /* Make space to fit sdp header */
1548         b = padblock(b, 4 + c->out.cipherivlen);
1549         b->rp[0] = (type << 4) | subtype;
1550         c->out.seq++;
1551         if(c->out.seq == (1<<24)) {
1552                 c->out.seq = 0;
1553                 c->out.seqwrap++;
1554         }
1555         b->rp[1] = c->out.seq>>16;
1556         b->rp[2] = c->out.seq>>8;
1557         b->rp[3] = c->out.seq;
1558         
1559         if(c->out.cipher)
1560                 (*c->out.cipher)(&c->out, b->rp+4, BLEN(b)-4);
1561
1562         // auth
1563         if(c->out.auth) {
1564                 b->wp += c->out.authlen;
1565                 (*c->out.auth)(&c->out, b->rp, BLEN(b));
1566         }
1567         
1568         convwriteblock(c, b);
1569 }
1570
1571 // assume hold conv lock
1572 static void
1573 convoconnect(Conv *c, int op, ulong dialid, ulong acceptid)
1574 {
1575         Block *b;
1576
1577         c->lstats.outPackets++;
1578         assert(c->chan != nil);
1579         b = allocb(9);
1580         b->wp[0] = (TConnect << 4) | op;
1581         hnputl(b->wp+1, dialid);
1582         hnputl(b->wp+5, acceptid);
1583         b->wp += 9;
1584
1585         if(!waserror()) {
1586                 convwriteblock(c, b);
1587                 poperror();
1588         }
1589 }
1590
1591 static Block *
1592 convreadblock(Conv *c, int n)
1593 {
1594         Block *b;
1595         Chan *ch;
1596
1597         qlock(&c->readlk);
1598         if(waserror()) {
1599                 c->readproc = nil;
1600                 qunlock(&c->readlk);
1601                 nexterror();
1602         }
1603         qlock(c);
1604         if(c->state == CClosed) {
1605                 qunlock(c);
1606                 error("closed");
1607         }
1608         c->readproc = up;
1609         ch = c->chan;
1610         assert(c->ref > 0);
1611         qunlock(c);
1612
1613         b = devtab[ch->type]->bread(ch, n, 0);
1614         c->readproc = nil;
1615         poperror();
1616         qunlock(&c->readlk);
1617
1618         return b;
1619 }
1620
1621 static int
1622 readready(void *a)
1623 {
1624         Conv *c = a;
1625
1626         return c->in.controlpkt != nil || (c->state == CClosed) || (c->state == CRemoteClose);
1627 }
1628
1629 static Block *
1630 readcontrol(Conv *c, int n)
1631 {
1632         Block *b;
1633
1634         USED(n);
1635
1636         qlock(&c->in.controllk);
1637         if(waserror()) {
1638                 qunlock(&c->in.controllk);
1639                 nexterror();
1640         }
1641         qlock(c);       // this lock is not held during the sleep below
1642
1643         for(;;) {
1644                 if(c->chan == nil || c->state == CClosed) {
1645                         qunlock(c);
1646 if(0)print("readcontrol: return error - state = %s\n", convstatename[c->state]);
1647                         error("conversation closed");
1648                 }
1649
1650                 if(c->in.controlpkt != nil)
1651                         break;
1652
1653                 if(c->state == CRemoteClose) {
1654                         qunlock(c);
1655 if(0)print("readcontrol: return nil - state = %s\n", convstatename[c->state]);
1656                         poperror();
1657                         return nil;
1658                 }
1659                 qunlock(c);
1660                 sleep(&c->in.controlready, readready, c);
1661                 qlock(c);
1662         }
1663
1664         convack(c);
1665
1666         b = c->in.controlpkt;
1667         c->in.controlpkt = nil;
1668         qunlock(c);
1669         poperror();
1670         qunlock(&c->in.controllk);
1671         return b;
1672 }
1673
1674
1675 static int
1676 writeready(void *a)
1677 {
1678         Conv *c = a;
1679
1680         return c->out.controlpkt == nil || (c->state == CClosed) || (c->state == CRemoteClose);
1681 }
1682
1683 // c is locked
1684 static void
1685 writewait(Conv *c)
1686 {
1687         for(;;) {
1688                 if(c->state == CFree || c->state == CInit ||
1689                    c->state == CClosed || c->state == CRemoteClose)
1690                         error("conversation closed");
1691
1692                 if(c->state == COpen && c->out.controlpkt == nil)
1693                         break;
1694
1695                 qunlock(c);
1696                 if(waserror()) {
1697                         qlock(c);
1698                         nexterror();
1699                 }
1700                 sleep(&c->out.controlready, writeready, c);
1701                 poperror();
1702                 qlock(c);
1703         }
1704 }
1705
1706 static void
1707 writecontrol(Conv *c, void *p, int n, int wait)
1708 {
1709         Block *b;
1710
1711         qlock(&c->out.controllk);
1712         qlock(c);
1713         if(waserror()) {
1714                 qunlock(c);
1715                 qunlock(&c->out.controllk);
1716                 nexterror();
1717         }
1718         writewait(c);
1719         b = allocb(4+n);
1720         c->out.controlseq++;
1721         hnputl(b->wp, c->out.controlseq);
1722         memmove(b->wp+4, p, n);
1723         b->wp += 4+n;
1724         c->out.controlpkt = b;
1725         convretryinit(c);
1726         convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
1727         if(wait)
1728                 writewait(c);
1729         poperror();
1730         qunlock(c);
1731         qunlock(&c->out.controllk);
1732 }
1733
1734 static Block *
1735 readdata(Conv *c, int n)
1736 {
1737         Block *b;
1738         int nn;
1739
1740         for(;;) {
1741
1742                 // some slack for tunneling overhead
1743                 nn = n + 100;
1744
1745                 // make sure size is big enough for control messages
1746                 if(nn < 1000)
1747                         nn = 1000;
1748                 b = convreadblock(c, nn);
1749                 if(b == nil)
1750                         return nil;
1751                 qlock(c);
1752                 if(waserror()) {
1753                         qunlock(c);
1754                         return nil;
1755                 }
1756                 b = conviput(c, b, 0);
1757                 poperror();
1758                 qunlock(c);
1759                 if(b != nil) {
1760                         if(BLEN(b) > n)
1761                                 b->wp = b->rp + n;
1762                         return b;
1763                 }
1764         }
1765 }
1766
1767 static long
1768 writedata(Conv *c, Block *b)
1769 {
1770         int n;
1771         ulong seq;
1772         int subtype;
1773
1774         qlock(c);
1775         if(waserror()) {
1776                 qunlock(c);
1777                 nexterror();
1778         }
1779
1780         if(c->state != COpen) {
1781                 freeb(b);
1782                 error("conversation not open");
1783         }
1784
1785         n = BLEN(b);
1786         c->lstats.outDataPackets++;
1787         c->lstats.outDataBytes += n;
1788
1789         if(c->out.comp != nil) {
1790                 // must generate same value as convoput
1791                 seq = (c->out.seq + 1) & (SeqMax-1);
1792
1793                 subtype = (*c->out.comp)(c, 0, seq, &b);
1794                 c->lstats.outCompDataBytes += BLEN(b);
1795                 convoput(c, TCompData, subtype, b);
1796         } else
1797                 convoput(c, TData, 0, b);
1798
1799         poperror();
1800         qunlock(c);
1801         return n;
1802 }
1803
1804 static void
1805 convreader(void *a)
1806 {
1807         Conv *c = a;
1808         Block *b;
1809
1810         qlock(c);
1811         assert(c->reader == 1);
1812         while(c->dataopen == 0 && c->state != CClosed) {
1813                 qunlock(c);
1814                 b = nil;
1815                 if(!waserror()) {
1816                         b = convreadblock(c, 2000);
1817                         poperror();
1818                 }
1819                 qlock(c);
1820                 if(b == nil) {
1821                         if(strcmp(up->errstr, Eintr) != 0) {
1822                                 convsetstate(c, CClosed);
1823                                 break;
1824                         }
1825                 } else if(!waserror()) {
1826                         conviput(c, b, 1);
1827                         poperror();
1828                 }
1829         }
1830         c->reader = 0;
1831         convderef(c);
1832         qunlock(c);
1833         pexit("hangup", 1);
1834 }
1835
1836
1837 /* ciphers, authenticators, and compressors  */
1838
1839 static void
1840 setalg(Conv *c, char *name, Algorithm *alg, Algorithm **p)
1841 {
1842         for(; alg->name; alg++)
1843                 if(strcmp(name, alg->name) == 0)
1844                         break;
1845         if(alg->name == nil)
1846                 error("unknown algorithm");
1847
1848         *p = alg;
1849         alg->init(c);
1850 }
1851
1852 static void
1853 setsecret(OneWay *ow, char *secret)
1854 {
1855         char *p;
1856         int i, c;
1857         
1858         i = 0;
1859         memset(ow->secret, 0, sizeof(ow->secret));
1860         for(p=secret; *p; p++) {
1861                 if(i >= sizeof(ow->secret)*2)
1862                         break;
1863                 c = *p;
1864                 if(c >= '0' && c <= '9')
1865                         c -= '0';
1866                 else if(c >= 'a' && c <= 'f')
1867                         c -= 'a'-10;
1868                 else if(c >= 'A' && c <= 'F')
1869                         c -= 'A'-10;
1870                 else
1871                         error("bad character in secret");
1872                 if((i&1) == 0)
1873                         c <<= 4;
1874                 ow->secret[i>>1] |= c;
1875                 i++;
1876         }
1877 }
1878
1879 static void
1880 setkey(uchar *key, int n, OneWay *ow, char *prefix)
1881 {
1882         uchar ibuf[SHA1dlen], obuf[MD5dlen], salt[10];
1883         int i, round = 0;
1884
1885         while(n > 0){
1886                 for(i=0; i<round+1; i++)
1887                         salt[i] = 'A'+round;
1888                 sha1((uchar*)prefix, strlen(prefix), ibuf, sha1(salt, round+1, nil, nil));
1889                 md5(ibuf, SHA1dlen, obuf, md5(ow->secret, sizeof(ow->secret), nil, nil));
1890                 i = (n<MD5dlen) ? n : MD5dlen;
1891                 memmove(key, obuf, i);
1892                 key += i;
1893                 n -= i;
1894                 if(++round > sizeof salt)
1895                         panic("setkey: you ask too much");
1896         }
1897 }
1898
1899 static void
1900 cipherfree(Conv *c)
1901 {
1902         if(c->in.cipherstate) {
1903                 free(c->in.cipherstate);
1904                 c->in.cipherstate = nil;
1905         }
1906         if(c->out.cipherstate) {
1907                 free(c->out.cipherstate);
1908                 c->out.cipherstate = nil;
1909         }
1910         c->in.cipher = nil;
1911         c->in.cipherblklen = 0;
1912         c->out.cipherblklen = 0;
1913         c->in.cipherivlen = 0;
1914         c->out.cipherivlen = 0;
1915 }
1916
1917 static void
1918 authfree(Conv *c)
1919 {
1920         if(c->in.authstate) {
1921                 free(c->in.authstate);
1922                 c->in.authstate = nil;
1923         }
1924         if(c->out.authstate) {
1925                 free(c->out.authstate);
1926                 c->out.authstate = nil;
1927         }
1928         c->in.auth = nil;
1929         c->in.authlen = 0;
1930         c->out.authlen = 0;
1931 }
1932
1933 static void
1934 compfree(Conv *c)
1935 {
1936         if(c->in.compstate) {
1937                 free(c->in.compstate);
1938                 c->in.compstate = nil;
1939         }
1940         if(c->out.compstate) {
1941                 free(c->out.compstate);
1942                 c->out.compstate = nil;
1943         }
1944         c->in.comp = nil;
1945 }
1946
1947 static void
1948 nullcipherinit(Conv *c)
1949 {
1950         cipherfree(c);
1951 }
1952
1953 static int
1954 desencrypt(OneWay *ow, uchar *p, int n)
1955 {
1956         uchar *pp, *ip, *eip, *ep;
1957         DESstate *ds = ow->cipherstate;
1958
1959         if(n < 8 || (n & 0x7 != 0))
1960                 return 0;
1961         ep = p + n;
1962         memmove(p, ds->ivec, 8);
1963         for(p += 8; p < ep; p += 8){
1964                 pp = p;
1965                 ip = ds->ivec;
1966                 for(eip = ip+8; ip < eip; )
1967                         *pp++ ^= *ip++;
1968                 block_cipher(ds->expanded, p, 0);
1969                 memmove(ds->ivec, p, 8);
1970         }
1971         return 1;
1972 }
1973
1974 static int
1975 desdecrypt(OneWay *ow, uchar *p, int n)
1976 {
1977         uchar tmp[8];
1978         uchar *tp, *ip, *eip, *ep;
1979         DESstate *ds = ow->cipherstate;
1980
1981         if(n < 8 || (n & 0x7 != 0))
1982                 return 0;
1983         ep = p + n;
1984         memmove(ds->ivec, p, 8);
1985         p += 8;
1986         while(p < ep){
1987                 memmove(tmp, p, 8);
1988                 block_cipher(ds->expanded, p, 1);
1989                 tp = tmp;
1990                 ip = ds->ivec;
1991                 for(eip = ip+8; ip < eip; ){
1992                         *p++ ^= *ip;
1993                         *ip++ = *tp++;
1994                 }
1995         }
1996         return 1;
1997 }
1998
1999 static void
2000 descipherinit(Conv *c)
2001 {
2002         uchar key[8];
2003         uchar ivec[8];
2004         int i;
2005         int n = c->cipher->keylen;
2006
2007         cipherfree(c);
2008         
2009         if(n > sizeof(key))
2010                 n = sizeof(key);
2011
2012         /* in */
2013         memset(key, 0, sizeof(key));
2014         setkey(key, n, &c->in, "cipher");
2015         memset(ivec, 0, sizeof(ivec));
2016         c->in.cipherblklen = 8;
2017         c->in.cipherivlen = 8;
2018         c->in.cipher = desdecrypt;
2019         c->in.cipherstate = smalloc(sizeof(DESstate));
2020         setupDESstate(c->in.cipherstate, key, ivec);
2021         
2022         /* out */
2023         memset(key, 0, sizeof(key));
2024         setkey(key, n, &c->out, "cipher");
2025         for(i=0; i<8; i++)
2026                 ivec[i] = nrand(256);
2027         c->out.cipherblklen = 8;
2028         c->out.cipherivlen = 8;
2029         c->out.cipher = desencrypt;
2030         c->out.cipherstate = smalloc(sizeof(DESstate));
2031         setupDESstate(c->out.cipherstate, key, ivec);
2032 }
2033
2034 static int
2035 rc4encrypt(OneWay *ow, uchar *p, int n)
2036 {
2037         CipherRc4 *cr = ow->cipherstate;
2038
2039         if(n < 4)
2040                 return 0;
2041
2042         hnputl(p, cr->cseq);
2043         p += 4;
2044         n -= 4;
2045         rc4(&cr->current, p, n);
2046         cr->cseq += n;
2047         return 1;
2048 }
2049
2050 static int
2051 rc4decrypt(OneWay *ow, uchar *p, int n)
2052 {
2053         CipherRc4 *cr = ow->cipherstate;
2054         RC4state tmpstate;
2055         ulong seq;
2056         long d, dd;
2057
2058         if(n < 4)
2059                 return 0;
2060
2061         seq = nhgetl(p);
2062         p += 4;
2063         n -= 4;
2064         d = seq-cr->cseq;
2065         if(d == 0) {
2066                 rc4(&cr->current, p, n);
2067                 cr->cseq += n;
2068                 if(cr->ovalid) {
2069                         dd = cr->cseq - cr->lgseq;
2070                         if(dd > RC4back)
2071                                 cr->ovalid = 0;
2072                 }
2073         } else if(d > 0) {
2074 //print("missing packet: %uld %ld\n", seq, d);
2075                 // this link is hosed 
2076                 if(d > RC4forward)
2077                         return 0;
2078                 cr->lgseq = seq;
2079                 if(!cr->ovalid) {
2080                         cr->ovalid = 1;
2081                         cr->oseq = cr->cseq;
2082                         memmove(&cr->old, &cr->current, sizeof(RC4state));
2083                 }
2084                 rc4skip(&cr->current, d);
2085                 rc4(&cr->current, p, n);
2086                 cr->cseq = seq+n;
2087         } else {
2088 //print("reordered packet: %uld %ld\n", seq, d);
2089                 dd = seq - cr->oseq;
2090                 if(!cr->ovalid || -d > RC4back || dd < 0)
2091                         return 0;
2092                 memmove(&tmpstate, &cr->old, sizeof(RC4state));
2093                 rc4skip(&tmpstate, dd);
2094                 rc4(&tmpstate, p, n);
2095                 return 1;
2096         }
2097
2098         // move old state up
2099         if(cr->ovalid) {
2100                 dd = cr->cseq - RC4back - cr->oseq;
2101                 if(dd > 0) {
2102                         rc4skip(&cr->old, dd);
2103                         cr->oseq += dd;
2104                 }
2105         }
2106
2107         return 1;
2108 }
2109
2110 static void
2111 rc4cipherinit(Conv *c)
2112 {
2113         uchar key[32];
2114         CipherRc4 *cr;
2115         int n;
2116
2117         cipherfree(c);
2118
2119         n = c->cipher->keylen;
2120         if(n > sizeof(key))
2121                 n = sizeof(key);
2122
2123         /* in */
2124         memset(key, 0, sizeof(key));
2125         setkey(key, n, &c->in, "cipher");
2126         c->in.cipherblklen = 1;
2127         c->in.cipherivlen = 4;
2128         c->in.cipher = rc4decrypt;
2129         cr = smalloc(sizeof(CipherRc4));
2130         memset(cr, 0, sizeof(*cr));
2131         setupRC4state(&cr->current, key, n);
2132         c->in.cipherstate = cr;
2133
2134         /* out */
2135         memset(key, 0, sizeof(key));
2136         setkey(key, n, &c->out, "cipher");
2137         c->out.cipherblklen = 1;
2138         c->out.cipherivlen = 4;
2139         c->out.cipher = rc4encrypt;
2140         cr = smalloc(sizeof(CipherRc4));
2141         memset(cr, 0, sizeof(*cr));
2142         setupRC4state(&cr->current, key, n);
2143         c->out.cipherstate = cr;
2144 }
2145
2146 static void
2147 nullauthinit(Conv *c)
2148 {
2149         authfree(c);
2150 }
2151
2152 static void
2153 shaauthinit(Conv *c)
2154 {
2155         authfree(c);
2156 }
2157
2158 static void
2159 seanq_hmac_md5(uchar hash[MD5dlen], ulong wrap, uchar *t, long tlen, uchar *key, long klen)
2160 {
2161         uchar ipad[65], opad[65], wbuf[4];
2162         int i;
2163         DigestState *digest;
2164         uchar innerhash[MD5dlen];
2165
2166         for(i=0; i<64; i++){
2167                 ipad[i] = 0x36;
2168                 opad[i] = 0x5c;
2169         }
2170         ipad[64] = opad[64] = 0;
2171         for(i=0; i<klen; i++){
2172                 ipad[i] ^= key[i];
2173                 opad[i] ^= key[i];
2174         }
2175         hnputl(wbuf, wrap);
2176         digest = md5(ipad, 64, nil, nil);
2177         digest = md5(wbuf, sizeof(wbuf), nil, digest);
2178         md5(t, tlen, innerhash, digest);
2179         digest = md5(opad, 64, nil, nil);
2180         md5(innerhash, MD5dlen, hash, digest);
2181 }
2182
2183 static int
2184 md5auth(OneWay *ow, uchar *t, int tlen)
2185 {
2186         uchar hash[MD5dlen];
2187         int r;
2188
2189         if(tlen < ow->authlen)
2190                 return 0;
2191         tlen -= ow->authlen;
2192
2193         memset(hash, 0, MD5dlen);
2194         seanq_hmac_md5(hash, ow->seqwrap, t, tlen, (uchar*)ow->authstate, 16);
2195         r = memcmp(t+tlen, hash, ow->authlen) == 0;
2196         memmove(t+tlen, hash, ow->authlen);
2197         return r;
2198 }
2199
2200 static void
2201 md5authinit(Conv *c)
2202 {
2203         int keylen;
2204
2205         authfree(c);
2206
2207         keylen = c->auth->keylen;
2208         if(keylen > 16)
2209                 keylen = 16;
2210
2211         /* in */
2212         c->in.authstate = smalloc(16);
2213         memset(c->in.authstate, 0, 16);
2214         setkey(c->in.authstate, keylen, &c->in, "auth");
2215         c->in.authlen = 12;
2216         c->in.auth = md5auth;
2217         
2218         /* out */
2219         c->out.authstate = smalloc(16);
2220         memset(c->out.authstate, 0, 16);
2221         setkey(c->out.authstate, keylen, &c->out, "auth");
2222         c->out.authlen = 12;
2223         c->out.auth = md5auth;
2224 }
2225
2226 static void
2227 nullcompinit(Conv *c)
2228 {
2229         compfree(c);
2230 }
2231
2232 static int
2233 thwackcomp(Conv *c, int, ulong seq, Block **bp)
2234 {
2235         Block *b, *bb;
2236         int nn;
2237         ulong ackseq;
2238         uchar mask;
2239
2240         // add ack info
2241         b = padblock(*bp, 4);
2242
2243         ackseq = unthwackstate(c->in.compstate, &mask);
2244         b->rp[0] = mask;
2245         b->rp[1] = ackseq>>16;
2246         b->rp[2] = ackseq>>8;
2247         b->rp[3] = ackseq;
2248
2249         bb = allocb(BLEN(b));
2250         nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq, c->lstats.outCompStats);
2251         if(nn < 0) {
2252                 freeb(bb);
2253                 *bp = b;
2254                 return ThwackU;
2255         } else {
2256                 bb->wp += nn;
2257                 freeb(b);
2258                 *bp = bb;
2259                 return ThwackC;
2260         }
2261 }
2262
2263 static int
2264 thwackuncomp(Conv *c, int subtype, ulong seq, Block **bp)
2265 {
2266         Block *b, *bb;
2267         ulong mask;
2268         ulong mseq;
2269         int n;
2270
2271         switch(subtype) {
2272         default:
2273                 return 0;
2274         case ThwackU:
2275                 b = *bp;
2276                 mask = b->rp[0];
2277                 mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2278                 b->rp += 4;
2279                 thwackack(c->out.compstate, mseq, mask);
2280                 return 1;
2281         case ThwackC:
2282                 bb = *bp;
2283                 b = allocb(ThwMaxBlock);
2284                 n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq);
2285                 freeb(bb);
2286                 *bp = nil;
2287                 if(n < 0) {
2288 if(0)print("unthwack failed: %d\n", n);
2289                         freeb(b);
2290                         return 0;
2291                 }
2292                 b->wp += n;
2293                 mask = b->rp[0];
2294                 mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2295                 thwackack(c->out.compstate, mseq, mask);
2296                 b->rp += 4;
2297                 *bp = b;
2298                 return 1;
2299         }
2300 }
2301
2302 static void
2303 thwackcompinit(Conv *c)
2304 {
2305         compfree(c);
2306
2307         c->in.compstate = malloc(sizeof(Unthwack));
2308         if(c->in.compstate == nil)
2309                 error(Enomem);
2310         unthwackinit(c->in.compstate);
2311         c->out.compstate = malloc(sizeof(Thwack));
2312         if(c->out.compstate == nil)
2313                 error(Enomem);
2314         thwackinit(c->out.compstate);
2315         c->in.comp = thwackuncomp;
2316         c->out.comp = thwackcomp;
2317 }