~kris/9p

9hist

5e14454e3445b374009a0ce4928095fda8cbc7a7 — David du Colombier 25 years ago b854052
Plan 9 from Bell Labs 2001-04-03
3 files changed, 540 insertions(+), 443 deletions(-)

M alphapc/ether2114x.c
M ip/devip.c
M port/devtls.c
M alphapc/ether2114x.c => alphapc/ether2114x.c +130 -24
@@ 19,27 19,30 @@

#include "etherif.h"

#define DEBUG		0
#define DEBUG		(1)
#define debug		if(DEBUG)print

enum {
	Nrde		= 64,
	Ntde		= 64,

	Rxbufsz		= ROUNDUP(sizeof(Etherpkt)+4, 4),
};

#define Rbsz		ROUNDUP(sizeof(Etherpkt)+4, 4)

enum {					/* CRS0 - Bus Mode */
	Swr		= 0x00000001,	/* Software Reset */
	Bar		= 0x00000002,	/* Bus Arbitration */
	Dsl		= 0x0000007C,	/* Descriptor Skip Length (field) */
	DslMASK		= 0x0000007C,	/* Descriptor Skip Length */
	DslSHIFT	= 2,
	Ble		= 0x00000080,	/* Big/Little Endian */
	Pbl		= 0x00003F00,	/* Programmable Burst Length (field) */
	PblMASK		= 0x00003F00,	/* Programmable Burst Length (field) */
	PblSHIFT	= 8,
	Cal		= 0x0000C000,	/* Cache Alignment (field) */
	Cal8		= 0x00004000,	/* 8 longword boundary alignment */
	Cal16		= 0x00008000,	/* 16 longword boundary alignment */
	Cal32		= 0x0000C000,	/* 32 longword boundary alignment */
	Tap		= 0x000E0000,	/* Transmit Automatic Polling (field) */
	TapMASK		= 0x000E0000,	/* Transmit Automatic Polling */
	TapSHIFT	= 17,
	Dbo		= 0x00100000,	/* Descriptor Byte Ordering Mode */
	Rml		= 0x00200000,	/* Read Multiple */
}; 


@@ 183,12 186,20 @@ enum {					/* PHY registers */
	Aner		= 6,		/* Auto-Negotiation Expansion */
};

enum {					/* Variants */
	Tulip0		= (0x0009<<16)|0x1011,
	Tulip3		= (0x0019<<16)|0x1011,
	Pnic		= (0x0002<<16)|0x11AD,
	Pnic2		= (0xC115<<16)|0x11AD,
};

typedef struct Ctlr Ctlr;
typedef struct Ctlr {
	int	port;
	Pcidev*	pcidev;
	Ctlr*	next;
	int	active;
	int	id;			/* (pcidev->did<<16)|pcidev->vid */

	uchar	srom[128];
	uchar*	sromea;			/* MAC address */


@@ 457,7 468,7 @@ interrupt(Ureg*, void* arg)
					if(des->status & De)
						ctlr->de++;
				}
				else if(bp = iallocb(Rxbufsz)){
				else if(bp = iallocb(Rbsz)){
					len = ((des->status & Fl)>>16)-4;
					des->bp->wp = des->bp->rp+len;
					etheriq(ether, des->bp, 1);


@@ 466,7 477,7 @@ interrupt(Ureg*, void* arg)
				}

				des->control &= Er;
				des->control |= Rxbufsz;
				des->control |= Rbsz;
				coherence();
				des->status = Own;



@@ 566,11 577,11 @@ ctlrinit(Ether* ether)
	 * create and post a setup packet to initialise
	 * the physical ethernet address.
	 */
	ctlr->rdr = xspanalloc(ctlr->nrdr*sizeof(Des), 8*sizeof(ulong), 0);
	ctlr->rdr = malloc(ctlr->nrdr*sizeof(Des));
	for(des = ctlr->rdr; des < &ctlr->rdr[ctlr->nrdr]; des++){
		des->bp = allocb(Rxbufsz);
		des->bp = allocb(Rbsz);
		des->status = Own;
		des->control = Rxbufsz;
		des->control = Rbsz;
		des->addr = PCIWADDR(des->bp->rp);
	}
	ctlr->rdr[ctlr->nrdr-1].control |= Er;


@@ 583,7 594,12 @@ ctlrinit(Ether* ether)
	ctlr->tdri = 0;
	csr32w(ctlr, 4, PCIWADDR(ctlr->tdr));

	/*
	 * Clear any bits in the Status Register (CSR5) as
	 * the PNIC has a different reset value from a true 2114x.
	 */
	ctlr->mask = Nis|Ais|Fbe|Rwt|Rps|Ru|Ri|Unf|Tjt|Tps|Ti;
	csr32w(ctlr, 5, ctlr->mask);
	csr32w(ctlr, 7, ctlr->mask);
	ctlr->csr6 |= St;
	csr32w(ctlr, 6, ctlr->csr6);


@@ 654,7 670,20 @@ miimdo(Ctlr* ctlr, int bits, int n)
static int
miir(Ctlr* ctlr, int phyad, int regad)
{
	int data;
	int data, i;

	if(ctlr->id == Pnic){
		i = 1000;
		csr32w(ctlr, 20, 0x60020000|(phyad<<23)|(regad<<18));
		do{
			microdelay(1);
			data = csr32r(ctlr, 20);
		}while((data & 0x80000000) && --i);

		if(i == 0)
			return -1;
		return data & 0xFFFF;
	}

	/*
	 * Preamble;


@@ 692,6 721,17 @@ sromr(Ctlr* ctlr, int r)
{
	int i, op, data;

	if(ctlr->id == Pnic){
		i = 1000;
		csr32w(ctlr, 19, 0x600|r);
		do{
			microdelay(1);
			data = csr32r(ctlr, 19);
		}while((data & 0x80000000) && --i);

		return csr32r(ctlr, 9) & 0xFFFF;
	}

	/*
	 * This sequence for reading a 16-bit register 'r'
	 * in the EEPROM is taken straight from Section


@@ 739,7 779,7 @@ softreset(Ctlr* ctlr)
	 */
	csr32w(ctlr, 0, Swr);
	microdelay(10);
	csr32w(ctlr, 0, Rml|Cal16);
	csr32w(ctlr, 0, Rml|Cal16|(32<<PblSHIFT));
	delay(1);
}



@@ 762,7 802,7 @@ type5block(Ctlr* ctlr, uchar* block)
	 * sequence.
	 */
	len = *block++;
	if(ctlr->pcidev->did == 0x0009){
	if(ctlr->id != Tulip3){
		for(i = 0; i < len; i++){
			csr32w(ctlr, 12, *block);
			block++;


@@ 817,8 857,17 @@ typephylink(Ctlr* ctlr, uchar*)
	}
	else if((bmcr & 0x2100) == 0x2100)
		x = 0x4000;
	else if(bmcr & 0x2000)
		x = 0x2000;
	else if(bmcr & 0x2000){
		/*
		 * If FD capable, force it if necessary.
		 */
		if((bmsr & 0x4000) && ctlr->fd){
			miiw(ctlr, ctlr->curphyad, Bmcr, 0x2100);
			x = 0x4000;
		}
		else
			x = 0x2000;
	}
	else if(bmcr & 0x0100)
		x = 0x1000;
	else


@@ 949,7 998,8 @@ type0mode(Ctlr* ctlr, uchar* block, int wait)
{
	int csr6, m, timeo;

	csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE;
//	csr6 = Sc|Mbo|Hbd|Ca|Sb|TrMODE;
	csr6 = Mbo|Hbd|Sb|TrMODE;
debug("type0: medium 0x%uX, fd %d: 0x%2.2uX 0x%2.2uX 0x%2.2uX 0x%2.2uX\n",
    ctlr->medium, ctlr->fd, block[0], block[1], block[2], block[3]); 
	switch(block[0]){


@@ 1132,6 1182,30 @@ static uchar* leaf21140[] = {
	nil,
};

/*
 * Copied to ctlr->srom at offset 20.
 */
static uchar leafpnic[] = {
	0x00, 0x00, 0x00, 0x00,		/* MAC address */
	0x00, 0x00,
	0x00,				/* controller 0 device number */
	0x1E, 0x00,			/* controller 0 info leaf offset */
	0x00,				/* reserved */
	0x00, 0x08,			/* selected connection type */
	0x00,				/* general purpose control */
	0x01,				/* block count */

	0x8C,				/* format indicator and count */
	0x01,				/* block type */
	0x00,				/* PHY number */
	0x00,				/* GPR sequence length */
	0x00,				/* reset sequence length */
	0x00, 0x78,			/* media capabilities */
	0xE0, 0x01,			/* Nway advertisment */
	0x00, 0x50,			/* FDX bitmap */
	0x00, 0x18,			/* TTM bitmap */
};

static int
srom(Ctlr* ctlr)
{


@@ 1173,6 1247,19 @@ srom(Ctlr* ctlr)
	}

	/*
	 * Fake up the SROM for the PNIC.
	 * It looks like a 21140 with a PHY.
	 * The MAC address is byte-swapped in the orginal SROM data.
	 */
	if(ctlr->id == Pnic){
		memmove(&ctlr->srom[20], leafpnic, sizeof(leafpnic));
		for(i = 0; i < Eaddrlen; i += 2){
			ctlr->srom[20+i] = ctlr->srom[i+1];
			ctlr->srom[20+i+1] = ctlr->srom[i];
		}
	}

	/*
	 * Next, try to find the info leaf in the SROM for media detection.
	 * If it's a non-conforming card try to match the vendor ethernet code
	 * and point p at a fake info leaf with compact 21140 entries.


@@ 1205,7 1292,7 @@ srom(Ctlr* ctlr)
	ctlr->leaf = p;
	ctlr->sct = *p++;
	ctlr->sct |= *p++<<8;
	if(ctlr->pcidev->did == 0x0009){
	if(ctlr->id != Tulip3){
		csr32w(ctlr, 12, Gpc|*p++);
		delay(200);
	}


@@ 1220,7 1307,7 @@ srom(Ctlr* ctlr)
		 * The RAMIX PMC665 has a badly-coded SROM,
		 * hence the test for 21143 and type 3.
		 */
		if((*p & 0x80) || (ctlr->pcidev->did == 0x0019 && *(p+1) == 3)){
		if((*p & 0x80) || (ctlr->id == Tulip3 && *(p+1) == 3)){
			*p |= 0x80;
			if(*(p+1) == 1 || *(p+1) == 3)
				phy = 1;


@@ 1271,12 1358,14 @@ dec2114xpci(void)
	int x;

	p = nil;
	while(p = pcimatch(p, 0x1011, 0)){
		switch(p->did){
	while(p = pcimatch(p, 0, 0)){
		if(p->ccrb != 0x02 || p->ccru != 0)
			continue;
		switch((p->did<<16)|p->vid){
		default:
			continue;

		case 0x0019:		/* 21143 */
		case Tulip3:			/* 21143 */
			/*
			 * Exit sleep mode.
			 */


@@ 1285,7 1374,9 @@ dec2114xpci(void)
			pcicfgw32(p, 0x40, x);
			/*FALLTHROUGH*/

		case 0x0009:		/* 21140 */
		case Pnic:			/* PNIC */
		case Pnic2:			/* PNIC-II */
		case Tulip0:			/* 21140 */
			break;
		}



@@ 1296,9 1387,10 @@ dec2114xpci(void)
		ctlr = malloc(sizeof(Ctlr));
		ctlr->port = p->mem[0].bar & ~0x01;
		ctlr->pcidev = p;
		ctlr->id = (p->did<<16)|p->vid;

		if(ioalloc(ctlr->port, p->mem[0].size, 0, "dec2114x") < 0){
			print("dec2114x: port %d in use\n", ctlr->port);
			print("dec2114x: port 0x%uX in use\n", ctlr->port);
			free(ctlr);
			continue;
		}


@@ 1312,7 1404,20 @@ dec2114xpci(void)
		softreset(ctlr);

		if(srom(ctlr)){
			iofree(ctlr->port);
			free(ctlr);
			continue;
		}

		switch(ctlr->id){
		default:
//			break;
//
//		case Pnic:			/* PNIC */
			/*
			 * Turn off the jabber timer.
			 */
			csr32w(ctlr, 15, 0x00000001);
			break;
		}



@@ 1424,5 1529,6 @@ reset(Ether* ether)
void
ether2114xlink(void)
{
	addethercard("21140",  reset);
	addethercard("2114x",  reset);
}

M ip/devip.c => ip/devip.c +38 -5
@@ 82,9 82,9 @@ ip3gen(Chan *c, int i, Dir *dp)
		devdir(c, q, "err", qlen(cv->eq), cv->owner, cv->perm, dp);
		return 1;
	case Qlisten:
		p = "listen";
		q = (Qid){QID(PROTO(c->qid), CONV(c->qid), Qlisten), 0};
		break;
		devdir(c, q, "listen", qlen(cv->eq), cv->owner, cv->perm, dp);
		return 1;
	case Qlocal:
		p = "local";
		q = (Qid){QID(PROTO(c->qid), CONV(c->qid), Qlocal), 0};


@@ 98,7 98,7 @@ ip3gen(Chan *c, int i, Dir *dp)
		q = (Qid){QID(PROTO(c->qid), CONV(c->qid), Qstatus), 0};
		break;
	}
	devdir(c, q, p, 0, cv->owner, 0666, dp);
	devdir(c, q, p, 0, cv->owner, 0444, dp);
	return 1;
}



@@ 419,6 419,14 @@ ipopen(Chan* c, int omode)
		break;
	case Qlisten:
		cv = f->p[PROTO(c->qid)]->conv[CONV(c->qid)];
		if((perm & (cv->perm>>6)) != perm) {
			if(strcmp(commonuser(), cv->owner) != 0)
				error(Eperm);
		 	if((perm & cv->perm) != perm)
				error(Eperm); 

		}

		if(cv->state != Announced)
			error("not announced");



@@ 470,9 478,34 @@ ipremove(Chan*)
}

static void
ipwstat(Chan*, char*)
ipwstat(Chan *c, char *dp)
{
	error(Eperm);
	Dir d;
	Conv *cv;
	Fs *f;
	Proto *p;

	f = ipfs[c->dev];
	switch(TYPE(c->qid)) {
	default:
		error(Eperm);
		break;
	case Qctl:
	case Qdata:
		break;
	}

	convM2D(dp, &d);

	p = f->p[PROTO(c->qid)];
	cv = p->conv[CONV(c->qid)];
	if(!iseve() && strcmp(commonuser(), cv->owner) != 0)
		error(Eperm);
	if(d.uid[0]){
		strncpy(cv->owner, d.uid, sizeof(cv->owner));
		cv->owner[sizeof(cv->owner)-1] = 0;
	}
	cv->perm = d.mode & 0777;
}

void

M port/devtls.c => port/devtls.c +372 -414
@@ 10,18 10,8 @@

#include	<libsec.h>

#define NOSPOOKS 1

typedef struct OneWay OneWay;
enum
{

	/* encryption algorithms */
	Noencryption=	0,
	DESCBC=		1,
	DESECB=		2,
	RC4=		3
};
typedef struct Secret Secret;

enum {
	MaxRecLen	= 1<<14,	/* max payload length of a record layer message */


@@ 79,19 69,23 @@ enum {

	EMAX = 256
};
struct OneWay
{
	QLock		q;		/* locks io access */
	QLock		ctlq;		/* locks one-way paramaters */

	ulong		seq;
	int		keyed;		/* have key, waiting for cipher enable */

	int		protected;	/* cipher is enabled */
struct Secret
{
	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
	int		maclen;
	RC4state	rc4;
	uchar		mackey[64];
};

struct OneWay
{
	QLock		io;		/* locks io access */
	QLock		seclock;	/* locks secret paramaters */
	ulong		seq;
	Secret		*sec;		/* cipher in use */
	Secret		*new;		/* cipher waiting for enable */
};

typedef struct TlsRec TlsRec;
struct TlsRec


@@ 105,9 99,7 @@ struct TlsRec
	int		state;			/* must be set using setstate */

	/* record layer mac functions for different protocol versions */
	void		(*packMac)(TlsRec*, uchar*, uchar*, uchar*, uchar*, int, uchar*);
	DigestState	*(*mac)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*);
	int		maclen;
	void		(*packMac)(Secret*, uchar*, uchar*, uchar*, uchar*, int, uchar*);

	/* input side -- protected by in.q */
	OneWay	in;


@@ 154,15 146,13 @@ enum{

static void	ensure(TlsRec*, Block**, int);
static void	consume(Block**, uchar*, int);
static void	setsecret(OneWay*, uchar*, int);
static Block*	encryptb(TlsRec*, Block*, int);
static Block*	decryptb(TlsRec*, Block*);
static Block*	digestb(TlsRec*, Block*, int);
static void	checkdigestb(TlsRec*, Block*);
static Chan*	buftochan(char*);
static void	tlshangup(TlsRec*);
static TlsRec*	dsclone(Chan *c);
static void	dsnew(Chan *c, TlsRec **);
static DigestState*sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s);
static void	sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
static void	tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac);
static void	put64(uchar *p, vlong x);
static void	put32(uchar *p, u32int);
static void	put24(uchar *p, int);


@@ 297,11 287,32 @@ tlsstat(Chan *c, char *db)
	devstat(c, db, 0, 0, tlsgen);
}

static void
closedown(Chan *c, TlsRec *tr)
{
	lock(&dslock);
	if(--tr->ref > 0) {
		unlock(&dslock);
		return;
	}
	dstate[CONV(c->qid)] = nil;
	unlock(&dslock);

	tlshangup(tr);
	if(tr->c)
		cclose(tr->c);
	free(tr->in.sec);
	free(tr->in.new);
	free(tr->out.sec);
	free(tr->out.new);
	free(tr);
}

static Chan*
tlsopen(Chan *c, int omode)
{
	TlsRec *s, **pp;
	int perm;
	TlsRec *tr, **pp;
	int t, perm;

	perm = 0;
	omode &= 3;


@@ 317,7 328,8 @@ tlsopen(Chan *c, int omode)
		break;
	}

	switch(TYPE(c->qid)) {
	t = TYPE(c->qid);
	switch(t) {
	default:
		panic("tlsopen");
	case Qtopdir:


@@ 327,8 339,8 @@ tlsopen(Chan *c, int omode)
			error(Eperm);
		break;
	case Qclonus:
		s = dsclone(c);
		if(s == 0)
		tr = dsclone(c);
		if(tr == nil)
			error(Enodev);
		break;
	case Qctl:


@@ 340,19 352,33 @@ tlsopen(Chan *c, int omode)
		}
		lock(&dslock);
		pp = &dstate[CONV(c->qid)];
		s = *pp;
		if(s == 0)
		tr = *pp;
		if(tr == nil)
			dsnew(c, pp);
		else {
			if((perm & (s->perm>>6)) != perm
			   && (strcmp(up->user, s->user) != 0
			     || (perm & s->perm) != perm))
			if((perm & (tr->perm>>6)) != perm
			   && (strcmp(up->user, tr->user) != 0
			     || (perm & tr->perm) != perm))
				error(Eperm);

			s->ref++;
			tr->ref++;
		}
		unlock(&dslock);
		poperror();
		if(t == Qhand){
			if(waserror()){
				qunlock(&tr->in.io);
				closedown(c, tr);
			}
			qlock(&tr->in.io);
			if(tr->handq != nil)
				error(Einuse);
			tr->handq = qopen(MaxRecLen, 0, nil, nil);
			if(tr->handq == nil)
				error("can't allocate handshake queue");
			qunlock(&tr->in.io);
			poperror();
		}
		break;
	case Qencalgs:
	case Qhashalgs:


@@ 370,48 396,48 @@ static void
tlswstat(Chan *c, char *dp)
{
	Dir d;
	TlsRec *s;
	TlsRec *tr;

	convM2D(dp, &d);

	s = dstate[CONV(c->qid)];
	if(s == nil)
	tr = dstate[CONV(c->qid)];
	if(tr == nil)
		error(Ebadusefd);
	if(strcmp(s->user, up->user) != 0)
	if(strcmp(tr->user, up->user) != 0)
		error(Eperm);

	memmove(s->user, d.uid, NAMELEN);
	s->perm = d.mode;
	memmove(tr->user, d.uid, NAMELEN);
	tr->perm = d.mode;
}

static void
tlsclose(Chan *c)
{
	TlsRec *s;
	TlsRec *tr;
	int t;

	switch(TYPE(c->qid)) {
	t = TYPE(c->qid);
	switch(t) {
	case Qctl:
	case Qdata:
	case Qhand:
		if((c->flag & COPEN) == 0)
			break;

		s = dstate[CONV(c->qid)];
		if(s == nil)
		tr = dstate[CONV(c->qid)];
		if(tr == nil)
			break;

		lock(&dslock);
		if(--s->ref > 0) {
			unlock(&dslock);
			break;
		if(t == Qhand){
			qlock(&tr->in.io);
			if(tr->handq != nil){
				qfree(tr->handq);
				tr->handq = nil;
			}
			qunlock(&tr->in.io);
		}
		dstate[CONV(c->qid)] = nil;
		unlock(&dslock);

		tlshangup(s);
		if(s->c)
			cclose(s->c);
		free(s);
		closedown(c, tr);
		break;
	}
}



@@ 548,7 574,7 @@ qremove(Block **l, int n, int discard)

/*
 * read and process one tls record layer message
 * must be called with tr->in.q held
 * must be called with tr->in.io held
 */
static void
tlsrecread(TlsRec *tr)


@@ 581,38 607,39 @@ tlsrecread(TlsRec *tr)
	nconsumed = 0;

	/*
	 *  if an Eintr happens after this, we're screwed.  Make
	 *  sure nothing we call can sleep.  Luckily, allocb
	 *  won't sleep, it'll just error out.
	 *  grab the next message and decode/decrypt it
	 * If an Eintr happens after this, we'll get out of sync.
	 * Make sure nothing we call can sleep.
	 * Errors are ok, as they kill the connection.
	 * Luckily, allocb won't sleep, it'll just error out.
	 */
	b = qremove(&tr->unprocessed, len, 0);

	in = &tr->in;
	if(waserror()){
		qunlock(&in->ctlq);
//ZZZ kill the connection
		qunlock(&in->seclock);
		if(b != nil)
			freeb(b);
		nexterror();
	}
	qlock(&in->ctlq);
	qlock(&in->seclock);
	b = pullupblock(b, len);
	p = b->rp;
	if(in->protected) {
		if(len <= tr->maclen)
	if(in->sec != nil) {
		if(len <= in->sec->maclen)
			tlsError(tr, EDecodeError, "record message too short for mac");
		rc4(&in->rc4, p, len);
		len -= tr->maclen;
		rc4(&in->sec->rc4, p, len);
		len -= in->sec->maclen;

		/* update length */
		put16(header+3, len);
		put64(seq, in->seq);
		in->seq++;
		(*tr->packMac)(tr, in->mackey, seq, header, p, len, hmac);
		if(memcmp(hmac, p+len, tr->maclen) != 0)
		(*tr->packMac)(in->sec, in->sec->mackey, seq, header, p, len, hmac);
		if(memcmp(hmac, p+len, in->sec->maclen) != 0)
			tlsError(tr, EBadRecordMac, "record mac mismatch");
	}
	qunlock(&tr->in.ctlq);
	qunlock(&tr->in.seclock);
	poperror();
	if(len <= 0)
		tlsError(tr, EDecodeError, "runt record message");


@@ 624,15 651,16 @@ tlsrecread(TlsRec *tr)
	case RChangeCipherSpec:
		if(len != 1 || p[0] != 1)
			tlsError(tr, EHandshakeFailure, "invalid change cipher spec");
		qlock(&in->ctlq);
		if(!tr->in.keyed){
			qunlock(&in->ctlq);
		qlock(&in->seclock);
		if(in->new == nil){
			qunlock(&in->seclock);
			tlsError(tr, EUnexpectedMessage, "unexpected change cipher spec");
		}
		tr->in.keyed = 0;
		tr->in.protected = 1;
		free(tr->in.sec);
		tr->in.sec = tr->in.new;
		tr->in.new = nil;
		tr->in.seq = 0;
		qunlock(&in->ctlq);
		qunlock(&in->seclock);
		break;
	case RAlert:
		if(len != 2)


@@ 695,10 723,10 @@ tlsbread(Chan *c, long n, ulong offset)
		panic("tlsbread");

	if(waserror()){
		qunlock(&tr->in.q);
		qunlock(&tr->in.io);
		nexterror();
	}
	qlock(&tr->in.q);
	qlock(&tr->in.io);
	if(TYPE(c->qid) == Qdata){
//ZZZ race setting state
		if(tr->state != SOpen)


@@ 708,13 736,13 @@ tlsbread(Chan *c, long n, ulong offset)

		/* return at most what was asked for */
		b = qremove(&tr->processed, n, 0);
		qunlock(&tr->in.q);
		qunlock(&tr->in.io);
		poperror();
	}else{
//ZZZ race setting state
		while(tr->state == SHandshake && !qcanread(tr->handq))
			tlsrecread(tr);
		qunlock(&tr->in.q);
		qunlock(&tr->in.io);
		poperror();
		b = qbread(tr->handq, n);
	}


@@ 729,7 757,7 @@ tlsread(Chan *c, void *a, long n, vlong off)
	Block *nb;
	uchar *va;
	int i;
	char buf[128];
	char buf[16];
	ulong offset = off;

	if(c->qid.path & CHDIR)


@@ 739,7 767,7 @@ tlsread(Chan *c, void *a, long n, vlong off)
	default:
		error(Ebadusefd);
	case Qctl:
		sprint(buf, "%lud", CONV(c->qid));
		snprint(buf, sizeof(buf), "%lud", CONV(c->qid));
		return readstr(offset, a, n, buf);
	case Qdata:
		b = tlsbread(c, n, offset);


@@ 795,12 823,12 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
	out = &tr->out;
	bb = b;
	if(waserror()){
		qunlock(&out->q);
		qunlock(&out->io);
		if(bb != nil)
			freeb(bb);
		nexterror();
	}
	qlock(&out->q);
	qlock(&out->io);

	while(bb != nil){
//ZZZ race on state


@@ 812,13 840,13 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
		 * with padding on the front for header and back for mac
		 */
		if(waserror()){
			qunlock(&out->ctlq);
			qunlock(&out->seclock);
			nexterror();
		}
		qlock(&out->ctlq);
		qlock(&out->seclock);
		maclen = 0;
		if(out->protected)
			maclen = tr->maclen;
		if(out->sec != nil)
			maclen = out->sec->maclen;
		n = BLEN(bb);
		if(n > MaxRecLen){
			n = MaxRecLen;


@@ 843,10 871,10 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
		put16(p+1, tr->version);
		put16(p+3, n);

		if(out->protected){
		if(out->sec != nil){
			put64(seq, out->seq);
			out->seq++;
			(*tr->packMac)(tr, out->mackey, seq, p, p + RecHdrLen, n, nb->wp);
			(*tr->packMac)(out->sec, out->sec->mackey, seq, p, p + RecHdrLen, n, nb->wp);
			b->wp += maclen;
			n += maclen;



@@ 854,9 882,9 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
			put16(p+3, n);

			/* encrypt */
			rc4(&out->rc4, p+RecHdrLen, n);
			rc4(&out->sec->rc4, p+RecHdrLen, n);
		}
		qunlock(&out->ctlq);
		qunlock(&out->seclock);
		poperror();

		/*


@@ 865,7 893,7 @@ tlsrecwrite(TlsRec *tr, int type, Block *b)
		 */
		devtab[tr->c->type]->bwrite(tr->c, nb, 0);
	}
	qunlock(&out->q);
	qunlock(&out->io);
	poperror();
}



@@ 901,109 929,88 @@ tlsbwrite(Chan *c, Block *b, ulong offset)
	return n;
}

static void
setsecret(OneWay *w, uchar *secret, int n)
{
	if(w->secret)
		free(w->secret);

	w->secret = smalloc(n);
	memmove(w->secret, secret, n);
	w->slen = n;
}

/*
 *  128 bit RC4 is the same as n-bit RC4.  However,
 *  we ignore all but the first 128 bits of the key.
 */
static void
initRC4key_128(OneWay *w)
{
	if(w->state){
		free(w->state);
		w->state = 0;
	}

	if(w->slen > 16)
		w->slen = 16;

	w->state = malloc(sizeof(RC4state));
	setupRC4state(w->state, w->secret, w->slen);
}


typedef struct Hashalg Hashalg;
struct Hashalg
{
	char	*name;
	int	diglen;
	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);
	int	maclen;
	void	(*initkey)(Hashalg *, int, Secret *, uchar*);
};

static void
initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
{
	s->maclen = ha->maclen;
	if(version == SSL3Version)
		s->mac = sslmac_md5;
	else
		s->mac = hmac_md5;
	memmove(s->mackey, p, ha->maclen);
}

Hashalg hashtab[] =
{
	{ "md5", MD5dlen, md5, },
	{ "clear" },
	{ "md5", MD5dlen, initmd5key, },
	{ 0 }
};

static int
parsehashalg(char *p, TlsRec *s)
static Hashalg*
parsehashalg(char *p)
{
	Hashalg *ha;

	for(ha = hashtab; ha->name; ha++){
		if(strcmp(p, ha->name) == 0){
			s->hf = ha->hf;
			s->diglen = ha->diglen;
			s->state &= ~Sclear;
			s->state |= Sdigesting;
			return 0;
		}
	}
	return -1;
	for(ha = hashtab; ha->name; ha++)
		if(strcmp(p, ha->name) == 0)
			return ha;
	error("unsupported hash algorithm");
	return nil;
}

typedef struct Encalg Encalg;
struct Encalg
{
	char	*name;
	int	blocklen;
	int	alg;
	void	(*keyinit)(OneWay*);
	int	keylen;
	int	ivlen;
	void	(*initkey)(Encalg *ea, Secret *, uchar*, uchar*);
};

static void
initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *)
{
	setupRC4state(&s->rc4, p, ea->keylen);
}

Encalg encrypttab[] =
{
	{ "rc4_128", 1, RC4, initRC4key_128, },
	{ "clear" },
	{ "rc4_128", 128/8, 0, initRC4key, },
	{ 0 }
};

static int
parseencryptalg(char *p, TlsRec *s)
static Encalg*
parseencalg(char *p)
{
	Encalg *ea;

	for(ea = encrypttab; ea->name; ea++){
		if(strcmp(p, ea->name) == 0){
			s->encryptalg = ea->alg;
			s->blocklen = ea->blocklen;
			(*ea->keyinit)(&s->in);
			(*ea->keyinit)(&s->out);
			s->state &= ~Sclear;
			s->state |= Sencrypting;
			return 0;
		}
	}
	return -1;
	for(ea = encrypttab; ea->name; ea++)
		if(strcmp(p, ea->name) == 0)
			return ea;
	error("unsupported encryption algorithm");
	return nil;
}

static long
tlswrite(Chan *c, void *a, long n, vlong off)
{
	Encalg *ea;
	Hashalg *ha;
	TlsRec *volatile tr;
	Block *volatile b;
	int m, t;
	char *p, *np, *e, buf[128];
	Cmdbuf *volatile cb;
	int m;
	char *p, *e, buf[128];
	uchar *x;
	ulong offset = off;



@@ 1011,11 1018,12 @@ tlswrite(Chan *c, void *a, long n, vlong off)
	if(tr == nil)
		panic("tlswrite");

	t = TYPE(c->qid);
	if(t == Qdata || t == Qhand){
	switch(TYPE(c->qid)){
	case Qdata:
	case Qhand:
		p = a;
		e = p + n;
		do {
		do{
			m = e - p;
			if(m > MaxRecLen)
				m = MaxRecLen;


@@ 1032,115 1040,140 @@ tlswrite(Chan *c, void *a, long n, vlong off)
			tlsbwrite(c, b, offset);

			p += m;
		} while(p < e);
		}while(p < e);
		return n;
	case Qctl:
		break;
	default:
		error(Ebadusefd);
		return -1;
	}

	/* mutex with operations using what we're about to change */
	cb = parsecmd(buf, n);
	if(waserror()){
		qunlock(&s->in.ctlq);
		qunlock(&s->out.q);
		free(cb);
		nexterror();
	}
	qlock(&s->in.ctlq);
	qlock(&s->out.q);
	if(cb->nf < 1)
		error("short control request");

	switch(t){
	default:
		error(Ebadusefd);
	case Qctl:
		break;
	/* mutex with operations using what we're about to change */
//ZZZ check this locking
	if(waserror()){
		qunlock(&tr->in.seclock);
		qunlock(&tr->out.seclock);
		nexterror();
	}
	qlock(&tr->in.seclock);
	qlock(&tr->out.seclock);

	if(n >= sizeof(buf))
		error("arg too long");
	strncpy(buf, a, n);
	buf[n] = 0;
	p = strchr(buf, '\n');
	if(p)
		*p = 0;
	p = strchr(buf, ' ');
	if(p)
		*p++ = 0;

	if(strcmp(buf, "fd") == 0){
		if(s->c != nil)
	if(strcmp(cb->f[0], "fd") == 0){
		if(cb->nf != 3)
			error("usage: fd n version");
		if(tr->c != nil)
			error(Einuse);
		s->c = buftochan(p);

		/* default is clear (msg delimiters only) */
		s->state = Sclear;
		s->blocklen = 1;
		s->diglen = 0;
		s->in.mid = 0;
		s->out.mid = 0;
	} else if(strcmp(buf, "alg") == 0 && p != 0){
		s->blocklen = 1;
		s->diglen = 0;

		if(s->c == 0)
			error("must set fd before algorithm");

		s->state = Sclear;
		s->maxpad = s->max = (1<<15) - s->diglen - 1;
		if(strcmp(p, "clear") == 0){
			goto out;
		}
		n = strtol(cb->f[2], nil, 0);
		if(n < MinProtoVersion || n > MinProtoVersion)
			error("unsupported version");
		tr->c = buftochan(cb->f[1]);
		tr->version = n;
	}else if(strcmp(cb->f[0], "version") == 0){
		if(cb->nf != 2)
			error("usage: version n");
		if(tr->c == nil)
			error("must set fd before version");
		if(tr->verset)
			error("version already set");
		n = strtol(cb->f[1], nil, 0);
		if(n == SSL3Version)
			tr->packMac = sslPackMac;
		else if(n == TLSVersion)
			tr->packMac = tlsPackMac;
		else
			error("unsupported version");
		tr->verset = 1;
		tr->version = n;
	}else if(strcmp(cb->f[0], "alert") == 0){
		if(cb->nf != 2)
			error("usage: alert n");
		if(tr->c == nil)
			error("must set fd before sending alerts");

		n = strtol(cb->f[1], nil, 0);

		tlsError(tr, n);
		b = allocb(2);
//ZZZ need to check for fatal error
		*b->wp++ = 1;
		*b->wp++ = n;
		tlsrecwrite(tr, RAlert, b);
		freeb(b);
//ZZZ race on state
		tlsSetState(tr, SError);
	}else if(strcmp(cb->f[0], "changecipher") == 0){
		if(cb->nf != 1)
			error("usage: changecipher");
		if(tr->out.new == nil)
			error("can't change cipher spec without setting secret");

		b = allocb(1);
		*b->wp++ = 1;
		tlsrecwrite(tr, RChangeCipherSpec, b);
		freeb(b);

		if(s->in.secret && s->out.secret == 0)
			setsecret(&s->out, s->in.secret, s->in.slen);
		if(s->out.secret && s->in.secret == 0)
			setsecret(&s->in, s->out.secret, s->out.slen);
		if(s->in.secret == 0 || s->out.secret == 0)
			error("algorithm but no secret");

		s->hf = 0;
		s->encryptalg = Noencryption;
		s->blocklen = 1;

		for(;;){
			np = strchr(p, ' ');
			if(np)
				*np++ = 0;

			if(parsehashalg(p, s) < 0)
			if(parseencryptalg(p, s) < 0)
				error("bad algorithm");

			if(np == 0)
				break;
			p = np;
		free(tr->out.sec);
		tr->out.sec = tr->out.new;
		tr->out.new = nil;
	}else if(strcmp(cb->f[0], "secret") == 0){
		if(cb->nf != 4)
			error("usage: secret hashalg encalg secretdata");
		if(tr->c == nil || !tr->verset)
			error("must set fd and version before secrets");

		if(tr->in.new != nil){
			free(tr->in.new);
			tr->in.new = nil;
		}
		if(tr->out.new != nil){
			free(tr->out.new);
			tr->out.new = nil;
		}

		if(s->hf == 0 && s->encryptalg == Noencryption)
			error("bad algorithm");
		ha = parsehashalg(cb->f[1]);
		ea = parseencalg(cb->f[2]);

		if(s->blocklen != 1){
			s->max = (1<<15) - s->diglen - 1;
			s->max -= s->max % s->blocklen;
			s->maxpad = (1<<14) - s->diglen - 1;
			s->maxpad -= s->maxpad % s->blocklen;
		} else
			s->maxpad = s->max = (1<<15) - s->diglen - 1;
	} else if(strcmp(buf, "secretin") == 0 && p != 0) {
		m = (strlen(p)*3)/2;
		x = smalloc(m);
		n = dec64(x, m, p, strlen(p));
		setsecret(&s->in, x, n);
		free(x);
	} else if(strcmp(buf, "secretout") == 0 && p != 0) {
		m = (strlen(p)*3)/2 + 1;
		m = (strlen(cb->f[3])*3)/2;
		x = smalloc(m);
		n = dec64(x, m, p, strlen(p));
		setsecret(&s->out, x, n);
		if(waserror()){
			free(x);
			poperror();
		}
		m = dec64(x, m, cb->f[3], strlen(cb->f[3]));
		if(m != 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
			error("bad secret data length");

		tr->out.new = smalloc(sizeof(Secret));
		tr->in.new = smalloc(sizeof(Secret));
		if(ha->initkey){
			(*ha->initkey)(ha, tr->version, tr->in.new, &x[0]);
			(*ha->initkey)(ha, tr->version, tr->out.new, &x[ha->maclen]);
		}
		if(ea->initkey){
			(*ea->initkey)(ea, tr->in.new, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
			(*ea->initkey)(ea, tr->out.new, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
		}

		free(x);
		poperror();
	} else
		error(Ebadarg);

out:
	qunlock(&s->in.ctlq);
	qunlock(&s->out.q);
	qunlock(&tr->in.seclock);
	qunlock(&tr->out.seclock);
	poperror();
	free(cb);
	poperror();

	return n;
}



@@ 1205,162 1238,6 @@ Dev tlsdevtab = {
	tlswstat,
};

static Block*
encryptb(TlsRec *s, Block *b, int offset)
{
	uchar *p, *ep, *p2, *ip, *eip;
	DESstate *ds;

	switch(s->encryptalg){
	case DESECB:
		ds = s->out.state;
		ep = b->rp + BLEN(b);
		for(p = b->rp + offset; p < ep; p += 8)
			block_cipher(ds->expanded, p, 0);
		break;
	case DESCBC:
		ds = s->out.state;
		ep = b->rp + BLEN(b);
		for(p = b->rp + offset; p < ep; p += 8){
			p2 = p;
			ip = ds->ivec;
			for(eip = ip+8; ip < eip; )
				*p2++ ^= *ip++;
			block_cipher(ds->expanded, p, 0);
			memmove(ds->ivec, p, 8);
		}
		break;
	case RC4:
		rc4(s->out.state, b->rp + offset, BLEN(b) - offset);
		break;
	}
	return b;
}

static Block*
decryptb(TlsRec *s, Block *bin)
{
	Block *b, **l;
	uchar *p, *ep, *tp, *ip, *eip;
	DESstate *ds;
	uchar tmp[8];
	int i;

	l = &bin;
	for(b = bin; b; b = b->next){
		/* make sure we have a multiple of s->blocklen */
		if(s->blocklen > 1){
			i = BLEN(b);
			if(i % s->blocklen){
				*l = b = pullupblock(b, i + s->blocklen - (i%s->blocklen));
				if(b == 0)
					error("tls encrypted message too short");
			}
		}
		l = &b->next;

		/* decrypt */
		switch(s->encryptalg){
		case DESECB:
			ds = s->in.state;
			ep = b->rp + BLEN(b);
			for(p = b->rp; p < ep; p += 8)
				block_cipher(ds->expanded, p, 1);
			break;
		case DESCBC:
			ds = s->in.state;
			ep = b->rp + BLEN(b);
			for(p = b->rp; 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++;
				}
			}
			break;
		case RC4:
			rc4(s->in.state, b->rp, BLEN(b));
			break;
		}
	}
	return bin;
}

static Block*
digestb(TlsRec *s, Block *b, int offset)
{
	uchar *p;
	DigestState ss;
	uchar msgid[4];
	ulong n, h;
	OneWay *w;

	w = &s->out;

	memset(&ss, 0, sizeof(ss));
	h = s->diglen + offset;
	n = BLEN(b) - h;

	/* hash secret + message */
	(*s->hf)(w->secret, w->slen, 0, &ss);
	(*s->hf)(b->rp + h, n, 0, &ss);

	/* hash message id */
	p = msgid;
	n = w->mid;
	*p++ = n>>24;
	*p++ = n>>16;
	*p++ = n>>8;
	*p = n;
	(*s->hf)(msgid, 4, b->rp + offset, &ss);

	return b;
}

static void
checkdigestb(TlsRec *s, Block *bin)
{
	uchar *p;
	DigestState ss;
	uchar msgid[4];
	int n, h;
	OneWay *w;
	uchar digest[128];
	Block *b;

	w = &s->in;

	memset(&ss, 0, sizeof(ss));

	/* hash secret */
	(*s->hf)(w->secret, w->slen, 0, &ss);

	/* hash message */
	h = s->diglen;
	for(b = bin; b; b = b->next){
		n = BLEN(b) - h;
		if(n < 0)
			panic("checkdigestb");
		(*s->hf)(b->rp + h, n, 0, &ss);
		h = 0;
	}

	/* hash message id */
	p = msgid;
	n = w->mid;
	*p++ = n>>24;
	*p++ = n>>16;
	*p++ = n>>8;
	*p = n;
	(*s->hf)(msgid, 4, digest, &ss);

	if(memcmp(digest, bin->rp, s->diglen) != 0)
		error("bad digest");
}

/* get channel associated with an fd */
static Chan*
buftochan(char *p)


@@ 1383,7 1260,7 @@ tlshangup(TlsRec *s)
{
	Block *b;

	qlock(&s->in.q);
	qlock(&s->in.io);
	for(b = s->processed; b; b = s->processed){
		s->processed = b->next;
		freeb(b);


@@ 1392,8 1269,8 @@ tlshangup(TlsRec *s)
		freeb(s->unprocessed);
		s->unprocessed = nil;
	}
	s->state = Sincomplete;
	qunlock(&s->in.q);
	s->state = SClosed;
	qunlock(&s->in.io);
}

static TlsRec*


@@ 1450,7 1327,7 @@ dsnew(Chan *ch, TlsRec **pp)
	if(pp - dstate >= dshiwat)
		dshiwat++;
	memset(s, 0, sizeof(*s));
	s->state = Sincomplete;
	s->state = SClosed;
	s->ref = 1;
	strncpy(s->user, up->user, sizeof(s->user));
	s->perm = 0660;


@@ 1461,6 1338,87 @@ dsnew(Chan *ch, TlsRec **pp)
	ch->qid.vers = 0;
}

/*
 * sslmac: mac calculations for ssl 3.0 only; tls 1.0 uses the standard hmac.
 */
static DigestState*
sslmac_x(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s,
	DigestState*(*x)(uchar*, ulong, uchar*, DigestState*), int xlen, int padlen)
{
	int i;
	uchar pad[48], innerdigest[20];

	if(xlen > sizeof(innerdigest)
	|| padlen > sizeof(pad))
		return nil;

	if(klen>64)
		return nil;

	/* first time through */
	if(s == nil){
		for(i=0; i<padlen; i++)
			pad[i] = 0x36;
		s = (*x)(key, klen, nil, nil);
		s = (*x)(pad, padlen, nil, s);
		if(s == nil)
			return nil;
	}

	s = (*x)(p, len, nil, s);
	if(digest == nil)
		return s;

	/* last time through */
	for(i=0; i<padlen; i++)
		pad[i] = 0x5c;
	(*x)(nil, 0, innerdigest, s);
	s = (*x)(key, klen, nil, nil);
	s = (*x)(pad, padlen, nil, s);
	(*x)(innerdigest, xlen, digest, s);
	return nil;
}

static DigestState*
sslmac_sha1(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
{
	return sslmac_x(p, len, key, klen, digest, s, sha1, SHA1dlen, 40);
}

static DigestState*
sslmac_md5(uchar *p, ulong len, uchar *key, ulong klen, uchar *digest, DigestState *s)
{
	return sslmac_x(p, len, key, klen, digest, s, md5, MD5dlen, 48);
}

static void
sslPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
{
	DigestState *s;
	uchar buf[11];

	memmove(buf, seq, 8);
	buf[8] = header[0];
	buf[9] = header[3];
	buf[10] = header[4];

	s = (*sec->mac)(buf, 11, mackey, sec->maclen, 0, 0);
	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
}

static void
tlsPackMac(Secret *sec, uchar *mackey, uchar *seq, uchar *header, uchar *body, int len, uchar *mac)
{
	DigestState *s;
	uchar buf[13];

	memmove(buf, seq, 8);
	memmove(&buf[8], header, 5);

	s = (*sec->mac)(buf, 13, mackey, sec->maclen, 0, 0);
	(*sec->mac)(body, len, mackey, sec->maclen, mac, s);
}

static void
put32(uchar *p, u32int x)
{