xref: /plan9/sys/src/9/port/devsdp.c (revision 4e3613ab15c331a9ada113286cc0f2a35bc0373d)
146ecee76SDavid du Colombier #include "u.h"
246ecee76SDavid du Colombier #include "../port/lib.h"
346ecee76SDavid du Colombier #include "mem.h"
446ecee76SDavid du Colombier #include "dat.h"
546ecee76SDavid du Colombier #include "fns.h"
646ecee76SDavid du Colombier #include "../port/netif.h"
746ecee76SDavid du Colombier #include "../port/error.h"
846ecee76SDavid du Colombier 
946ecee76SDavid du Colombier #include	<libsec.h>
1046ecee76SDavid du Colombier #include "../port/thwack.h"
1146ecee76SDavid du Colombier 
1246ecee76SDavid du Colombier /*
1346ecee76SDavid du Colombier  * sdp - secure datagram protocol
1446ecee76SDavid du Colombier  */
1546ecee76SDavid du Colombier 
1646ecee76SDavid du Colombier typedef struct Sdp Sdp;
1746ecee76SDavid du Colombier typedef struct Conv Conv;
1846ecee76SDavid du Colombier typedef struct OneWay OneWay;
1946ecee76SDavid du Colombier typedef struct Stats Stats;
2046ecee76SDavid du Colombier typedef struct AckPkt AckPkt;
2146ecee76SDavid du Colombier typedef struct Algorithm Algorithm;
2246ecee76SDavid du Colombier typedef struct CipherRc4 CipherRc4;
2346ecee76SDavid du Colombier 
2446ecee76SDavid du Colombier enum
2546ecee76SDavid du Colombier {
2646ecee76SDavid du Colombier 	Qtopdir=	1,		/* top level directory */
2746ecee76SDavid du Colombier 
2846ecee76SDavid du Colombier 	Qsdpdir,			/* sdp directory */
2946ecee76SDavid du Colombier 	Qclone,
3046ecee76SDavid du Colombier 	Qlog,
3146ecee76SDavid du Colombier 
3246ecee76SDavid du Colombier 	Qconvdir,			/* directory per conversation */
3346ecee76SDavid du Colombier 	Qctl,
3446ecee76SDavid du Colombier 	Qdata,				/* unreliable packet channel */
3546ecee76SDavid du Colombier 	Qcontrol,			/* reliable control channel */
3646ecee76SDavid du Colombier 	Qstatus,
3746ecee76SDavid du Colombier 	Qstats,
3846ecee76SDavid du Colombier 	Qrstats,
3946ecee76SDavid du Colombier 
4046ecee76SDavid du Colombier 	MaxQ,
4146ecee76SDavid du Colombier 
4246ecee76SDavid du Colombier 	Maxconv= 256,		// power of 2
4346ecee76SDavid du Colombier 	Nfs= 4,			// number of file systems
4446ecee76SDavid du Colombier 	MaxRetries=	12,
4546ecee76SDavid du Colombier 	KeepAlive = 300,	// keep alive in seconds - should probably be about 60 but is higher to avoid linksys bug
4646ecee76SDavid du Colombier 	SecretLength= 32,	// a secret per direction
4746ecee76SDavid du Colombier 	SeqMax = (1<<24),
4846ecee76SDavid du Colombier 	SeqWindow = 32,
4946ecee76SDavid du Colombier 	NCompStats = 8,
5046ecee76SDavid du Colombier };
5146ecee76SDavid du Colombier 
5246ecee76SDavid du Colombier #define TYPE(x) 	(((ulong)(x).path) & 0xff)
5346ecee76SDavid du Colombier #define CONV(x) 	((((ulong)(x).path) >> 8)&(Maxconv-1))
5446ecee76SDavid du Colombier #define QID(x, y) 	(((x)<<8) | (y))
5546ecee76SDavid du Colombier 
5646ecee76SDavid du Colombier struct Stats
5746ecee76SDavid du Colombier {
5846ecee76SDavid du Colombier 	ulong	outPackets;
5946ecee76SDavid du Colombier 	ulong	outDataPackets;
6046ecee76SDavid du Colombier 	ulong	outDataBytes;
6146ecee76SDavid du Colombier 	ulong	outCompDataBytes;
6246ecee76SDavid du Colombier 	ulong	outCompBytes;
6346ecee76SDavid du Colombier 	ulong	outCompStats[NCompStats];
6446ecee76SDavid du Colombier 	ulong	inPackets;
6546ecee76SDavid du Colombier 	ulong	inDataPackets;
6646ecee76SDavid du Colombier 	ulong	inDataBytes;
6746ecee76SDavid du Colombier 	ulong	inCompDataBytes;
6846ecee76SDavid du Colombier 	ulong	inMissing;
6946ecee76SDavid du Colombier 	ulong	inDup;
7046ecee76SDavid du Colombier 	ulong	inReorder;
7146ecee76SDavid du Colombier 	ulong	inBadComp;
7246ecee76SDavid du Colombier 	ulong	inBadAuth;
7346ecee76SDavid du Colombier 	ulong	inBadSeq;
7446ecee76SDavid du Colombier 	ulong	inBadOther;
7546ecee76SDavid du Colombier };
7646ecee76SDavid du Colombier 
7746ecee76SDavid du Colombier struct OneWay
7846ecee76SDavid du Colombier {
7946ecee76SDavid du Colombier 	Rendez	statsready;
8046ecee76SDavid du Colombier 
8146ecee76SDavid du Colombier 	ulong	seqwrap;	// number of wraps of the sequence number
8246ecee76SDavid du Colombier 	ulong	seq;
8346ecee76SDavid du Colombier 	ulong	window;
8446ecee76SDavid du Colombier 
8546ecee76SDavid du Colombier 	uchar	secret[SecretLength];
8646ecee76SDavid du Colombier 
8746ecee76SDavid du Colombier 	QLock	controllk;
8846ecee76SDavid du Colombier 	Rendez	controlready;
8946ecee76SDavid du Colombier 	Block	*controlpkt;		// control channel
9046ecee76SDavid du Colombier 	ulong	controlseq;
9146ecee76SDavid du Colombier 
9246ecee76SDavid du Colombier 	void	*cipherstate;	// state cipher
9346ecee76SDavid du Colombier 	int		cipherivlen;	// initial vector length
9446ecee76SDavid du Colombier 	int		cipherblklen;	// block length
9546ecee76SDavid du Colombier 	int		(*cipher)(OneWay*, uchar *buf, int len);
9646ecee76SDavid du Colombier 
9746ecee76SDavid du Colombier 	void	*authstate;		// auth state
9846ecee76SDavid du Colombier 	int		authlen;		// auth data length in bytes
9946ecee76SDavid du Colombier 	int		(*auth)(OneWay*, uchar *buf, int len);
10046ecee76SDavid du Colombier 
10146ecee76SDavid du Colombier 	void	*compstate;
10246ecee76SDavid du Colombier 	int		(*comp)(Conv*, int subtype, ulong seq, Block **);
10346ecee76SDavid du Colombier };
10446ecee76SDavid du Colombier 
10546ecee76SDavid du Colombier // conv states
10646ecee76SDavid du Colombier enum {
10746ecee76SDavid du Colombier 	CFree,
10846ecee76SDavid du Colombier 	CInit,
10946ecee76SDavid du Colombier 	CDial,
11046ecee76SDavid du Colombier 	CAccept,
11146ecee76SDavid du Colombier 	COpen,
11246ecee76SDavid du Colombier 	CLocalClose,
11346ecee76SDavid du Colombier 	CRemoteClose,
11446ecee76SDavid du Colombier 	CClosed,
11546ecee76SDavid du Colombier };
11646ecee76SDavid du Colombier 
11746ecee76SDavid du Colombier struct Conv {
11846ecee76SDavid du Colombier 	QLock;
11946ecee76SDavid du Colombier 	Sdp	*sdp;
12046ecee76SDavid du Colombier 	int	id;
12146ecee76SDavid du Colombier 
12246ecee76SDavid du Colombier 	int ref;	// holds conv up
12346ecee76SDavid du Colombier 
12446ecee76SDavid du Colombier 	int state;
12546ecee76SDavid du Colombier 
12646ecee76SDavid du Colombier 	int dataopen;	// ref count of opens on Qdata
12746ecee76SDavid du Colombier 	int controlopen;	// ref count of opens on Qcontrol
12846ecee76SDavid du Colombier 	int reader;		// reader proc has been started
12946ecee76SDavid du Colombier 
13046ecee76SDavid du Colombier 	Stats	lstats;
13146ecee76SDavid du Colombier 	Stats	rstats;
13246ecee76SDavid du Colombier 
13346ecee76SDavid du Colombier 	ulong	lastrecv;	// time last packet was received
13446ecee76SDavid du Colombier 	ulong	timeout;
13546ecee76SDavid du Colombier 	int		retries;
13646ecee76SDavid du Colombier 
13746ecee76SDavid du Colombier 	// the following pair uniquely define conversation on this port
13846ecee76SDavid du Colombier 	ulong dialid;
13946ecee76SDavid du Colombier 	ulong acceptid;
14046ecee76SDavid du Colombier 
14146ecee76SDavid du Colombier 	QLock readlk;		// protects readproc
14246ecee76SDavid du Colombier 	Proc *readproc;
14346ecee76SDavid du Colombier 
14446ecee76SDavid du Colombier 	Chan *chan;		// packet channel
14546ecee76SDavid du Colombier 	char *channame;
14646ecee76SDavid du Colombier 
14746ecee76SDavid du Colombier 	char owner[KNAMELEN];		/* protections */
14846ecee76SDavid du Colombier 	int	perm;
14946ecee76SDavid du Colombier 
15046ecee76SDavid du Colombier 	Algorithm *auth;
15146ecee76SDavid du Colombier 	Algorithm *cipher;
15246ecee76SDavid du Colombier 	Algorithm *comp;
15346ecee76SDavid du Colombier 
15446ecee76SDavid du Colombier 	int drop;
15546ecee76SDavid du Colombier 
15646ecee76SDavid du Colombier 	OneWay	in;
15746ecee76SDavid du Colombier 	OneWay	out;
15846ecee76SDavid du Colombier };
15946ecee76SDavid du Colombier 
16046ecee76SDavid du Colombier struct Sdp {
16146ecee76SDavid du Colombier 	QLock;
16246ecee76SDavid du Colombier 	Log;
16346ecee76SDavid du Colombier 	int	nconv;
16446ecee76SDavid du Colombier 	Conv *conv[Maxconv];
16546ecee76SDavid du Colombier 	int ackproc;
16646ecee76SDavid du Colombier };
16746ecee76SDavid du Colombier 
16846ecee76SDavid du Colombier enum {
16946ecee76SDavid du Colombier 	TConnect,
17046ecee76SDavid du Colombier 	TControl,
17146ecee76SDavid du Colombier 	TData,
17246ecee76SDavid du Colombier 	TCompData,
17346ecee76SDavid du Colombier };
17446ecee76SDavid du Colombier 
17546ecee76SDavid du Colombier enum {
17646ecee76SDavid du Colombier 	ControlMesg,
17746ecee76SDavid du Colombier 	ControlAck,
17846ecee76SDavid du Colombier };
17946ecee76SDavid du Colombier 
18046ecee76SDavid du Colombier enum {
18146ecee76SDavid du Colombier 	ThwackU,
18246ecee76SDavid du Colombier 	ThwackC,
18346ecee76SDavid du Colombier };
18446ecee76SDavid du Colombier 
18546ecee76SDavid du Colombier enum {
18646ecee76SDavid du Colombier 	ConOpenRequest,
18746ecee76SDavid du Colombier 	ConOpenAck,
18846ecee76SDavid du Colombier 	ConOpenAckAck,
18946ecee76SDavid du Colombier 	ConClose,
19046ecee76SDavid du Colombier 	ConCloseAck,
19146ecee76SDavid du Colombier 	ConReset,
19246ecee76SDavid du Colombier };
19346ecee76SDavid du Colombier 
19446ecee76SDavid du Colombier struct AckPkt
19546ecee76SDavid du Colombier {
19646ecee76SDavid du Colombier 	uchar	cseq[4];
19746ecee76SDavid du Colombier 	uchar	outPackets[4];
19846ecee76SDavid du Colombier 	uchar	outDataPackets[4];
19946ecee76SDavid du Colombier 	uchar	outDataBytes[4];
20046ecee76SDavid du Colombier 	uchar	outCompDataBytes[4];
20146ecee76SDavid du Colombier 	uchar	outCompStats[4*NCompStats];
20246ecee76SDavid du Colombier 	uchar	inPackets[4];
20346ecee76SDavid du Colombier 	uchar	inDataPackets[4];
20446ecee76SDavid du Colombier 	uchar	inDataBytes[4];
20546ecee76SDavid du Colombier 	uchar	inCompDataBytes[4];
20646ecee76SDavid du Colombier 	uchar	inMissing[4];
20746ecee76SDavid du Colombier 	uchar	inDup[4];
20846ecee76SDavid du Colombier 	uchar	inReorder[4];
20946ecee76SDavid du Colombier 	uchar	inBadComp[4];
21046ecee76SDavid du Colombier 	uchar	inBadAuth[4];
21146ecee76SDavid du Colombier 	uchar	inBadSeq[4];
21246ecee76SDavid du Colombier 	uchar	inBadOther[4];
21346ecee76SDavid du Colombier };
21446ecee76SDavid du Colombier 
21546ecee76SDavid du Colombier struct Algorithm
21646ecee76SDavid du Colombier {
21746ecee76SDavid du Colombier 	char 	*name;
21846ecee76SDavid du Colombier 	int		keylen;		// in bytes
21946ecee76SDavid du Colombier 	void	(*init)(Conv*);
22046ecee76SDavid du Colombier };
22146ecee76SDavid du Colombier 
22246ecee76SDavid du Colombier enum {
22346ecee76SDavid du Colombier 	RC4forward	= 10*1024*1024,	// maximum skip forward
22446ecee76SDavid du Colombier 	RC4back = 100*1024,		// maximum look back
22546ecee76SDavid du Colombier };
22646ecee76SDavid du Colombier 
22746ecee76SDavid du Colombier struct CipherRc4
22846ecee76SDavid du Colombier {
22946ecee76SDavid du Colombier 	ulong cseq;	// current byte sequence number
23046ecee76SDavid du Colombier 	RC4state current;
23146ecee76SDavid du Colombier 
23246ecee76SDavid du Colombier 	int ovalid;	// old is valid
23346ecee76SDavid du Colombier 	ulong lgseq; // last good sequence
23446ecee76SDavid du Colombier 	ulong oseq;	// old byte sequence number
23546ecee76SDavid du Colombier 	RC4state old;
23646ecee76SDavid du Colombier };
23746ecee76SDavid du Colombier 
23846ecee76SDavid du Colombier static Dirtab sdpdirtab[]={
23946ecee76SDavid du Colombier 	"log",		{Qlog},		0,	0666,
24046ecee76SDavid du Colombier 	"clone",	{Qclone},		0,	0666,
24146ecee76SDavid du Colombier };
24246ecee76SDavid du Colombier 
24346ecee76SDavid du Colombier static Dirtab convdirtab[]={
24446ecee76SDavid du Colombier 	"ctl",		{Qctl},	0,	0666,
24546ecee76SDavid du Colombier 	"data",		{Qdata},	0,	0666,
24646ecee76SDavid du Colombier 	"control",	{Qcontrol},	0,	0666,
24746ecee76SDavid du Colombier 	"status",	{Qstatus},	0,	0444,
24846ecee76SDavid du Colombier 	"stats",	{Qstats},	0,	0444,
24946ecee76SDavid du Colombier 	"rstats",	{Qrstats},	0,	0444,
25046ecee76SDavid du Colombier };
25146ecee76SDavid du Colombier 
25246ecee76SDavid du Colombier static int m2p[] = {
25346ecee76SDavid du Colombier 	[OREAD]		4,
25446ecee76SDavid du Colombier 	[OWRITE]	2,
25546ecee76SDavid du Colombier 	[ORDWR]		6
25646ecee76SDavid du Colombier };
25746ecee76SDavid du Colombier 
25846ecee76SDavid du Colombier enum {
25946ecee76SDavid du Colombier 	Logcompress=	(1<<0),
26046ecee76SDavid du Colombier 	Logauth=	(1<<1),
26146ecee76SDavid du Colombier 	Loghmac=	(1<<2),
26246ecee76SDavid du Colombier };
26346ecee76SDavid du Colombier 
26446ecee76SDavid du Colombier static Logflag logflags[] =
26546ecee76SDavid du Colombier {
26646ecee76SDavid du Colombier 	{ "compress",	Logcompress, },
26746ecee76SDavid du Colombier 	{ "auth",	Logauth, },
26846ecee76SDavid du Colombier 	{ "hmac",	Loghmac, },
26946ecee76SDavid du Colombier 	{ nil,		0, },
27046ecee76SDavid du Colombier };
27146ecee76SDavid du Colombier 
27246ecee76SDavid du Colombier static Dirtab	*dirtab[MaxQ];
27346ecee76SDavid du Colombier static Sdp sdptab[Nfs];
27446ecee76SDavid du Colombier static char *convstatename[] = {
27546ecee76SDavid du Colombier 	[CFree]		"Free",
27646ecee76SDavid du Colombier 	[CInit]		"Init",
27746ecee76SDavid du Colombier 	[CDial]		"Dial",
27846ecee76SDavid du Colombier 	[CAccept]	"Accept",
27946ecee76SDavid du Colombier 	[COpen]		"Open",
28046ecee76SDavid du Colombier 	[CLocalClose] "LocalClose",
28146ecee76SDavid du Colombier 	[CRemoteClose] "RemoteClose",
28246ecee76SDavid du Colombier 	[CClosed]	"Closed",
28346ecee76SDavid du Colombier };
28446ecee76SDavid du Colombier 
28546ecee76SDavid du Colombier static int sdpgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp);
28646ecee76SDavid du Colombier static Conv *sdpclone(Sdp *sdp);
28746ecee76SDavid du Colombier static void sdpackproc(void *a);
28846ecee76SDavid du Colombier static void onewaycleanup(OneWay *ow);
28946ecee76SDavid du Colombier static int readready(void *a);
29046ecee76SDavid du Colombier static int controlread();
29146ecee76SDavid du Colombier static void convsetstate(Conv *c, int state);
29246ecee76SDavid du Colombier static Block *readcontrol(Conv *c, int n);
29346ecee76SDavid du Colombier static void writecontrol(Conv *c, void *p, int n, int wait);
29446ecee76SDavid du Colombier static Block *readdata(Conv *c, int n);
29546ecee76SDavid du Colombier static long writedata(Conv *c, Block *b);
29646ecee76SDavid du Colombier static void convderef(Conv *c);
29746ecee76SDavid du Colombier static Block *conviput(Conv *c, Block *b, int control);
29846ecee76SDavid du Colombier static void conviconnect(Conv *c, int op, Block *b);
29946ecee76SDavid du Colombier static void convicontrol(Conv *c, int op, Block *b);
30046ecee76SDavid du Colombier static Block *convicomp(Conv *c, int op, ulong, Block *b);
30146ecee76SDavid du Colombier static void convoput(Conv *c, int type, int subtype, Block *b);
30246ecee76SDavid du Colombier static void convoconnect(Conv *c, int op, ulong dialid, ulong acceptid);
30346ecee76SDavid du Colombier static void convopenchan(Conv *c, char *path);
30446ecee76SDavid du Colombier static void convstats(Conv *c, int local, char *buf, int n);
30546ecee76SDavid du Colombier static void convreader(void *a);
30646ecee76SDavid du Colombier 
30746ecee76SDavid du Colombier static void setalg(Conv *c, char *name, Algorithm *tab, Algorithm **);
30846ecee76SDavid du Colombier static void setsecret(OneWay *cc, char *secret);
30946ecee76SDavid du Colombier 
31046ecee76SDavid du Colombier static void nullcipherinit(Conv*c);
31146ecee76SDavid du Colombier static void descipherinit(Conv*c);
31246ecee76SDavid du Colombier static void rc4cipherinit(Conv*c);
31346ecee76SDavid du Colombier static void nullauthinit(Conv*c);
31446ecee76SDavid du Colombier static void shaauthinit(Conv*c);
31546ecee76SDavid du Colombier static void md5authinit(Conv*c);
31646ecee76SDavid du Colombier static void nullcompinit(Conv*c);
31746ecee76SDavid du Colombier static void thwackcompinit(Conv*c);
31846ecee76SDavid du Colombier 
31946ecee76SDavid du Colombier static Algorithm cipheralg[] =
32046ecee76SDavid du Colombier {
32146ecee76SDavid du Colombier 	"null",			0,	nullcipherinit,
32246ecee76SDavid du Colombier 	"des_56_cbc",	7,	descipherinit,
32346ecee76SDavid du Colombier 	"rc4_128",		16,	rc4cipherinit,
32446ecee76SDavid du Colombier 	"rc4_256",		32,	rc4cipherinit,
32546ecee76SDavid du Colombier 	nil,			0,	nil,
32646ecee76SDavid du Colombier };
32746ecee76SDavid du Colombier 
32846ecee76SDavid du Colombier static Algorithm authalg[] =
32946ecee76SDavid du Colombier {
33046ecee76SDavid du Colombier 	"null",			0,	nullauthinit,
33146ecee76SDavid du Colombier 	"hmac_sha1_96",	16,	shaauthinit,
33246ecee76SDavid du Colombier 	"hmac_md5_96",	16,	md5authinit,
33346ecee76SDavid du Colombier 	nil,			0,	nil,
33446ecee76SDavid du Colombier };
33546ecee76SDavid du Colombier 
33646ecee76SDavid du Colombier static Algorithm compalg[] =
33746ecee76SDavid du Colombier {
33846ecee76SDavid du Colombier 	"null",			0,	nullcompinit,
33946ecee76SDavid du Colombier 	"thwack",		0,	thwackcompinit,
34046ecee76SDavid du Colombier 	nil,			0,	nil,
34146ecee76SDavid du Colombier };
34246ecee76SDavid du Colombier 
34346ecee76SDavid du Colombier 
34446ecee76SDavid du Colombier static void
sdpinit(void)34546ecee76SDavid du Colombier sdpinit(void)
34646ecee76SDavid du Colombier {
34746ecee76SDavid du Colombier 	int i;
34846ecee76SDavid du Colombier 	Dirtab *dt;
34946ecee76SDavid du Colombier 
35046ecee76SDavid du Colombier 	// setup dirtab with non directory entries
35146ecee76SDavid du Colombier 	for(i=0; i<nelem(sdpdirtab); i++) {
35246ecee76SDavid du Colombier 		dt = sdpdirtab + i;
35346ecee76SDavid du Colombier 		dirtab[TYPE(dt->qid)] = dt;
35446ecee76SDavid du Colombier 	}
35546ecee76SDavid du Colombier 
35646ecee76SDavid du Colombier 	for(i=0; i<nelem(convdirtab); i++) {
35746ecee76SDavid du Colombier 		dt = convdirtab + i;
35846ecee76SDavid du Colombier 		dirtab[TYPE(dt->qid)] = dt;
35946ecee76SDavid du Colombier 	}
36046ecee76SDavid du Colombier 
36146ecee76SDavid du Colombier }
36246ecee76SDavid du Colombier 
36346ecee76SDavid du Colombier static Chan*
sdpattach(char * spec)36446ecee76SDavid du Colombier sdpattach(char* spec)
36546ecee76SDavid du Colombier {
36646ecee76SDavid du Colombier 	Chan *c;
36746ecee76SDavid du Colombier 	int dev;
36846ecee76SDavid du Colombier 	char buf[100];
36946ecee76SDavid du Colombier 	Sdp *sdp;
37046ecee76SDavid du Colombier 	int start;
37146ecee76SDavid du Colombier 
37246ecee76SDavid du Colombier 	dev = atoi(spec);
37346ecee76SDavid du Colombier 	if(dev<0 || dev >= Nfs)
37446ecee76SDavid du Colombier 		error("bad specification");
37546ecee76SDavid du Colombier 
37646ecee76SDavid du Colombier 	c = devattach('E', spec);
37746ecee76SDavid du Colombier 	c->qid = (Qid){QID(0, Qtopdir), 0, QTDIR};
37846ecee76SDavid du Colombier 	c->dev = dev;
37946ecee76SDavid du Colombier 
38046ecee76SDavid du Colombier 	sdp = sdptab + dev;
38146ecee76SDavid du Colombier 	qlock(sdp);
38246ecee76SDavid du Colombier 	start = sdp->ackproc == 0;
38346ecee76SDavid du Colombier 	sdp->ackproc = 1;
38446ecee76SDavid du Colombier 	qunlock(sdp);
38546ecee76SDavid du Colombier 
38646ecee76SDavid du Colombier 	if(start) {
38746ecee76SDavid du Colombier 		snprint(buf, sizeof(buf), "sdpackproc%d", dev);
38846ecee76SDavid du Colombier 		kproc(buf, sdpackproc, sdp);
38946ecee76SDavid du Colombier 	}
39046ecee76SDavid du Colombier 
39146ecee76SDavid du Colombier 	return c;
39246ecee76SDavid du Colombier }
39346ecee76SDavid du Colombier 
39446ecee76SDavid du Colombier static Walkqid*
sdpwalk(Chan * c,Chan * nc,char ** name,int nname)39546ecee76SDavid du Colombier sdpwalk(Chan *c, Chan *nc, char **name, int nname)
39646ecee76SDavid du Colombier {
39746ecee76SDavid du Colombier 	return devwalk(c, nc, name, nname, 0, 0, sdpgen);
39846ecee76SDavid du Colombier }
39946ecee76SDavid du Colombier 
40046ecee76SDavid du Colombier static int
sdpstat(Chan * c,uchar * db,int n)40146ecee76SDavid du Colombier sdpstat(Chan* c, uchar* db, int n)
40246ecee76SDavid du Colombier {
40346ecee76SDavid du Colombier 	return devstat(c, db, n, nil, 0, sdpgen);
40446ecee76SDavid du Colombier }
40546ecee76SDavid du Colombier 
40646ecee76SDavid du Colombier static Chan*
sdpopen(Chan * ch,int omode)40746ecee76SDavid du Colombier sdpopen(Chan* ch, int omode)
40846ecee76SDavid du Colombier {
40946ecee76SDavid du Colombier 	int perm;
41046ecee76SDavid du Colombier 	Sdp *sdp;
41146ecee76SDavid du Colombier 	Conv *c;
41246ecee76SDavid du Colombier 
41346ecee76SDavid du Colombier 	omode &= 3;
41446ecee76SDavid du Colombier 	perm = m2p[omode];
41546ecee76SDavid du Colombier 	USED(perm);
41646ecee76SDavid du Colombier 
41746ecee76SDavid du Colombier 	sdp = sdptab + ch->dev;
41846ecee76SDavid du Colombier 
41946ecee76SDavid du Colombier 	switch(TYPE(ch->qid)) {
42046ecee76SDavid du Colombier 	default:
42146ecee76SDavid du Colombier 		break;
42246ecee76SDavid du Colombier 	case Qtopdir:
42346ecee76SDavid du Colombier 	case Qsdpdir:
42446ecee76SDavid du Colombier 	case Qconvdir:
42546ecee76SDavid du Colombier 		if(omode != OREAD)
42646ecee76SDavid du Colombier 			error(Eperm);
42746ecee76SDavid du Colombier 		break;
42846ecee76SDavid du Colombier 	case Qlog:
42946ecee76SDavid du Colombier 		logopen(sdp);
43046ecee76SDavid du Colombier 		break;
43146ecee76SDavid du Colombier 	case Qclone:
43246ecee76SDavid du Colombier 		c = sdpclone(sdp);
43346ecee76SDavid du Colombier 		if(c == nil)
43446ecee76SDavid du Colombier 			error(Enodev);
43546ecee76SDavid du Colombier 		ch->qid.path = QID(c->id, Qctl);
43646ecee76SDavid du Colombier 		break;
43746ecee76SDavid du Colombier 	case Qdata:
43846ecee76SDavid du Colombier 	case Qctl:
43946ecee76SDavid du Colombier 	case Qstatus:
44046ecee76SDavid du Colombier 	case Qcontrol:
44146ecee76SDavid du Colombier 	case Qstats:
44246ecee76SDavid du Colombier 	case Qrstats:
44346ecee76SDavid du Colombier 		c = sdp->conv[CONV(ch->qid)];
44446ecee76SDavid du Colombier 		qlock(c);
44546ecee76SDavid du Colombier 		if(waserror()) {
44646ecee76SDavid du Colombier 			qunlock(c);
44746ecee76SDavid du Colombier 			nexterror();
44846ecee76SDavid du Colombier 		}
44946ecee76SDavid du Colombier 		if((perm & (c->perm>>6)) != perm)
45046ecee76SDavid du Colombier 		if(strcmp(up->user, c->owner) != 0 || (perm & c->perm) != perm)
45146ecee76SDavid du Colombier 				error(Eperm);
45246ecee76SDavid du Colombier 
45346ecee76SDavid du Colombier 		c->ref++;
45446ecee76SDavid du Colombier 		if(TYPE(ch->qid) == Qdata) {
45546ecee76SDavid du Colombier 			c->dataopen++;
45646ecee76SDavid du Colombier 			// kill reader if Qdata is opened for the first time
45746ecee76SDavid du Colombier 			if(c->dataopen == 1)
45846ecee76SDavid du Colombier 			if(c->readproc != nil)
45946ecee76SDavid du Colombier 				postnote(c->readproc, 1, "interrupt", 0);
46046ecee76SDavid du Colombier 		} else if(TYPE(ch->qid) == Qcontrol) {
46146ecee76SDavid du Colombier 			c->controlopen++;
46246ecee76SDavid du Colombier 		}
46346ecee76SDavid du Colombier 		qunlock(c);
46446ecee76SDavid du Colombier 		poperror();
46546ecee76SDavid du Colombier 		break;
46646ecee76SDavid du Colombier 	}
46746ecee76SDavid du Colombier 	ch->mode = openmode(omode);
46846ecee76SDavid du Colombier 	ch->flag |= COPEN;
46946ecee76SDavid du Colombier 	ch->offset = 0;
47046ecee76SDavid du Colombier 	return ch;
47146ecee76SDavid du Colombier }
47246ecee76SDavid du Colombier 
47346ecee76SDavid du Colombier static void
sdpclose(Chan * ch)47446ecee76SDavid du Colombier sdpclose(Chan* ch)
47546ecee76SDavid du Colombier {
47646ecee76SDavid du Colombier 	Sdp *sdp  = sdptab + ch->dev;
47746ecee76SDavid du Colombier 	Conv *c;
47846ecee76SDavid du Colombier 
47946ecee76SDavid du Colombier 	if(!(ch->flag & COPEN))
48046ecee76SDavid du Colombier 		return;
48146ecee76SDavid du Colombier 	switch(TYPE(ch->qid)) {
48246ecee76SDavid du Colombier 	case Qlog:
48346ecee76SDavid du Colombier 		logclose(sdp);
48446ecee76SDavid du Colombier 		break;
48546ecee76SDavid du Colombier 	case Qctl:
48646ecee76SDavid du Colombier 	case Qstatus:
48746ecee76SDavid du Colombier 	case Qstats:
48846ecee76SDavid du Colombier 	case Qrstats:
48946ecee76SDavid du Colombier 		c = sdp->conv[CONV(ch->qid)];
49046ecee76SDavid du Colombier 		qlock(c);
49146ecee76SDavid du Colombier 		convderef(c);
49246ecee76SDavid du Colombier 		qunlock(c);
49346ecee76SDavid du Colombier 		break;
49446ecee76SDavid du Colombier 
49546ecee76SDavid du Colombier 	case Qdata:
49646ecee76SDavid du Colombier 		c = sdp->conv[CONV(ch->qid)];
49746ecee76SDavid du Colombier 		qlock(c);
49846ecee76SDavid du Colombier 		c->dataopen--;
49946ecee76SDavid du Colombier 		convderef(c);
50046ecee76SDavid du Colombier 		if(c->dataopen == 0)
50146ecee76SDavid du Colombier 		if(c->reader == 0)
50246ecee76SDavid du Colombier 		if(c->chan != nil)
50346ecee76SDavid du Colombier 		if(!waserror()) {
50446ecee76SDavid du Colombier 			kproc("convreader", convreader, c);
50546ecee76SDavid du Colombier 			c->reader = 1;
50646ecee76SDavid du Colombier 			c->ref++;
50746ecee76SDavid du Colombier 			poperror();
50846ecee76SDavid du Colombier 		}
50946ecee76SDavid du Colombier 		qunlock(c);
51046ecee76SDavid du Colombier 		break;
51146ecee76SDavid du Colombier 
51246ecee76SDavid du Colombier 	case Qcontrol:
51346ecee76SDavid du Colombier 		c = sdp->conv[CONV(ch->qid)];
51446ecee76SDavid du Colombier 		qlock(c);
51546ecee76SDavid du Colombier 		c->controlopen--;
51646ecee76SDavid du Colombier 		convderef(c);
51746ecee76SDavid du Colombier 		if(c->controlopen == 0 && c->ref != 0) {
51846ecee76SDavid du Colombier 			switch(c->state) {
51946ecee76SDavid du Colombier 			default:
52046ecee76SDavid du Colombier 				convsetstate(c, CClosed);
52146ecee76SDavid du Colombier 				break;
52246ecee76SDavid du Colombier 			case CAccept:
52346ecee76SDavid du Colombier 			case COpen:
52446ecee76SDavid du Colombier 				convsetstate(c, CLocalClose);
52546ecee76SDavid du Colombier 				break;
52646ecee76SDavid du Colombier 			}
52746ecee76SDavid du Colombier 		}
52846ecee76SDavid du Colombier 		qunlock(c);
52946ecee76SDavid du Colombier 		break;
53046ecee76SDavid du Colombier 	}
53146ecee76SDavid du Colombier }
53246ecee76SDavid du Colombier 
53346ecee76SDavid du Colombier static long
sdpread(Chan * ch,void * a,long n,vlong off)53446ecee76SDavid du Colombier sdpread(Chan *ch, void *a, long n, vlong off)
53546ecee76SDavid du Colombier {
53646ecee76SDavid du Colombier 	char buf[256];
53746ecee76SDavid du Colombier 	char *s;
53846ecee76SDavid du Colombier 	Sdp *sdp = sdptab + ch->dev;
53946ecee76SDavid du Colombier 	Conv *c;
54046ecee76SDavid du Colombier 	Block *b;
54146ecee76SDavid du Colombier 	int rv;
54246ecee76SDavid du Colombier 
54346ecee76SDavid du Colombier 	USED(off);
54446ecee76SDavid du Colombier 	switch(TYPE(ch->qid)) {
54546ecee76SDavid du Colombier 	default:
54646ecee76SDavid du Colombier 		error(Eperm);
54746ecee76SDavid du Colombier 	case Qtopdir:
54846ecee76SDavid du Colombier 	case Qsdpdir:
54946ecee76SDavid du Colombier 	case Qconvdir:
55046ecee76SDavid du Colombier 		return devdirread(ch, a, n, 0, 0, sdpgen);
55146ecee76SDavid du Colombier 	case Qlog:
55246ecee76SDavid du Colombier 		return logread(sdp, a, off, n);
55346ecee76SDavid du Colombier 	case Qstatus:
55446ecee76SDavid du Colombier 		c = sdp->conv[CONV(ch->qid)];
55546ecee76SDavid du Colombier 		qlock(c);
55646ecee76SDavid du Colombier 		n = readstr(off, a, n, convstatename[c->state]);
55746ecee76SDavid du Colombier 		qunlock(c);
55846ecee76SDavid du Colombier 		return n;
55946ecee76SDavid du Colombier 	case Qctl:
560*4e3613abSDavid du Colombier 		snprint(buf, sizeof buf, "%lud", CONV(ch->qid));
56146ecee76SDavid du Colombier 		return readstr(off, a, n, buf);
56246ecee76SDavid du Colombier 	case Qcontrol:
56346ecee76SDavid du Colombier 		b = readcontrol(sdp->conv[CONV(ch->qid)], n);
56446ecee76SDavid du Colombier 		if(b == nil)
56546ecee76SDavid du Colombier 			return 0;
56646ecee76SDavid du Colombier 		if(BLEN(b) < n)
56746ecee76SDavid du Colombier 			n = BLEN(b);
56846ecee76SDavid du Colombier 		memmove(a, b->rp, n);
56946ecee76SDavid du Colombier 		freeb(b);
57046ecee76SDavid du Colombier 		return n;
57146ecee76SDavid du Colombier 	case Qdata:
57246ecee76SDavid du Colombier 		b = readdata(sdp->conv[CONV(ch->qid)], n);
57346ecee76SDavid du Colombier 		if(b == nil)
57446ecee76SDavid du Colombier 			return 0;
57546ecee76SDavid du Colombier 		if(BLEN(b) < n)
57646ecee76SDavid du Colombier 			n = BLEN(b);
57746ecee76SDavid du Colombier 		memmove(a, b->rp, n);
57846ecee76SDavid du Colombier 		freeb(b);
57946ecee76SDavid du Colombier 		return n;
58046ecee76SDavid du Colombier 	case Qstats:
58146ecee76SDavid du Colombier 	case Qrstats:
58246ecee76SDavid du Colombier 		c = sdp->conv[CONV(ch->qid)];
58346ecee76SDavid du Colombier 		s = smalloc(1000);
58446ecee76SDavid du Colombier 		convstats(c, TYPE(ch->qid) == Qstats, s, 1000);
58546ecee76SDavid du Colombier 		rv = readstr(off, a, n, s);
58646ecee76SDavid du Colombier 		free(s);
58746ecee76SDavid du Colombier 		return rv;
58846ecee76SDavid du Colombier 	}
58946ecee76SDavid du Colombier }
59046ecee76SDavid du Colombier 
59146ecee76SDavid du Colombier static Block*
sdpbread(Chan * ch,long n,ulong offset)59246ecee76SDavid du Colombier sdpbread(Chan* ch, long n, ulong offset)
59346ecee76SDavid du Colombier {
59446ecee76SDavid du Colombier 	Sdp *sdp = sdptab + ch->dev;
59546ecee76SDavid du Colombier 
59646ecee76SDavid du Colombier 	if(TYPE(ch->qid) != Qdata)
59746ecee76SDavid du Colombier 		return devbread(ch, n, offset);
59846ecee76SDavid du Colombier 	return readdata(sdp->conv[CONV(ch->qid)], n);
59946ecee76SDavid du Colombier }
60046ecee76SDavid du Colombier 
60146ecee76SDavid du Colombier 
60246ecee76SDavid du Colombier static long
sdpwrite(Chan * ch,void * a,long n,vlong off)60346ecee76SDavid du Colombier sdpwrite(Chan *ch, void *a, long n, vlong off)
60446ecee76SDavid du Colombier {
60546ecee76SDavid du Colombier 	Sdp *sdp = sdptab + ch->dev;
60646ecee76SDavid du Colombier 	Cmdbuf *cb;
60746ecee76SDavid du Colombier 	char *arg0;
60846ecee76SDavid du Colombier 	char *p;
60946ecee76SDavid du Colombier 	Conv *c;
61046ecee76SDavid du Colombier 	Block *b;
61146ecee76SDavid du Colombier 
61246ecee76SDavid du Colombier 	USED(off);
61346ecee76SDavid du Colombier 	switch(TYPE(ch->qid)) {
61446ecee76SDavid du Colombier 	default:
61546ecee76SDavid du Colombier 		error(Eperm);
61646ecee76SDavid du Colombier 	case Qctl:
61746ecee76SDavid du Colombier 		c = sdp->conv[CONV(ch->qid)];
61846ecee76SDavid du Colombier 		cb = parsecmd(a, n);
61946ecee76SDavid du Colombier 		qlock(c);
62046ecee76SDavid du Colombier 		if(waserror()) {
62146ecee76SDavid du Colombier 			qunlock(c);
62246ecee76SDavid du Colombier 			free(cb);
62346ecee76SDavid du Colombier 			nexterror();
62446ecee76SDavid du Colombier 		}
62546ecee76SDavid du Colombier 		if(cb->nf == 0)
62646ecee76SDavid du Colombier 			error("short write");
62746ecee76SDavid du Colombier 		arg0 = cb->f[0];
62846ecee76SDavid du Colombier 		if(strcmp(arg0, "accept") == 0) {
62946ecee76SDavid du Colombier 			if(cb->nf != 2)
630567483c8SDavid du Colombier 				error("usage: accept file");
63146ecee76SDavid du Colombier 			convopenchan(c, cb->f[1]);
63246ecee76SDavid du Colombier 		} else if(strcmp(arg0, "dial") == 0) {
63346ecee76SDavid du Colombier 			if(cb->nf != 2)
634567483c8SDavid du Colombier 				error("usage: dial file");
63546ecee76SDavid du Colombier 			convopenchan(c, cb->f[1]);
63646ecee76SDavid du Colombier 			convsetstate(c, CDial);
63746ecee76SDavid du Colombier 		} else if(strcmp(arg0, "drop") == 0) {
63846ecee76SDavid du Colombier 			if(cb->nf != 2)
63946ecee76SDavid du Colombier 				error("usage: drop permil");
64046ecee76SDavid du Colombier 			c->drop = atoi(cb->f[1]);
64146ecee76SDavid du Colombier 		} else if(strcmp(arg0, "cipher") == 0) {
64246ecee76SDavid du Colombier 			if(cb->nf != 2)
64346ecee76SDavid du Colombier 				error("usage: cipher alg");
64446ecee76SDavid du Colombier 			setalg(c, cb->f[1], cipheralg, &c->cipher);
64546ecee76SDavid du Colombier 		} else if(strcmp(arg0, "auth") == 0) {
64646ecee76SDavid du Colombier 			if(cb->nf != 2)
64746ecee76SDavid du Colombier 				error("usage: auth alg");
64846ecee76SDavid du Colombier 			setalg(c, cb->f[1], authalg, &c->auth);
64946ecee76SDavid du Colombier 		} else if(strcmp(arg0, "comp") == 0) {
65046ecee76SDavid du Colombier 			if(cb->nf != 2)
65146ecee76SDavid du Colombier 				error("usage: comp alg");
65246ecee76SDavid du Colombier 			setalg(c, cb->f[1], compalg, &c->comp);
65346ecee76SDavid du Colombier 		} else if(strcmp(arg0, "insecret") == 0) {
65446ecee76SDavid du Colombier 			if(cb->nf != 2)
65546ecee76SDavid du Colombier 				error("usage: insecret secret");
65646ecee76SDavid du Colombier 			setsecret(&c->in, cb->f[1]);
65746ecee76SDavid du Colombier 			if(c->cipher)
65846ecee76SDavid du Colombier 				c->cipher->init(c);
65946ecee76SDavid du Colombier 			if(c->auth)
66046ecee76SDavid du Colombier 				c->auth->init(c);
66146ecee76SDavid du Colombier 		} else if(strcmp(arg0, "outsecret") == 0) {
66246ecee76SDavid du Colombier 			if(cb->nf != 2)
66346ecee76SDavid du Colombier 				error("usage: outsecret secret");
66446ecee76SDavid du Colombier 			setsecret(&c->out, cb->f[1]);
66546ecee76SDavid du Colombier 			if(c->cipher)
66646ecee76SDavid du Colombier 				c->cipher->init(c);
66746ecee76SDavid du Colombier 			if(c->auth)
66846ecee76SDavid du Colombier 				c->auth->init(c);
66946ecee76SDavid du Colombier 		} else
67046ecee76SDavid du Colombier 			error("unknown control request");
67146ecee76SDavid du Colombier 		poperror();
67246ecee76SDavid du Colombier 		qunlock(c);
67346ecee76SDavid du Colombier 		free(cb);
67446ecee76SDavid du Colombier 		return n;
67546ecee76SDavid du Colombier 	case Qlog:
67646ecee76SDavid du Colombier 		cb = parsecmd(a, n);
67746ecee76SDavid du Colombier 		p = logctl(sdp, cb->nf, cb->f, logflags);
67846ecee76SDavid du Colombier 		free(cb);
67946ecee76SDavid du Colombier 		if(p != nil)
68046ecee76SDavid du Colombier 			error(p);
68146ecee76SDavid du Colombier 		return n;
68246ecee76SDavid du Colombier 	case Qcontrol:
68346ecee76SDavid du Colombier 		writecontrol(sdp->conv[CONV(ch->qid)], a, n, 0);
68446ecee76SDavid du Colombier 		return n;
68546ecee76SDavid du Colombier 	case Qdata:
68646ecee76SDavid du Colombier 		b = allocb(n);
68746ecee76SDavid du Colombier 		memmove(b->wp, a, n);
68846ecee76SDavid du Colombier 		b->wp += n;
68946ecee76SDavid du Colombier 		return writedata(sdp->conv[CONV(ch->qid)], b);
69046ecee76SDavid du Colombier 	}
69146ecee76SDavid du Colombier }
69246ecee76SDavid du Colombier 
69346ecee76SDavid du Colombier long
sdpbwrite(Chan * ch,Block * bp,ulong offset)69446ecee76SDavid du Colombier sdpbwrite(Chan *ch, Block *bp, ulong offset)
69546ecee76SDavid du Colombier {
69646ecee76SDavid du Colombier 	Sdp *sdp = sdptab + ch->dev;
69746ecee76SDavid du Colombier 
69846ecee76SDavid du Colombier 	if(TYPE(ch->qid) != Qdata)
69946ecee76SDavid du Colombier 		return devbwrite(ch, bp, offset);
70046ecee76SDavid du Colombier 	return writedata(sdp->conv[CONV(ch->qid)], bp);
70146ecee76SDavid du Colombier }
70246ecee76SDavid du Colombier 
70346ecee76SDavid du Colombier static int
sdpgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)70446ecee76SDavid du Colombier sdpgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
70546ecee76SDavid du Colombier {
70646ecee76SDavid du Colombier 	Sdp *sdp = sdptab + c->dev;
70746ecee76SDavid du Colombier 	int type = TYPE(c->qid);
70846ecee76SDavid du Colombier 	Dirtab *dt;
70946ecee76SDavid du Colombier 	Qid qid;
71046ecee76SDavid du Colombier 
71146ecee76SDavid du Colombier 	if(s == DEVDOTDOT){
71246ecee76SDavid du Colombier 		switch(TYPE(c->qid)){
71346ecee76SDavid du Colombier 		case Qtopdir:
71446ecee76SDavid du Colombier 		case Qsdpdir:
71546ecee76SDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "#E%ld", c->dev);
71646ecee76SDavid du Colombier 			mkqid(&qid, Qtopdir, 0, QTDIR);
71746ecee76SDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
71846ecee76SDavid du Colombier 			break;
71946ecee76SDavid du Colombier 		case Qconvdir:
72046ecee76SDavid du Colombier 			snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
72146ecee76SDavid du Colombier 			mkqid(&qid, Qsdpdir, 0, QTDIR);
72246ecee76SDavid du Colombier 			devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
72346ecee76SDavid du Colombier 			break;
72446ecee76SDavid du Colombier 		default:
72546ecee76SDavid du Colombier 			panic("sdpwalk %llux", c->qid.path);
72646ecee76SDavid du Colombier 		}
72746ecee76SDavid du Colombier 		return 1;
72846ecee76SDavid du Colombier 	}
72946ecee76SDavid du Colombier 
73046ecee76SDavid du Colombier 	switch(type) {
73146ecee76SDavid du Colombier 	default:
73246ecee76SDavid du Colombier 		// non directory entries end up here
73346ecee76SDavid du Colombier 		if(c->qid.type & QTDIR)
73446ecee76SDavid du Colombier 			panic("sdpgen: unexpected directory");
73546ecee76SDavid du Colombier 		if(s != 0)
73646ecee76SDavid du Colombier 			return -1;
73746ecee76SDavid du Colombier 		dt = dirtab[TYPE(c->qid)];
73846ecee76SDavid du Colombier 		if(dt == nil)
73946ecee76SDavid du Colombier 			panic("sdpgen: unknown type: %lud", TYPE(c->qid));
74046ecee76SDavid du Colombier 		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
74146ecee76SDavid du Colombier 		return 1;
74246ecee76SDavid du Colombier 	case Qtopdir:
74346ecee76SDavid du Colombier 		if(s != 0)
74446ecee76SDavid du Colombier 			return -1;
74546ecee76SDavid du Colombier 		mkqid(&qid, QID(0, Qsdpdir), 0, QTDIR);
74646ecee76SDavid du Colombier 		devdir(c, qid, "sdp", 0, eve, 0555, dp);
74746ecee76SDavid du Colombier 		return 1;
74846ecee76SDavid du Colombier 	case Qsdpdir:
74946ecee76SDavid du Colombier 		if(s<nelem(sdpdirtab)) {
75046ecee76SDavid du Colombier 			dt = sdpdirtab+s;
75146ecee76SDavid du Colombier 			devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp);
75246ecee76SDavid du Colombier 			return 1;
75346ecee76SDavid du Colombier 		}
75446ecee76SDavid du Colombier 		s -= nelem(sdpdirtab);
75546ecee76SDavid du Colombier 		if(s >= sdp->nconv)
75646ecee76SDavid du Colombier 			return -1;
75746ecee76SDavid du Colombier 		mkqid(&qid, QID(s, Qconvdir), 0, QTDIR);
75846ecee76SDavid du Colombier 		snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
75946ecee76SDavid du Colombier 		devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
76046ecee76SDavid du Colombier 		return 1;
76146ecee76SDavid du Colombier 	case Qconvdir:
76246ecee76SDavid du Colombier 		if(s>=nelem(convdirtab))
76346ecee76SDavid du Colombier 			return -1;
76446ecee76SDavid du Colombier 		dt = convdirtab+s;
76546ecee76SDavid du Colombier 		mkqid(&qid, QID(CONV(c->qid),TYPE(dt->qid)), 0, QTFILE);
76646ecee76SDavid du Colombier 		devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);
76746ecee76SDavid du Colombier 		return 1;
76846ecee76SDavid du Colombier 	}
76946ecee76SDavid du Colombier }
77046ecee76SDavid du Colombier 
77146ecee76SDavid du Colombier static Conv*
sdpclone(Sdp * sdp)77246ecee76SDavid du Colombier sdpclone(Sdp *sdp)
77346ecee76SDavid du Colombier {
77446ecee76SDavid du Colombier 	Conv *c, **pp, **ep;
77546ecee76SDavid du Colombier 
77646ecee76SDavid du Colombier 	c = nil;
77746ecee76SDavid du Colombier 	ep = sdp->conv + nelem(sdp->conv);
77846ecee76SDavid du Colombier 	qlock(sdp);
77946ecee76SDavid du Colombier 	if(waserror()) {
78046ecee76SDavid du Colombier 		qunlock(sdp);
78146ecee76SDavid du Colombier 		nexterror();
78246ecee76SDavid du Colombier 	}
78346ecee76SDavid du Colombier 	for(pp = sdp->conv; pp < ep; pp++) {
78446ecee76SDavid du Colombier 		c = *pp;
78546ecee76SDavid du Colombier 		if(c == nil){
78646ecee76SDavid du Colombier 			c = malloc(sizeof(Conv));
78746ecee76SDavid du Colombier 			if(c == nil)
78846ecee76SDavid du Colombier 				error(Enomem);
78946ecee76SDavid du Colombier 			memset(c, 0, sizeof(Conv));
79046ecee76SDavid du Colombier 			qlock(c);
79146ecee76SDavid du Colombier 			c->sdp = sdp;
79246ecee76SDavid du Colombier 			c->id = pp - sdp->conv;
79346ecee76SDavid du Colombier 			*pp = c;
79446ecee76SDavid du Colombier 			sdp->nconv++;
79546ecee76SDavid du Colombier 			break;
79646ecee76SDavid du Colombier 		}
79746ecee76SDavid du Colombier 		if(c->ref == 0 && canqlock(c)){
79846ecee76SDavid du Colombier 			if(c->ref == 0)
79946ecee76SDavid du Colombier 				break;
80046ecee76SDavid du Colombier 			qunlock(c);
80146ecee76SDavid du Colombier 		}
80246ecee76SDavid du Colombier 	}
80346ecee76SDavid du Colombier 	poperror();
80446ecee76SDavid du Colombier 	qunlock(sdp);
80546ecee76SDavid du Colombier 
80646ecee76SDavid du Colombier 	if(pp >= ep)
80746ecee76SDavid du Colombier 		return nil;
80846ecee76SDavid du Colombier 
80946ecee76SDavid du Colombier 	assert(c->state == CFree);
81046ecee76SDavid du Colombier 	// set ref to 2 - 1 ref for open - 1 ref for channel state
81146ecee76SDavid du Colombier 	c->ref = 2;
81246ecee76SDavid du Colombier 	c->state = CInit;
81346ecee76SDavid du Colombier 	c->in.window = ~0;
81446ecee76SDavid du Colombier 	strncpy(c->owner, up->user, sizeof(c->owner));
81546ecee76SDavid du Colombier 	c->perm = 0660;
81646ecee76SDavid du Colombier 	qunlock(c);
81746ecee76SDavid du Colombier 
81846ecee76SDavid du Colombier 	return c;
81946ecee76SDavid du Colombier }
82046ecee76SDavid du Colombier 
82146ecee76SDavid du Colombier // assume c is locked
82246ecee76SDavid du Colombier static void
convretryinit(Conv * c)82346ecee76SDavid du Colombier convretryinit(Conv *c)
82446ecee76SDavid du Colombier {
82546ecee76SDavid du Colombier 	c->retries = 0;
82646ecee76SDavid du Colombier 	// +2 to avoid rounding effects.
82746ecee76SDavid du Colombier 	c->timeout = TK2SEC(m->ticks) + 2;
82846ecee76SDavid du Colombier }
82946ecee76SDavid du Colombier 
83046ecee76SDavid du Colombier // assume c is locked
83146ecee76SDavid du Colombier static int
convretry(Conv * c,int reset)83246ecee76SDavid du Colombier convretry(Conv *c, int reset)
83346ecee76SDavid du Colombier {
83446ecee76SDavid du Colombier 	c->retries++;
83546ecee76SDavid du Colombier 	if(c->retries > MaxRetries) {
83646ecee76SDavid du Colombier 		if(reset)
83746ecee76SDavid du Colombier 			convoconnect(c, ConReset, c->dialid, c->acceptid);
83846ecee76SDavid du Colombier 		convsetstate(c, CClosed);
83946ecee76SDavid du Colombier 		return 0;
84046ecee76SDavid du Colombier 	}
84146ecee76SDavid du Colombier 	c->timeout = TK2SEC(m->ticks) + (c->retries+1);
84246ecee76SDavid du Colombier 	return 1;
84346ecee76SDavid du Colombier }
84446ecee76SDavid du Colombier 
84546ecee76SDavid du Colombier // assumes c is locked
84646ecee76SDavid du Colombier static void
convtimer(Conv * c,ulong sec)84746ecee76SDavid du Colombier convtimer(Conv *c, ulong sec)
84846ecee76SDavid du Colombier {
84946ecee76SDavid du Colombier 	Block *b;
85046ecee76SDavid du Colombier 
85146ecee76SDavid du Colombier 	if(c->timeout > sec)
85246ecee76SDavid du Colombier 		return;
85346ecee76SDavid du Colombier 
85446ecee76SDavid du Colombier 	switch(c->state) {
85546ecee76SDavid du Colombier 	case CInit:
85646ecee76SDavid du Colombier 		break;
85746ecee76SDavid du Colombier 	case CDial:
85846ecee76SDavid du Colombier 		if(convretry(c, 1))
85946ecee76SDavid du Colombier 			convoconnect(c, ConOpenRequest, c->dialid, 0);
86046ecee76SDavid du Colombier 		break;
86146ecee76SDavid du Colombier 	case CAccept:
86246ecee76SDavid du Colombier 		if(convretry(c, 1))
86346ecee76SDavid du Colombier 			convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
86446ecee76SDavid du Colombier 		break;
86546ecee76SDavid du Colombier 	case COpen:
86646ecee76SDavid du Colombier 		b = c->out.controlpkt;
86746ecee76SDavid du Colombier 		if(b != nil) {
86846ecee76SDavid du Colombier 			if(convretry(c, 1))
86946ecee76SDavid du Colombier 				convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
87046ecee76SDavid du Colombier 			break;
87146ecee76SDavid du Colombier 		}
87246ecee76SDavid du Colombier 
87346ecee76SDavid du Colombier 		c->timeout = c->lastrecv + KeepAlive;
87446ecee76SDavid du Colombier 		if(c->timeout > sec)
87546ecee76SDavid du Colombier 			break;
87646ecee76SDavid du Colombier 		// keepalive - randomly spaced between KeepAlive and 2*KeepAlive
87746ecee76SDavid du Colombier 		if(c->timeout + KeepAlive > sec && nrand(c->lastrecv + 2*KeepAlive - sec) > 0)
87846ecee76SDavid du Colombier 			break;
87946ecee76SDavid du Colombier 		// can not use writecontrol
88046ecee76SDavid du Colombier 		b = allocb(4);
88146ecee76SDavid du Colombier 		c->out.controlseq++;
88246ecee76SDavid du Colombier 		hnputl(b->wp, c->out.controlseq);
88346ecee76SDavid du Colombier 		b->wp += 4;
88446ecee76SDavid du Colombier 		c->out.controlpkt = b;
88546ecee76SDavid du Colombier 		convretryinit(c);
88646ecee76SDavid du Colombier 		if(!waserror()) {
88746ecee76SDavid du Colombier 			convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
88846ecee76SDavid du Colombier 			poperror();
88946ecee76SDavid du Colombier 		}
89046ecee76SDavid du Colombier 		break;
89146ecee76SDavid du Colombier 	case CLocalClose:
89246ecee76SDavid du Colombier 		if(convretry(c, 0))
89346ecee76SDavid du Colombier 			convoconnect(c, ConClose, c->dialid, c->acceptid);
89446ecee76SDavid du Colombier 		break;
89546ecee76SDavid du Colombier 	case CRemoteClose:
89646ecee76SDavid du Colombier 	case CClosed:
89746ecee76SDavid du Colombier 		break;
89846ecee76SDavid du Colombier 	}
89946ecee76SDavid du Colombier }
90046ecee76SDavid du Colombier 
90146ecee76SDavid du Colombier 
90246ecee76SDavid du Colombier static void
sdpackproc(void * a)90346ecee76SDavid du Colombier sdpackproc(void *a)
90446ecee76SDavid du Colombier {
90546ecee76SDavid du Colombier 	Sdp *sdp = a;
90646ecee76SDavid du Colombier 	ulong sec;
90746ecee76SDavid du Colombier 	int i;
90846ecee76SDavid du Colombier 	Conv *c;
90946ecee76SDavid du Colombier 
91046ecee76SDavid du Colombier 	for(;;) {
91146ecee76SDavid du Colombier 		tsleep(&up->sleep, return0, 0, 1000);
91246ecee76SDavid du Colombier 		sec = TK2SEC(m->ticks);
91346ecee76SDavid du Colombier 		qlock(sdp);
91446ecee76SDavid du Colombier 		for(i=0; i<sdp->nconv; i++) {
91546ecee76SDavid du Colombier 			c = sdp->conv[i];
91646ecee76SDavid du Colombier 			if(c->ref == 0)
91746ecee76SDavid du Colombier 				continue;
91846ecee76SDavid du Colombier 			qunlock(sdp);
91946ecee76SDavid du Colombier 			qlock(c);
92046ecee76SDavid du Colombier 			if(c->ref > 0 && !waserror()) {
92146ecee76SDavid du Colombier 				convtimer(c, sec);
92246ecee76SDavid du Colombier 				poperror();
92346ecee76SDavid du Colombier 			}
92446ecee76SDavid du Colombier 			qunlock(c);
92546ecee76SDavid du Colombier 			qlock(sdp);
92646ecee76SDavid du Colombier 		}
92746ecee76SDavid du Colombier 		qunlock(sdp);
92846ecee76SDavid du Colombier 	}
92946ecee76SDavid du Colombier }
93046ecee76SDavid du Colombier 
93146ecee76SDavid du Colombier Dev sdpdevtab = {
93246ecee76SDavid du Colombier 	'E',
93346ecee76SDavid du Colombier 	"sdp",
93446ecee76SDavid du Colombier 
93546ecee76SDavid du Colombier 	devreset,
93646ecee76SDavid du Colombier 	sdpinit,
93746ecee76SDavid du Colombier 	devshutdown,
93846ecee76SDavid du Colombier 	sdpattach,
93946ecee76SDavid du Colombier 	sdpwalk,
94046ecee76SDavid du Colombier 	sdpstat,
94146ecee76SDavid du Colombier 	sdpopen,
94246ecee76SDavid du Colombier 	devcreate,
94346ecee76SDavid du Colombier 	sdpclose,
94446ecee76SDavid du Colombier 	sdpread,
94546ecee76SDavid du Colombier 	devbread,
94646ecee76SDavid du Colombier 	sdpwrite,
94746ecee76SDavid du Colombier 	devbwrite,
94846ecee76SDavid du Colombier 	devremove,
94946ecee76SDavid du Colombier 	devwstat,
95046ecee76SDavid du Colombier };
95146ecee76SDavid du Colombier 
95246ecee76SDavid du Colombier // assume hold lock on c
95346ecee76SDavid du Colombier static void
convsetstate(Conv * c,int state)95446ecee76SDavid du Colombier convsetstate(Conv *c, int state)
95546ecee76SDavid du Colombier {
95646ecee76SDavid du Colombier 
95746ecee76SDavid du Colombier if(0)print("convsetstate %d: %s -> %s\n", c->id, convstatename[c->state], convstatename[state]);
95846ecee76SDavid du Colombier 
95946ecee76SDavid du Colombier 	switch(state) {
96046ecee76SDavid du Colombier 	default:
96146ecee76SDavid du Colombier 		panic("setstate: bad state: %d", state);
96246ecee76SDavid du Colombier 	case CDial:
96346ecee76SDavid du Colombier 		assert(c->state == CInit);
96446ecee76SDavid du Colombier 		c->dialid = (rand()<<16) + rand();
96546ecee76SDavid du Colombier 		convretryinit(c);
96646ecee76SDavid du Colombier 		convoconnect(c, ConOpenRequest, c->dialid, 0);
96746ecee76SDavid du Colombier 		break;
96846ecee76SDavid du Colombier 	case CAccept:
96946ecee76SDavid du Colombier 		assert(c->state == CInit);
97046ecee76SDavid du Colombier 		c->acceptid = (rand()<<16) + rand();
97146ecee76SDavid du Colombier 		convretryinit(c);
97246ecee76SDavid du Colombier 		convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
97346ecee76SDavid du Colombier 		break;
97446ecee76SDavid du Colombier 	case COpen:
97546ecee76SDavid du Colombier 		assert(c->state == CDial || c->state == CAccept);
97646ecee76SDavid du Colombier 		c->lastrecv = TK2SEC(m->ticks);
97746ecee76SDavid du Colombier 		if(c->state == CDial) {
97846ecee76SDavid du Colombier 			convretryinit(c);
97946ecee76SDavid du Colombier 			convoconnect(c, ConOpenAckAck, c->dialid, c->acceptid);
98046ecee76SDavid du Colombier 			hnputl(c->in.secret, c->acceptid);
98146ecee76SDavid du Colombier 			hnputl(c->in.secret+4, c->dialid);
98246ecee76SDavid du Colombier 			hnputl(c->out.secret, c->dialid);
98346ecee76SDavid du Colombier 			hnputl(c->out.secret+4, c->acceptid);
98446ecee76SDavid du Colombier 		} else {
98546ecee76SDavid du Colombier 			hnputl(c->in.secret, c->dialid);
98646ecee76SDavid du Colombier 			hnputl(c->in.secret+4, c->acceptid);
98746ecee76SDavid du Colombier 			hnputl(c->out.secret, c->acceptid);
98846ecee76SDavid du Colombier 			hnputl(c->out.secret+4, c->dialid);
98946ecee76SDavid du Colombier 		}
99046ecee76SDavid du Colombier 		setalg(c, "hmac_md5_96", authalg, &c->auth);
99146ecee76SDavid du Colombier 		break;
99246ecee76SDavid du Colombier 	case CLocalClose:
99346ecee76SDavid du Colombier 		assert(c->state == CAccept || c->state == COpen);
99446ecee76SDavid du Colombier 		convretryinit(c);
99546ecee76SDavid du Colombier 		convoconnect(c, ConClose, c->dialid, c->acceptid);
99646ecee76SDavid du Colombier 		break;
99746ecee76SDavid du Colombier 	case CRemoteClose:
99846ecee76SDavid du Colombier 		wakeup(&c->in.controlready);
99946ecee76SDavid du Colombier 		wakeup(&c->out.controlready);
100046ecee76SDavid du Colombier 		break;
100146ecee76SDavid du Colombier 	case CClosed:
100246ecee76SDavid du Colombier 		wakeup(&c->in.controlready);
100346ecee76SDavid du Colombier 		wakeup(&c->out.controlready);
100446ecee76SDavid du Colombier 		if(c->readproc)
100546ecee76SDavid du Colombier 			postnote(c->readproc, 1, "interrupt", 0);
100646ecee76SDavid du Colombier 		if(c->state != CClosed)
100746ecee76SDavid du Colombier 			convderef(c);
100846ecee76SDavid du Colombier 		break;
100946ecee76SDavid du Colombier 	}
101046ecee76SDavid du Colombier 	c->state = state;
101146ecee76SDavid du Colombier }
101246ecee76SDavid du Colombier 
101346ecee76SDavid du Colombier 
101446ecee76SDavid du Colombier //assumes c is locked
101546ecee76SDavid du Colombier static void
convderef(Conv * c)101646ecee76SDavid du Colombier convderef(Conv *c)
101746ecee76SDavid du Colombier {
101846ecee76SDavid du Colombier 	c->ref--;
101946ecee76SDavid du Colombier 	if(c->ref > 0) {
102046ecee76SDavid du Colombier 		return;
102146ecee76SDavid du Colombier 	}
102246ecee76SDavid du Colombier 	assert(c->ref == 0);
102346ecee76SDavid du Colombier 	assert(c->dataopen == 0);
102446ecee76SDavid du Colombier 	assert(c->controlopen == 0);
102546ecee76SDavid du Colombier if(0)print("convderef: %d: ref == 0!\n", c->id);
102646ecee76SDavid du Colombier 	c->state = CFree;
102746ecee76SDavid du Colombier 	if(c->chan) {
102846ecee76SDavid du Colombier 		cclose(c->chan);
102946ecee76SDavid du Colombier 		c->chan = nil;
103046ecee76SDavid du Colombier 	}
103146ecee76SDavid du Colombier 	if(c->channame) {
103246ecee76SDavid du Colombier 		free(c->channame);
103346ecee76SDavid du Colombier 		c->channame = nil;
103446ecee76SDavid du Colombier 	}
103546ecee76SDavid du Colombier 	c->cipher = nil;
103646ecee76SDavid du Colombier 	c->auth = nil;
103746ecee76SDavid du Colombier 	c->comp = nil;
103846ecee76SDavid du Colombier 	strcpy(c->owner, "network");
103946ecee76SDavid du Colombier 	c->perm = 0660;
104046ecee76SDavid du Colombier 	c->dialid = 0;
104146ecee76SDavid du Colombier 	c->acceptid = 0;
104246ecee76SDavid du Colombier 	c->timeout = 0;
104346ecee76SDavid du Colombier 	c->retries = 0;
104446ecee76SDavid du Colombier 	c->drop = 0;
104546ecee76SDavid du Colombier 	onewaycleanup(&c->in);
104646ecee76SDavid du Colombier 	onewaycleanup(&c->out);
104746ecee76SDavid du Colombier 	memset(&c->lstats, 0, sizeof(Stats));
104846ecee76SDavid du Colombier 	memset(&c->rstats, 0, sizeof(Stats));
104946ecee76SDavid du Colombier }
105046ecee76SDavid du Colombier 
105146ecee76SDavid du Colombier static void
onewaycleanup(OneWay * ow)105246ecee76SDavid du Colombier onewaycleanup(OneWay *ow)
105346ecee76SDavid du Colombier {
105446ecee76SDavid du Colombier 	if(ow->controlpkt)
105546ecee76SDavid du Colombier 		freeb(ow->controlpkt);
105646ecee76SDavid du Colombier 	if(ow->authstate)
105746ecee76SDavid du Colombier 		free(ow->authstate);
105846ecee76SDavid du Colombier 	if(ow->cipherstate)
105946ecee76SDavid du Colombier 		free(ow->cipherstate);
106046ecee76SDavid du Colombier 	if(ow->compstate)
106146ecee76SDavid du Colombier 		free(ow->compstate);
106246ecee76SDavid du Colombier 	memset(ow, 0, sizeof(OneWay));
106346ecee76SDavid du Colombier }
106446ecee76SDavid du Colombier 
106546ecee76SDavid du Colombier 
106646ecee76SDavid du Colombier // assumes conv is locked
106746ecee76SDavid du Colombier static void
convopenchan(Conv * c,char * path)106846ecee76SDavid du Colombier convopenchan(Conv *c, char *path)
106946ecee76SDavid du Colombier {
107046ecee76SDavid du Colombier 	if(c->state != CInit || c->chan != nil)
107146ecee76SDavid du Colombier 		error("already connected");
107246ecee76SDavid du Colombier 	c->chan = namec(path, Aopen, ORDWR, 0);
107346ecee76SDavid du Colombier 	c->channame = smalloc(strlen(path)+1);
107446ecee76SDavid du Colombier 	strcpy(c->channame, path);
107546ecee76SDavid du Colombier 	if(waserror()) {
107646ecee76SDavid du Colombier 		cclose(c->chan);
107746ecee76SDavid du Colombier 		c->chan = nil;
107846ecee76SDavid du Colombier 		free(c->channame);
107946ecee76SDavid du Colombier 		c->channame = nil;
108046ecee76SDavid du Colombier 		nexterror();
108146ecee76SDavid du Colombier 	}
108246ecee76SDavid du Colombier 	kproc("convreader", convreader, c);
108346ecee76SDavid du Colombier 
108446ecee76SDavid du Colombier 	assert(c->reader == 0 && c->ref > 0);
108546ecee76SDavid du Colombier 	// after kproc in case it fails
108646ecee76SDavid du Colombier 	c->reader = 1;
108746ecee76SDavid du Colombier 	c->ref++;
108846ecee76SDavid du Colombier 
108946ecee76SDavid du Colombier 	poperror();
109046ecee76SDavid du Colombier }
109146ecee76SDavid du Colombier 
109246ecee76SDavid du Colombier static void
convstats(Conv * c,int local,char * buf,int n)109346ecee76SDavid du Colombier convstats(Conv *c, int local, char *buf, int n)
109446ecee76SDavid du Colombier {
109546ecee76SDavid du Colombier 	Stats *stats;
109646ecee76SDavid du Colombier 	char *p, *ep;
109746ecee76SDavid du Colombier 	int i;
109846ecee76SDavid du Colombier 
109946ecee76SDavid du Colombier 	if(local) {
110046ecee76SDavid du Colombier 		stats = &c->lstats;
110146ecee76SDavid du Colombier 	} else {
110246ecee76SDavid du Colombier 		if(!waserror()) {
110346ecee76SDavid du Colombier 			writecontrol(c, 0, 0, 1);
110446ecee76SDavid du Colombier 			poperror();
110546ecee76SDavid du Colombier 		}
110646ecee76SDavid du Colombier 		stats = &c->rstats;
110746ecee76SDavid du Colombier 	}
110846ecee76SDavid du Colombier 
110946ecee76SDavid du Colombier 	qlock(c);
111046ecee76SDavid du Colombier 	p = buf;
111146ecee76SDavid du Colombier 	ep = buf + n;
111246ecee76SDavid du Colombier 	p += snprint(p, ep-p, "outPackets: %lud\n", stats->outPackets);
111346ecee76SDavid du Colombier 	p += snprint(p, ep-p, "outDataPackets: %lud\n", stats->outDataPackets);
111446ecee76SDavid du Colombier 	p += snprint(p, ep-p, "outDataBytes: %lud\n", stats->outDataBytes);
111546ecee76SDavid du Colombier 	p += snprint(p, ep-p, "outCompDataBytes: %lud\n", stats->outCompDataBytes);
111646ecee76SDavid du Colombier 	for(i=0; i<NCompStats; i++) {
111746ecee76SDavid du Colombier 		if(stats->outCompStats[i] == 0)
111846ecee76SDavid du Colombier 			continue;
111946ecee76SDavid du Colombier 		p += snprint(p, ep-p, "outCompStats[%d]: %lud\n", i, stats->outCompStats[i]);
112046ecee76SDavid du Colombier 	}
112146ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inPackets: %lud\n", stats->inPackets);
112246ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inDataPackets: %lud\n", stats->inDataPackets);
112346ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inDataBytes: %lud\n", stats->inDataBytes);
112446ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inCompDataBytes: %lud\n", stats->inCompDataBytes);
112546ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inMissing: %lud\n", stats->inMissing);
112646ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inDup: %lud\n", stats->inDup);
112746ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inReorder: %lud\n", stats->inReorder);
112846ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inBadComp: %lud\n", stats->inBadComp);
112946ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inBadAuth: %lud\n", stats->inBadAuth);
113046ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inBadSeq: %lud\n", stats->inBadSeq);
113146ecee76SDavid du Colombier 	p += snprint(p, ep-p, "inBadOther: %lud\n", stats->inBadOther);
113246ecee76SDavid du Colombier 	USED(p);
113346ecee76SDavid du Colombier 	qunlock(c);
113446ecee76SDavid du Colombier }
113546ecee76SDavid du Colombier 
113646ecee76SDavid du Colombier // c is locked
113746ecee76SDavid du Colombier static void
convack(Conv * c)113846ecee76SDavid du Colombier convack(Conv *c)
113946ecee76SDavid du Colombier {
114046ecee76SDavid du Colombier 	Block *b;
114146ecee76SDavid du Colombier 	AckPkt *ack;
114246ecee76SDavid du Colombier 	Stats *s;
114346ecee76SDavid du Colombier 	int i;
114446ecee76SDavid du Colombier 
114546ecee76SDavid du Colombier 	b = allocb(sizeof(AckPkt));
114646ecee76SDavid du Colombier 	ack = (AckPkt*)b->wp;
114746ecee76SDavid du Colombier 	b->wp += sizeof(AckPkt);
114846ecee76SDavid du Colombier 	s = &c->lstats;
114946ecee76SDavid du Colombier 	hnputl(ack->cseq, c->in.controlseq);
115046ecee76SDavid du Colombier 	hnputl(ack->outPackets, s->outPackets);
115146ecee76SDavid du Colombier 	hnputl(ack->outDataPackets, s->outDataPackets);
115246ecee76SDavid du Colombier 	hnputl(ack->outDataBytes, s->outDataBytes);
115346ecee76SDavid du Colombier 	hnputl(ack->outCompDataBytes, s->outCompDataBytes);
115446ecee76SDavid du Colombier 	for(i=0; i<NCompStats; i++)
115546ecee76SDavid du Colombier 		hnputl(ack->outCompStats+i*4, s->outCompStats[i]);
115646ecee76SDavid du Colombier 	hnputl(ack->inPackets, s->inPackets);
115746ecee76SDavid du Colombier 	hnputl(ack->inDataPackets, s->inDataPackets);
115846ecee76SDavid du Colombier 	hnputl(ack->inDataBytes, s->inDataBytes);
115946ecee76SDavid du Colombier 	hnputl(ack->inCompDataBytes, s->inCompDataBytes);
116046ecee76SDavid du Colombier 	hnputl(ack->inMissing, s->inMissing);
116146ecee76SDavid du Colombier 	hnputl(ack->inDup, s->inDup);
116246ecee76SDavid du Colombier 	hnputl(ack->inReorder, s->inReorder);
116346ecee76SDavid du Colombier 	hnputl(ack->inBadComp, s->inBadComp);
116446ecee76SDavid du Colombier 	hnputl(ack->inBadAuth, s->inBadAuth);
116546ecee76SDavid du Colombier 	hnputl(ack->inBadSeq, s->inBadSeq);
116646ecee76SDavid du Colombier 	hnputl(ack->inBadOther, s->inBadOther);
116746ecee76SDavid du Colombier 	convoput(c, TControl, ControlAck, b);
116846ecee76SDavid du Colombier }
116946ecee76SDavid du Colombier 
117046ecee76SDavid du Colombier 
117146ecee76SDavid du Colombier // assume we hold lock for c
117246ecee76SDavid du Colombier static Block *
conviput(Conv * c,Block * b,int control)117346ecee76SDavid du Colombier conviput(Conv *c, Block *b, int control)
117446ecee76SDavid du Colombier {
117546ecee76SDavid du Colombier 	int type, subtype;
117646ecee76SDavid du Colombier 	ulong seq, seqwrap;
117746ecee76SDavid du Colombier 	long seqdiff;
117846ecee76SDavid du Colombier 	int pad;
117946ecee76SDavid du Colombier 
118046ecee76SDavid du Colombier 	c->lstats.inPackets++;
118146ecee76SDavid du Colombier 
118246ecee76SDavid du Colombier 	if(BLEN(b) < 4) {
118346ecee76SDavid du Colombier 		c->lstats.inBadOther++;
118446ecee76SDavid du Colombier 		freeb(b);
118546ecee76SDavid du Colombier 		return nil;
118646ecee76SDavid du Colombier 	}
118746ecee76SDavid du Colombier 
118846ecee76SDavid du Colombier 	type = b->rp[0] >> 4;
118946ecee76SDavid du Colombier 	subtype = b->rp[0] & 0xf;
119046ecee76SDavid du Colombier 	b->rp += 1;
119146ecee76SDavid du Colombier 	if(type == TConnect) {
119246ecee76SDavid du Colombier 		conviconnect(c, subtype, b);
119346ecee76SDavid du Colombier 		return nil;
119446ecee76SDavid du Colombier 	}
119546ecee76SDavid du Colombier 
119646ecee76SDavid du Colombier 	switch(c->state) {
119746ecee76SDavid du Colombier 	case CInit:
119846ecee76SDavid du Colombier 	case CDial:
119946ecee76SDavid du Colombier 		c->lstats.inBadOther++;
120046ecee76SDavid du Colombier 		convoconnect(c, ConReset, c->dialid, c->acceptid);
120146ecee76SDavid du Colombier 		convsetstate(c, CClosed);
120246ecee76SDavid du Colombier 		break;
120346ecee76SDavid du Colombier 	case CAccept:
120446ecee76SDavid du Colombier 	case CRemoteClose:
120546ecee76SDavid du Colombier 	case CLocalClose:
120646ecee76SDavid du Colombier 		c->lstats.inBadOther++;
120746ecee76SDavid du Colombier 		freeb(b);
120846ecee76SDavid du Colombier 		return nil;
120946ecee76SDavid du Colombier 	}
121046ecee76SDavid du Colombier 
121146ecee76SDavid du Colombier 	seq = (b->rp[0]<<16) + (b->rp[1]<<8) + b->rp[2];
121246ecee76SDavid du Colombier 	b->rp += 3;
121346ecee76SDavid du Colombier 
121446ecee76SDavid du Colombier 	seqwrap = c->in.seqwrap;
121546ecee76SDavid du Colombier 	seqdiff = seq - c->in.seq;
121646ecee76SDavid du Colombier 	if(seqdiff < -(SeqMax*3/4)) {
121746ecee76SDavid du Colombier 		seqwrap++;
121846ecee76SDavid du Colombier 		seqdiff += SeqMax;
121946ecee76SDavid du Colombier 	} else if(seqdiff > SeqMax*3/4) {
122046ecee76SDavid du Colombier 		seqwrap--;
122146ecee76SDavid du Colombier 		seqdiff -= SeqMax;
122246ecee76SDavid du Colombier 	}
122346ecee76SDavid du Colombier 
122446ecee76SDavid du Colombier 	if(seqdiff <= 0) {
122546ecee76SDavid du Colombier 		if(seqdiff <= -SeqWindow) {
122646ecee76SDavid du Colombier if(0)print("old sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff);
122746ecee76SDavid du Colombier 			c->lstats.inBadSeq++;
122846ecee76SDavid du Colombier 			freeb(b);
122946ecee76SDavid du Colombier 			return nil;
123046ecee76SDavid du Colombier 		}
123146ecee76SDavid du Colombier 
123246ecee76SDavid du Colombier 		if(c->in.window & (1<<-seqdiff)) {
123346ecee76SDavid du Colombier if(0)print("dup sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff);
123446ecee76SDavid du Colombier 			c->lstats.inDup++;
123546ecee76SDavid du Colombier 			freeb(b);
123646ecee76SDavid du Colombier 			return nil;
123746ecee76SDavid du Colombier 		}
123846ecee76SDavid du Colombier 
123946ecee76SDavid du Colombier 		c->lstats.inReorder++;
124046ecee76SDavid du Colombier 	}
124146ecee76SDavid du Colombier 
124246ecee76SDavid du Colombier 	// ok the sequence number looks ok
124346ecee76SDavid du Colombier if(0) print("coniput seq=%ulx\n", seq);
124446ecee76SDavid du Colombier 	if(c->in.auth != 0) {
124546ecee76SDavid du Colombier 		if(!(*c->in.auth)(&c->in, b->rp-4, BLEN(b)+4)) {
124646ecee76SDavid du Colombier if(0)print("bad auth %ld\n", BLEN(b)+4);
124746ecee76SDavid du Colombier 			c->lstats.inBadAuth++;
124846ecee76SDavid du Colombier 			freeb(b);
124946ecee76SDavid du Colombier 			return nil;
125046ecee76SDavid du Colombier 		}
125146ecee76SDavid du Colombier 		b->wp -= c->in.authlen;
125246ecee76SDavid du Colombier 	}
125346ecee76SDavid du Colombier 
125446ecee76SDavid du Colombier 	if(c->in.cipher != 0) {
125546ecee76SDavid du Colombier 		if(!(*c->in.cipher)(&c->in, b->rp, BLEN(b))) {
125646ecee76SDavid du Colombier if(0)print("bad cipher\n");
125746ecee76SDavid du Colombier 			c->lstats.inBadOther++;
125846ecee76SDavid du Colombier 			freeb(b);
125946ecee76SDavid du Colombier 			return nil;
126046ecee76SDavid du Colombier 		}
126146ecee76SDavid du Colombier 		b->rp += c->in.cipherivlen;
126246ecee76SDavid du Colombier 		if(c->in.cipherblklen > 1) {
126346ecee76SDavid du Colombier 			pad = b->wp[-1];
126446ecee76SDavid du Colombier 			if(pad > BLEN(b)) {
126546ecee76SDavid du Colombier if(0)print("pad too big\n");
126646ecee76SDavid du Colombier 				c->lstats.inBadOther++;
126746ecee76SDavid du Colombier 				freeb(b);
126846ecee76SDavid du Colombier 				return nil;
126946ecee76SDavid du Colombier 			}
127046ecee76SDavid du Colombier 			b->wp -= pad;
127146ecee76SDavid du Colombier 		}
127246ecee76SDavid du Colombier 	}
127346ecee76SDavid du Colombier 
127446ecee76SDavid du Colombier 	// ok the packet is good
127546ecee76SDavid du Colombier 	if(seqdiff > 0) {
127646ecee76SDavid du Colombier 		while(seqdiff > 0 && c->in.window != 0) {
127746ecee76SDavid du Colombier 			if((c->in.window & (1<<(SeqWindow-1))) == 0) {
127846ecee76SDavid du Colombier 				c->lstats.inMissing++;
127946ecee76SDavid du Colombier 			}
128046ecee76SDavid du Colombier 			c->in.window <<= 1;
128146ecee76SDavid du Colombier 			seqdiff--;
128246ecee76SDavid du Colombier 		}
128346ecee76SDavid du Colombier 		if(seqdiff > 0) {
128446ecee76SDavid du Colombier 			c->lstats.inMissing += seqdiff;
128546ecee76SDavid du Colombier 			seqdiff = 0;
128646ecee76SDavid du Colombier 		}
128746ecee76SDavid du Colombier 		c->in.seq = seq;
128846ecee76SDavid du Colombier 		c->in.seqwrap = seqwrap;
128946ecee76SDavid du Colombier 	}
129046ecee76SDavid du Colombier 	c->in.window |= 1<<-seqdiff;
129146ecee76SDavid du Colombier 	c->lastrecv = TK2SEC(m->ticks);
129246ecee76SDavid du Colombier 
129346ecee76SDavid du Colombier 	switch(type) {
129446ecee76SDavid du Colombier 	case TControl:
129546ecee76SDavid du Colombier 		convicontrol(c, subtype, b);
129646ecee76SDavid du Colombier 		return nil;
129746ecee76SDavid du Colombier 	case TData:
129846ecee76SDavid du Colombier 		c->lstats.inDataPackets++;
129946ecee76SDavid du Colombier 		c->lstats.inDataBytes += BLEN(b);
130046ecee76SDavid du Colombier 		if(control)
130146ecee76SDavid du Colombier 			break;
130246ecee76SDavid du Colombier 		return b;
130346ecee76SDavid du Colombier 	case TCompData:
130446ecee76SDavid du Colombier 		c->lstats.inDataPackets++;
130546ecee76SDavid du Colombier 		c->lstats.inCompDataBytes += BLEN(b);
130646ecee76SDavid du Colombier 		b = convicomp(c, subtype, seq, b);
130746ecee76SDavid du Colombier 		if(b == nil) {
130846ecee76SDavid du Colombier 			c->lstats.inBadComp++;
130946ecee76SDavid du Colombier 			return nil;
131046ecee76SDavid du Colombier 		}
131146ecee76SDavid du Colombier 		c->lstats.inDataBytes += BLEN(b);
131246ecee76SDavid du Colombier 		if(control)
131346ecee76SDavid du Colombier 			break;
131446ecee76SDavid du Colombier 		return b;
131546ecee76SDavid du Colombier 	}
131646ecee76SDavid du Colombier if(0)print("dropping packet id=%d: type=%d n=%ld control=%d\n", c->id, type, BLEN(b), control);
131746ecee76SDavid du Colombier 	c->lstats.inBadOther++;
131846ecee76SDavid du Colombier 	freeb(b);
131946ecee76SDavid du Colombier 	return nil;
132046ecee76SDavid du Colombier }
132146ecee76SDavid du Colombier 
132246ecee76SDavid du Colombier // assume hold conv lock
132346ecee76SDavid du Colombier static void
conviconnect(Conv * c,int subtype,Block * b)132446ecee76SDavid du Colombier conviconnect(Conv *c, int subtype, Block *b)
132546ecee76SDavid du Colombier {
132646ecee76SDavid du Colombier 	ulong dialid;
132746ecee76SDavid du Colombier 	ulong acceptid;
132846ecee76SDavid du Colombier 
132946ecee76SDavid du Colombier 	if(BLEN(b) != 8) {
133046ecee76SDavid du Colombier 		freeb(b);
133146ecee76SDavid du Colombier 		return;
133246ecee76SDavid du Colombier 	}
133346ecee76SDavid du Colombier 	dialid = nhgetl(b->rp);
133446ecee76SDavid du Colombier 	acceptid = nhgetl(b->rp + 4);
133546ecee76SDavid du Colombier 	freeb(b);
133646ecee76SDavid du Colombier 
1337d9814433SDavid du Colombier if(0)print("sdp: conviconnect: %s: %d %uld %uld\n", convstatename[c->state], subtype, dialid, acceptid);
133846ecee76SDavid du Colombier 
133946ecee76SDavid du Colombier 	if(subtype == ConReset) {
134046ecee76SDavid du Colombier 		convsetstate(c, CClosed);
134146ecee76SDavid du Colombier 		return;
134246ecee76SDavid du Colombier 	}
134346ecee76SDavid du Colombier 
134446ecee76SDavid du Colombier 	switch(c->state) {
134546ecee76SDavid du Colombier 	default:
134646ecee76SDavid du Colombier 		panic("unknown state: %d", c->state);
134746ecee76SDavid du Colombier 	case CInit:
134846ecee76SDavid du Colombier 		break;
134946ecee76SDavid du Colombier 	case CDial:
135046ecee76SDavid du Colombier 		if(dialid != c->dialid)
135146ecee76SDavid du Colombier 			goto Reset;
135246ecee76SDavid du Colombier 		break;
135346ecee76SDavid du Colombier 	case CAccept:
135446ecee76SDavid du Colombier 	case COpen:
135546ecee76SDavid du Colombier 	case CLocalClose:
135646ecee76SDavid du Colombier 	case CRemoteClose:
135746ecee76SDavid du Colombier 		if(dialid != c->dialid
135846ecee76SDavid du Colombier 		|| subtype != ConOpenRequest && acceptid != c->acceptid)
135946ecee76SDavid du Colombier 			goto Reset;
136046ecee76SDavid du Colombier 		break;
136146ecee76SDavid du Colombier 	case CClosed:
136246ecee76SDavid du Colombier 		goto Reset;
136346ecee76SDavid du Colombier 	}
136446ecee76SDavid du Colombier 
136546ecee76SDavid du Colombier 	switch(subtype) {
136646ecee76SDavid du Colombier 	case ConOpenRequest:
136746ecee76SDavid du Colombier 		switch(c->state) {
136846ecee76SDavid du Colombier 		case CInit:
136946ecee76SDavid du Colombier 			c->dialid = dialid;
137046ecee76SDavid du Colombier 			convsetstate(c, CAccept);
137146ecee76SDavid du Colombier 			return;
137246ecee76SDavid du Colombier 		case CAccept:
137346ecee76SDavid du Colombier 		case COpen:
137446ecee76SDavid du Colombier 			// duplicate ConOpenRequest that we ignore
137546ecee76SDavid du Colombier 			return;
137646ecee76SDavid du Colombier 		}
137746ecee76SDavid du Colombier 		break;
137846ecee76SDavid du Colombier 	case ConOpenAck:
137946ecee76SDavid du Colombier 		switch(c->state) {
138046ecee76SDavid du Colombier 		case CDial:
138146ecee76SDavid du Colombier 			c->acceptid = acceptid;
138246ecee76SDavid du Colombier 			convsetstate(c, COpen);
138346ecee76SDavid du Colombier 			return;
138446ecee76SDavid du Colombier 		case COpen:
138546ecee76SDavid du Colombier 			// duplicate that we have to ack
138646ecee76SDavid du Colombier 			convoconnect(c, ConOpenAckAck, acceptid, dialid);
138746ecee76SDavid du Colombier 			return;
138846ecee76SDavid du Colombier 		}
138946ecee76SDavid du Colombier 		break;
139046ecee76SDavid du Colombier 	case ConOpenAckAck:
139146ecee76SDavid du Colombier 		switch(c->state) {
139246ecee76SDavid du Colombier 		case CAccept:
139346ecee76SDavid du Colombier 			convsetstate(c, COpen);
139446ecee76SDavid du Colombier 			return;
139546ecee76SDavid du Colombier 		case COpen:
139646ecee76SDavid du Colombier 		case CLocalClose:
139746ecee76SDavid du Colombier 		case CRemoteClose:
139846ecee76SDavid du Colombier 			// duplicate that we ignore
139946ecee76SDavid du Colombier 			return;
140046ecee76SDavid du Colombier 		}
140146ecee76SDavid du Colombier 		break;
140246ecee76SDavid du Colombier 	case ConClose:
140346ecee76SDavid du Colombier 		switch(c->state) {
140446ecee76SDavid du Colombier 		case COpen:
140546ecee76SDavid du Colombier 			convoconnect(c, ConCloseAck, dialid, acceptid);
140646ecee76SDavid du Colombier 			convsetstate(c, CRemoteClose);
140746ecee76SDavid du Colombier 			return;
140846ecee76SDavid du Colombier 		case CRemoteClose:
140946ecee76SDavid du Colombier 			// duplicate ConClose
141046ecee76SDavid du Colombier 			convoconnect(c, ConCloseAck, dialid, acceptid);
141146ecee76SDavid du Colombier 			return;
141246ecee76SDavid du Colombier 		}
141346ecee76SDavid du Colombier 		break;
141446ecee76SDavid du Colombier 	case ConCloseAck:
141546ecee76SDavid du Colombier 		switch(c->state) {
141646ecee76SDavid du Colombier 		case CLocalClose:
141746ecee76SDavid du Colombier 			convsetstate(c, CClosed);
141846ecee76SDavid du Colombier 			return;
141946ecee76SDavid du Colombier 		}
142046ecee76SDavid du Colombier 		break;
142146ecee76SDavid du Colombier 	}
142246ecee76SDavid du Colombier Reset:
142346ecee76SDavid du Colombier 	// invalid connection message - reset to sender
1424d9814433SDavid du Colombier if(1)print("sdp: invalid conviconnect - sending reset\n");
142546ecee76SDavid du Colombier 	convoconnect(c, ConReset, dialid, acceptid);
142646ecee76SDavid du Colombier 	convsetstate(c, CClosed);
142746ecee76SDavid du Colombier }
142846ecee76SDavid du Colombier 
142946ecee76SDavid du Colombier static void
convicontrol(Conv * c,int subtype,Block * b)143046ecee76SDavid du Colombier convicontrol(Conv *c, int subtype, Block *b)
143146ecee76SDavid du Colombier {
143246ecee76SDavid du Colombier 	ulong cseq;
143346ecee76SDavid du Colombier 	AckPkt *ack;
143446ecee76SDavid du Colombier 	int i;
143546ecee76SDavid du Colombier 
143646ecee76SDavid du Colombier 	if(BLEN(b) < 4)
143746ecee76SDavid du Colombier 		return;
143846ecee76SDavid du Colombier 	cseq = nhgetl(b->rp);
143946ecee76SDavid du Colombier 
144046ecee76SDavid du Colombier 	switch(subtype){
144146ecee76SDavid du Colombier 	case ControlMesg:
144246ecee76SDavid du Colombier 		if(cseq == c->in.controlseq) {
144346ecee76SDavid du Colombier if(0)print("duplicate control packet: %ulx\n", cseq);
144446ecee76SDavid du Colombier 			// duplicate control packet
144546ecee76SDavid du Colombier 			freeb(b);
144646ecee76SDavid du Colombier 			if(c->in.controlpkt == nil)
144746ecee76SDavid du Colombier 				convack(c);
144846ecee76SDavid du Colombier 			return;
144946ecee76SDavid du Colombier 		}
145046ecee76SDavid du Colombier 
145146ecee76SDavid du Colombier 		if(cseq != c->in.controlseq+1)
145246ecee76SDavid du Colombier 			return;
145346ecee76SDavid du Colombier 		c->in.controlseq = cseq;
145446ecee76SDavid du Colombier 		b->rp += 4;
145546ecee76SDavid du Colombier 		if(BLEN(b) == 0) {
145646ecee76SDavid du Colombier 			// just a ping
145746ecee76SDavid du Colombier 			freeb(b);
145846ecee76SDavid du Colombier 			convack(c);
145946ecee76SDavid du Colombier 		} else {
146046ecee76SDavid du Colombier 			c->in.controlpkt = b;
146146ecee76SDavid du Colombier if(0) print("recv %ld size=%ld\n", cseq, BLEN(b));
146246ecee76SDavid du Colombier 			wakeup(&c->in.controlready);
146346ecee76SDavid du Colombier 		}
146446ecee76SDavid du Colombier 		return;
146546ecee76SDavid du Colombier 	case ControlAck:
146646ecee76SDavid du Colombier 		if(cseq != c->out.controlseq)
146746ecee76SDavid du Colombier 			return;
146846ecee76SDavid du Colombier 		if(BLEN(b) < sizeof(AckPkt))
146946ecee76SDavid du Colombier 			return;
147046ecee76SDavid du Colombier 		ack = (AckPkt*)(b->rp);
147146ecee76SDavid du Colombier 		c->rstats.outPackets = nhgetl(ack->outPackets);
147246ecee76SDavid du Colombier 		c->rstats.outDataPackets = nhgetl(ack->outDataPackets);
147346ecee76SDavid du Colombier 		c->rstats.outDataBytes = nhgetl(ack->outDataBytes);
147446ecee76SDavid du Colombier 		c->rstats.outCompDataBytes = nhgetl(ack->outCompDataBytes);
147546ecee76SDavid du Colombier 		for(i=0; i<NCompStats; i++)
147646ecee76SDavid du Colombier 			c->rstats.outCompStats[i] = nhgetl(ack->outCompStats + 4*i);
147746ecee76SDavid du Colombier 		c->rstats.inPackets = nhgetl(ack->inPackets);
147846ecee76SDavid du Colombier 		c->rstats.inDataPackets = nhgetl(ack->inDataPackets);
147946ecee76SDavid du Colombier 		c->rstats.inDataBytes = nhgetl(ack->inDataBytes);
148046ecee76SDavid du Colombier 		c->rstats.inCompDataBytes = nhgetl(ack->inCompDataBytes);
148146ecee76SDavid du Colombier 		c->rstats.inMissing = nhgetl(ack->inMissing);
148246ecee76SDavid du Colombier 		c->rstats.inDup = nhgetl(ack->inDup);
148346ecee76SDavid du Colombier 		c->rstats.inReorder = nhgetl(ack->inReorder);
148446ecee76SDavid du Colombier 		c->rstats.inBadComp = nhgetl(ack->inBadComp);
148546ecee76SDavid du Colombier 		c->rstats.inBadAuth = nhgetl(ack->inBadAuth);
148646ecee76SDavid du Colombier 		c->rstats.inBadSeq = nhgetl(ack->inBadSeq);
148746ecee76SDavid du Colombier 		c->rstats.inBadOther = nhgetl(ack->inBadOther);
148846ecee76SDavid du Colombier 		freeb(b);
148946ecee76SDavid du Colombier 		freeb(c->out.controlpkt);
149046ecee76SDavid du Colombier 		c->out.controlpkt = nil;
149146ecee76SDavid du Colombier 		c->timeout = c->lastrecv + KeepAlive;
149246ecee76SDavid du Colombier 		wakeup(&c->out.controlready);
149346ecee76SDavid du Colombier 		return;
149446ecee76SDavid du Colombier 	}
149546ecee76SDavid du Colombier }
149646ecee76SDavid du Colombier 
149746ecee76SDavid du Colombier static Block*
convicomp(Conv * c,int subtype,ulong seq,Block * b)149846ecee76SDavid du Colombier convicomp(Conv *c, int subtype, ulong seq, Block *b)
149946ecee76SDavid du Colombier {
150046ecee76SDavid du Colombier 	if(c->in.comp == nil) {
150146ecee76SDavid du Colombier 		freeb(b);
150246ecee76SDavid du Colombier 		return nil;
150346ecee76SDavid du Colombier 	}
150446ecee76SDavid du Colombier 	if(!(*c->in.comp)(c, subtype, seq, &b))
150546ecee76SDavid du Colombier 		return nil;
150646ecee76SDavid du Colombier 	return b;
150746ecee76SDavid du Colombier }
150846ecee76SDavid du Colombier 
150946ecee76SDavid du Colombier // c is locked
151046ecee76SDavid du Colombier static void
convwriteblock(Conv * c,Block * b)151146ecee76SDavid du Colombier convwriteblock(Conv *c, Block *b)
151246ecee76SDavid du Colombier {
151346ecee76SDavid du Colombier 	// simulated errors
151446ecee76SDavid du Colombier 	if(c->drop && nrand(c->drop) == 0)
151546ecee76SDavid du Colombier 		return;
151646ecee76SDavid du Colombier 
151746ecee76SDavid du Colombier 	if(waserror()) {
151846ecee76SDavid du Colombier 		convsetstate(c, CClosed);
151946ecee76SDavid du Colombier 		nexterror();
152046ecee76SDavid du Colombier 	}
152146ecee76SDavid du Colombier 	devtab[c->chan->type]->bwrite(c->chan, b, 0);
152246ecee76SDavid du Colombier 	poperror();
152346ecee76SDavid du Colombier }
152446ecee76SDavid du Colombier 
152546ecee76SDavid du Colombier 
152646ecee76SDavid du Colombier // assume hold conv lock
152746ecee76SDavid du Colombier static void
convoput(Conv * c,int type,int subtype,Block * b)152846ecee76SDavid du Colombier convoput(Conv *c, int type, int subtype, Block *b)
152946ecee76SDavid du Colombier {
153046ecee76SDavid du Colombier 	int pad;
153146ecee76SDavid du Colombier 
153246ecee76SDavid du Colombier 	c->lstats.outPackets++;
153346ecee76SDavid du Colombier 	/* Make room for sdp trailer */
153446ecee76SDavid du Colombier 	if(c->out.cipherblklen > 1)
153546ecee76SDavid du Colombier 		pad = c->out.cipherblklen - (BLEN(b) + c->out.cipherivlen) % c->out.cipherblklen;
153646ecee76SDavid du Colombier 	else
153746ecee76SDavid du Colombier 		pad = 0;
153846ecee76SDavid du Colombier 
153946ecee76SDavid du Colombier 	b = padblock(b, -(pad+c->out.authlen));
154046ecee76SDavid du Colombier 
154146ecee76SDavid du Colombier 	if(pad) {
154246ecee76SDavid du Colombier 		memset(b->wp, 0, pad-1);
154346ecee76SDavid du Colombier 		b->wp[pad-1] = pad;
154446ecee76SDavid du Colombier 		b->wp += pad;
154546ecee76SDavid du Colombier 	}
154646ecee76SDavid du Colombier 
154746ecee76SDavid du Colombier 	/* Make space to fit sdp header */
154846ecee76SDavid du Colombier 	b = padblock(b, 4 + c->out.cipherivlen);
154946ecee76SDavid du Colombier 	b->rp[0] = (type << 4) | subtype;
155046ecee76SDavid du Colombier 	c->out.seq++;
155146ecee76SDavid du Colombier 	if(c->out.seq == (1<<24)) {
155246ecee76SDavid du Colombier 		c->out.seq = 0;
155346ecee76SDavid du Colombier 		c->out.seqwrap++;
155446ecee76SDavid du Colombier 	}
155546ecee76SDavid du Colombier 	b->rp[1] = c->out.seq>>16;
155646ecee76SDavid du Colombier 	b->rp[2] = c->out.seq>>8;
155746ecee76SDavid du Colombier 	b->rp[3] = c->out.seq;
155846ecee76SDavid du Colombier 
155946ecee76SDavid du Colombier 	if(c->out.cipher)
156046ecee76SDavid du Colombier 		(*c->out.cipher)(&c->out, b->rp+4, BLEN(b)-4);
156146ecee76SDavid du Colombier 
156246ecee76SDavid du Colombier 	// auth
156346ecee76SDavid du Colombier 	if(c->out.auth) {
156446ecee76SDavid du Colombier 		b->wp += c->out.authlen;
156546ecee76SDavid du Colombier 		(*c->out.auth)(&c->out, b->rp, BLEN(b));
156646ecee76SDavid du Colombier 	}
156746ecee76SDavid du Colombier 
156846ecee76SDavid du Colombier 	convwriteblock(c, b);
156946ecee76SDavid du Colombier }
157046ecee76SDavid du Colombier 
157146ecee76SDavid du Colombier // assume hold conv lock
157246ecee76SDavid du Colombier static void
convoconnect(Conv * c,int op,ulong dialid,ulong acceptid)157346ecee76SDavid du Colombier convoconnect(Conv *c, int op, ulong dialid, ulong acceptid)
157446ecee76SDavid du Colombier {
157546ecee76SDavid du Colombier 	Block *b;
157646ecee76SDavid du Colombier 
157746ecee76SDavid du Colombier 	c->lstats.outPackets++;
157846ecee76SDavid du Colombier 	assert(c->chan != nil);
157946ecee76SDavid du Colombier 	b = allocb(9);
158046ecee76SDavid du Colombier 	b->wp[0] = (TConnect << 4) | op;
158146ecee76SDavid du Colombier 	hnputl(b->wp+1, dialid);
158246ecee76SDavid du Colombier 	hnputl(b->wp+5, acceptid);
158346ecee76SDavid du Colombier 	b->wp += 9;
158446ecee76SDavid du Colombier 
158546ecee76SDavid du Colombier 	if(!waserror()) {
158646ecee76SDavid du Colombier 		convwriteblock(c, b);
158746ecee76SDavid du Colombier 		poperror();
158846ecee76SDavid du Colombier 	}
158946ecee76SDavid du Colombier }
159046ecee76SDavid du Colombier 
159146ecee76SDavid du Colombier static Block *
convreadblock(Conv * c,int n)159246ecee76SDavid du Colombier convreadblock(Conv *c, int n)
159346ecee76SDavid du Colombier {
159446ecee76SDavid du Colombier 	Block *b;
159546ecee76SDavid du Colombier 	Chan *ch;
159646ecee76SDavid du Colombier 
159746ecee76SDavid du Colombier 	qlock(&c->readlk);
159846ecee76SDavid du Colombier 	if(waserror()) {
159946ecee76SDavid du Colombier 		c->readproc = nil;
160046ecee76SDavid du Colombier 		qunlock(&c->readlk);
160146ecee76SDavid du Colombier 		nexterror();
160246ecee76SDavid du Colombier 	}
160346ecee76SDavid du Colombier 	qlock(c);
160446ecee76SDavid du Colombier 	if(c->state == CClosed) {
160546ecee76SDavid du Colombier 		qunlock(c);
160646ecee76SDavid du Colombier 		error("closed");
160746ecee76SDavid du Colombier 	}
160846ecee76SDavid du Colombier 	c->readproc = up;
160946ecee76SDavid du Colombier 	ch = c->chan;
161046ecee76SDavid du Colombier 	assert(c->ref > 0);
161146ecee76SDavid du Colombier 	qunlock(c);
161246ecee76SDavid du Colombier 
161346ecee76SDavid du Colombier 	b = devtab[ch->type]->bread(ch, n, 0);
161446ecee76SDavid du Colombier 	c->readproc = nil;
161546ecee76SDavid du Colombier 	poperror();
161646ecee76SDavid du Colombier 	qunlock(&c->readlk);
161746ecee76SDavid du Colombier 
161846ecee76SDavid du Colombier 	return b;
161946ecee76SDavid du Colombier }
162046ecee76SDavid du Colombier 
162146ecee76SDavid du Colombier static int
readready(void * a)162246ecee76SDavid du Colombier readready(void *a)
162346ecee76SDavid du Colombier {
162446ecee76SDavid du Colombier 	Conv *c = a;
162546ecee76SDavid du Colombier 
162646ecee76SDavid du Colombier 	return c->in.controlpkt != nil || (c->state == CClosed) || (c->state == CRemoteClose);
162746ecee76SDavid du Colombier }
162846ecee76SDavid du Colombier 
162946ecee76SDavid du Colombier static Block *
readcontrol(Conv * c,int n)163046ecee76SDavid du Colombier readcontrol(Conv *c, int n)
163146ecee76SDavid du Colombier {
163246ecee76SDavid du Colombier 	Block *b;
163346ecee76SDavid du Colombier 
163446ecee76SDavid du Colombier 	USED(n);
163546ecee76SDavid du Colombier 
163646ecee76SDavid du Colombier 	qlock(&c->in.controllk);
163746ecee76SDavid du Colombier 	if(waserror()) {
163846ecee76SDavid du Colombier 		qunlock(&c->in.controllk);
163946ecee76SDavid du Colombier 		nexterror();
164046ecee76SDavid du Colombier 	}
164146ecee76SDavid du Colombier 	qlock(c);	// this lock is not held during the sleep below
164246ecee76SDavid du Colombier 
164346ecee76SDavid du Colombier 	for(;;) {
164446ecee76SDavid du Colombier 		if(c->chan == nil || c->state == CClosed) {
164546ecee76SDavid du Colombier 			qunlock(c);
164646ecee76SDavid du Colombier if(0)print("readcontrol: return error - state = %s\n", convstatename[c->state]);
164746ecee76SDavid du Colombier 			error("conversation closed");
164846ecee76SDavid du Colombier 		}
164946ecee76SDavid du Colombier 
165046ecee76SDavid du Colombier 		if(c->in.controlpkt != nil)
165146ecee76SDavid du Colombier 			break;
165246ecee76SDavid du Colombier 
165346ecee76SDavid du Colombier 		if(c->state == CRemoteClose) {
165446ecee76SDavid du Colombier 			qunlock(c);
165546ecee76SDavid du Colombier if(0)print("readcontrol: return nil - state = %s\n", convstatename[c->state]);
165646ecee76SDavid du Colombier 			poperror();
165746ecee76SDavid du Colombier 			return nil;
165846ecee76SDavid du Colombier 		}
165946ecee76SDavid du Colombier 		qunlock(c);
166046ecee76SDavid du Colombier 		sleep(&c->in.controlready, readready, c);
166146ecee76SDavid du Colombier 		qlock(c);
166246ecee76SDavid du Colombier 	}
166346ecee76SDavid du Colombier 
166446ecee76SDavid du Colombier 	convack(c);
166546ecee76SDavid du Colombier 
166646ecee76SDavid du Colombier 	b = c->in.controlpkt;
166746ecee76SDavid du Colombier 	c->in.controlpkt = nil;
166846ecee76SDavid du Colombier 	qunlock(c);
166946ecee76SDavid du Colombier 	poperror();
167046ecee76SDavid du Colombier 	qunlock(&c->in.controllk);
167146ecee76SDavid du Colombier 	return b;
167246ecee76SDavid du Colombier }
167346ecee76SDavid du Colombier 
167446ecee76SDavid du Colombier 
167546ecee76SDavid du Colombier static int
writeready(void * a)167646ecee76SDavid du Colombier writeready(void *a)
167746ecee76SDavid du Colombier {
167846ecee76SDavid du Colombier 	Conv *c = a;
167946ecee76SDavid du Colombier 
168046ecee76SDavid du Colombier 	return c->out.controlpkt == nil || (c->state == CClosed) || (c->state == CRemoteClose);
168146ecee76SDavid du Colombier }
168246ecee76SDavid du Colombier 
168346ecee76SDavid du Colombier // c is locked
168446ecee76SDavid du Colombier static void
writewait(Conv * c)168546ecee76SDavid du Colombier writewait(Conv *c)
168646ecee76SDavid du Colombier {
168746ecee76SDavid du Colombier 	for(;;) {
168846ecee76SDavid du Colombier 		if(c->state == CFree || c->state == CInit ||
168946ecee76SDavid du Colombier 		   c->state == CClosed || c->state == CRemoteClose)
169046ecee76SDavid du Colombier 			error("conversation closed");
169146ecee76SDavid du Colombier 
169246ecee76SDavid du Colombier 		if(c->state == COpen && c->out.controlpkt == nil)
169346ecee76SDavid du Colombier 			break;
169446ecee76SDavid du Colombier 
169546ecee76SDavid du Colombier 		qunlock(c);
169646ecee76SDavid du Colombier 		if(waserror()) {
169746ecee76SDavid du Colombier 			qlock(c);
169846ecee76SDavid du Colombier 			nexterror();
169946ecee76SDavid du Colombier 		}
170046ecee76SDavid du Colombier 		sleep(&c->out.controlready, writeready, c);
170146ecee76SDavid du Colombier 		poperror();
170246ecee76SDavid du Colombier 		qlock(c);
170346ecee76SDavid du Colombier 	}
170446ecee76SDavid du Colombier }
170546ecee76SDavid du Colombier 
170646ecee76SDavid du Colombier static void
writecontrol(Conv * c,void * p,int n,int wait)170746ecee76SDavid du Colombier writecontrol(Conv *c, void *p, int n, int wait)
170846ecee76SDavid du Colombier {
170946ecee76SDavid du Colombier 	Block *b;
171046ecee76SDavid du Colombier 
171146ecee76SDavid du Colombier 	qlock(&c->out.controllk);
171246ecee76SDavid du Colombier 	qlock(c);
171346ecee76SDavid du Colombier 	if(waserror()) {
171446ecee76SDavid du Colombier 		qunlock(c);
171546ecee76SDavid du Colombier 		qunlock(&c->out.controllk);
171646ecee76SDavid du Colombier 		nexterror();
171746ecee76SDavid du Colombier 	}
171846ecee76SDavid du Colombier 	writewait(c);
171946ecee76SDavid du Colombier 	b = allocb(4+n);
172046ecee76SDavid du Colombier 	c->out.controlseq++;
172146ecee76SDavid du Colombier 	hnputl(b->wp, c->out.controlseq);
172246ecee76SDavid du Colombier 	memmove(b->wp+4, p, n);
172346ecee76SDavid du Colombier 	b->wp += 4+n;
172446ecee76SDavid du Colombier 	c->out.controlpkt = b;
172546ecee76SDavid du Colombier 	convretryinit(c);
172646ecee76SDavid du Colombier 	convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
172746ecee76SDavid du Colombier 	if(wait)
172846ecee76SDavid du Colombier 		writewait(c);
172946ecee76SDavid du Colombier 	poperror();
173046ecee76SDavid du Colombier 	qunlock(c);
173146ecee76SDavid du Colombier 	qunlock(&c->out.controllk);
173246ecee76SDavid du Colombier }
173346ecee76SDavid du Colombier 
173446ecee76SDavid du Colombier static Block *
readdata(Conv * c,int n)173546ecee76SDavid du Colombier readdata(Conv *c, int n)
173646ecee76SDavid du Colombier {
173746ecee76SDavid du Colombier 	Block *b;
173846ecee76SDavid du Colombier 	int nn;
173946ecee76SDavid du Colombier 
174046ecee76SDavid du Colombier 	for(;;) {
174146ecee76SDavid du Colombier 
174246ecee76SDavid du Colombier 		// some slack for tunneling overhead
174346ecee76SDavid du Colombier 		nn = n + 100;
174446ecee76SDavid du Colombier 
174546ecee76SDavid du Colombier 		// make sure size is big enough for control messages
174646ecee76SDavid du Colombier 		if(nn < 1000)
174746ecee76SDavid du Colombier 			nn = 1000;
174846ecee76SDavid du Colombier 		b = convreadblock(c, nn);
174946ecee76SDavid du Colombier 		if(b == nil)
175046ecee76SDavid du Colombier 			return nil;
175146ecee76SDavid du Colombier 		qlock(c);
175246ecee76SDavid du Colombier 		if(waserror()) {
175346ecee76SDavid du Colombier 			qunlock(c);
175446ecee76SDavid du Colombier 			return nil;
175546ecee76SDavid du Colombier 		}
175646ecee76SDavid du Colombier 		b = conviput(c, b, 0);
175746ecee76SDavid du Colombier 		poperror();
175846ecee76SDavid du Colombier 		qunlock(c);
175946ecee76SDavid du Colombier 		if(b != nil) {
176046ecee76SDavid du Colombier 			if(BLEN(b) > n)
176146ecee76SDavid du Colombier 				b->wp = b->rp + n;
176246ecee76SDavid du Colombier 			return b;
176346ecee76SDavid du Colombier 		}
176446ecee76SDavid du Colombier 	}
176546ecee76SDavid du Colombier }
176646ecee76SDavid du Colombier 
176746ecee76SDavid du Colombier static long
writedata(Conv * c,Block * b)176846ecee76SDavid du Colombier writedata(Conv *c, Block *b)
176946ecee76SDavid du Colombier {
177046ecee76SDavid du Colombier 	int n;
177146ecee76SDavid du Colombier 	ulong seq;
177246ecee76SDavid du Colombier 	int subtype;
177346ecee76SDavid du Colombier 
177446ecee76SDavid du Colombier 	qlock(c);
177546ecee76SDavid du Colombier 	if(waserror()) {
177646ecee76SDavid du Colombier 		qunlock(c);
177746ecee76SDavid du Colombier 		nexterror();
177846ecee76SDavid du Colombier 	}
177946ecee76SDavid du Colombier 
178046ecee76SDavid du Colombier 	if(c->state != COpen) {
178146ecee76SDavid du Colombier 		freeb(b);
178246ecee76SDavid du Colombier 		error("conversation not open");
178346ecee76SDavid du Colombier 	}
178446ecee76SDavid du Colombier 
178546ecee76SDavid du Colombier 	n = BLEN(b);
178646ecee76SDavid du Colombier 	c->lstats.outDataPackets++;
178746ecee76SDavid du Colombier 	c->lstats.outDataBytes += n;
178846ecee76SDavid du Colombier 
178946ecee76SDavid du Colombier 	if(c->out.comp != nil) {
179046ecee76SDavid du Colombier 		// must generate same value as convoput
179146ecee76SDavid du Colombier 		seq = (c->out.seq + 1) & (SeqMax-1);
179246ecee76SDavid du Colombier 
179346ecee76SDavid du Colombier 		subtype = (*c->out.comp)(c, 0, seq, &b);
179446ecee76SDavid du Colombier 		c->lstats.outCompDataBytes += BLEN(b);
179546ecee76SDavid du Colombier 		convoput(c, TCompData, subtype, b);
179646ecee76SDavid du Colombier 	} else
179746ecee76SDavid du Colombier 		convoput(c, TData, 0, b);
179846ecee76SDavid du Colombier 
179946ecee76SDavid du Colombier 	poperror();
180046ecee76SDavid du Colombier 	qunlock(c);
180146ecee76SDavid du Colombier 	return n;
180246ecee76SDavid du Colombier }
180346ecee76SDavid du Colombier 
180446ecee76SDavid du Colombier static void
convreader(void * a)180546ecee76SDavid du Colombier convreader(void *a)
180646ecee76SDavid du Colombier {
180746ecee76SDavid du Colombier 	Conv *c = a;
180846ecee76SDavid du Colombier 	Block *b;
180946ecee76SDavid du Colombier 
181046ecee76SDavid du Colombier 	qlock(c);
181146ecee76SDavid du Colombier 	assert(c->reader == 1);
181246ecee76SDavid du Colombier 	while(c->dataopen == 0 && c->state != CClosed) {
181346ecee76SDavid du Colombier 		qunlock(c);
181446ecee76SDavid du Colombier 		b = nil;
181546ecee76SDavid du Colombier 		if(!waserror()) {
181646ecee76SDavid du Colombier 			b = convreadblock(c, 2000);
181746ecee76SDavid du Colombier 			poperror();
181846ecee76SDavid du Colombier 		}
181946ecee76SDavid du Colombier 		qlock(c);
182046ecee76SDavid du Colombier 		if(b == nil) {
182146ecee76SDavid du Colombier 			if(strcmp(up->errstr, Eintr) != 0) {
182246ecee76SDavid du Colombier 				convsetstate(c, CClosed);
182346ecee76SDavid du Colombier 				break;
182446ecee76SDavid du Colombier 			}
182546ecee76SDavid du Colombier 		} else if(!waserror()) {
182646ecee76SDavid du Colombier 			conviput(c, b, 1);
182746ecee76SDavid du Colombier 			poperror();
182846ecee76SDavid du Colombier 		}
182946ecee76SDavid du Colombier 	}
183046ecee76SDavid du Colombier 	c->reader = 0;
183146ecee76SDavid du Colombier 	convderef(c);
183246ecee76SDavid du Colombier 	qunlock(c);
183346ecee76SDavid du Colombier 	pexit("hangup", 1);
183446ecee76SDavid du Colombier }
183546ecee76SDavid du Colombier 
183646ecee76SDavid du Colombier 
183746ecee76SDavid du Colombier /* ciphers, authenticators, and compressors  */
183846ecee76SDavid du Colombier 
183946ecee76SDavid du Colombier static void
setalg(Conv * c,char * name,Algorithm * alg,Algorithm ** p)184046ecee76SDavid du Colombier setalg(Conv *c, char *name, Algorithm *alg, Algorithm **p)
184146ecee76SDavid du Colombier {
184246ecee76SDavid du Colombier 	for(; alg->name; alg++)
184346ecee76SDavid du Colombier 		if(strcmp(name, alg->name) == 0)
184446ecee76SDavid du Colombier 			break;
184546ecee76SDavid du Colombier 	if(alg->name == nil)
184646ecee76SDavid du Colombier 		error("unknown algorithm");
184746ecee76SDavid du Colombier 
184846ecee76SDavid du Colombier 	*p = alg;
184946ecee76SDavid du Colombier 	alg->init(c);
185046ecee76SDavid du Colombier }
185146ecee76SDavid du Colombier 
185246ecee76SDavid du Colombier static void
setsecret(OneWay * ow,char * secret)185346ecee76SDavid du Colombier setsecret(OneWay *ow, char *secret)
185446ecee76SDavid du Colombier {
185546ecee76SDavid du Colombier 	char *p;
185646ecee76SDavid du Colombier 	int i, c;
185746ecee76SDavid du Colombier 
185846ecee76SDavid du Colombier 	i = 0;
185946ecee76SDavid du Colombier 	memset(ow->secret, 0, sizeof(ow->secret));
186046ecee76SDavid du Colombier 	for(p=secret; *p; p++) {
186146ecee76SDavid du Colombier 		if(i >= sizeof(ow->secret)*2)
186246ecee76SDavid du Colombier 			break;
186346ecee76SDavid du Colombier 		c = *p;
186446ecee76SDavid du Colombier 		if(c >= '0' && c <= '9')
186546ecee76SDavid du Colombier 			c -= '0';
186646ecee76SDavid du Colombier 		else if(c >= 'a' && c <= 'f')
186746ecee76SDavid du Colombier 			c -= 'a'-10;
186846ecee76SDavid du Colombier 		else if(c >= 'A' && c <= 'F')
186946ecee76SDavid du Colombier 			c -= 'A'-10;
187046ecee76SDavid du Colombier 		else
187146ecee76SDavid du Colombier 			error("bad character in secret");
187246ecee76SDavid du Colombier 		if((i&1) == 0)
187346ecee76SDavid du Colombier 			c <<= 4;
187446ecee76SDavid du Colombier 		ow->secret[i>>1] |= c;
187546ecee76SDavid du Colombier 		i++;
187646ecee76SDavid du Colombier 	}
187746ecee76SDavid du Colombier }
187846ecee76SDavid du Colombier 
187946ecee76SDavid du Colombier static void
setkey(uchar * key,int n,OneWay * ow,char * prefix)188046ecee76SDavid du Colombier setkey(uchar *key, int n, OneWay *ow, char *prefix)
188146ecee76SDavid du Colombier {
188246ecee76SDavid du Colombier 	uchar ibuf[SHA1dlen], obuf[MD5dlen], salt[10];
188346ecee76SDavid du Colombier 	int i, round = 0;
188446ecee76SDavid du Colombier 
188546ecee76SDavid du Colombier 	while(n > 0){
188646ecee76SDavid du Colombier 		for(i=0; i<round+1; i++)
188746ecee76SDavid du Colombier 			salt[i] = 'A'+round;
188846ecee76SDavid du Colombier 		sha1((uchar*)prefix, strlen(prefix), ibuf, sha1(salt, round+1, nil, nil));
188946ecee76SDavid du Colombier 		md5(ibuf, SHA1dlen, obuf, md5(ow->secret, sizeof(ow->secret), nil, nil));
189046ecee76SDavid du Colombier 		i = (n<MD5dlen) ? n : MD5dlen;
189146ecee76SDavid du Colombier 		memmove(key, obuf, i);
189246ecee76SDavid du Colombier 		key += i;
189346ecee76SDavid du Colombier 		n -= i;
189446ecee76SDavid du Colombier 		if(++round > sizeof salt)
189546ecee76SDavid du Colombier 			panic("setkey: you ask too much");
189646ecee76SDavid du Colombier 	}
189746ecee76SDavid du Colombier }
189846ecee76SDavid du Colombier 
189946ecee76SDavid du Colombier static void
cipherfree(Conv * c)190046ecee76SDavid du Colombier cipherfree(Conv *c)
190146ecee76SDavid du Colombier {
190246ecee76SDavid du Colombier 	if(c->in.cipherstate) {
190346ecee76SDavid du Colombier 		free(c->in.cipherstate);
190446ecee76SDavid du Colombier 		c->in.cipherstate = nil;
190546ecee76SDavid du Colombier 	}
190646ecee76SDavid du Colombier 	if(c->out.cipherstate) {
190746ecee76SDavid du Colombier 		free(c->out.cipherstate);
190846ecee76SDavid du Colombier 		c->out.cipherstate = nil;
190946ecee76SDavid du Colombier 	}
191046ecee76SDavid du Colombier 	c->in.cipher = nil;
191146ecee76SDavid du Colombier 	c->in.cipherblklen = 0;
191246ecee76SDavid du Colombier 	c->out.cipherblklen = 0;
191346ecee76SDavid du Colombier 	c->in.cipherivlen = 0;
191446ecee76SDavid du Colombier 	c->out.cipherivlen = 0;
191546ecee76SDavid du Colombier }
191646ecee76SDavid du Colombier 
191746ecee76SDavid du Colombier static void
authfree(Conv * c)191846ecee76SDavid du Colombier authfree(Conv *c)
191946ecee76SDavid du Colombier {
192046ecee76SDavid du Colombier 	if(c->in.authstate) {
192146ecee76SDavid du Colombier 		free(c->in.authstate);
192246ecee76SDavid du Colombier 		c->in.authstate = nil;
192346ecee76SDavid du Colombier 	}
192446ecee76SDavid du Colombier 	if(c->out.authstate) {
192546ecee76SDavid du Colombier 		free(c->out.authstate);
192646ecee76SDavid du Colombier 		c->out.authstate = nil;
192746ecee76SDavid du Colombier 	}
192846ecee76SDavid du Colombier 	c->in.auth = nil;
192946ecee76SDavid du Colombier 	c->in.authlen = 0;
193046ecee76SDavid du Colombier 	c->out.authlen = 0;
193146ecee76SDavid du Colombier }
193246ecee76SDavid du Colombier 
193346ecee76SDavid du Colombier static void
compfree(Conv * c)193446ecee76SDavid du Colombier compfree(Conv *c)
193546ecee76SDavid du Colombier {
193646ecee76SDavid du Colombier 	if(c->in.compstate) {
193746ecee76SDavid du Colombier 		free(c->in.compstate);
193846ecee76SDavid du Colombier 		c->in.compstate = nil;
193946ecee76SDavid du Colombier 	}
194046ecee76SDavid du Colombier 	if(c->out.compstate) {
194146ecee76SDavid du Colombier 		free(c->out.compstate);
194246ecee76SDavid du Colombier 		c->out.compstate = nil;
194346ecee76SDavid du Colombier 	}
194446ecee76SDavid du Colombier 	c->in.comp = nil;
194546ecee76SDavid du Colombier }
194646ecee76SDavid du Colombier 
194746ecee76SDavid du Colombier static void
nullcipherinit(Conv * c)194846ecee76SDavid du Colombier nullcipherinit(Conv *c)
194946ecee76SDavid du Colombier {
195046ecee76SDavid du Colombier 	cipherfree(c);
195146ecee76SDavid du Colombier }
195246ecee76SDavid du Colombier 
195346ecee76SDavid du Colombier static int
desencrypt(OneWay * ow,uchar * p,int n)195446ecee76SDavid du Colombier desencrypt(OneWay *ow, uchar *p, int n)
195546ecee76SDavid du Colombier {
195646ecee76SDavid du Colombier 	uchar *pp, *ip, *eip, *ep;
195746ecee76SDavid du Colombier 	DESstate *ds = ow->cipherstate;
195846ecee76SDavid du Colombier 
195946ecee76SDavid du Colombier 	if(n < 8 || (n & 0x7 != 0))
196046ecee76SDavid du Colombier 		return 0;
196146ecee76SDavid du Colombier 	ep = p + n;
196246ecee76SDavid du Colombier 	memmove(p, ds->ivec, 8);
196346ecee76SDavid du Colombier 	for(p += 8; p < ep; p += 8){
196446ecee76SDavid du Colombier 		pp = p;
196546ecee76SDavid du Colombier 		ip = ds->ivec;
196646ecee76SDavid du Colombier 		for(eip = ip+8; ip < eip; )
196746ecee76SDavid du Colombier 			*pp++ ^= *ip++;
196846ecee76SDavid du Colombier 		block_cipher(ds->expanded, p, 0);
196946ecee76SDavid du Colombier 		memmove(ds->ivec, p, 8);
197046ecee76SDavid du Colombier 	}
197146ecee76SDavid du Colombier 	return 1;
197246ecee76SDavid du Colombier }
197346ecee76SDavid du Colombier 
197446ecee76SDavid du Colombier static int
desdecrypt(OneWay * ow,uchar * p,int n)197546ecee76SDavid du Colombier desdecrypt(OneWay *ow, uchar *p, int n)
197646ecee76SDavid du Colombier {
197746ecee76SDavid du Colombier 	uchar tmp[8];
197846ecee76SDavid du Colombier 	uchar *tp, *ip, *eip, *ep;
197946ecee76SDavid du Colombier 	DESstate *ds = ow->cipherstate;
198046ecee76SDavid du Colombier 
198146ecee76SDavid du Colombier 	if(n < 8 || (n & 0x7 != 0))
198246ecee76SDavid du Colombier 		return 0;
198346ecee76SDavid du Colombier 	ep = p + n;
198446ecee76SDavid du Colombier 	memmove(ds->ivec, p, 8);
198546ecee76SDavid du Colombier 	p += 8;
198646ecee76SDavid du Colombier 	while(p < ep){
198746ecee76SDavid du Colombier 		memmove(tmp, p, 8);
198846ecee76SDavid du Colombier 		block_cipher(ds->expanded, p, 1);
198946ecee76SDavid du Colombier 		tp = tmp;
199046ecee76SDavid du Colombier 		ip = ds->ivec;
199146ecee76SDavid du Colombier 		for(eip = ip+8; ip < eip; ){
199246ecee76SDavid du Colombier 			*p++ ^= *ip;
199346ecee76SDavid du Colombier 			*ip++ = *tp++;
199446ecee76SDavid du Colombier 		}
199546ecee76SDavid du Colombier 	}
199646ecee76SDavid du Colombier 	return 1;
199746ecee76SDavid du Colombier }
199846ecee76SDavid du Colombier 
199946ecee76SDavid du Colombier static void
descipherinit(Conv * c)200046ecee76SDavid du Colombier descipherinit(Conv *c)
200146ecee76SDavid du Colombier {
200246ecee76SDavid du Colombier 	uchar key[8];
200346ecee76SDavid du Colombier 	uchar ivec[8];
200446ecee76SDavid du Colombier 	int i;
200546ecee76SDavid du Colombier 	int n = c->cipher->keylen;
200646ecee76SDavid du Colombier 
200746ecee76SDavid du Colombier 	cipherfree(c);
200846ecee76SDavid du Colombier 
200946ecee76SDavid du Colombier 	if(n > sizeof(key))
201046ecee76SDavid du Colombier 		n = sizeof(key);
201146ecee76SDavid du Colombier 
201246ecee76SDavid du Colombier 	/* in */
201346ecee76SDavid du Colombier 	memset(key, 0, sizeof(key));
201446ecee76SDavid du Colombier 	setkey(key, n, &c->in, "cipher");
201546ecee76SDavid du Colombier 	memset(ivec, 0, sizeof(ivec));
201646ecee76SDavid du Colombier 	c->in.cipherblklen = 8;
201746ecee76SDavid du Colombier 	c->in.cipherivlen = 8;
201846ecee76SDavid du Colombier 	c->in.cipher = desdecrypt;
201946ecee76SDavid du Colombier 	c->in.cipherstate = smalloc(sizeof(DESstate));
202046ecee76SDavid du Colombier 	setupDESstate(c->in.cipherstate, key, ivec);
202146ecee76SDavid du Colombier 
202246ecee76SDavid du Colombier 	/* out */
202346ecee76SDavid du Colombier 	memset(key, 0, sizeof(key));
202446ecee76SDavid du Colombier 	setkey(key, n, &c->out, "cipher");
202546ecee76SDavid du Colombier 	for(i=0; i<8; i++)
202646ecee76SDavid du Colombier 		ivec[i] = nrand(256);
202746ecee76SDavid du Colombier 	c->out.cipherblklen = 8;
202846ecee76SDavid du Colombier 	c->out.cipherivlen = 8;
202946ecee76SDavid du Colombier 	c->out.cipher = desencrypt;
203046ecee76SDavid du Colombier 	c->out.cipherstate = smalloc(sizeof(DESstate));
203146ecee76SDavid du Colombier 	setupDESstate(c->out.cipherstate, key, ivec);
203246ecee76SDavid du Colombier }
203346ecee76SDavid du Colombier 
203446ecee76SDavid du Colombier static int
rc4encrypt(OneWay * ow,uchar * p,int n)203546ecee76SDavid du Colombier rc4encrypt(OneWay *ow, uchar *p, int n)
203646ecee76SDavid du Colombier {
203746ecee76SDavid du Colombier 	CipherRc4 *cr = ow->cipherstate;
203846ecee76SDavid du Colombier 
203946ecee76SDavid du Colombier 	if(n < 4)
204046ecee76SDavid du Colombier 		return 0;
204146ecee76SDavid du Colombier 
204246ecee76SDavid du Colombier 	hnputl(p, cr->cseq);
204346ecee76SDavid du Colombier 	p += 4;
204446ecee76SDavid du Colombier 	n -= 4;
204546ecee76SDavid du Colombier 	rc4(&cr->current, p, n);
204646ecee76SDavid du Colombier 	cr->cseq += n;
204746ecee76SDavid du Colombier 	return 1;
204846ecee76SDavid du Colombier }
204946ecee76SDavid du Colombier 
205046ecee76SDavid du Colombier static int
rc4decrypt(OneWay * ow,uchar * p,int n)205146ecee76SDavid du Colombier rc4decrypt(OneWay *ow, uchar *p, int n)
205246ecee76SDavid du Colombier {
205346ecee76SDavid du Colombier 	CipherRc4 *cr = ow->cipherstate;
205446ecee76SDavid du Colombier 	RC4state tmpstate;
205546ecee76SDavid du Colombier 	ulong seq;
205646ecee76SDavid du Colombier 	long d, dd;
205746ecee76SDavid du Colombier 
205846ecee76SDavid du Colombier 	if(n < 4)
205946ecee76SDavid du Colombier 		return 0;
206046ecee76SDavid du Colombier 
206146ecee76SDavid du Colombier 	seq = nhgetl(p);
206246ecee76SDavid du Colombier 	p += 4;
206346ecee76SDavid du Colombier 	n -= 4;
206446ecee76SDavid du Colombier 	d = seq-cr->cseq;
206546ecee76SDavid du Colombier 	if(d == 0) {
206646ecee76SDavid du Colombier 		rc4(&cr->current, p, n);
206746ecee76SDavid du Colombier 		cr->cseq += n;
206846ecee76SDavid du Colombier 		if(cr->ovalid) {
206946ecee76SDavid du Colombier 			dd = cr->cseq - cr->lgseq;
207046ecee76SDavid du Colombier 			if(dd > RC4back)
207146ecee76SDavid du Colombier 				cr->ovalid = 0;
207246ecee76SDavid du Colombier 		}
207346ecee76SDavid du Colombier 	} else if(d > 0) {
207446ecee76SDavid du Colombier //print("missing packet: %uld %ld\n", seq, d);
207546ecee76SDavid du Colombier 		// this link is hosed
207646ecee76SDavid du Colombier 		if(d > RC4forward)
207746ecee76SDavid du Colombier 			return 0;
207846ecee76SDavid du Colombier 		cr->lgseq = seq;
207946ecee76SDavid du Colombier 		if(!cr->ovalid) {
208046ecee76SDavid du Colombier 			cr->ovalid = 1;
208146ecee76SDavid du Colombier 			cr->oseq = cr->cseq;
208246ecee76SDavid du Colombier 			memmove(&cr->old, &cr->current, sizeof(RC4state));
208346ecee76SDavid du Colombier 		}
208446ecee76SDavid du Colombier 		rc4skip(&cr->current, d);
208546ecee76SDavid du Colombier 		rc4(&cr->current, p, n);
208646ecee76SDavid du Colombier 		cr->cseq = seq+n;
208746ecee76SDavid du Colombier 	} else {
208846ecee76SDavid du Colombier //print("reordered packet: %uld %ld\n", seq, d);
208946ecee76SDavid du Colombier 		dd = seq - cr->oseq;
209046ecee76SDavid du Colombier 		if(!cr->ovalid || -d > RC4back || dd < 0)
209146ecee76SDavid du Colombier 			return 0;
209246ecee76SDavid du Colombier 		memmove(&tmpstate, &cr->old, sizeof(RC4state));
209346ecee76SDavid du Colombier 		rc4skip(&tmpstate, dd);
209446ecee76SDavid du Colombier 		rc4(&tmpstate, p, n);
209546ecee76SDavid du Colombier 		return 1;
209646ecee76SDavid du Colombier 	}
209746ecee76SDavid du Colombier 
209846ecee76SDavid du Colombier 	// move old state up
209946ecee76SDavid du Colombier 	if(cr->ovalid) {
210046ecee76SDavid du Colombier 		dd = cr->cseq - RC4back - cr->oseq;
210146ecee76SDavid du Colombier 		if(dd > 0) {
210246ecee76SDavid du Colombier 			rc4skip(&cr->old, dd);
210346ecee76SDavid du Colombier 			cr->oseq += dd;
210446ecee76SDavid du Colombier 		}
210546ecee76SDavid du Colombier 	}
210646ecee76SDavid du Colombier 
210746ecee76SDavid du Colombier 	return 1;
210846ecee76SDavid du Colombier }
210946ecee76SDavid du Colombier 
211046ecee76SDavid du Colombier static void
rc4cipherinit(Conv * c)211146ecee76SDavid du Colombier rc4cipherinit(Conv *c)
211246ecee76SDavid du Colombier {
211346ecee76SDavid du Colombier 	uchar key[32];
211446ecee76SDavid du Colombier 	CipherRc4 *cr;
211546ecee76SDavid du Colombier 	int n;
211646ecee76SDavid du Colombier 
211746ecee76SDavid du Colombier 	cipherfree(c);
211846ecee76SDavid du Colombier 
211946ecee76SDavid du Colombier 	n = c->cipher->keylen;
212046ecee76SDavid du Colombier 	if(n > sizeof(key))
212146ecee76SDavid du Colombier 		n = sizeof(key);
212246ecee76SDavid du Colombier 
212346ecee76SDavid du Colombier 	/* in */
212446ecee76SDavid du Colombier 	memset(key, 0, sizeof(key));
212546ecee76SDavid du Colombier 	setkey(key, n, &c->in, "cipher");
212646ecee76SDavid du Colombier 	c->in.cipherblklen = 1;
212746ecee76SDavid du Colombier 	c->in.cipherivlen = 4;
212846ecee76SDavid du Colombier 	c->in.cipher = rc4decrypt;
212946ecee76SDavid du Colombier 	cr = smalloc(sizeof(CipherRc4));
213046ecee76SDavid du Colombier 	memset(cr, 0, sizeof(*cr));
213146ecee76SDavid du Colombier 	setupRC4state(&cr->current, key, n);
213246ecee76SDavid du Colombier 	c->in.cipherstate = cr;
213346ecee76SDavid du Colombier 
213446ecee76SDavid du Colombier 	/* out */
213546ecee76SDavid du Colombier 	memset(key, 0, sizeof(key));
213646ecee76SDavid du Colombier 	setkey(key, n, &c->out, "cipher");
213746ecee76SDavid du Colombier 	c->out.cipherblklen = 1;
213846ecee76SDavid du Colombier 	c->out.cipherivlen = 4;
213946ecee76SDavid du Colombier 	c->out.cipher = rc4encrypt;
214046ecee76SDavid du Colombier 	cr = smalloc(sizeof(CipherRc4));
214146ecee76SDavid du Colombier 	memset(cr, 0, sizeof(*cr));
214246ecee76SDavid du Colombier 	setupRC4state(&cr->current, key, n);
214346ecee76SDavid du Colombier 	c->out.cipherstate = cr;
214446ecee76SDavid du Colombier }
214546ecee76SDavid du Colombier 
214646ecee76SDavid du Colombier static void
nullauthinit(Conv * c)214746ecee76SDavid du Colombier nullauthinit(Conv *c)
214846ecee76SDavid du Colombier {
214946ecee76SDavid du Colombier 	authfree(c);
215046ecee76SDavid du Colombier }
215146ecee76SDavid du Colombier 
215246ecee76SDavid du Colombier static void
shaauthinit(Conv * c)215346ecee76SDavid du Colombier shaauthinit(Conv *c)
215446ecee76SDavid du Colombier {
215546ecee76SDavid du Colombier 	authfree(c);
215646ecee76SDavid du Colombier }
215746ecee76SDavid du Colombier 
215846ecee76SDavid du Colombier static void
seanq_hmac_md5(uchar hash[MD5dlen],ulong wrap,uchar * t,long tlen,uchar * key,long klen)215946ecee76SDavid du Colombier seanq_hmac_md5(uchar hash[MD5dlen], ulong wrap, uchar *t, long tlen, uchar *key, long klen)
216046ecee76SDavid du Colombier {
216146ecee76SDavid du Colombier 	uchar ipad[65], opad[65], wbuf[4];
216246ecee76SDavid du Colombier 	int i;
216346ecee76SDavid du Colombier 	DigestState *digest;
216446ecee76SDavid du Colombier 	uchar innerhash[MD5dlen];
216546ecee76SDavid du Colombier 
216646ecee76SDavid du Colombier 	for(i=0; i<64; i++){
216746ecee76SDavid du Colombier 		ipad[i] = 0x36;
216846ecee76SDavid du Colombier 		opad[i] = 0x5c;
216946ecee76SDavid du Colombier 	}
217046ecee76SDavid du Colombier 	ipad[64] = opad[64] = 0;
217146ecee76SDavid du Colombier 	for(i=0; i<klen; i++){
217246ecee76SDavid du Colombier 		ipad[i] ^= key[i];
217346ecee76SDavid du Colombier 		opad[i] ^= key[i];
217446ecee76SDavid du Colombier 	}
217546ecee76SDavid du Colombier 	hnputl(wbuf, wrap);
217646ecee76SDavid du Colombier 	digest = md5(ipad, 64, nil, nil);
217746ecee76SDavid du Colombier 	digest = md5(wbuf, sizeof(wbuf), nil, digest);
217846ecee76SDavid du Colombier 	md5(t, tlen, innerhash, digest);
217946ecee76SDavid du Colombier 	digest = md5(opad, 64, nil, nil);
218046ecee76SDavid du Colombier 	md5(innerhash, MD5dlen, hash, digest);
218146ecee76SDavid du Colombier }
218246ecee76SDavid du Colombier 
218346ecee76SDavid du Colombier static int
md5auth(OneWay * ow,uchar * t,int tlen)218446ecee76SDavid du Colombier md5auth(OneWay *ow, uchar *t, int tlen)
218546ecee76SDavid du Colombier {
218646ecee76SDavid du Colombier 	uchar hash[MD5dlen];
218746ecee76SDavid du Colombier 	int r;
218846ecee76SDavid du Colombier 
218946ecee76SDavid du Colombier 	if(tlen < ow->authlen)
219046ecee76SDavid du Colombier 		return 0;
219146ecee76SDavid du Colombier 	tlen -= ow->authlen;
219246ecee76SDavid du Colombier 
219346ecee76SDavid du Colombier 	memset(hash, 0, MD5dlen);
219446ecee76SDavid du Colombier 	seanq_hmac_md5(hash, ow->seqwrap, t, tlen, (uchar*)ow->authstate, 16);
219546ecee76SDavid du Colombier 	r = memcmp(t+tlen, hash, ow->authlen) == 0;
219646ecee76SDavid du Colombier 	memmove(t+tlen, hash, ow->authlen);
219746ecee76SDavid du Colombier 	return r;
219846ecee76SDavid du Colombier }
219946ecee76SDavid du Colombier 
220046ecee76SDavid du Colombier static void
md5authinit(Conv * c)220146ecee76SDavid du Colombier md5authinit(Conv *c)
220246ecee76SDavid du Colombier {
220346ecee76SDavid du Colombier 	int keylen;
220446ecee76SDavid du Colombier 
220546ecee76SDavid du Colombier 	authfree(c);
220646ecee76SDavid du Colombier 
220746ecee76SDavid du Colombier 	keylen = c->auth->keylen;
220846ecee76SDavid du Colombier 	if(keylen > 16)
220946ecee76SDavid du Colombier 		keylen = 16;
221046ecee76SDavid du Colombier 
221146ecee76SDavid du Colombier 	/* in */
221246ecee76SDavid du Colombier 	c->in.authstate = smalloc(16);
221346ecee76SDavid du Colombier 	memset(c->in.authstate, 0, 16);
221446ecee76SDavid du Colombier 	setkey(c->in.authstate, keylen, &c->in, "auth");
221546ecee76SDavid du Colombier 	c->in.authlen = 12;
221646ecee76SDavid du Colombier 	c->in.auth = md5auth;
221746ecee76SDavid du Colombier 
221846ecee76SDavid du Colombier 	/* out */
221946ecee76SDavid du Colombier 	c->out.authstate = smalloc(16);
222046ecee76SDavid du Colombier 	memset(c->out.authstate, 0, 16);
222146ecee76SDavid du Colombier 	setkey(c->out.authstate, keylen, &c->out, "auth");
222246ecee76SDavid du Colombier 	c->out.authlen = 12;
222346ecee76SDavid du Colombier 	c->out.auth = md5auth;
222446ecee76SDavid du Colombier }
222546ecee76SDavid du Colombier 
222646ecee76SDavid du Colombier static void
nullcompinit(Conv * c)222746ecee76SDavid du Colombier nullcompinit(Conv *c)
222846ecee76SDavid du Colombier {
222946ecee76SDavid du Colombier 	compfree(c);
223046ecee76SDavid du Colombier }
223146ecee76SDavid du Colombier 
223246ecee76SDavid du Colombier static int
thwackcomp(Conv * c,int,ulong seq,Block ** bp)223346ecee76SDavid du Colombier thwackcomp(Conv *c, int, ulong seq, Block **bp)
223446ecee76SDavid du Colombier {
223546ecee76SDavid du Colombier 	Block *b, *bb;
223646ecee76SDavid du Colombier 	int nn;
223746ecee76SDavid du Colombier 	ulong ackseq;
223846ecee76SDavid du Colombier 	uchar mask;
223946ecee76SDavid du Colombier 
224046ecee76SDavid du Colombier 	// add ack info
224146ecee76SDavid du Colombier 	b = padblock(*bp, 4);
224246ecee76SDavid du Colombier 
224346ecee76SDavid du Colombier 	ackseq = unthwackstate(c->in.compstate, &mask);
224446ecee76SDavid du Colombier 	b->rp[0] = mask;
224546ecee76SDavid du Colombier 	b->rp[1] = ackseq>>16;
224646ecee76SDavid du Colombier 	b->rp[2] = ackseq>>8;
224746ecee76SDavid du Colombier 	b->rp[3] = ackseq;
224846ecee76SDavid du Colombier 
224946ecee76SDavid du Colombier 	bb = allocb(BLEN(b));
225046ecee76SDavid du Colombier 	nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq, c->lstats.outCompStats);
225146ecee76SDavid du Colombier 	if(nn < 0) {
225246ecee76SDavid du Colombier 		freeb(bb);
225346ecee76SDavid du Colombier 		*bp = b;
225446ecee76SDavid du Colombier 		return ThwackU;
225546ecee76SDavid du Colombier 	} else {
225646ecee76SDavid du Colombier 		bb->wp += nn;
225746ecee76SDavid du Colombier 		freeb(b);
225846ecee76SDavid du Colombier 		*bp = bb;
225946ecee76SDavid du Colombier 		return ThwackC;
226046ecee76SDavid du Colombier 	}
226146ecee76SDavid du Colombier }
226246ecee76SDavid du Colombier 
226346ecee76SDavid du Colombier static int
thwackuncomp(Conv * c,int subtype,ulong seq,Block ** bp)226446ecee76SDavid du Colombier thwackuncomp(Conv *c, int subtype, ulong seq, Block **bp)
226546ecee76SDavid du Colombier {
226646ecee76SDavid du Colombier 	Block *b, *bb;
226746ecee76SDavid du Colombier 	ulong mask;
226846ecee76SDavid du Colombier 	ulong mseq;
226946ecee76SDavid du Colombier 	int n;
227046ecee76SDavid du Colombier 
227146ecee76SDavid du Colombier 	switch(subtype) {
227246ecee76SDavid du Colombier 	default:
227346ecee76SDavid du Colombier 		return 0;
227446ecee76SDavid du Colombier 	case ThwackU:
227546ecee76SDavid du Colombier 		b = *bp;
227646ecee76SDavid du Colombier 		mask = b->rp[0];
227746ecee76SDavid du Colombier 		mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
227846ecee76SDavid du Colombier 		b->rp += 4;
227946ecee76SDavid du Colombier 		thwackack(c->out.compstate, mseq, mask);
228046ecee76SDavid du Colombier 		return 1;
228146ecee76SDavid du Colombier 	case ThwackC:
228246ecee76SDavid du Colombier 		bb = *bp;
228346ecee76SDavid du Colombier 		b = allocb(ThwMaxBlock);
228446ecee76SDavid du Colombier 		n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq);
228546ecee76SDavid du Colombier 		freeb(bb);
2286d9814433SDavid du Colombier 		*bp = nil;
228746ecee76SDavid du Colombier 		if(n < 0) {
228846ecee76SDavid du Colombier if(0)print("unthwack failed: %d\n", n);
228946ecee76SDavid du Colombier 			freeb(b);
229046ecee76SDavid du Colombier 			return 0;
229146ecee76SDavid du Colombier 		}
229246ecee76SDavid du Colombier 		b->wp += n;
229346ecee76SDavid du Colombier 		mask = b->rp[0];
229446ecee76SDavid du Colombier 		mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
229546ecee76SDavid du Colombier 		thwackack(c->out.compstate, mseq, mask);
229646ecee76SDavid du Colombier 		b->rp += 4;
229746ecee76SDavid du Colombier 		*bp = b;
229846ecee76SDavid du Colombier 		return 1;
229946ecee76SDavid du Colombier 	}
230046ecee76SDavid du Colombier }
230146ecee76SDavid du Colombier 
230246ecee76SDavid du Colombier static void
thwackcompinit(Conv * c)230346ecee76SDavid du Colombier thwackcompinit(Conv *c)
230446ecee76SDavid du Colombier {
230546ecee76SDavid du Colombier 	compfree(c);
230646ecee76SDavid du Colombier 
230746ecee76SDavid du Colombier 	c->in.compstate = malloc(sizeof(Unthwack));
230846ecee76SDavid du Colombier 	if(c->in.compstate == nil)
230946ecee76SDavid du Colombier 		error(Enomem);
231046ecee76SDavid du Colombier 	unthwackinit(c->in.compstate);
231146ecee76SDavid du Colombier 	c->out.compstate = malloc(sizeof(Thwack));
231246ecee76SDavid du Colombier 	if(c->out.compstate == nil)
231346ecee76SDavid du Colombier 		error(Enomem);
231446ecee76SDavid du Colombier 	thwackinit(c->out.compstate);
231546ecee76SDavid du Colombier 	c->in.comp = thwackuncomp;
231646ecee76SDavid du Colombier 	c->out.comp = thwackcomp;
231746ecee76SDavid du Colombier }
2318