FreeBSD/Linux Kernel Cross Reference
sys/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("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("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 >= '' && c <= '9')
1865 c -= '';
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 if(n < 0) {
2287 if(0)print("unthwack failed: %d\n", n);
2288 freeb(b);
2289 return 0;
2290 }
2291 b->wp += n;
2292 mask = b->rp[0];
2293 mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2294 thwackack(c->out.compstate, mseq, mask);
2295 b->rp += 4;
2296 *bp = b;
2297 return 1;
2298 }
2299 }
2300
2301 static void
2302 thwackcompinit(Conv *c)
2303 {
2304 compfree(c);
2305
2306 c->in.compstate = malloc(sizeof(Unthwack));
2307 if(c->in.compstate == nil)
2308 error(Enomem);
2309 unthwackinit(c->in.compstate);
2310 c->out.compstate = malloc(sizeof(Thwack));
2311 if(c->out.compstate == nil)
2312 error(Enomem);
2313 thwackinit(c->out.compstate);
2314 c->in.comp = thwackuncomp;
2315 c->out.comp = thwackcomp;
2316 }
Cache object: 1f6ec704b44a413973cdde4b7caf1e6b
|