plan 9 kernel history: overview | file list | diff list

1999/1211/port/devsdp.c (diff list | history)

1999/1211/sys/src/9/port/devsdp.c:1,23061999/1221/sys/src/9/port/devsdp.c:1,2306 (short | long | prev | next)
1999/0824    
#include "u.h" 
#include "../port/lib.h" 
#include "mem.h" 
#include "dat.h" 
#include "fns.h" 
#include "../port/netif.h" 
#include "../port/error.h" 
 
#include	<libcrypt.h> 
1999/1001    
#include "../port/thwack.h" 
1999/0824    
 
1999/0907    
/* 
 * sdp - secure datagram protocol 
 */ 
 
1999/0902    
typedef struct Sdp Sdp; 
typedef struct Conv Conv; 
1999/0907    
typedef struct OneWay OneWay; 
1999/0929    
typedef struct Stats Stats; 
typedef struct AckPkt AckPkt; 
1999/1001    
typedef struct Algorithm Algorithm; 
1999/1022    
typedef struct CipherRc4 CipherRc4; 
1999/0824    
 
enum 
{ 
	Qtopdir=	1,		/* top level directory */ 
 
	Qsdpdir,			/* sdp directory */ 
	Qclone, 
	Qlog, 
 
1999/0901    
	Qconvdir,			/* directory per conversation */ 
1999/0824    
	Qctl, 
1999/0902    
	Qdata,				/* unreliable packet channel */ 
	Qcontrol,			/* reliable control channel */ 
1999/0824    
	Qstatus, 
1999/0929    
	Qstats, 
	Qrstats, 
1999/0824    
 
	MaxQ, 
 
1999/0907    
	Maxconv= 256,		// power of 2 
	Nfs= 4,				// number of file systems 
1999/1211    
	MaxRetries=	4, 
	KeepAlive = 10,		// keep alive in seconds 
1999/1221    
	MaxRetries=	12, 
	KeepAlive = 120,	// keep alive in seconds 
1999/1015    
	SecretLength= 32,	// a secret per direction 
1999/1001    
	SeqMax = (1<<24), 
	SeqWindow = 32, 
1999/1019    
	NCompStats = 8, 
1999/0824    
}; 
 
#define TYPE(x) 	((x).path & 0xff) 
1999/0901    
#define CONV(x) 	(((x).path >> 8)&(Maxconv-1)) 
1999/0824    
#define QID(x, y) 	(((x)<<8) | (y)) 
 
1999/0929    
struct Stats 
{ 
	ulong	outPackets; 
	ulong	outDataPackets; 
	ulong	outDataBytes; 
	ulong	outCompDataBytes; 
	ulong	outCompBytes; 
1999/1019    
	ulong	outCompStats[NCompStats]; 
1999/0929    
	ulong	inPackets; 
	ulong	inDataPackets; 
	ulong	inDataBytes; 
	ulong	inCompDataBytes; 
	ulong	inMissing; 
	ulong	inDup; 
	ulong	inReorder; 
1999/1016    
	ulong	inBadComp; 
1999/0929    
	ulong	inBadAuth; 
	ulong	inBadSeq; 
1999/1022    
	ulong	inBadOther; 
1999/0929    
}; 
 
1999/0907    
struct OneWay 
1999/0901    
{ 
1999/0929    
	Rendez	statsready; 
 
1999/0901    
	ulong	seqwrap;	// number of wraps of the sequence number 
	ulong	seq; 
1999/0907    
	ulong	window; 
1999/0901    
 
1999/1015    
	uchar	secret[SecretLength]; 
 
1999/0915    
	QLock	controllk; 
1999/0907    
	Rendez	controlready; 
1999/0902    
	Block	*controlpkt;		// control channel 
1999/0907    
	ulong	controlseq; 
1999/0901    
 
1999/0902    
	void	*cipherstate;	// state cipher 
1999/0907    
	int		cipherivlen;	// initial vector length 
	int		cipherblklen;	// block length 
	int		(*cipher)(OneWay*, uchar *buf, int len); 
1999/0902    
 
	void	*authstate;		// auth state 
	int		authlen;		// auth data length in bytes 
1999/0907    
	int		(*auth)(OneWay*, uchar *buf, int len); 
1999/0902    
 
	void	*compstate; 
1999/1016    
	int		(*comp)(Conv*, int subtype, ulong seq, Block **); 
1999/0901    
}; 
 
1999/0907    
// conv states 
1999/0902    
enum { 
1999/1211    
	CFree, 
1999/0906    
	CInit, 
1999/0909    
	CDial, 
	CAccept, 
1999/0902    
	COpen, 
1999/0910    
	CLocalClose, 
	CRemoteClose, 
1999/0906    
	CClosed, 
1999/0902    
}; 
 
1999/0901    
struct Conv { 
	QLock; 
1999/0824    
	Sdp	*sdp; 
1999/1211    
	int	id; 
1999/0901    
 
1999/1211    
	int ref;	// holds conv up 
 
1999/0902    
	int state; 
1999/1211    
 
	int dataopen;	// ref count of opens on Qdata 
	int controlopen;	// ref count of opens on Qcontrol 
1999/0910    
	int reader;		// reader proc has been started 
1999/0901    
 
1999/0929    
	Stats	lstats; 
	Stats	rstats; 
1999/1001    
	 
	ulong	lastrecv;	// time last packet was received  
1999/0906    
	ulong	timeout; 
	int		retries; 
 
	// the following pair uniquely define conversation on this port 
	ulong dialid; 
	ulong acceptid; 
 
1999/0910    
	QLock readlk;		// protects readproc 
1999/0907    
	Proc *readproc; 
1999/0910    
 
1999/1211    
	Chan *chan;		// packet channel 
1999/0910    
	char *channame; 
1999/0902    
 
1999/0907    
	char owner[NAMELEN];		/* protections */ 
1999/0901    
	int	perm; 
 
1999/1022    
	Algorithm *auth; 
	Algorithm *cipher; 
	Algorithm *comp; 
1999/0907    
 
1999/0906    
	int drop; 
 
1999/0907    
	OneWay	in; 
	OneWay	out; 
1999/0824    
}; 
 
struct Sdp { 
	QLock; 
	Log; 
1999/0906    
	Rendez	vous;			/* used by sdpackproc */ 
1999/0901    
	int	nconv; 
1999/0902    
	Conv *conv[Maxconv]; 
1999/0906    
	int ackproc; 
1999/0824    
}; 
 
1999/0906    
enum { 
	TConnect, 
	TControl, 
	TData, 
1999/1015    
	TCompData, 
1999/0906    
}; 
 
enum { 
1999/1015    
	ControlMesg, 
	ControlAck, 
}; 
 
enum { 
1999/1016    
	ThwackU, 
	ThwackC, 
}; 
 
enum { 
1999/0909    
	ConOpenRequest, 
1999/0906    
	ConOpenAck, 
1999/0910    
	ConOpenAckAck, 
1999/0906    
	ConClose, 
1999/0909    
	ConReset, 
1999/0906    
}; 
 
1999/0929    
struct AckPkt 
{ 
	uchar	cseq[4]; 
	uchar	outPackets[4]; 
	uchar	outDataPackets[4]; 
	uchar	outDataBytes[4]; 
	uchar	outCompDataBytes[4]; 
1999/1019    
	uchar	outCompStats[4*NCompStats]; 
1999/0929    
	uchar	inPackets[4]; 
	uchar	inDataPackets[4]; 
	uchar	inDataBytes[4]; 
	uchar	inCompDataBytes[4]; 
	uchar	inMissing[4]; 
	uchar	inDup[4]; 
	uchar	inReorder[4]; 
1999/1016    
	uchar	inBadComp[4]; 
1999/0929    
	uchar	inBadAuth[4]; 
	uchar	inBadSeq[4]; 
1999/1022    
	uchar	inBadOther[4]; 
1999/0929    
}; 
1999/0907    
 
1999/1001    
struct Algorithm 
{ 
	char 	*name; 
	int		keylen;		// in bytes 
1999/1022    
	void	(*init)(Conv*); 
1999/1001    
}; 
1999/0929    
 
1999/1022    
enum { 
	RC4forward	= 10*1024*1024,	// maximum skip forward 
	RC4back = 100*1024,		// maximum look back 
}; 
 
struct CipherRc4 
{ 
	ulong cseq;	// current byte sequence number 
	RC4state current; 
 
	int ovalid;	// old is valid 
	ulong lgseq; // last good sequence 
	ulong oseq;	// old byte sequence number 
	RC4state old; 
}; 
 
1999/0824    
static Dirtab sdpdirtab[]={ 
	"log",		{Qlog},		0,	0666, 
1999/0901    
	"clone",	{Qclone},		0,	0666, 
1999/0824    
}; 
 
1999/0901    
static Dirtab convdirtab[]={ 
1999/0824    
	"ctl",		{Qctl},	0,	0666, 
	"data",		{Qdata},	0,	0666, 
1999/0902    
	"control",	{Qcontrol},	0,	0666, 
1999/0824    
	"status",	{Qstatus},	0,	0444, 
1999/0929    
	"stats",	{Qstats},	0,	0444, 
	"rstats",	{Qrstats},	0,	0444, 
1999/0824    
}; 
 
static int m2p[] = { 
	[OREAD]		4, 
	[OWRITE]	2, 
	[ORDWR]		6 
}; 
 
enum { 
	Logcompress=	(1<<0), 
	Logauth=	(1<<1), 
	Loghmac=	(1<<2), 
}; 
 
static Logflag logflags[] = 
{ 
	{ "compress",	Logcompress, }, 
	{ "auth",	Logauth, }, 
	{ "hmac",	Loghmac, }, 
	{ nil,		0, }, 
}; 
 
static Dirtab	*dirtab[MaxQ]; 
static Sdp sdptab[Nfs]; 
1999/0908    
static char *convstatename[] = { 
1999/1211    
	[CFree]		"Free", 
	[CInit]		"Init", 
	[CDial]		"Dial", 
	[CAccept]	"Accept", 
	[COpen]		"Open", 
1999/0910    
	[CLocalClose] "LocalClose", 
	[CRemoteClose] "RemoteClose", 
1999/1211    
	[CClosed]	"Closed", 
1999/0908    
}; 
1999/0824    
 
static int sdpgen(Chan *c, Dirtab*, int, int s, Dir *dp); 
1999/0901    
static Conv *sdpclone(Sdp *sdp); 
1999/0906    
static void sdpackproc(void *a); 
1999/0907    
static void onewaycleanup(OneWay *ow); 
static int readready(void *a); 
static int controlread(); 
1999/1211    
static void convsetstate(Conv *c, int state); 
static Block *readcontrol(Conv *c, int n); 
static void writecontrol(Conv *c, void *p, int n, int wait); 
static Block *readdata(Conv *c, int n); 
static long writedata(Conv *c, Block *b); 
static void convderef(Conv *c); 
1999/0907    
static Block *conviput(Conv *c, Block *b, int control); 
1999/1015    
static void conviconnect(Conv *c, int op, Block *b); 
static void convicontrol(Conv *c, int op, Block *b); 
1999/1016    
static Block *convicomp(Conv *c, int op, ulong, Block *b); 
1999/1015    
static void convoput(Conv *c, int type, int subtype, Block *b); 
static void convoconnect(Conv *c, int op, ulong dialid, ulong acceptid); 
1999/0914    
static void convopenchan(Conv *c, char *path); 
1999/0929    
static void convstats(Conv *c, int local, char *buf, int n); 
1999/1211    
static void convreader(void *a); 
1999/0824    
 
1999/1022    
static void setalg(Conv *c, char *name, Algorithm *tab, Algorithm **); 
1999/1015    
static void setsecret(OneWay *cc, char *secret); 
 
1999/1022    
static void nullcipherinit(Conv*c); 
static void descipherinit(Conv*c); 
static void rc4cipherinit(Conv*c); 
static void nullauthinit(Conv*c); 
static void shaauthinit(Conv*c); 
static void md5authinit(Conv*c); 
static void nullcompinit(Conv*c); 
static void thwackcompinit(Conv*c); 
1999/1015    
 
static Algorithm cipheralg[] = 
{ 
	"null",			0,	nullcipherinit, 
	"des_56_cbc",	7,	descipherinit, 
	"rc4_128",		16,	rc4cipherinit, 
1999/1022    
	"rc4_256",		32,	rc4cipherinit, 
1999/1015    
	nil,			0,	nil, 
}; 
 
static Algorithm authalg[] = 
{ 
	"null",			0,	nullauthinit, 
	"hmac_sha_96",	16,	shaauthinit, 
	"hmac_md5_96",	16,	md5authinit, 
	nil,			0,	nil, 
}; 
 
static Algorithm compalg[] = 
{ 
	"null",			0,	nullcompinit, 
1999/1028    
	"thwack",		0,	thwackcompinit, 
1999/1015    
	nil,			0,	nil, 
}; 
 
 
1999/0824    
static void 
sdpinit(void) 
{ 
	int i; 
	Dirtab *dt; 
1999/1027    
	 
1999/0824    
	// setup dirtab with non directory entries 
	for(i=0; i<nelem(sdpdirtab); i++) { 
		dt = sdpdirtab + i; 
		dirtab[TYPE(dt->qid)] = dt; 
	} 
 
1999/0901    
	for(i=0; i<nelem(convdirtab); i++) { 
		dt = convdirtab + i; 
1999/0824    
		dirtab[TYPE(dt->qid)] = dt; 
	} 
1999/0906    
 
1999/0824    
} 
 
static Chan* 
sdpattach(char* spec) 
{ 
	Chan *c; 
	int dev; 
1999/0906    
	char buf[100]; 
	Sdp *sdp; 
	int start; 
1999/0824    
 
	dev = atoi(spec); 
	if(dev<0 || dev >= Nfs) 
		error("bad specification"); 
 
1999/1027    
	c = devattach('E', spec); 
1999/0824    
	c->qid = (Qid){QID(0, Qtopdir)|CHDIR, 0}; 
	c->dev = dev; 
 
1999/0906    
	sdp = sdptab + dev; 
	qlock(sdp); 
	start = sdp->ackproc == 0; 
	sdp->ackproc = 1; 
	qunlock(sdp); 
 
	if(start) { 
		snprint(buf, sizeof(buf), "sdpackproc%d", dev); 
		kproc(buf, sdpackproc, sdp); 
	} 
	 
1999/0824    
	return c; 
} 
 
static int 
sdpwalk(Chan *c, char *name) 
{ 
	if(strcmp(name, "..") == 0){ 
		switch(TYPE(c->qid)){ 
		case Qtopdir: 
		case Qsdpdir: 
			c->qid = (Qid){CHDIR|Qtopdir, 0}; 
			break; 
1999/0901    
		case Qconvdir: 
1999/0824    
			c->qid = (Qid){CHDIR|Qsdpdir, 0}; 
			break; 
		default: 
			panic("sdpwalk %lux", c->qid.path); 
		} 
		return 1; 
	} 
 
	return devwalk(c, name, 0, 0, sdpgen); 
} 
 
static void 
sdpstat(Chan* c, char* db) 
{ 
	devstat(c, db, nil, 0, sdpgen); 
} 
 
1999/1211    
 
1999/0824    
static Chan* 
1999/0901    
sdpopen(Chan* ch, int omode) 
1999/0824    
{ 
	int perm; 
	Sdp *sdp; 
1999/0901    
	Conv *c; 
1999/0824    
 
	omode &= 3; 
	perm = m2p[omode]; 
	USED(perm); 
 
1999/0901    
	sdp = sdptab + ch->dev; 
1999/0824    
 
1999/0901    
	switch(TYPE(ch->qid)) { 
1999/0824    
	default: 
		break; 
	case Qtopdir: 
	case Qsdpdir: 
1999/0901    
	case Qconvdir: 
1999/0824    
		if(omode != OREAD) 
			error(Eperm); 
		break; 
	case Qlog: 
		logopen(sdp); 
		break; 
1999/0901    
	case Qclone: 
		c = sdpclone(sdp); 
		if(c == nil) 
			error(Enodev); 
		ch->qid.path = QID(c->id, Qctl); 
		break; 
1999/0902    
	case Qdata: 
	case Qctl: 
	case Qstatus: 
	case Qcontrol: 
1999/0929    
	case Qstats: 
	case Qrstats: 
1999/0902    
		c = sdp->conv[CONV(ch->qid)]; 
1999/1211    
print("open %d:%d: ref=%d\n", c->id, TYPE(ch->qid), c->ref); 
1999/0902    
		qlock(c); 
		if(waserror()) { 
			qunlock(c); 
			nexterror(); 
		} 
		if((perm & (c->perm>>6)) != perm) 
1999/0907    
		if(strcmp(up->user, c->owner) != 0 || (perm & c->perm) != perm) 
1999/0902    
				error(Eperm); 
1999/1211    
 
1999/0902    
		c->ref++; 
1999/0910    
		if(TYPE(ch->qid) == Qdata) { 
1999/1211    
			c->dataopen++; 
			// kill reader if Qdata is opened for the first time 
			if(c->dataopen == 1) 
1999/0910    
			if(c->readproc != nil) 
				postnote(c->readproc, 1, "interrupt", 0); 
1999/1211    
		} else if(TYPE(ch->qid) == Qcontrol) {	 
			c->controlopen++; 
1999/0910    
		} 
1999/0902    
		qunlock(c); 
		poperror(); 
		break; 
1999/0824    
	} 
1999/0901    
	ch->mode = openmode(omode); 
	ch->flag |= COPEN; 
	ch->offset = 0; 
	return ch; 
1999/0824    
} 
 
static void 
1999/0902    
sdpclose(Chan* ch) 
1999/0824    
{ 
1999/0902    
	Sdp *sdp  = sdptab + ch->dev; 
1999/0907    
	Conv *c; 
1999/0824    
 
1999/1211    
	if(!(ch->flag & COPEN)) 
		return; 
1999/0902    
	switch(TYPE(ch->qid)) { 
1999/0824    
	case Qlog: 
1999/1211    
		logclose(sdp); 
1999/0824    
		break; 
1999/0907    
	case Qctl: 
	case Qstatus: 
1999/1211    
	case Qstats: 
	case Qrstats: 
1999/0907    
		c = sdp->conv[CONV(ch->qid)]; 
		qlock(c); 
1999/1211    
		convderef(c); 
		qunlock(c); 
		break; 
 
	case Qdata: 
		c = sdp->conv[CONV(ch->qid)]; 
		qlock(c); 
		c->dataopen--; 
		convderef(c); 
		if(c->dataopen == 0) 
		if(c->reader == 0) 
		if(c->chan != nil) 
		if(!waserror()) { 
			kproc("convreader", convreader, c); 
			c->reader = 1; 
			c->ref++; 
			poperror(); 
1999/0907    
		} 
1999/1211    
		qunlock(c); 
		break; 
 
	case Qcontrol: 
		c = sdp->conv[CONV(ch->qid)]; 
		qlock(c); 
		c->controlopen--; 
		convderef(c); 
		if(c->controlopen == 0 && c->ref != 0) { 
1999/0907    
			switch(c->state) { 
			default: 
				convsetstate(c, CClosed); 
				break; 
1999/0909    
			case CAccept: 
1999/0907    
			case COpen: 
1999/0910    
				convsetstate(c, CLocalClose); 
1999/0907    
				break; 
			} 
		} 
		qunlock(c); 
		poperror(); 
		break; 
1999/0824    
	} 
} 
 
static long 
1999/0901    
sdpread(Chan *ch, void *a, long n, vlong off) 
1999/0824    
{ 
	char buf[256]; 
1999/0929    
	char *s; 
1999/0901    
	Sdp *sdp = sdptab + ch->dev; 
	Conv *c; 
1999/0907    
	Block *b; 
1999/0929    
	int rv; 
1999/0824    
 
	USED(off); 
1999/0901    
	switch(TYPE(ch->qid)) { 
1999/0824    
	default: 
		error(Eperm); 
	case Qtopdir: 
	case Qsdpdir: 
1999/0901    
	case Qconvdir: 
		return devdirread(ch, a, n, 0, 0, sdpgen); 
1999/0824    
	case Qlog: 
		return logread(sdp, a, off, n); 
	case Qstatus: 
1999/0901    
		c = sdp->conv[CONV(ch->qid)]; 
1999/0902    
		qlock(c); 
1999/0909    
		n = readstr(off, a, n, convstatename[c->state]); 
1999/0902    
		qunlock(c); 
1999/0824    
		return n; 
1999/0901    
	case Qctl: 
		sprint(buf, "%lud", CONV(ch->qid)); 
		return readstr(off, a, n, buf); 
1999/0907    
	case Qcontrol: 
		b = readcontrol(sdp->conv[CONV(ch->qid)], n); 
		if(b == nil) 
			return 0; 
1999/0914    
print("readcontrol asked %ld got %ld\n", n, BLEN(b)); 
1999/0907    
		if(BLEN(b) < n) 
			n = BLEN(b); 
		memmove(a, b->rp, n); 
		freeb(b); 
		return n; 
	case Qdata: 
		b = readdata(sdp->conv[CONV(ch->qid)], n); 
		if(b == nil) 
			return 0; 
		if(BLEN(b) < n) 
			n = BLEN(b); 
		memmove(a, b->rp, n); 
		freeb(b); 
		return n; 
1999/0929    
	case Qstats: 
	case Qrstats: 
		c = sdp->conv[CONV(ch->qid)]; 
		s = smalloc(1000); 
		convstats(c, TYPE(ch->qid) == Qstats, s, 1000); 
		rv = readstr(off, a, n, s); 
		free(s); 
		return rv; 
1999/0824    
	} 
} 
 
1999/0907    
static Block* 
sdpbread(Chan* ch, long n, ulong offset) 
{ 
	Sdp *sdp = sdptab + ch->dev; 
 
	if(TYPE(ch->qid) != Qdata) 
		return devbread(ch, n, offset); 
	return readdata(sdp->conv[CONV(ch->qid)], n); 
} 
 
1999/0914    
 
1999/0824    
static long 
1999/0901    
sdpwrite(Chan *ch, void *a, long n, vlong off) 
1999/0824    
{ 
1999/0901    
	Sdp *sdp = sdptab + ch->dev; 
1999/0824    
	Cmdbuf *cb; 
	char *arg0; 
	char *p; 
1999/0906    
	Conv *c; 
1999/0915    
	Block *b; 
1999/0824    
	 
	USED(off); 
1999/0901    
	switch(TYPE(ch->qid)) { 
1999/0824    
	default: 
		error(Eperm); 
	case Qctl: 
1999/0906    
		c = sdp->conv[CONV(ch->qid)]; 
1999/0824    
		cb = parsecmd(a, n); 
1999/0906    
		qlock(c); 
1999/0824    
		if(waserror()) { 
1999/0906    
			qunlock(c); 
1999/0824    
			free(cb); 
			nexterror(); 
		} 
		if(cb->nf == 0) 
			error("short write"); 
		arg0 = cb->f[0]; 
1999/0910    
		if(strcmp(arg0, "accept") == 0) { 
1999/0906    
			if(cb->nf != 2) 
1999/0910    
				error("usage: accect file"); 
1999/0914    
			convopenchan(c, cb->f[1]); 
1999/0906    
		} else if(strcmp(arg0, "dial") == 0) { 
1999/0910    
			if(cb->nf != 2) 
				error("usage: accect file"); 
1999/0914    
			convopenchan(c, cb->f[1]); 
1999/0909    
			convsetstate(c, CDial); 
1999/0906    
		} else if(strcmp(arg0, "drop") == 0) { 
			if(cb->nf != 2) 
				error("usage: drop permil"); 
			c->drop = atoi(cb->f[1]); 
1999/1015    
		} else if(strcmp(arg0, "cipher") == 0) { 
			if(cb->nf != 2) 
				error("usage: cipher alg"); 
1999/1022    
			setalg(c, cb->f[1], cipheralg, &c->cipher); 
1999/1015    
		} else if(strcmp(arg0, "auth") == 0) { 
			if(cb->nf != 2) 
				error("usage: auth alg"); 
1999/1022    
			setalg(c, cb->f[1], authalg, &c->auth); 
1999/1015    
		} else if(strcmp(arg0, "comp") == 0) { 
			if(cb->nf != 2) 
				error("usage: comp alg"); 
1999/1022    
			setalg(c, cb->f[1], compalg, &c->comp); 
1999/1015    
		} else if(strcmp(arg0, "insecret") == 0) { 
			if(cb->nf != 2) 
				error("usage: insecret secret"); 
			setsecret(&c->in, cb->f[1]); 
1999/1022    
			if(c->cipher) 
				c->cipher->init(c); 
			if(c->auth) 
				c->auth->init(c); 
1999/1015    
		} else if(strcmp(arg0, "outsecret") == 0) { 
			if(cb->nf != 2) 
				error("usage: outsecret secret"); 
			setsecret(&c->out, cb->f[1]); 
1999/1022    
			if(c->cipher) 
				c->cipher->init(c); 
			if(c->auth) 
				c->auth->init(c); 
1999/0824    
		} else 
			error("unknown control request"); 
		poperror(); 
1999/0906    
		qunlock(c); 
1999/0824    
		free(cb); 
		return n; 
	case Qlog: 
		cb = parsecmd(a, n); 
		p = logctl(sdp, cb->nf, cb->f, logflags); 
		free(cb); 
		if(p != nil) 
			error(p); 
		return n; 
1999/0914    
	case Qcontrol: 
1999/0929    
		writecontrol(sdp->conv[CONV(ch->qid)], a, n, 0); 
1999/0914    
		return n; 
1999/0915    
	case Qdata: 
		b = allocb(n); 
		memmove(b->wp, a, n); 
		b->wp += n; 
		return writedata(sdp->conv[CONV(ch->qid)], b); 
1999/0824    
	} 
} 
 
1999/0915    
long 
sdpbwrite(Chan *ch, Block *bp, ulong offset) 
{ 
	Sdp *sdp = sdptab + ch->dev; 
 
	if(TYPE(ch->qid) != Qdata) 
		return devbwrite(ch, bp, offset); 
	return writedata(sdp->conv[CONV(ch->qid)], bp); 
} 
 
1999/0824    
static int 
sdpgen(Chan *c, Dirtab*, int, int s, Dir *dp) 
{ 
	Sdp *sdp = sdptab + c->dev; 
	int type = TYPE(c->qid); 
	char buf[32]; 
	Dirtab *dt; 
	Qid qid; 
 
	switch(type) { 
	default: 
		// non directory entries end up here 
		if(c->qid.path & CHDIR) 
			panic("sdpgen: unexpected directory");	 
		if(s != 0) 
			return -1; 
		dt = dirtab[TYPE(c->qid)]; 
		if(dt == nil) 
			panic("sdpgen: unknown type: %d", TYPE(c->qid)); 
		devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp); 
		return 1; 
	case Qtopdir: 
		if(s != 0) 
			return -1; 
		devdir(c, (Qid){QID(0,Qsdpdir)|CHDIR,0}, "sdp", 0, eve, 0555, dp); 
		return 1; 
	case Qsdpdir: 
		if(s<nelem(sdpdirtab)) { 
			dt = sdpdirtab+s; 
			devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp); 
			return 1; 
		} 
		s -= nelem(sdpdirtab); 
1999/0901    
		if(s >= sdp->nconv) 
1999/0824    
			return -1; 
1999/0901    
		qid = (Qid){QID(s,Qconvdir)|CHDIR, 0}; 
1999/0824    
		snprint(buf, sizeof(buf), "%d", s); 
		devdir(c, qid, buf, 0, eve, 0555, dp); 
		return 1; 
1999/0901    
	case Qconvdir: 
		if(s>=nelem(convdirtab)) 
1999/0824    
			return -1; 
1999/0901    
		dt = convdirtab+s; 
		qid = (Qid){QID(CONV(c->qid),TYPE(dt->qid)),0}; 
1999/0824    
		devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp); 
		return 1; 
	} 
1999/0901    
} 
 
static Conv* 
sdpclone(Sdp *sdp) 
{ 
	Conv *c, **pp, **ep; 
 
	c = nil; 
	ep = sdp->conv + nelem(sdp->conv); 
1999/0902    
	qlock(sdp); 
	if(waserror()) { 
		qunlock(sdp); 
		nexterror(); 
	} 
1999/0901    
	for(pp = sdp->conv; pp < ep; pp++) { 
		c = *pp; 
		if(c == nil){ 
			c = malloc(sizeof(Conv)); 
			if(c == nil) 
				error(Enomem); 
1999/1001    
			memset(c, 0, sizeof(Conv)); 
1999/0901    
			qlock(c); 
			c->sdp = sdp; 
			c->id = pp - sdp->conv; 
			*pp = c; 
			sdp->nconv++; 
			break; 
		} 
1999/1211    
		if(c->ref == 0 && canqlock(c)){ 
print("%d: state=%d reader=%d ref=%d\n", c->id, c->state, c->reader, c->ref); 
			if(c->ref == 0) 
1999/0901    
				break; 
			qunlock(c); 
		} 
	} 
1999/0902    
	poperror(); 
	qunlock(sdp); 
1999/0901    
 
1999/0902    
	if(pp >= ep) 
1999/0901    
		return nil; 
 
1999/1211    
	assert(c->state == CFree); 
	// set ref to 2 - 1 ref for open - 1 ref for channel state 
	c->ref = 2; 
1999/0906    
	c->state = CInit; 
1999/1001    
	c->in.window = ~0; 
1999/0907    
	strncpy(c->owner, up->user, sizeof(c->owner)); 
1999/0901    
	c->perm = 0660; 
	qunlock(c); 
1999/0902    
 
1999/0901    
	return c; 
1999/0824    
} 
 
1999/0906    
// assume c is locked 
1999/0909    
static void 
convretryinit(Conv *c) 
{ 
	c->retries = 0; 
	// +2 to avoid rounding effects. 
	c->timeout = TK2SEC(m->ticks) + 2; 
1999/1015    
} 
1999/0909    
 
// assume c is locked 
1999/0906    
static int 
1999/0914    
convretry(Conv *c, int reset) 
1999/0906    
{ 
	c->retries++; 
1999/0914    
print("convretry: %s: %d\n", convstatename[c->state], c->retries); 
1999/0909    
	if(c->retries > MaxRetries) { 
1999/0906    
print("convretry: giving up\n"); 
1999/0914    
		if(reset) 
1999/1015    
			convoconnect(c, ConReset, c->dialid, c->acceptid); 
1999/0906    
		convsetstate(c, CClosed); 
		return 0; 
	} 
1999/0909    
	c->timeout = TK2SEC(m->ticks) + (c->retries+1); 
1999/0906    
	return 1; 
} 
 
1999/1211    
// assumes c is locked 
1999/0902    
static void 
1999/0906    
convtimer(Conv *c, ulong sec) 
1999/0902    
{ 
1999/0914    
	Block *b; 
 
1999/1001    
	if(c->timeout > sec) 
1999/0906    
		return; 
1999/1211    
 
1999/0906    
	switch(c->state) { 
1999/1211    
	case CInit: 
		break; 
1999/0909    
	case CDial: 
1999/0914    
		if(convretry(c, 1)) 
1999/1015    
			convoconnect(c, ConOpenRequest, c->dialid, 0); 
1999/0906    
		break; 
1999/0909    
	case CAccept: 
1999/0914    
		if(convretry(c, 1)) 
1999/1015    
			convoconnect(c, ConOpenAck, c->dialid, c->acceptid); 
1999/0909    
		break; 
1999/0906    
	case COpen: 
1999/0914    
		b = c->out.controlpkt; 
		if(b != nil) { 
			if(convretry(c, 1)) 
1999/1015    
				convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b))); 
1999/1001    
			break; 
1999/0914    
		} 
1999/1001    
 
		c->timeout = c->lastrecv + KeepAlive; 
		if(c->timeout > sec) 
			break; 
		// keepalive - randomly spaced between KeepAlive and 2*KeepAlive 
		if(c->timeout + KeepAlive > sec && nrand(c->lastrecv + 2*KeepAlive - sec) > 0) 
			break; 
print("sending keep alive: %ld\n", sec - c->lastrecv); 
		// can not use writecontrol 
		b = allocb(4); 
		c->out.controlseq++; 
		hnputl(b->wp, c->out.controlseq); 
		b->wp += 4; 
		c->out.controlpkt = b; 
		convretryinit(c); 
		if(!waserror()) { 
1999/1015    
			convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b))); 
1999/1001    
			poperror(); 
		} 
1999/0906    
		break; 
1999/0910    
	case CLocalClose: 
1999/0914    
		if(convretry(c, 0)) 
1999/1015    
			convoconnect(c, ConClose, c->dialid, c->acceptid); 
1999/0906    
		break; 
1999/0914    
	case CRemoteClose: 
	case CClosed: 
		break; 
1999/0906    
	} 
} 
1999/0902    
 
 
1999/0906    
static void 
sdpackproc(void *a) 
{ 
	Sdp *sdp = a; 
	ulong sec; 
	int i; 
	Conv *c; 
 
	for(;;) { 
		tsleep(&sdp->vous, return0, 0, 1000); 
		sec = TK2SEC(m->ticks); 
		qlock(sdp); 
		for(i=0; i<sdp->nconv; i++) { 
			c = sdp->conv[i]; 
1999/1211    
			if(c->ref == 0) 
				continue; 
			qunlock(sdp); 
			qlock(c); 
			if(c->ref > 0 && !waserror()) { 
1999/0906    
				convtimer(c, sec); 
				poperror(); 
			} 
1999/1211    
			qunlock(c); 
			qlock(sdp); 
1999/0906    
		} 
		qunlock(sdp); 
1999/0902    
	} 
} 
1999/0824    
 
Dev sdpdevtab = { 
1999/1027    
	'E', 
1999/0824    
	"sdp", 
 
	devreset, 
	sdpinit, 
	sdpattach, 
	devclone, 
	sdpwalk, 
	sdpstat, 
	sdpopen, 
	devcreate, 
	sdpclose, 
	sdpread, 
	devbread, 
	sdpwrite, 
	devbwrite, 
	devremove, 
	devwstat, 
}; 
1999/0906    
 
// assume hold lock on c 
static void 
convsetstate(Conv *c, int state) 
{ 
1999/0907    
 
1999/0908    
print("convsetstate %s -> %s\n", convstatename[c->state], convstatename[state]); 
1999/0907    
 
1999/0906    
	switch(state) { 
	default: 
		panic("setstate: bad state: %d", state); 
1999/0909    
	case CDial: 
		assert(c->state == CInit); 
1999/0906    
		c->dialid = (rand()<<16) + rand(); 
1999/0909    
		convretryinit(c); 
1999/1015    
		convoconnect(c, ConOpenRequest, c->dialid, 0); 
1999/0906    
		break; 
1999/0909    
	case CAccept: 
		assert(c->state == CInit); 
		c->acceptid = (rand()<<16) + rand(); 
		convretryinit(c); 
1999/1015    
		convoconnect(c, ConOpenAck, c->dialid, c->acceptid); 
1999/0909    
		break; 
1999/0906    
	case COpen: 
1999/0909    
		assert(c->state == CDial || c->state == CAccept); 
1999/1211    
		c->lastrecv = TK2SEC(m->ticks); 
1999/0909    
		if(c->state == CDial) { 
			convretryinit(c); 
1999/1015    
			convoconnect(c, ConOpenAckAck, c->dialid, c->acceptid); 
			hnputl(c->in.secret, c->acceptid); 
			hnputl(c->in.secret+4, c->dialid); 
			hnputl(c->out.secret, c->dialid); 
			hnputl(c->out.secret+4, c->acceptid); 
		} else { 
			hnputl(c->in.secret, c->dialid); 
			hnputl(c->in.secret+4, c->acceptid); 
			hnputl(c->out.secret, c->acceptid); 
			hnputl(c->out.secret+4, c->dialid); 
1999/0906    
		} 
1999/1022    
		setalg(c, "hmac_md5_96", authalg, &c->auth); 
1999/0906    
		break; 
1999/0910    
	case CLocalClose: 
		assert(c->state == CAccept || c->state == COpen); 
1999/0909    
		convretryinit(c); 
1999/1015    
		convoconnect(c, ConClose, c->dialid, c->acceptid); 
1999/0906    
		break; 
1999/0910    
	case CRemoteClose: 
1999/0914    
		wakeup(&c->in.controlready); 
1999/1211    
		wakeup(&c->out.controlready); 
1999/0910    
		break; 
1999/0906    
	case CClosed: 
1999/0914    
		wakeup(&c->in.controlready); 
1999/0915    
		wakeup(&c->out.controlready); 
1999/0907    
		if(c->readproc) 
			postnote(c->readproc, 1, "interrupt", 0); 
1999/1211    
		if(c->state != CClosed) 
			convderef(c); 
1999/0906    
		break; 
	} 
	c->state = state; 
} 
 
1999/1211    
 
//assumes c is locked 
1999/0906    
static void 
1999/1211    
convderef(Conv *c) 
{ 
	c->ref--; 
print("convderef: %d: ref == %d\n", c->id, c->ref); 
	if(c->ref > 0) 
		return; 
	assert(c->ref == 0); 
	assert(c->dataopen == 0); 
	assert(c->controlopen == 0); 
//print("convderef: %d: ref == 0!\n", c->id); 
	c->state = CFree; 
	if(c->chan) {	 
		cclose(c->chan); 
		c->chan = nil; 
	} 
	if(c->channame) { 
		free(c->channame); 
		c->channame = nil; 
	} 
	c->cipher = nil; 
	c->auth = nil; 
	c->comp = nil; 
	strcpy(c->owner, "network"); 
	c->perm = 0660; 
	c->dialid = 0; 
	c->acceptid = 0; 
	c->timeout = 0; 
	c->retries = 0; 
	c->drop = 0; 
	onewaycleanup(&c->in); 
	onewaycleanup(&c->out); 
	memset(&c->lstats, 0, sizeof(Stats)); 
	memset(&c->rstats, 0, sizeof(Stats)); 
} 
 
static void 
1999/0907    
onewaycleanup(OneWay *ow) 
1999/0906    
{ 
1999/0907    
	if(ow->controlpkt) 
		freeb(ow->controlpkt); 
	if(ow->authstate) 
		free(ow->authstate); 
	if(ow->cipherstate) 
		free(ow->cipherstate); 
	if(ow->compstate) 
		free(ow->compstate); 
	memset(ow, 0, sizeof(OneWay)); 
} 
1999/0906    
 
1999/0907    
 
1999/0914    
// assumes conv is locked 
static void 
convopenchan(Conv *c, char *path) 
{ 
1999/0915    
	if(c->state != CInit || c->chan != nil) 
1999/0914    
		error("already connected"); 
	c->chan = namec(path, Aopen, ORDWR, 0); 
	c->channame = malloc(strlen(path)+1); 
	strcpy(c->channame, path); 
	if(waserror()) { 
		cclose(c->chan); 
		c->chan = nil; 
		free(c->channame); 
		c->channame = nil; 
		nexterror(); 
	} 
	kproc("convreader", convreader, c); 
1999/1211    
 
	assert(c->reader == 0 && c->ref > 0); 
	// after kproc in case it fails 
1999/0914    
	c->reader = 1; 
1999/1211    
	c->ref++; 
 
1999/0914    
	poperror(); 
} 
1999/0907    
 
1999/0929    
static void 
convstats(Conv *c, int local, char *buf, int n) 
{ 
	Stats *stats; 
	char *p, *ep; 
1999/1019    
	int i; 
1999/0907    
 
1999/0929    
	if(local) { 
		stats = &c->lstats; 
	} else { 
		if(!waserror()) { 
			writecontrol(c, 0, 0, 1); 
			poperror(); 
		} 
		stats = &c->rstats; 
	} 
1999/0930    
 
	qlock(c); 
1999/0929    
	p = buf; 
	ep = buf + n; 
1999/1019    
	p += snprint(p, ep-p, "outPackets: %lud\n", stats->outPackets); 
	p += snprint(p, ep-p, "outDataPackets: %lud\n", stats->outDataPackets); 
	p += snprint(p, ep-p, "outDataBytes: %lud\n", stats->outDataBytes); 
	p += snprint(p, ep-p, "outCompDataBytes: %lud\n", stats->outCompDataBytes); 
1999/1021    
	for(i=0; i<NCompStats; i++) { 
		if(stats->outCompStats[i] == 0) 
1999/1019    
			continue; 
		p += snprint(p, ep-p, "outCompStats[%d]: %lud\n", i, stats->outCompStats[i]); 
	} 
	p += snprint(p, ep-p, "inPackets: %lud\n", stats->inPackets); 
	p += snprint(p, ep-p, "inDataPackets: %lud\n", stats->inDataPackets); 
	p += snprint(p, ep-p, "inDataBytes: %lud\n", stats->inDataBytes); 
	p += snprint(p, ep-p, "inCompDataBytes: %lud\n", stats->inCompDataBytes); 
	p += snprint(p, ep-p, "inMissing: %lud\n", stats->inMissing); 
	p += snprint(p, ep-p, "inDup: %lud\n", stats->inDup); 
	p += snprint(p, ep-p, "inReorder: %lud\n", stats->inReorder); 
	p += snprint(p, ep-p, "inBadComp: %lud\n", stats->inBadComp); 
	p += snprint(p, ep-p, "inBadAuth: %lud\n", stats->inBadAuth); 
	p += snprint(p, ep-p, "inBadSeq: %lud\n", stats->inBadSeq); 
1999/1022    
	p += snprint(p, ep-p, "inBadOther: %lud\n", stats->inBadOther); 
1999/0929    
	USED(p); 
	qunlock(c); 
} 
 
// c is locked 
static void 
convack(Conv *c) 
{ 
	Block *b; 
	AckPkt *ack; 
	Stats *s; 
1999/1019    
	int i; 
1999/0929    
 
	b = allocb(sizeof(AckPkt)); 
1999/0930    
	ack = (AckPkt*)b->wp; 
1999/0929    
	b->wp += sizeof(AckPkt); 
	s = &c->lstats; 
	hnputl(ack->cseq, c->in.controlseq); 
	hnputl(ack->outPackets, s->outPackets); 
	hnputl(ack->outDataPackets, s->outDataPackets); 
	hnputl(ack->outDataBytes, s->outDataBytes); 
	hnputl(ack->outCompDataBytes, s->outCompDataBytes); 
1999/1021    
	for(i=0; i<NCompStats; i++) 
1999/1019    
		hnputl(ack->outCompStats+i*4, s->outCompStats[i]); 
1999/0929    
	hnputl(ack->inPackets, s->inPackets); 
	hnputl(ack->inDataPackets, s->inDataPackets); 
	hnputl(ack->inDataBytes, s->inDataBytes); 
	hnputl(ack->inCompDataBytes, s->inCompDataBytes); 
	hnputl(ack->inMissing, s->inMissing); 
	hnputl(ack->inDup, s->inDup); 
	hnputl(ack->inReorder, s->inReorder); 
1999/1016    
	hnputl(ack->inBadComp, s->inBadComp); 
1999/0929    
	hnputl(ack->inBadAuth, s->inBadAuth); 
	hnputl(ack->inBadSeq, s->inBadSeq); 
1999/1022    
	hnputl(ack->inBadOther, s->inBadOther); 
1999/1015    
	convoput(c, TControl, ControlAck, b); 
1999/0929    
} 
 
 
1999/0907    
// assume we hold lock for c 
static Block * 
conviput(Conv *c, Block *b, int control) 
{ 
1999/1015    
	int type, subtype; 
	ulong seq, seqwrap; 
1999/1001    
	long seqdiff; 
1999/1022    
	int pad; 
1999/0907    
 
1999/0930    
	c->lstats.inPackets++; 
1999/0929    
 
1999/0907    
	if(BLEN(b) < 4) { 
1999/1022    
		c->lstats.inBadOther++; 
1999/0907    
		freeb(b); 
		return nil; 
	} 
	 
1999/1015    
	type = b->rp[0] >> 4; 
1999/1016    
	subtype = b->rp[0] & 0xf; 
1999/1015    
	b->rp += 1; 
1999/0907    
	if(type == TConnect) { 
1999/1015    
		conviconnect(c, subtype, b); 
1999/0907    
		return nil; 
	} 
 
1999/1015    
	seq = (b->rp[0]<<16) + (b->rp[1]<<8) + b->rp[2]; 
	b->rp += 3; 
1999/0907    
 
1999/1001    
	seqwrap = c->in.seqwrap; 
	seqdiff = seq - c->in.seq; 
	if(seqdiff < -(SeqMax*3/4)) { 
		seqwrap++; 
		seqdiff += SeqMax; 
	} else if(seqdiff > SeqMax*3/4) { 
		seqwrap--; 
		seqdiff -= SeqMax; 
	} 
 
	if(seqdiff <= 0) { 
		if(seqdiff <= -SeqWindow) { 
print("old sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff); 
			c->lstats.inBadSeq++; 
			freeb(b); 
			return nil; 
		} 
 
		if(c->in.window & (1<<-seqdiff)) { 
print("dup sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff); 
			c->lstats.inDup++; 
			freeb(b); 
			return nil; 
		} 
 
		c->lstats.inReorder++; 
	} 
 
	// ok the sequence number looks ok 
1999/0930    
if(0) print("coniput seq=%ulx\n", seq); 
1999/1015    
	if(c->in.auth != 0) { 
		if(!(*c->in.auth)(&c->in, b->rp-4, BLEN(b)+4)) { 
1999/1106    
print("bad auth %ld\n", BLEN(b)+4); 
1999/1015    
			c->lstats.inBadAuth++; 
			freeb(b); 
			return nil; 
		} 
		b->wp -= c->in.authlen; 
	} 
 
1999/1022    
	if(c->in.cipher != 0) { 
		if(!(*c->in.cipher)(&c->in, b->rp, BLEN(b))) { 
print("bad cipher\n"); 
			c->lstats.inBadOther++; 
			freeb(b); 
			return nil; 
		} 
		b->rp += c->in.cipherivlen; 
		if(c->in.cipherblklen > 1) { 
			pad = b->wp[-1]; 
			if(pad > BLEN(b)) { 
print("pad too big\n"); 
				c->lstats.inBadOther++; 
				freeb(b); 
				return nil; 
			} 
			b->wp -= pad; 
		} 
	} 
1999/0907    
 
	// ok the packet is good 
1999/1001    
	if(seqdiff > 0) { 
		while(seqdiff > 0 && c->in.window != 0) { 
			if((c->in.window & (1<<(SeqWindow-1))) == 0) { 
1999/1211    
//print("missing packet: %ld\n", seq - seqdiff); 
1999/1001    
				c->lstats.inMissing++; 
			} 
			c->in.window <<= 1; 
			seqdiff--; 
		} 
		if(seqdiff > 0) { 
1999/1211    
//print("missing packets: %ld-%ld\n", seq - SeqWindow - seqdiff+1, seq-SeqWindow); 
1999/1001    
			c->lstats.inMissing += seqdiff; 
		} 
		c->in.seq = seq; 
		c->in.seqwrap = seqwrap; 
		c->in.window |= 1; 
	} 
	c->lastrecv = TK2SEC(m->ticks); 
1999/0907    
 
	switch(type) { 
	case TControl: 
1999/1015    
		convicontrol(c, subtype, b); 
1999/0907    
		return nil; 
	case TData: 
1999/0929    
		c->lstats.inDataPackets++; 
		c->lstats.inDataBytes += BLEN(b); 
1999/0907    
		if(control) 
			break; 
		return b; 
1999/1015    
	case TCompData: 
1999/1001    
		c->lstats.inDataPackets++; 
		c->lstats.inCompDataBytes += BLEN(b); 
1999/1016    
		b = convicomp(c, subtype, seq, b); 
		if(b == nil) { 
			c->lstats.inBadComp++; 
1999/1015    
			return nil; 
1999/1016    
		} 
1999/1001    
		c->lstats.inDataBytes += BLEN(b); 
		if(control) 
			break; 
		return b; 
1999/0907    
	} 
print("droping packet %d n=%ld\n", type, BLEN(b)); 
1999/1022    
	c->lstats.inBadOther++; 
1999/0907    
	freeb(b); 
	return nil; 
} 
 
// assume hold conv lock 
static void 
1999/1015    
conviconnect(Conv *c, int subtype, Block *b) 
1999/0907    
{ 
	ulong dialid; 
	ulong acceptid; 
 
1999/1015    
	if(BLEN(b) != 8) { 
1999/0907    
		freeb(b); 
		return; 
	} 
1999/1015    
	dialid = nhgetl(b->rp); 
	acceptid = nhgetl(b->rp + 4); 
	freeb(b); 
1999/0907    
 
1999/1015    
print("conviconnect: %s: %d %uld %uld\n", convstatename[c->state], subtype, dialid, acceptid); 
1999/0914    
 
1999/0910    
	switch(c->state) { 
	default: 
		panic("unknown state: %d", c->state); 
	case CInit: 
		break; 
	case CDial: 
		if(dialid != c->dialid) 
			goto Reset; 
		break; 
	case CAccept: 
	case COpen: 
	case CLocalClose: 
	case CRemoteClose: 
1999/1015    
		if(dialid != c->dialid 
		|| subtype != ConOpenRequest && acceptid != c->acceptid) 
1999/0910    
			goto Reset; 
		break; 
	case CClosed: 
		goto Reset; 
	} 
 
1999/1015    
	switch(subtype) { 
1999/0909    
	case ConOpenRequest: 
		switch(c->state) { 
		case CInit: 
			c->dialid = dialid; 
			convsetstate(c, CAccept); 
1999/0910    
			return; 
1999/0909    
		case CAccept: 
		case COpen: 
1999/0910    
			// duplicate ConOpenRequest that we ignore 
			return; 
1999/0909    
		} 
1999/0907    
		break; 
	case ConOpenAck: 
1999/0909    
		switch(c->state) { 
		case CDial: 
			c->acceptid = acceptid; 
			convsetstate(c, COpen); 
1999/0910    
			return; 
		case COpen: 
			// duplicate that we have to ack 
1999/1015    
			convoconnect(c, ConOpenAckAck, acceptid, dialid); 
1999/0910    
			return; 
		} 
		break; 
	case ConOpenAckAck: 
		switch(c->state) { 
1999/0909    
		case CAccept: 
			convsetstate(c, COpen); 
1999/0910    
			return; 
		case COpen: 
			// duplicate that we ignore 
			return; 
1999/0909    
		} 
1999/0907    
		break; 
	case ConClose: 
1999/1015    
		convoconnect(c, ConReset, dialid, acceptid); 
1999/0909    
		switch(c->state) { 
1999/0910    
		case CInit: 
1999/0909    
		case CDial: 
		case CAccept: 
1999/0910    
		case CLocalClose: 
			convsetstate(c, CClosed); 
			return; 
1999/0909    
		case COpen: 
1999/0910    
			convsetstate(c, CRemoteClose); 
			return; 
		case CRemoteClose: 
			return; 
1999/0909    
		} 
1999/1015    
		break; 
1999/0910    
	case ConReset: 
		switch(c->state) { 
		case CInit: 
		case CDial: 
		case CAccept: 
		case COpen: 
		case CLocalClose: 
1999/0909    
			convsetstate(c, CClosed); 
1999/0910    
			return; 
		case CRemoteClose: 
			return; 
		} 
1999/1015    
		break; 
1999/0907    
	} 
1999/0910    
Reset: 
	// invalid connection message - reset to sender 
1999/1015    
print("invalid conviconnect - sending reset\n"); 
	convoconnect(c, ConReset, dialid, acceptid); 
1999/0907    
} 
 
1999/1015    
static void 
convicontrol(Conv *c, int subtype, Block *b) 
{ 
	ulong cseq; 
	AckPkt *ack; 
1999/1022    
	int i; 
1999/1015    
 
	if(BLEN(b) < 4) 
		return; 
	cseq = nhgetl(b->rp); 
	 
	switch(subtype){ 
	case ControlMesg: 
		if(cseq == c->in.controlseq) { 
print("duplicate control packet: %ulx\n", cseq); 
			// duplicate control packet 
			freeb(b); 
			if(c->in.controlpkt == nil) 
				convack(c); 
			return; 
		} 
 
		if(cseq != c->in.controlseq+1) 
			return; 
		c->in.controlseq = cseq; 
		b->rp += 4; 
		if(BLEN(b) == 0) { 
			// just a ping 
			freeb(b); 
			convack(c); 
		} else { 
			c->in.controlpkt = b; 
if(0) print("recv %ld size=%ld\n", cseq, BLEN(b)); 
			wakeup(&c->in.controlready); 
		} 
		return; 
	case ControlAck: 
		if(cseq != c->out.controlseq) { 
print("ControlAck expected %ulx got %ulx\n", c->out.controlseq, cseq); 
			return; 
		} 
		if(BLEN(b) < sizeof(AckPkt)) 
			return; 
		ack = (AckPkt*)(b->rp); 
		c->rstats.outPackets = nhgetl(ack->outPackets); 
		c->rstats.outDataPackets = nhgetl(ack->outDataPackets); 
		c->rstats.outDataBytes = nhgetl(ack->outDataBytes); 
		c->rstats.outCompDataBytes = nhgetl(ack->outCompDataBytes); 
1999/1022    
		for(i=0; i<NCompStats; i++) 
			c->rstats.outCompStats[i] = nhgetl(ack->outCompStats + 4*i); 
1999/1015    
		c->rstats.inPackets = nhgetl(ack->inPackets); 
		c->rstats.inDataPackets = nhgetl(ack->inDataPackets); 
		c->rstats.inDataBytes = nhgetl(ack->inDataBytes); 
		c->rstats.inCompDataBytes = nhgetl(ack->inCompDataBytes); 
		c->rstats.inMissing = nhgetl(ack->inMissing); 
		c->rstats.inDup = nhgetl(ack->inDup); 
		c->rstats.inReorder = nhgetl(ack->inReorder); 
1999/1016    
		c->rstats.inBadComp = nhgetl(ack->inBadComp); 
1999/1015    
		c->rstats.inBadAuth = nhgetl(ack->inBadAuth); 
		c->rstats.inBadSeq = nhgetl(ack->inBadSeq); 
1999/1022    
		c->rstats.inBadOther = nhgetl(ack->inBadOther); 
1999/1015    
		freeb(b); 
		freeb(c->out.controlpkt); 
		c->out.controlpkt = nil; 
		c->timeout = c->lastrecv + KeepAlive; 
		wakeup(&c->out.controlready); 
		return; 
	} 
} 
 
static Block* 
1999/1016    
convicomp(Conv *c, int subtype, ulong seq, Block *b) 
1999/1015    
{ 
	if(c->in.comp == nil) { 
		freeb(b); 
		return nil; 
	} 
1999/1016    
	if(!(*c->in.comp)(c, subtype, seq, &b)) 
1999/1015    
		return nil; 
	return b; 
} 
 
1999/0930    
// c is locked 
static void 
convwriteblock(Conv *c, Block *b) 
{ 
	// simulated errors 
1999/1001    
	if(c->drop && nrand(c->drop) == 0) 
1999/0930    
		return; 
 
	if(waserror()) { 
		convsetstate(c, CClosed); 
		nexterror(); 
	} 
	devtab[c->chan->type]->bwrite(c->chan, b, 0); 
	poperror(); 
} 
 
 
1999/0907    
// assume hold conv lock 
static void 
1999/1015    
convoput(Conv *c, int type, int subtype, Block *b) 
1999/0907    
{ 
1999/1022    
	int pad; 
	 
1999/0929    
	c->lstats.outPackets++; 
1999/1022    
	/* Make room for sdp trailer */ 
	if(c->out.cipherblklen > 1) 
		pad = c->out.cipherblklen - (BLEN(b) + c->out.cipherivlen) % c->out.cipherblklen; 
	else 
		pad = 0; 
 
	b = padblock(b, -(pad+c->out.authlen)); 
 
	if(pad) { 
		memset(b->wp, 0, pad-1); 
		b->wp[pad-1] = pad; 
		b->wp += pad; 
	} 
 
1999/0907    
	/* Make space to fit sdp header */ 
	b = padblock(b, 4 + c->out.cipherivlen); 
1999/1015    
	b->rp[0] = (type << 4) | subtype; 
1999/0907    
	c->out.seq++; 
	if(c->out.seq == (1<<24)) { 
		c->out.seq = 0; 
		c->out.seqwrap++; 
	} 
	b->rp[1] = c->out.seq>>16; 
	b->rp[2] = c->out.seq>>8; 
	b->rp[3] = c->out.seq; 
	 
1999/1022    
	if(c->out.cipher) 
		(*c->out.cipher)(&c->out, b->rp+4, BLEN(b)-4); 
 
1999/0907    
	// auth 
1999/1015    
	if(c->out.auth) { 
		b->wp += c->out.authlen; 
		(*c->out.auth)(&c->out, b->rp, BLEN(b)); 
	} 
1999/0930    
	 
	convwriteblock(c, b); 
1999/0907    
} 
 
// assume hold conv lock 
static void 
1999/1015    
convoconnect(Conv *c, int op, ulong dialid, ulong acceptid) 
1999/0907    
{ 
1999/0930    
	Block *b; 
1999/0907    
 
1999/0929    
	c->lstats.outPackets++; 
1999/1211    
	assert(c->chan != nil); 
1999/1015    
	b = allocb(9); 
	b->wp[0] = (TConnect << 4) | op; 
	hnputl(b->wp+1, dialid); 
	hnputl(b->wp+5, acceptid); 
	b->wp += 9; 
1999/0906    
 
1999/1211    
	if(!waserror()) { 
		convwriteblock(c, b); 
		poperror(); 
	} 
1999/0907    
} 
 
1999/0910    
static Block * 
convreadblock(Conv *c, int n) 
{ 
	Block *b; 
1999/1211    
	Chan *ch; 
1999/0910    
 
	qlock(&c->readlk); 
	if(waserror()) { 
		c->readproc = nil; 
		qunlock(&c->readlk); 
		nexterror(); 
	} 
	qlock(c); 
	if(c->state == CClosed) { 
		qunlock(c); 
		error("closed"); 
	} 
	c->readproc = up; 
	ch = c->chan; 
1999/1211    
	assert(c->ref > 0); 
1999/0910    
	qunlock(c); 
 
	b = devtab[ch->type]->bread(ch, n, 0); 
	c->readproc = nil; 
	poperror(); 
	qunlock(&c->readlk); 
 
	return b; 
} 
 
1999/0907    
static int 
readready(void *a) 
{ 
	Conv *c = a; 
 
1999/0914    
	return c->in.controlpkt != nil || (c->state == CClosed) || (c->state == CRemoteClose); 
1999/0907    
} 
 
static Block * 
readcontrol(Conv *c, int n) 
{ 
	Block *b; 
 
1999/0910    
	USED(n); 
1999/0915    
 
	qlock(&c->in.controllk); 
	if(waserror()) { 
		qunlock(&c->in.controllk); 
		nexterror(); 
	} 
	qlock(c);	// this lock is not held during the sleep below 
 
1999/0907    
	for(;;) { 
1999/0910    
		if(c->state == CInit || c->state == CClosed) { 
1999/0907    
			qunlock(c); 
1999/0914    
print("readcontrol: return error - state = %s\n", convstatename[c->state]); 
			error("conversation closed"); 
1999/0907    
		} 
 
1999/0914    
		if(c->in.controlpkt != nil) 
			break; 
 
		if(c->state == CRemoteClose) { 
1999/0907    
			qunlock(c); 
1999/0914    
print("readcontrol: return nil - state = %s\n", convstatename[c->state]); 
1999/0915    
			poperror(); 
1999/0914    
			return nil; 
1999/0907    
		} 
		qunlock(c); 
1999/0910    
		sleep(&c->in.controlready, readready, c); 
1999/0914    
		qlock(c); 
1999/0907    
	} 
1999/0914    
 
1999/0929    
	convack(c); 
1999/0914    
 
	b = c->in.controlpkt; 
	c->in.controlpkt = nil; 
	qunlock(c); 
1999/0915    
	poperror(); 
	qunlock(&c->in.controllk); 
1999/0914    
	return b; 
1999/0907    
} 
 
1999/0914    
 
static int 
writeready(void *a) 
{ 
	Conv *c = a; 
 
	return c->out.controlpkt == nil || (c->state == CClosed) || (c->state == CRemoteClose); 
} 
 
1999/0929    
// c is locked 
1999/0914    
static void 
1999/0929    
writewait(Conv *c) 
1999/0914    
{ 
	for(;;) { 
		if(c->state == CInit || c->state == CClosed || c->state == CRemoteClose) { 
print("writecontrol: return error - state = %s\n", convstatename[c->state]); 
			error("conversation closed"); 
		} 
 
		if(c->state == COpen && c->out.controlpkt == nil) 
			break; 
 
		qunlock(c); 
1999/0929    
		if(waserror()) { 
			qlock(c); 
			nexterror(); 
		} 
1999/0914    
		sleep(&c->out.controlready, writeready, c); 
1999/0929    
		poperror(); 
1999/0914    
		qlock(c); 
	} 
1999/0929    
} 
 
static void 
writecontrol(Conv *c, void *p, int n, int wait) 
{ 
	Block *b; 
 
	qlock(&c->out.controllk); 
	qlock(c); 
	if(waserror()) { 
		qunlock(c); 
		qunlock(&c->out.controllk); 
		nexterror(); 
	} 
	writewait(c); 
1999/0914    
	b = allocb(4+n); 
	c->out.controlseq++; 
	hnputl(b->wp, c->out.controlseq); 
	memmove(b->wp+4, p, n); 
	b->wp += 4+n; 
	c->out.controlpkt = b; 
	convretryinit(c); 
1999/1015    
	convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b))); 
1999/1001    
	if(wait) 
1999/0929    
		writewait(c); 
1999/0915    
	poperror(); 
1999/0929    
	qunlock(c); 
1999/0915    
	qunlock(&c->out.controllk); 
1999/0914    
} 
 
1999/0907    
static Block * 
readdata(Conv *c, int n) 
{ 
	Block *b; 
1999/1106    
	int nn; 
1999/0907    
 
	for(;;) { 
1999/1106    
 
		// some slack for tunneling overhead 
		nn = n + 100; 
 
		// make sure size is big enough for control messages 
		if(nn < 1000) 
			nn = 1000; 
		b = convreadblock(c, nn); 
1999/0930    
		if(b == nil) 
			return nil; 
1999/0907    
		qlock(c); 
		if(waserror()) { 
			qunlock(c); 
			return nil; 
		} 
		b = conviput(c, b, 0); 
		poperror(); 
		qunlock(c); 
1999/1106    
		if(b != nil) { 
			if(BLEN(b) > n) 
				b->wp = b->rp + n; 
1999/0907    
			return b; 
1999/1106    
		} 
1999/0907    
	} 
1999/0910    
} 
 
1999/0915    
static long 
writedata(Conv *c, Block *b) 
{ 
1999/1015    
	int n; 
1999/1016    
	ulong seq; 
	int subtype; 
1999/0915    
 
	qlock(c); 
	if(waserror()) { 
		qunlock(c); 
		nexterror(); 
	} 
 
	if(c->state != COpen) { 
		freeb(b); 
		error("conversation not open"); 
	} 
 
	n = BLEN(b); 
1999/0930    
	c->lstats.outDataPackets++; 
	c->lstats.outDataBytes += n; 
1999/1001    
 
1999/1111    
	if(c->out.comp != nil) { 
1999/1016    
		// must generate same value as convoput 
		seq = (c->out.seq + 1) & (SeqMax-1); 
 
		subtype = (*c->out.comp)(c, 0, seq, &b); 
1999/1001    
		c->lstats.outCompDataBytes += BLEN(b); 
1999/1015    
		convoput(c, TCompData, subtype, b); 
	} else 
		convoput(c, TData, 0, b); 
1999/1001    
 
1999/0915    
	poperror(); 
	qunlock(c); 
	return n; 
} 
 
1999/0910    
static void 
convreader(void *a) 
{ 
	Conv *c = a; 
	Block *b; 
 
1999/0914    
print("convreader\n"); 
1999/0910    
	qlock(c); 
	assert(c->reader == 1); 
1999/0915    
	while(c->dataopen == 0 && c->state != CClosed) { 
1999/0910    
		qunlock(c); 
		b = nil; 
		if(!waserror()) { 
			b = convreadblock(c, 2000); 
			poperror(); 
		} 
		qlock(c); 
		if(b == nil) { 
1999/0915    
print("up->error = %s\n", up->error); 
			if(strcmp(up->error, Eintr) != 0) { 
1999/1211    
				convsetstate(c, CClosed); 
1999/0915    
				break; 
			} 
		} else if(!waserror()) { 
1999/0910    
			conviput(c, b, 1); 
			poperror(); 
		} 
	} 
1999/0914    
print("convreader exiting\n"); 
1999/0910    
	c->reader = 0; 
1999/1211    
	convderef(c); 
1999/0910    
	qunlock(c); 
	pexit("hangup", 1); 
1999/0906    
} 
1999/1015    
 
 
/* ciphers, authenticators, and compressors  */ 
 
static void 
1999/1022    
setalg(Conv *c, char *name, Algorithm *alg, Algorithm **p) 
1999/1015    
{ 
	for(; alg->name; alg++) 
		if(strcmp(name, alg->name) == 0) 
			break; 
	if(alg->name == nil) 
		error("unknown algorithm"); 
 
1999/1022    
	*p = alg; 
	alg->init(c); 
1999/1015    
} 
 
static void 
setsecret(OneWay *ow, char *secret) 
{ 
	char *p; 
	int i, c; 
	 
	i = 0; 
	memset(ow->secret, 0, sizeof(ow->secret)); 
	for(p=secret; *p; p++) { 
		if(i >= sizeof(ow->secret)*2) 
			break; 
		c = *p; 
		if(c >= '0' && c <= '9') 
			c -= '0'; 
		else if(c >= 'a' && c <= 'f') 
			c -= 'a'-10; 
		else if(c >= 'A' && c <= 'F') 
			c -= 'A'-10; 
		else 
			error("bad character in secret"); 
		if((i&1) == 0) 
			c <<= 4; 
		ow->secret[i>>1] |= c; 
		i++; 
	} 
} 
 
static void 
setkey(uchar *key, int n, OneWay *ow, char *prefix) 
{ 
	uchar ibuf[SHAdlen], obuf[MD5dlen], salt[10]; 
	int i, round = 0; 
 
	while(n > 0){ 
		for(i=0; i<round+1; i++) 
			salt[i] = 'A'+round; 
		sha((uchar*)prefix, strlen(prefix), ibuf, sha(salt, round+1, nil, nil)); 
		md5(ibuf, SHAdlen, obuf, md5(ow->secret, sizeof(ow->secret), nil, nil)); 
		i = (n<MD5dlen) ? n : MD5dlen; 
		memmove(key, obuf, i); 
		key += i; 
		n -= i; 
		if(++round > sizeof salt) 
			panic("setkey: you ask too much"); 
	} 
} 
 
static void 
cipherfree(Conv *c) 
{ 
	if(c->in.cipherstate) { 
		free(c->in.cipherstate); 
		c->in.cipherstate = nil; 
	} 
	if(c->out.cipherstate) { 
		free(c->out.cipherstate); 
		c->out.cipherstate = nil; 
	} 
	c->in.cipher = nil; 
1999/1022    
	c->in.cipherblklen = 0; 
	c->out.cipherblklen = 0; 
	c->in.cipherivlen = 0; 
	c->out.cipherivlen = 0; 
1999/1015    
} 
 
static void 
authfree(Conv *c) 
{ 
	if(c->in.authstate) { 
		free(c->in.authstate); 
		c->in.authstate = nil; 
	} 
	if(c->out.authstate) { 
		free(c->out.authstate); 
		c->out.authstate = nil; 
	} 
	c->in.auth = nil; 
1999/1022    
	c->in.authlen = 0; 
	c->out.authlen = 0; 
1999/1015    
} 
 
static void 
compfree(Conv *c) 
{ 
	if(c->in.compstate) { 
		free(c->in.compstate); 
		c->in.compstate = nil; 
	} 
	if(c->out.compstate) { 
		free(c->out.compstate); 
		c->out.compstate = nil; 
	} 
	c->in.comp = nil; 
} 
 
static void 
1999/1022    
nullcipherinit(Conv *c) 
1999/1015    
{ 
	cipherfree(c); 
} 
 
static int 
desencrypt(OneWay *ow, uchar *p, int n) 
{ 
	uchar *pp, *ip, *eip, *ep; 
	DESstate *ds = ow->cipherstate; 
 
1999/1022    
	if(n < 8 || (n & 0x7 != 0)) 
		return 0; 
1999/1015    
	ep = p + n; 
	memmove(p, ds->ivec, 8); 
	for(p += 8; p < ep; p += 8){ 
		pp = p; 
		ip = ds->ivec; 
		for(eip = ip+8; ip < eip; ) 
			*pp++ ^= *ip++; 
		block_cipher(ds->expanded, p, 0); 
		memmove(ds->ivec, p, 8); 
	} 
	return 1; 
} 
 
static int 
desdecrypt(OneWay *ow, uchar *p, int n) 
{ 
	uchar tmp[8]; 
	uchar *tp, *ip, *eip, *ep; 
	DESstate *ds = ow->cipherstate; 
 
1999/1022    
	if(n < 8 || (n & 0x7 != 0)) 
		return 0; 
1999/1015    
	ep = p + n; 
	memmove(ds->ivec, p, 8); 
	p += 8; 
	while(p < ep){ 
		memmove(tmp, p, 8); 
		block_cipher(ds->expanded, p, 1); 
		tp = tmp; 
		ip = ds->ivec; 
		for(eip = ip+8; ip < eip; ){ 
			*p++ ^= *ip; 
			*ip++ = *tp++; 
		} 
	} 
	return 1; 
} 
 
static void 
1999/1022    
descipherinit(Conv *c) 
1999/1015    
{ 
	uchar key[8]; 
	uchar ivec[8]; 
	int i; 
1999/1022    
	int n = c->cipher->keylen; 
1999/1015    
 
	cipherfree(c); 
	 
	if(n > sizeof(key)) 
		n = sizeof(key); 
 
	/* in */ 
	memset(key, 0, sizeof(key)); 
	setkey(key, n, &c->in, "cipher"); 
	memset(ivec, 0, sizeof(ivec)); 
	c->in.cipherblklen = 8; 
	c->in.cipherivlen = 8; 
	c->in.cipher = desdecrypt; 
	c->in.cipherstate = smalloc(sizeof(DESstate)); 
	setupDESstate(c->in.cipherstate, key, ivec); 
	 
	/* out */ 
	memset(key, 0, sizeof(key)); 
	setkey(key, n, &c->out, "cipher"); 
	for(i=0; i<8; i++) 
		ivec[i] = nrand(256); 
	c->out.cipherblklen = 8; 
	c->out.cipherivlen = 8; 
	c->out.cipher = desencrypt; 
	c->out.cipherstate = smalloc(sizeof(DESstate)); 
	setupDESstate(c->out.cipherstate, key, ivec); 
} 
 
1999/1022    
static int 
rc4encrypt(OneWay *ow, uchar *p, int n) 
{ 
	CipherRc4 *cr = ow->cipherstate; 
 
	if(n < 4) 
		return 0; 
 
	hnputl(p, cr->cseq); 
	p += 4; 
	n -= 4; 
	rc4(&cr->current, p, n); 
	cr->cseq += n; 
	return 1; 
} 
 
static int 
rc4decrypt(OneWay *ow, uchar *p, int n) 
{ 
	CipherRc4 *cr = ow->cipherstate; 
	RC4state tmpstate; 
	ulong seq; 
	long d, dd; 
 
	if(n < 4) 
		return 0; 
 
	seq = nhgetl(p); 
	p += 4; 
	n -= 4; 
	d = seq-cr->cseq; 
	if(d == 0) { 
		rc4(&cr->current, p, n); 
		cr->cseq += n; 
		if(cr->ovalid) { 
			dd = cr->cseq - cr->lgseq; 
			if(dd > RC4back) 
				cr->ovalid = 0; 
		} 
	} else if(d > 0) { 
1999/1211    
//print("missing packet: %uld %ld\n", seq, d); 
1999/1022    
		// this link is hosed  
		if(d > RC4forward) 
			return 0; 
		cr->lgseq = seq; 
		if(!cr->ovalid) { 
			cr->ovalid = 1; 
			cr->oseq = cr->cseq; 
			memmove(&cr->old, &cr->current, sizeof(RC4state)); 
		} 
		rc4skip(&cr->current, d); 
		rc4(&cr->current, p, n); 
		cr->cseq = seq+n; 
	} else { 
1999/1211    
//print("reordered packet: %uld %ld\n", seq, d); 
1999/1022    
		dd = seq - cr->oseq; 
		if(!cr->ovalid || -d > RC4back || dd < 0) 
			return 0; 
		memmove(&tmpstate, &cr->old, sizeof(RC4state)); 
		rc4skip(&tmpstate, dd); 
		rc4(&tmpstate, p, n); 
		return 1; 
	} 
 
	// move old state up 
	if(cr->ovalid) { 
		dd = cr->cseq - RC4back - cr->oseq; 
		if(dd > 0) { 
			rc4skip(&cr->old, dd); 
			cr->oseq += dd; 
		} 
	} 
 
	return 1; 
} 
 
1999/1015    
static void 
1999/1022    
rc4cipherinit(Conv *c) 
1999/1015    
{ 
1999/1022    
	uchar key[32]; 
	CipherRc4 *cr; 
	int n; 
 
1999/1016    
	cipherfree(c); 
1999/1022    
 
	n = c->cipher->keylen; 
	if(n > sizeof(key)) 
		n = sizeof(key); 
 
	/* in */ 
	memset(key, 0, sizeof(key)); 
	setkey(key, n, &c->in, "cipher"); 
	c->in.cipherblklen = 1; 
	c->in.cipherivlen = 4; 
	c->in.cipher = rc4decrypt; 
	cr = smalloc(sizeof(CipherRc4)); 
	memset(cr, 0, sizeof(*cr)); 
	setupRC4state(&cr->current, key, n); 
	c->in.cipherstate = cr; 
 
	/* out */ 
	memset(key, 0, sizeof(key)); 
	setkey(key, n, &c->out, "cipher"); 
	c->out.cipherblklen = 1; 
	c->out.cipherivlen = 4; 
	c->out.cipher = rc4encrypt; 
	cr = smalloc(sizeof(CipherRc4)); 
	memset(cr, 0, sizeof(*cr)); 
	setupRC4state(&cr->current, key, n); 
	c->out.cipherstate = cr; 
1999/1015    
} 
 
static void 
1999/1022    
nullauthinit(Conv *c) 
1999/1015    
{ 
	authfree(c); 
} 
 
static void 
1999/1022    
shaauthinit(Conv *c) 
1999/1015    
{ 
	authfree(c); 
} 
 
static void 
hmac_md5(uchar hash[MD5dlen], ulong wrap, uchar *t, long tlen, uchar *key, long klen) 
{ 
	uchar ipad[65], opad[65], wbuf[4]; 
	int i; 
	DigestState *digest; 
	uchar innerhash[MD5dlen]; 
 
	for(i=0; i<64; i++){ 
		ipad[i] = 0x36; 
		opad[i] = 0x5c; 
	} 
	ipad[64] = opad[64] = 0; 
	for(i=0; i<klen; i++){ 
		ipad[i] ^= key[i]; 
		opad[i] ^= key[i]; 
	} 
	hnputl(wbuf, wrap); 
	digest = md5(ipad, 64, nil, nil); 
	digest = md5(wbuf, sizeof(wbuf), nil, digest); 
	md5(t, tlen, innerhash, digest); 
	digest = md5(opad, 64, nil, nil); 
	md5(innerhash, MD5dlen, hash, digest); 
} 
 
static int 
md5auth(OneWay *ow, uchar *t, int tlen) 
{ 
	uchar hash[MD5dlen]; 
	int r; 
 
	if(tlen < ow->authlen) 
		return 0; 
	tlen -= ow->authlen; 
 
	memset(hash, 0, MD5dlen); 
	hmac_md5(hash, ow->seqwrap, t, tlen, (uchar*)ow->authstate, 16); 
	r = memcmp(t+tlen, hash, ow->authlen) == 0; 
	memmove(t+tlen, hash, ow->authlen); 
	return r; 
} 
 
static void 
1999/1022    
md5authinit(Conv *c) 
1999/1015    
{ 
1999/1022    
	int keylen; 
 
1999/1015    
	authfree(c); 
 
1999/1022    
	keylen = c->auth->keylen; 
1999/1015    
	if(keylen > 16) 
		keylen = 16; 
 
	/* in */ 
	c->in.authstate = smalloc(16); 
	memset(c->in.authstate, 0, 16); 
	setkey(c->in.authstate, keylen, &c->in, "auth"); 
	c->in.authlen = 12; 
	c->in.auth = md5auth; 
	 
	/* out */ 
	c->out.authstate = smalloc(16); 
	memset(c->out.authstate, 0, 16); 
	setkey(c->out.authstate, keylen, &c->out, "auth"); 
	c->out.authlen = 12; 
	c->out.auth = md5auth; 
} 
 
static void 
1999/1022    
nullcompinit(Conv *c) 
1999/1015    
{ 
1999/1016    
	compfree(c); 
1999/1015    
} 
 
1999/1016    
static int 
thwackcomp(Conv *c, int, ulong seq, Block **bp) 
1999/1015    
{ 
1999/1016    
	Block *b, *bb; 
	int nn; 
 
	// add ack info 
	b = padblock(*bp, 4); 
	b->rp[0] = (c->in.window>>1) & 0xff; 
	b->rp[1] = c->in.seq>>16; 
	b->rp[2] = c->in.seq>>8; 
	b->rp[3] = c->in.seq; 
 
	bb = allocb(BLEN(b)); 
1999/1022    
	nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq, c->lstats.outCompStats); 
1999/1016    
	if(nn < 0) { 
		freeb(bb); 
		*bp = b; 
		return ThwackU; 
	} else { 
		bb->wp += nn; 
		freeb(b); 
		*bp = bb; 
		return ThwackC; 
	} 
1999/1015    
} 
 
1999/1016    
static int 
thwackuncomp(Conv *c, int subtype, ulong seq, Block **bp) 
{ 
	Block *b, *bb; 
	ulong mask; 
	ulong mseq; 
	int n; 
1999/1015    
 
1999/1016    
	switch(subtype) { 
	default: 
		return 0; 
	case ThwackU: 
		b = *bp; 
1999/1015    
		mask = b->rp[0]; 
		mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3]; 
		b->rp += 4; 
		thwackack(c->out.compstate, mseq, mask); 
1999/1016    
		return 1; 
	case ThwackC: 
		bb = *bp; 
1999/1015    
		b = allocb(ThwMaxBlock); 
		n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq); 
		freeb(bb); 
1999/1016    
		if(n < 0) { 
print("unthwack failed: %r!\n"); 
			freeb(b); 
			return 0; 
		} 
1999/1015    
		b->wp += n; 
		mask = b->rp[0]; 
		mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3]; 
		thwackack(c->out.compstate, mseq, mask); 
		b->rp += 4; 
1999/1016    
		*bp = b; 
		return 1; 
1999/1015    
	} 
1999/1016    
} 
1999/1015    
 
1999/1016    
static void 
1999/1022    
thwackcompinit(Conv *c) 
1999/1016    
{ 
	compfree(c); 
1999/1015    
 
1999/1016    
	c->in.compstate = malloc(sizeof(Unthwack)); 
	unthwackinit(c->in.compstate); 
	c->out.compstate = malloc(sizeof(Thwack)); 
	thwackinit(c->out.compstate); 
	c->in.comp = thwackuncomp; 
	c->out.comp = thwackcomp; 
} 


source code copyright © 1990-2005 Lucent Technologies; see license
Plan 9 distribution
comments to russ cox (rsc@swtch.com)