]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devsdp.c
kernel: make allocb() wait instead of panic() when possible
[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         for(;;) {
912                 tsleep(&up->sleep, return0, 0, 1000);
913                 sec = TK2SEC(m->ticks);
914                 qlock(sdp);
915                 for(i=0; i<sdp->nconv; i++) {
916                         c = sdp->conv[i];
917                         if(c->ref == 0)
918                                 continue;
919                         qunlock(sdp);
920                         qlock(c);
921                         if(c->ref > 0 && !waserror()) {
922                                 convtimer(c, sec);
923                                 poperror();
924                         }
925                         qunlock(c);
926                         qlock(sdp);
927                 }
928                 qunlock(sdp);
929         }
930 }
931
932 Dev sdpdevtab = {
933         'E',
934         "sdp",
935
936         devreset,
937         sdpinit,
938         devshutdown,
939         sdpattach,
940         sdpwalk,
941         sdpstat,
942         sdpopen,
943         devcreate,
944         sdpclose,
945         sdpread,
946         devbread,
947         sdpwrite,
948         devbwrite,
949         devremove,
950         devwstat,
951 };
952
953 // assume hold lock on c
954 static void
955 convsetstate(Conv *c, int state)
956 {
957
958 if(0)print("convsetstate %d: %s -> %s\n", c->id, convstatename[c->state], convstatename[state]);
959
960         switch(state) {
961         default:
962                 panic("setstate: bad state: %d", state);
963         case CDial:
964                 assert(c->state == CInit);
965                 c->dialid = (rand()<<16) + rand();
966                 convretryinit(c);
967                 convoconnect(c, ConOpenRequest, c->dialid, 0);
968                 break;
969         case CAccept:
970                 assert(c->state == CInit);
971                 c->acceptid = (rand()<<16) + rand();
972                 convretryinit(c);
973                 convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
974                 break;
975         case COpen:
976                 assert(c->state == CDial || c->state == CAccept);
977                 c->lastrecv = TK2SEC(m->ticks);
978                 if(c->state == CDial) {
979                         convretryinit(c);
980                         convoconnect(c, ConOpenAckAck, c->dialid, c->acceptid);
981                         hnputl(c->in.secret, c->acceptid);
982                         hnputl(c->in.secret+4, c->dialid);
983                         hnputl(c->out.secret, c->dialid);
984                         hnputl(c->out.secret+4, c->acceptid);
985                 } else {
986                         hnputl(c->in.secret, c->dialid);
987                         hnputl(c->in.secret+4, c->acceptid);
988                         hnputl(c->out.secret, c->acceptid);
989                         hnputl(c->out.secret+4, c->dialid);
990                 }
991                 setalg(c, "hmac_md5_96", authalg, &c->auth);
992                 break;
993         case CLocalClose:
994                 assert(c->state == CAccept || c->state == COpen);
995                 convretryinit(c);
996                 convoconnect(c, ConClose, c->dialid, c->acceptid);
997                 break;
998         case CRemoteClose:
999                 wakeup(&c->in.controlready);
1000                 wakeup(&c->out.controlready);
1001                 break;
1002         case CClosed:
1003                 wakeup(&c->in.controlready);
1004                 wakeup(&c->out.controlready);
1005                 if(c->readproc)
1006                         postnote(c->readproc, 1, "interrupt", 0);
1007                 if(c->state != CClosed)
1008                         convderef(c);
1009                 break;
1010         }
1011         c->state = state;
1012 }
1013
1014
1015 //assumes c is locked
1016 static void
1017 convderef(Conv *c)
1018 {
1019         c->ref--;
1020         if(c->ref > 0) {
1021                 return;
1022         }
1023         assert(c->ref == 0);
1024         assert(c->dataopen == 0);
1025         assert(c->controlopen == 0);
1026 if(0)print("convderef: %d: ref == 0!\n", c->id);
1027         c->state = CFree;
1028         if(c->chan) {   
1029                 cclose(c->chan);
1030                 c->chan = nil;
1031         }
1032         if(c->channame) {
1033                 free(c->channame);
1034                 c->channame = nil;
1035         }
1036         c->cipher = nil;
1037         c->auth = nil;
1038         c->comp = nil;
1039         strcpy(c->owner, "network");
1040         c->perm = 0660;
1041         c->dialid = 0;
1042         c->acceptid = 0;
1043         c->timeout = 0;
1044         c->retries = 0;
1045         c->drop = 0;
1046         onewaycleanup(&c->in);
1047         onewaycleanup(&c->out);
1048         memset(&c->lstats, 0, sizeof(Stats));
1049         memset(&c->rstats, 0, sizeof(Stats));
1050 }
1051
1052 static void
1053 onewaycleanup(OneWay *ow)
1054 {
1055         if(ow->controlpkt)
1056                 freeb(ow->controlpkt);
1057         if(ow->authstate)
1058                 free(ow->authstate);
1059         if(ow->cipherstate)
1060                 free(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         if(c->in.authstate) {
1922                 free(c->in.authstate);
1923                 c->in.authstate = nil;
1924         }
1925         if(c->out.authstate) {
1926                 free(c->out.authstate);
1927                 c->out.authstate = nil;
1928         }
1929         c->in.auth = nil;
1930         c->in.authlen = 0;
1931         c->out.authlen = 0;
1932 }
1933
1934 static void
1935 compfree(Conv *c)
1936 {
1937         if(c->in.compstate) {
1938                 free(c->in.compstate);
1939                 c->in.compstate = nil;
1940         }
1941         if(c->out.compstate) {
1942                 free(c->out.compstate);
1943                 c->out.compstate = nil;
1944         }
1945         c->in.comp = nil;
1946 }
1947
1948 static void
1949 nullcipherinit(Conv *c)
1950 {
1951         cipherfree(c);
1952 }
1953
1954 static int
1955 desencrypt(OneWay *ow, uchar *p, int n)
1956 {
1957         uchar *pp, *ip, *eip, *ep;
1958         DESstate *ds = ow->cipherstate;
1959
1960         if(n < 8 || (n & 0x7 != 0))
1961                 return 0;
1962         ep = p + n;
1963         memmove(p, ds->ivec, 8);
1964         for(p += 8; p < ep; p += 8){
1965                 pp = p;
1966                 ip = ds->ivec;
1967                 for(eip = ip+8; ip < eip; )
1968                         *pp++ ^= *ip++;
1969                 block_cipher(ds->expanded, p, 0);
1970                 memmove(ds->ivec, p, 8);
1971         }
1972         return 1;
1973 }
1974
1975 static int
1976 desdecrypt(OneWay *ow, uchar *p, int n)
1977 {
1978         uchar tmp[8];
1979         uchar *tp, *ip, *eip, *ep;
1980         DESstate *ds = ow->cipherstate;
1981
1982         if(n < 8 || (n & 0x7 != 0))
1983                 return 0;
1984         ep = p + n;
1985         memmove(ds->ivec, p, 8);
1986         p += 8;
1987         while(p < ep){
1988                 memmove(tmp, p, 8);
1989                 block_cipher(ds->expanded, p, 1);
1990                 tp = tmp;
1991                 ip = ds->ivec;
1992                 for(eip = ip+8; ip < eip; ){
1993                         *p++ ^= *ip;
1994                         *ip++ = *tp++;
1995                 }
1996         }
1997         return 1;
1998 }
1999
2000 static void
2001 descipherinit(Conv *c)
2002 {
2003         uchar key[8];
2004         uchar ivec[8];
2005         int i;
2006         int n = c->cipher->keylen;
2007
2008         cipherfree(c);
2009         
2010         if(n > sizeof(key))
2011                 n = sizeof(key);
2012
2013         /* in */
2014         memset(key, 0, sizeof(key));
2015         setkey(key, n, &c->in, "cipher");
2016         memset(ivec, 0, sizeof(ivec));
2017         c->in.cipherblklen = 8;
2018         c->in.cipherivlen = 8;
2019         c->in.cipher = desdecrypt;
2020         c->in.cipherstate = smalloc(sizeof(DESstate));
2021         setupDESstate(c->in.cipherstate, key, ivec);
2022         
2023         /* out */
2024         memset(key, 0, sizeof(key));
2025         setkey(key, n, &c->out, "cipher");
2026         for(i=0; i<8; i++)
2027                 ivec[i] = nrand(256);
2028         c->out.cipherblklen = 8;
2029         c->out.cipherivlen = 8;
2030         c->out.cipher = desencrypt;
2031         c->out.cipherstate = smalloc(sizeof(DESstate));
2032         setupDESstate(c->out.cipherstate, key, ivec);
2033 }
2034
2035 static int
2036 rc4encrypt(OneWay *ow, uchar *p, int n)
2037 {
2038         CipherRc4 *cr = ow->cipherstate;
2039
2040         if(n < 4)
2041                 return 0;
2042
2043         hnputl(p, cr->cseq);
2044         p += 4;
2045         n -= 4;
2046         rc4(&cr->current, p, n);
2047         cr->cseq += n;
2048         return 1;
2049 }
2050
2051 static int
2052 rc4decrypt(OneWay *ow, uchar *p, int n)
2053 {
2054         CipherRc4 *cr = ow->cipherstate;
2055         RC4state tmpstate;
2056         ulong seq;
2057         long d, dd;
2058
2059         if(n < 4)
2060                 return 0;
2061
2062         seq = nhgetl(p);
2063         p += 4;
2064         n -= 4;
2065         d = seq-cr->cseq;
2066         if(d == 0) {
2067                 rc4(&cr->current, p, n);
2068                 cr->cseq += n;
2069                 if(cr->ovalid) {
2070                         dd = cr->cseq - cr->lgseq;
2071                         if(dd > RC4back)
2072                                 cr->ovalid = 0;
2073                 }
2074         } else if(d > 0) {
2075 //print("missing packet: %uld %ld\n", seq, d);
2076                 // this link is hosed 
2077                 if(d > RC4forward)
2078                         return 0;
2079                 cr->lgseq = seq;
2080                 if(!cr->ovalid) {
2081                         cr->ovalid = 1;
2082                         cr->oseq = cr->cseq;
2083                         memmove(&cr->old, &cr->current, sizeof(RC4state));
2084                 }
2085                 rc4skip(&cr->current, d);
2086                 rc4(&cr->current, p, n);
2087                 cr->cseq = seq+n;
2088         } else {
2089 //print("reordered packet: %uld %ld\n", seq, d);
2090                 dd = seq - cr->oseq;
2091                 if(!cr->ovalid || -d > RC4back || dd < 0)
2092                         return 0;
2093                 memmove(&tmpstate, &cr->old, sizeof(RC4state));
2094                 rc4skip(&tmpstate, dd);
2095                 rc4(&tmpstate, p, n);
2096                 return 1;
2097         }
2098
2099         // move old state up
2100         if(cr->ovalid) {
2101                 dd = cr->cseq - RC4back - cr->oseq;
2102                 if(dd > 0) {
2103                         rc4skip(&cr->old, dd);
2104                         cr->oseq += dd;
2105                 }
2106         }
2107
2108         return 1;
2109 }
2110
2111 static void
2112 rc4cipherinit(Conv *c)
2113 {
2114         uchar key[32];
2115         CipherRc4 *cr;
2116         int n;
2117
2118         cipherfree(c);
2119
2120         n = c->cipher->keylen;
2121         if(n > sizeof(key))
2122                 n = sizeof(key);
2123
2124         /* in */
2125         memset(key, 0, sizeof(key));
2126         setkey(key, n, &c->in, "cipher");
2127         c->in.cipherblklen = 1;
2128         c->in.cipherivlen = 4;
2129         c->in.cipher = rc4decrypt;
2130         cr = smalloc(sizeof(CipherRc4));
2131         memset(cr, 0, sizeof(*cr));
2132         setupRC4state(&cr->current, key, n);
2133         c->in.cipherstate = cr;
2134
2135         /* out */
2136         memset(key, 0, sizeof(key));
2137         setkey(key, n, &c->out, "cipher");
2138         c->out.cipherblklen = 1;
2139         c->out.cipherivlen = 4;
2140         c->out.cipher = rc4encrypt;
2141         cr = smalloc(sizeof(CipherRc4));
2142         memset(cr, 0, sizeof(*cr));
2143         setupRC4state(&cr->current, key, n);
2144         c->out.cipherstate = cr;
2145 }
2146
2147 static void
2148 nullauthinit(Conv *c)
2149 {
2150         authfree(c);
2151 }
2152
2153 static void
2154 shaauthinit(Conv *c)
2155 {
2156         authfree(c);
2157 }
2158
2159 static void
2160 seanq_hmac_md5(uchar hash[MD5dlen], ulong wrap, uchar *t, long tlen, uchar *key, long klen)
2161 {
2162         uchar ipad[65], opad[65], wbuf[4];
2163         int i;
2164         DigestState *digest;
2165         uchar innerhash[MD5dlen];
2166
2167         for(i=0; i<64; i++){
2168                 ipad[i] = 0x36;
2169                 opad[i] = 0x5c;
2170         }
2171         ipad[64] = opad[64] = 0;
2172         for(i=0; i<klen; i++){
2173                 ipad[i] ^= key[i];
2174                 opad[i] ^= key[i];
2175         }
2176         hnputl(wbuf, wrap);
2177         digest = md5(ipad, 64, nil, nil);
2178         digest = md5(wbuf, sizeof(wbuf), nil, digest);
2179         md5(t, tlen, innerhash, digest);
2180         digest = md5(opad, 64, nil, nil);
2181         md5(innerhash, MD5dlen, hash, digest);
2182 }
2183
2184 static int
2185 md5auth(OneWay *ow, uchar *t, int tlen)
2186 {
2187         uchar hash[MD5dlen];
2188         int r;
2189
2190         if(tlen < ow->authlen)
2191                 return 0;
2192         tlen -= ow->authlen;
2193
2194         memset(hash, 0, MD5dlen);
2195         seanq_hmac_md5(hash, ow->seqwrap, t, tlen, (uchar*)ow->authstate, 16);
2196         r = memcmp(t+tlen, hash, ow->authlen) == 0;
2197         memmove(t+tlen, hash, ow->authlen);
2198         return r;
2199 }
2200
2201 static void
2202 md5authinit(Conv *c)
2203 {
2204         int keylen;
2205
2206         authfree(c);
2207
2208         keylen = c->auth->keylen;
2209         if(keylen > 16)
2210                 keylen = 16;
2211
2212         /* in */
2213         c->in.authstate = smalloc(16);
2214         memset(c->in.authstate, 0, 16);
2215         setkey(c->in.authstate, keylen, &c->in, "auth");
2216         c->in.authlen = 12;
2217         c->in.auth = md5auth;
2218         
2219         /* out */
2220         c->out.authstate = smalloc(16);
2221         memset(c->out.authstate, 0, 16);
2222         setkey(c->out.authstate, keylen, &c->out, "auth");
2223         c->out.authlen = 12;
2224         c->out.auth = md5auth;
2225 }
2226
2227 static void
2228 nullcompinit(Conv *c)
2229 {
2230         compfree(c);
2231 }
2232
2233 static int
2234 thwackcomp(Conv *c, int, ulong seq, Block **bp)
2235 {
2236         Block *b, *bb;
2237         int nn;
2238         ulong ackseq;
2239         uchar mask;
2240
2241         // add ack info
2242         b = padblock(*bp, 4);
2243
2244         ackseq = unthwackstate(c->in.compstate, &mask);
2245         b->rp[0] = mask;
2246         b->rp[1] = ackseq>>16;
2247         b->rp[2] = ackseq>>8;
2248         b->rp[3] = ackseq;
2249
2250         bb = allocb(BLEN(b));
2251         nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq, c->lstats.outCompStats);
2252         if(nn < 0) {
2253                 freeb(bb);
2254                 *bp = b;
2255                 return ThwackU;
2256         } else {
2257                 bb->wp += nn;
2258                 freeb(b);
2259                 *bp = bb;
2260                 return ThwackC;
2261         }
2262 }
2263
2264 static int
2265 thwackuncomp(Conv *c, int subtype, ulong seq, Block **bp)
2266 {
2267         Block *b, *bb;
2268         ulong mask;
2269         ulong mseq;
2270         int n;
2271
2272         switch(subtype) {
2273         default:
2274                 return 0;
2275         case ThwackU:
2276                 b = *bp;
2277                 mask = b->rp[0];
2278                 mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2279                 b->rp += 4;
2280                 thwackack(c->out.compstate, mseq, mask);
2281                 return 1;
2282         case ThwackC:
2283                 bb = *bp;
2284                 b = allocb(ThwMaxBlock);
2285                 n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq);
2286                 freeb(bb);
2287                 *bp = nil;
2288                 if(n < 0) {
2289 if(0)print("unthwack failed: %d\n", n);
2290                         freeb(b);
2291                         return 0;
2292                 }
2293                 b->wp += n;
2294                 mask = b->rp[0];
2295                 mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2296                 thwackack(c->out.compstate, mseq, mask);
2297                 b->rp += 4;
2298                 *bp = b;
2299                 return 1;
2300         }
2301 }
2302
2303 static void
2304 thwackcompinit(Conv *c)
2305 {
2306         compfree(c);
2307
2308         c->in.compstate = malloc(sizeof(Unthwack));
2309         if(c->in.compstate == nil)
2310                 error(Enomem);
2311         unthwackinit(c->in.compstate);
2312         c->out.compstate = malloc(sizeof(Thwack));
2313         if(c->out.compstate == nil)
2314                 error(Enomem);
2315         thwackinit(c->out.compstate);
2316         c->in.comp = thwackuncomp;
2317         c->out.comp = thwackcomp;
2318 }