Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

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