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

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

port/devsdp.c on 1999/0824
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/0907    
/* 
 * sdp - secure datagram protocol 
 */ 
 
1999/0902    
typedef struct Sdp Sdp; 
typedef struct Conv Conv; 
1999/0907    
typedef struct OneWay OneWay; 
typedef struct ConnectPkt ConnectPkt; 
1999/0824    
 
enum 
{ 
	Qtopdir=	1,		/* top level directory */ 
 
	Qsdpdir,			/* sdp directory */ 
	Qclone, 
	Qstats, 
	Qlog, 
 
1999/0901    
	Qconvdir,			/* directory per conversation */ 
1999/0824    
	Qctl, 
1999/0902    
	Qdata,				/* unreliable packet channel */ 
	Qcontrol,			/* reliable control channel */ 
1999/0824    
	Qstatus, 
 
	MaxQ, 
 
1999/0907    
	Maxconv= 256,		// power of 2 
	Nfs= 4,				// number of file systems 
1999/0906    
	Maxretries=	4, 
1999/0907    
	KeyLength= 32, 
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/0907    
struct OneWay 
1999/0901    
{ 
	ulong	seqwrap;	// number of wraps of the sequence number 
	ulong	seq; 
1999/0907    
	ulong	window; 
1999/0901    
 
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/0907    
	int		(*comp)(OneWay*, uchar *dst, uchar *src, int n); 
1999/0901    
}; 
 
1999/0907    
// conv states 
1999/0902    
enum { 
1999/0906    
	CInit, 
1999/0902    
	COpening, 
	COpen, 
	CClosing, 
1999/0906    
	CClosed, 
1999/0902    
}; 
 
1999/0901    
struct Conv { 
	QLock; 
1999/0824    
	int	id; 
1999/0906    
	int ref;	// number of times the conv is opened 
1999/0824    
	Sdp	*sdp; 
1999/0901    
 
1999/0902    
	int state; 
1999/0906    
	int dataopen; 
1999/0901    
 
1999/0906    
 
	ulong	timeout; 
	int		retries; 
 
	// the following pair uniquely define conversation on this port 
	ulong dialid; 
	ulong acceptid; 
 
1999/0907    
	Proc *readproc; 
	QLock readlk; 
1999/0902    
	Chan *chan;	// packet channel 
 
1999/0907    
	char owner[NAMELEN];		/* protections */ 
1999/0901    
	int	perm; 
 
1999/0907    
	uchar	masterkey[KeyLength]; 
 
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, 
	TControlAck, 
	TData, 
	TThwackC, 
	TThwackU, 
}; 
 
enum { 
	ConOpen, 
	ConOpenAck, 
	ConOpenNack, 
	ConClose, 
	ConCloseAck, 
}; 
 
1999/0907    
struct ConnectPkt 
1999/0906    
{ 
	uchar type;		// always zero = connection packet 
	uchar op; 
	uchar pad[2]; 
	uchar dialid[4]; 
	uchar acceptid[4]; 
}; 
 
1999/0907    
 
1999/0824    
static Dirtab sdpdirtab[]={ 
	"stats",	{Qstats},	0,	0444, 
	"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, 
}; 
 
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]; 
 
static int sdpgen(Chan *c, Dirtab*, int, int s, Dir *dp); 
1999/0901    
static Conv *sdpclone(Sdp *sdp); 
1999/0906    
static void convsetstate(Conv *c, int state); 
static void sdpackproc(void *a); 
1999/0907    
static void onewaycleanup(OneWay *ow); 
static int readready(void *a); 
static int controlread(); 
static Block *conviput(Conv *c, Block *b, int control); 
static void conviput2(Conv *c, Block *b); 
static Block *readcontrol(Conv *c, int n); 
static Block *readdata(Conv *c, int n); 
static void convoput(Conv *c, int type, Block *b); 
static void convoput2(Conv *c, int op, ulong dialid, ulong acceptid); 
1999/0824    
 
1999/0907    
 
1999/0824    
static void 
sdpinit(void) 
{ 
	int i; 
	Dirtab *dt; 
 
	// 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/0901    
	c = devattach('T', 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); 
} 
 
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    
	case Qstats: 
		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: 
		c = sdp->conv[CONV(ch->qid)]; 
		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); 
		c->ref++; 
1999/0907    
		if(TYPE(ch->qid) == Qdata) 
			c->dataopen++; 
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/0902    
	switch(TYPE(ch->qid)) { 
1999/0824    
	case Qlog: 
1999/0902    
		if(ch->flag & COPEN) 
1999/0824    
			logclose(sdp); 
		break; 
1999/0907    
	case Qdata: 
	case Qctl: 
	case Qstatus: 
	case Qcontrol: 
		if(!(ch->flag & COPEN)) 
			break; 
		c = sdp->conv[CONV(ch->qid)]; 
		qlock(c); 
		if(waserror()) { 
			qunlock(c); 
			nexterror(); 
		} 
		c->ref--; 
		if(TYPE(ch->qid) == Qdata) { 
			c->dataopen--; 
			if(c->dataopen == 0) 
				wakeup(&c->in.controlready); 
		} 
 
		if(c->ref == 0) { 
			switch(c->state) { 
			default: 
				convsetstate(c, CClosed); 
				break; 
			case COpen: 
				convsetstate(c, CClosing); 
				break; 
			case CClosing: 
				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/0901    
	Sdp *sdp = sdptab + ch->dev; 
1999/0902    
	char *s; 
1999/0901    
	Conv *c; 
1999/0907    
	Block *b; 
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); 
		switch(c->state) { 
		default: 
			panic("unknown state"); 
		case CClosed: 
			s = "closed"; 
			break; 
		case COpening: 
			s = "opening"; 
			break; 
		case COpen: 
			s = "open"; 
			break; 
		case CClosing: 
			s = "closing"; 
			break; 
1999/0824    
		} 
1999/0902    
		n = readstr(off, a, n, s); 
		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; 
		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/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/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/0824    
	 
	USED(off); 
1999/0901    
	switch(TYPE(ch->qid)) { 
1999/0824    
	default: 
		error(Eperm); 
	case Qctl: 
1999/0906    
		c = sdp->conv[CONV(ch->qid)]; 
print("Qctl write : conv->id = %d\n", c->id); 
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/0901    
print("cmd = %s\n", arg0); 
1999/0906    
		if(strcmp(arg0, "chan") == 0) { 
			if(cb->nf != 2) 
				error("usage: chan file"); 
			if(c->chan != nil) 
				error("chan already set"); 
			c->chan = namec(cb->f[1], Aopen, ORDWR, 0); 
		} else if(strcmp(arg0, "accept") == 0) { 
			if(cb->nf != 2) 
				error("usage: accect id"); 
			c->dialid = atoi(cb->f[1]); 
			convsetstate(c, COpen); 
		} else if(strcmp(arg0, "dial") == 0) { 
			if(cb->nf != 1) 
				error("usage: dial"); 
			convsetstate(c, COpening); 
		} else if(strcmp(arg0, "drop") == 0) { 
			if(cb->nf != 2) 
				error("usage: drop permil"); 
			c->drop = atoi(cb->f[1]); 
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; 
	} 
} 
 
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); 
			qlock(c); 
			c->sdp = sdp; 
			c->id = pp - sdp->conv; 
			*pp = c; 
			sdp->nconv++; 
			break; 
		} 
		if(canqlock(c)){ 
1999/0902    
			if(c->state == CClosed) 
1999/0901    
				break; 
			qunlock(c); 
		} 
	} 
1999/0902    
	poperror(); 
	qunlock(sdp); 
1999/0901    
 
1999/0902    
	if(pp >= ep) 
1999/0901    
		return nil; 
 
1999/0902    
	c->ref++; 
1999/0906    
	c->state = CInit; 
1999/0902    
 
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 
static int 
convretry(Conv *c) 
{ 
	c->retries++; 
	if(c->retries > Maxretries) { 
print("convretry: giving up\n"); 
		convsetstate(c, CClosed); 
		return 0; 
	} 
	c->timeout = TK2SEC(m->ticks) + (1<<c->retries); 
	return 1; 
} 
 
1999/0902    
static void 
1999/0906    
convtimer(Conv *c, ulong sec) 
1999/0902    
{ 
1999/0906    
	if(c->timeout == 0 || c->timeout > sec) 
		return; 
1999/0902    
	qlock(c); 
1999/0906    
	if(waserror()) { 
1999/0902    
		qunlock(c); 
1999/0906    
		nexterror(); 
1999/0902    
	} 
1999/0906    
	switch(c->state) { 
	case COpening: 
print("COpening timeout\n"); 
		if(convretry(c)) 
1999/0907    
			convoput2(c, ConOpen, c->dialid, 0); 
1999/0906    
		break; 
	case COpen: 
		// check for control packet 
		break; 
	case CClosing: 
print("CClosing timeout\n"); 
		if(convretry(c)) 
1999/0907    
			convoput2(c, ConClose, c->dialid, c->acceptid); 
1999/0906    
		break; 
	} 
	qunlock(c); 
} 
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]; 
			if(!waserror()) { 
				convtimer(c, sec); 
				poperror(); 
			} 
		} 
		qunlock(sdp); 
1999/0902    
	} 
} 
1999/0824    
 
Dev sdpdevtab = { 
	'T', 
	"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    
 
print("convsetstate %d -> %d\n", c->state, state); 
 
1999/0906    
	switch(state) { 
	default: 
		panic("setstate: bad state: %d", state); 
	case COpening: 
		if(c->state != CInit) 
			error("convsetstate: illegal transition"); 
		c->dialid = (rand()<<16) + rand(); 
		c->timeout = TK2SEC(m->ticks) + 2; 
		c->retries = 0; 
1999/0907    
		convoput2(c, ConOpen, c->dialid, 0); 
1999/0906    
		break; 
	case COpen: 
		switch(c->state) { 
		default: 
			error("convsetstate: illegal transition"); 
		case CInit: 
			c->acceptid = (rand()<<16) + rand(); 
1999/0907    
			convoput2(c, ConOpenAck, c->dialid, c->acceptid); 
1999/0906    
			break; 
		case COpening: 
			break; 
		} 
		// setup initial key and auth method 
		break; 
	case CClosing: 
1999/0907    
		convoput2(c, ConClose, c->dialid, c->acceptid); 
1999/0906    
		c->timeout = TK2SEC(m->ticks) + 2; 
		c->retries = 0; 
		break; 
	case CClosed: 
1999/0907    
		if(c->readproc) 
			postnote(c->readproc, 1, "interrupt", 0); 
		if(c->ref) 
			break; 
		if(c->chan) {	 
			cclose(c->chan); 
			c->chan = nil; 
		} 
		strcpy(c->owner, "network"); 
		c->perm = 0660; 
		c->dialid = 0; 
		c->acceptid = 0; 
		c->timeout = 0; 
		c->retries = 0; 
		c->drop = 0; 
		memset(c->masterkey, 0, sizeof(c->masterkey)); 
		onewaycleanup(&c->in); 
		onewaycleanup(&c->out); 
1999/0906    
		break; 
	} 
	c->state = state; 
} 
 
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    
 
static Block * 
convreadblock(Conv *c, int n) 
{ 
	Block *b; 
 
	qlock(&c->readlk); 
	if(waserror()) { 
		c->readproc = nil; 
		qunlock(&c->readlk); 
		nexterror(); 
	} 
	qlock(c); 
	if(c->state == CClosed) { 
		qunlock(c); 
		poperror(); 
		qunlock(&c->readlk); 
		return 0; 
	} 
	c->readproc = up; 
	qunlock(c); 
 
	b = devtab[c->chan->type]->bread(c->chan, n, 0); 
	c->readproc = nil; 
	poperror(); 
	qunlock(&c->readlk); 
 
	return b; 
} 
 
 
// assume we hold lock for c 
static Block * 
conviput(Conv *c, Block *b, int control) 
{ 
	int type; 
	ulong seq, cseq; 
 
	if(BLEN(b) < 4) { 
		freeb(b); 
		return nil; 
	} 
	 
	type = b->rp[0]; 
	if(type == TConnect) { 
		conviput2(c, b); 
		return nil; 
	} 
 
	seq = (b->rp[1]<<16) + (b->rp[2]<<8) + b->rp[3]; 
	b->rp += 4; 
 
	USED(seq); 
	// auth 
	// decrypt 
 
	// ok the packet is good 
 
	switch(type) { 
	case TControl: 
		if(BLEN(b) <= 4) 
			break; 
		cseq = nhgetl(b->rp); 
		if(cseq == c->in.controlseq) { 
			// duplicate control packet 
			// send ack 
			b->wp = b->rp + 4; 
			convoput(c, TControlAck, b); 
			return nil; 
		} 
 
		if(cseq != c->in.controlseq+1) 
			break; 
	 
		c->in.controlseq = cseq; 
		b->rp += 4; 
		if(control) 
			return b; 
		c->in.controlpkt = b; 
		wakeup(&c->in.controlready); 
		return nil; 
	case TControlAck: 
		if(BLEN(b) != 4) 
			break; 
		cseq = nhgetl(b->rp); 
		if(cseq != c->out.controlseq) 
			break; 
		freeb(b); 
		freeb(c->out.controlpkt); 
		c->out.controlpkt = 0; 
		wakeup(&c->out.controlready); 
		return nil; 
	case TData: 
		if(control) 
			break; 
		return b; 
	} 
print("droping packet %d n=%ld\n", type, BLEN(b)); 
	freeb(b); 
	return nil; 
} 
 
// assume hold conv lock 
static void 
conviput2(Conv *c, Block *b) 
{ 
	ConnectPkt *con; 
	ulong dialid; 
	ulong acceptid; 
 
	if(BLEN(b) != sizeof(ConnectPkt)) { 
		freeb(b); 
		return; 
	} 
	con = (ConnectPkt*)b->rp; 
	dialid = nhgetl(con->dialid); 
	acceptid = nhgetl(con->acceptid); 
 
print("conviput2: %d %uld %uld\n", con->op, dialid, acceptid); 
	switch(con->op) { 
	case ConOpen: 
		if(c->state != COpen || dialid != c->dialid || acceptid != c->acceptid) 
			convoput2(c, ConOpenNack, dialid, acceptid); 
		else 
			convoput2(c, ConOpenAck, dialid, acceptid); 
		break; 
	case ConOpenAck: 
		if(c->state != COpening || dialid != c->dialid) 
			break; 
		c->acceptid = acceptid; 
		convsetstate(c, COpen); 
		break; 
	case ConOpenNack: 
		if(c->state != COpening || dialid != c->dialid) 
			break; 
		convsetstate(c, CClosed); 
		break; 
	case ConClose: 
		if(dialid != c->dialid || acceptid != c->acceptid) 
			break; 
		convsetstate(c, CClosed); 
		break; 
	case ConCloseAck: 
		if(c->state != CClosing || dialid != c->dialid || acceptid != c->acceptid) 
			break; 
		convsetstate(c, CClosed); 
		break; 
	} 
} 
 
// assume hold conv lock 
static void 
convoput(Conv *c, int type, Block *b) 
{ 
	// try and compress 
 
	/* Make space to fit sdp header */ 
	b = padblock(b, 4 + c->out.cipherivlen); 
	b->rp[0] = type; 
	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; 
	 
	// encrypt 
	// auth 
 
	// simulated errors 
	if(c->drop && c->drop > nrand(c->drop)) 
		return; 
	devtab[c->chan->type]->bwrite(c->chan, b, 0); 
} 
 
// assume hold conv lock 
static void 
convoput2(Conv *c, int op, ulong dialid, ulong acceptid) 
{ 
	ConnectPkt con; 
 
1999/0906    
	if(c->chan == nil) { 
print("chan = nil\n"); 
		error("no channel attached"); 
	} 
	memset(&con, 0, sizeof(con)); 
	con.type = TConnect; 
	con.op = op; 
	hnputl(con.dialid, dialid); 
	hnputl(con.acceptid, acceptid); 
 
	// simulated errors 
	if(c->drop && c->drop > nrand(c->drop)) 
		return; 
	devtab[c->chan->type]->write(c->chan, &con, sizeof(con), 0); 
1999/0907    
} 
 
static int 
readready(void *a) 
{ 
	Conv *c = a; 
 
	return (c->state == CClosed) || c->in.controlpkt != nil || c->dataopen == 0; 
} 
 
static Block * 
readcontrol(Conv *c, int n) 
{ 
	Block *b; 
 
	for(;;) { 
		qlock(c); 
		if(c->state == CClosed || c->state == CInit) { 
			qunlock(c); 
			return nil; 
		} 
 
		if(c->in.controlpkt != nil) { 
			b = c->in.controlpkt; 
			c->in.controlpkt = nil; 
			qunlock(c); 
			return b; 
		} 
		qunlock(c); 
 
		// hack - this is to avoid gating onto the 
		// read which will in general result in excessive 
		// context switches. 
		// The assumed behavior is that the client will read 
		// from the control channel until the session is authenticated 
		// at which point it will open the data channel and 
		// start reading on that.  After the data channel is opened, 
		// read on the channel are required for packets to 
		// be delivered to the control channel 
 
		if(c->dataopen) { 
			sleep(&c->in.controlready, readready, c); 
		} else { 
			b = convreadblock(c, n); 
			if(b == nil) 
				return nil; 
			qlock(c); 
			if(waserror()) { 
				qunlock(c); 
				return nil; 
			} 
			b = conviput(c, b, 1); 
			poperror(); 
			qunlock(c); 
			if(b != nil) 
				return b; 
		} 
	} 
} 
 
static Block * 
readdata(Conv *c, int n) 
{ 
	Block *b; 
 
	for(;;) { 
		b = convreadblock(c, n); 
		if(b == nil) 
			return nil; 
		qlock(c); 
		if(waserror()) { 
			qunlock(c); 
			return nil; 
		} 
		b = conviput(c, b, 0); 
		poperror(); 
		qunlock(c); 
		if(b != nil) 
			return b; 
	} 
1999/0906    
} 


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