xref: /plan9/sys/src/9/port/devsdp.c (revision 4e3613ab15c331a9ada113286cc0f2a35bc0373d)
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
sdpinit(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*
sdpattach(char * spec)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*
sdpwalk(Chan * c,Chan * nc,char ** name,int nname)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
sdpstat(Chan * c,uchar * db,int n)401 sdpstat(Chan* c, uchar* db, int n)
402 {
403 	return devstat(c, db, n, nil, 0, sdpgen);
404 }
405 
406 static Chan*
sdpopen(Chan * ch,int omode)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
sdpclose(Chan * ch)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
sdpread(Chan * ch,void * a,long n,vlong off)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 		snprint(buf, sizeof 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*
sdpbread(Chan * ch,long n,ulong offset)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
sdpwrite(Chan * ch,void * a,long n,vlong off)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
sdpbwrite(Chan * ch,Block * bp,ulong offset)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
sdpgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)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*
sdpclone(Sdp * sdp)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
convretryinit(Conv * c)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
convretry(Conv * c,int reset)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
convtimer(Conv * c,ulong sec)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
sdpackproc(void * a)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
convsetstate(Conv * c,int state)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
convderef(Conv * c)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
onewaycleanup(OneWay * ow)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
convopenchan(Conv * c,char * path)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
convstats(Conv * c,int local,char * buf,int n)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
convack(Conv * c)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 *
conviput(Conv * c,Block * b,int control)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
conviconnect(Conv * c,int subtype,Block * b)1324 conviconnect(Conv *c, int subtype, Block *b)
1325 {
1326 	ulong dialid;
1327 	ulong acceptid;
1328 
1329 	if(BLEN(b) != 8) {
1330 		freeb(b);
1331 		return;
1332 	}
1333 	dialid = nhgetl(b->rp);
1334 	acceptid = nhgetl(b->rp + 4);
1335 	freeb(b);
1336 
1337 if(0)print("sdp: conviconnect: %s: %d %uld %uld\n", convstatename[c->state], subtype, dialid, acceptid);
1338 
1339 	if(subtype == ConReset) {
1340 		convsetstate(c, CClosed);
1341 		return;
1342 	}
1343 
1344 	switch(c->state) {
1345 	default:
1346 		panic("unknown state: %d", c->state);
1347 	case CInit:
1348 		break;
1349 	case CDial:
1350 		if(dialid != c->dialid)
1351 			goto Reset;
1352 		break;
1353 	case CAccept:
1354 	case COpen:
1355 	case CLocalClose:
1356 	case CRemoteClose:
1357 		if(dialid != c->dialid
1358 		|| subtype != ConOpenRequest && acceptid != c->acceptid)
1359 			goto Reset;
1360 		break;
1361 	case CClosed:
1362 		goto Reset;
1363 	}
1364 
1365 	switch(subtype) {
1366 	case ConOpenRequest:
1367 		switch(c->state) {
1368 		case CInit:
1369 			c->dialid = dialid;
1370 			convsetstate(c, CAccept);
1371 			return;
1372 		case CAccept:
1373 		case COpen:
1374 			// duplicate ConOpenRequest that we ignore
1375 			return;
1376 		}
1377 		break;
1378 	case ConOpenAck:
1379 		switch(c->state) {
1380 		case CDial:
1381 			c->acceptid = acceptid;
1382 			convsetstate(c, COpen);
1383 			return;
1384 		case COpen:
1385 			// duplicate that we have to ack
1386 			convoconnect(c, ConOpenAckAck, acceptid, dialid);
1387 			return;
1388 		}
1389 		break;
1390 	case ConOpenAckAck:
1391 		switch(c->state) {
1392 		case CAccept:
1393 			convsetstate(c, COpen);
1394 			return;
1395 		case COpen:
1396 		case CLocalClose:
1397 		case CRemoteClose:
1398 			// duplicate that we ignore
1399 			return;
1400 		}
1401 		break;
1402 	case ConClose:
1403 		switch(c->state) {
1404 		case COpen:
1405 			convoconnect(c, ConCloseAck, dialid, acceptid);
1406 			convsetstate(c, CRemoteClose);
1407 			return;
1408 		case CRemoteClose:
1409 			// duplicate ConClose
1410 			convoconnect(c, ConCloseAck, dialid, acceptid);
1411 			return;
1412 		}
1413 		break;
1414 	case ConCloseAck:
1415 		switch(c->state) {
1416 		case CLocalClose:
1417 			convsetstate(c, CClosed);
1418 			return;
1419 		}
1420 		break;
1421 	}
1422 Reset:
1423 	// invalid connection message - reset to sender
1424 if(1)print("sdp: invalid conviconnect - sending reset\n");
1425 	convoconnect(c, ConReset, dialid, acceptid);
1426 	convsetstate(c, CClosed);
1427 }
1428 
1429 static void
convicontrol(Conv * c,int subtype,Block * b)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*
convicomp(Conv * c,int subtype,ulong seq,Block * b)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
convwriteblock(Conv * c,Block * b)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
convoput(Conv * c,int type,int subtype,Block * b)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
convoconnect(Conv * c,int op,ulong dialid,ulong acceptid)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 *
convreadblock(Conv * c,int n)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
readready(void * a)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 *
readcontrol(Conv * c,int n)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
writeready(void * a)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
writewait(Conv * c)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
writecontrol(Conv * c,void * p,int n,int wait)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 *
readdata(Conv * c,int n)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
writedata(Conv * c,Block * b)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
convreader(void * a)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
setalg(Conv * c,char * name,Algorithm * alg,Algorithm ** p)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
setsecret(OneWay * ow,char * secret)1853 setsecret(OneWay *ow, char *secret)
1854 {
1855 	char *p;
1856 	int i, c;
1857 
1858 	i = 0;
1859 	memset(ow->secret, 0, sizeof(ow->secret));
1860 	for(p=secret; *p; p++) {
1861 		if(i >= sizeof(ow->secret)*2)
1862 			break;
1863 		c = *p;
1864 		if(c >= '0' && c <= '9')
1865 			c -= '0';
1866 		else if(c >= 'a' && c <= 'f')
1867 			c -= 'a'-10;
1868 		else if(c >= 'A' && c <= 'F')
1869 			c -= 'A'-10;
1870 		else
1871 			error("bad character in secret");
1872 		if((i&1) == 0)
1873 			c <<= 4;
1874 		ow->secret[i>>1] |= c;
1875 		i++;
1876 	}
1877 }
1878 
1879 static void
setkey(uchar * key,int n,OneWay * ow,char * prefix)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
cipherfree(Conv * c)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
authfree(Conv * c)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
compfree(Conv * c)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
nullcipherinit(Conv * c)1948 nullcipherinit(Conv *c)
1949 {
1950 	cipherfree(c);
1951 }
1952 
1953 static int
desencrypt(OneWay * ow,uchar * p,int n)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
desdecrypt(OneWay * ow,uchar * p,int n)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
descipherinit(Conv * c)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
rc4encrypt(OneWay * ow,uchar * p,int n)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
rc4decrypt(OneWay * ow,uchar * p,int n)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
rc4cipherinit(Conv * c)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
nullauthinit(Conv * c)2147 nullauthinit(Conv *c)
2148 {
2149 	authfree(c);
2150 }
2151 
2152 static void
shaauthinit(Conv * c)2153 shaauthinit(Conv *c)
2154 {
2155 	authfree(c);
2156 }
2157 
2158 static void
seanq_hmac_md5(uchar hash[MD5dlen],ulong wrap,uchar * t,long tlen,uchar * key,long klen)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
md5auth(OneWay * ow,uchar * t,int tlen)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
md5authinit(Conv * c)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
nullcompinit(Conv * c)2227 nullcompinit(Conv *c)
2228 {
2229 	compfree(c);
2230 }
2231 
2232 static int
thwackcomp(Conv * c,int,ulong seq,Block ** bp)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
thwackuncomp(Conv * c,int subtype,ulong seq,Block ** bp)2264 thwackuncomp(Conv *c, int subtype, ulong seq, Block **bp)
2265 {
2266 	Block *b, *bb;
2267 	ulong mask;
2268 	ulong mseq;
2269 	int n;
2270 
2271 	switch(subtype) {
2272 	default:
2273 		return 0;
2274 	case ThwackU:
2275 		b = *bp;
2276 		mask = b->rp[0];
2277 		mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2278 		b->rp += 4;
2279 		thwackack(c->out.compstate, mseq, mask);
2280 		return 1;
2281 	case ThwackC:
2282 		bb = *bp;
2283 		b = allocb(ThwMaxBlock);
2284 		n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq);
2285 		freeb(bb);
2286 		*bp = nil;
2287 		if(n < 0) {
2288 if(0)print("unthwack failed: %d\n", n);
2289 			freeb(b);
2290 			return 0;
2291 		}
2292 		b->wp += n;
2293 		mask = b->rp[0];
2294 		mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
2295 		thwackack(c->out.compstate, mseq, mask);
2296 		b->rp += 4;
2297 		*bp = b;
2298 		return 1;
2299 	}
2300 }
2301 
2302 static void
thwackcompinit(Conv * c)2303 thwackcompinit(Conv *c)
2304 {
2305 	compfree(c);
2306 
2307 	c->in.compstate = malloc(sizeof(Unthwack));
2308 	if(c->in.compstate == nil)
2309 		error(Enomem);
2310 	unthwackinit(c->in.compstate);
2311 	c->out.compstate = malloc(sizeof(Thwack));
2312 	if(c->out.compstate == nil)
2313 		error(Enomem);
2314 	thwackinit(c->out.compstate);
2315 	c->in.comp = thwackuncomp;
2316 	c->out.comp = thwackcomp;
2317 }
2318