~kris/9p

9hist

b85405263c6cd5855e446a312c5eeddcdeea4049 — David du Colombier 25 years ago fecdad3
Plan 9 from Bell Labs 2001-03-31
7 files changed, 463 insertions(+), 400 deletions(-)

M bitsy/devuda1341.c
M port/devkprof.c
M port/devloopback.c
M port/devssl.c
M port/devtls.c
M port/parse.c
M port/portclock.c
M bitsy/devuda1341.c => bitsy/devuda1341.c +8 -7
@@ 44,13 44,13 @@ static int debug = 0;
 * L3 setup and hold times (expressed in µs)
 */
enum {
	L3_DataSetupTime =	1,		/* 190 ns */
	L3_DataHoldTime =	1,		/*  30 ns */
	L3_ModeSetupTime =	1,		/* 190 ns */
	L3_ModeHoldTime =	1,		/* 190 ns */
	L3_ClockHighTime =	10,		/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
	L3_ClockLowTime =	10,		/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
	L3_HaltTime =		1,		/* 190 ns */
	L3_DataSetupTime =	10,	/* 190 ns */
	L3_DataHoldTime =		10,	/*  30 ns */
	L3_ModeSetupTime =	10,	/* 190 ns */
	L3_ModeHoldTime =		10,	/* 190 ns */
	L3_ClockHighTime =		100,	/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
	L3_ClockLowTime =		100,	/* 250 ns (min is 64*fs, 35µs @ 44.1 Khz) */
	L3_HaltTime =			10,	/* 190 ns */
};

/* UDA 1341 Registers */


@@ 940,6 940,7 @@ audioread(Chan *c, void *v, long n, vlong off)
	ulong offset = off;
	char *p;
	IOstate *s;
	uchar voldata;

	n0 = n;
	p = v;

M port/devkprof.c => port/devkprof.c +2 -2
@@ 76,7 76,7 @@ kprofattach(char *spec)
			error(Enomem);
	}
	kproftab[0].length = n;
	return devattach('T', spec);
	return devattach('K', spec);
}

static int


@@ 171,7 171,7 @@ kprofwrite(Chan *c, void *a, long n, vlong)
}

Dev kprofdevtab = {
	'T',
	'K',
	"kprof",

	devreset,

M port/devloopback.c => port/devloopback.c +8 -3
@@ 123,7 123,7 @@ loopbackinit(void)
static Chan*
loopbackattach(char *spec)
{
	Loop *lb;
	Loop *volatile lb;
	Queue *q;
	Chan *c;
	int chan;


@@ 472,8 472,8 @@ loopbackwrite(Chan *c, void *va, long n, vlong off)
{
	Loop *lb;
	Link *link;
	Cmdbuf *cb;
	Block *bp;
	Cmdbuf *volatile cb;
	Block *volatile bp;
	vlong d0, d0ns;
	long dn, dnns;



@@ 492,6 492,10 @@ loopbackwrite(Chan *c, void *va, long n, vlong off)
		lb = c->aux;
		link = &lb->link[ID(c->qid.path)];
		cb = parsecmd(va, n);
		if(waserror()){
			free(cb);
			nexterror();
		}
		if(cb->nf < 1)
			error("short control request");
		if(strcmp(cb->f[0], "delay") == 0){


@@ 545,6 549,7 @@ loopbackwrite(Chan *c, void *va, long n, vlong off)
			iunlock(link);
		}else
			error("unknown control request");
		free(cb);
		break;
	default:
		error(Eperm);

M port/devssl.c => port/devssl.c +1 -3
@@ 525,7 525,7 @@ sslbread(Chan *c, long n, ulong)

	nconsumed = 0;
	if(waserror()){
		if(strcmp(up->error, Eintr) != 0)
		if(strcmp(up->error, Eintr) == 0)
			regurgitate(s.s, consumed, nconsumed);
		qunlock(&s.s->in.q);
		nexterror();


@@ 773,9 773,7 @@ sslbwrite(Chan *c, Block *b, ulong offset)

		s.s->out.mid++;

		m = BLEN(nb);
		devtab[s.s->c->type]->bwrite(s.s->c, nb, s.s->c->offset);
		s.s->c->offset += m;
	}
	qunlock(&s.s->out.q);
	poperror();

M port/devtls.c => port/devtls.c +431 -379
@@ 1,5 1,5 @@
/*
 *  devtls - transport layer security 1.0 and secure sockets layer 3.0 record layer
 *  devtls - record layer for transport layer security 1.0 and secure sockets layer 3.0
 */
#include	"u.h"
#include	"../port/lib.h"


@@ 13,25 13,8 @@
#define NOSPOOKS 1

typedef struct OneWay OneWay;
struct OneWay
{
	QLock	q;
	QLock	ctlq;

	void	*state;		/* encryption state */
	int	slen;		/* hash data length */
	uchar	*secret;	/* secret */
	ulong	mid;		/* message id */
};

enum
{
	/* connection states */
	Sincomplete=	0,
	Sclear=		1,
	Sencrypting=	2,
	Sdigesting=	4,
	Sdigenc=	Sencrypting|Sdigesting,

	/* encryption algorithms */
	Noencryption=	0,


@@ 40,27 23,97 @@ enum
	RC4=		3
};

typedef struct Dstate Dstate;
struct Dstate
enum {
	MaxRecLen	= 1<<14,	/* max payload length of a record layer message */
	RecHdrLen	= 5,
	TLSVersion	= 0x0301,
	SSL3Version	= 0x0300,
	ProtocolVersion	= 0x0301,	/* maximum version we speak */
	MinProtoVersion	= 0x0300,	/* limits on version we accept */
	MaxProtoVersion	= 0x03ff,
};

/* connection states */
enum {
	SHandshake,		// doing handshake
	SOpen,			// application data can be sent
	SRemoteClosed,		// remote side has closed down
	SError,			// some sort of error has occured
	SClosed,		// it is all over
};

/* record types */
enum {
	RChangeCipherSpec = 20,
	RAlert,
	RHandshake,
	RApplication,
};

/* alerts */
enum {
	ECloseNotify = 0,
	EUnexpectedMessage = 10,
	EBadRecordMac = 20,
	EDecryptionFailed = 21,
	ERecordOverflow = 22,
	EDecompressionFailure = 30,
	EHandshakeFailure = 40,
	ENoCertificate = 41,
	EBadCertificate = 42,
	EUnsupportedCertificate = 43,
	ECertificateRevoked = 44,
	ECertificateExpired = 45,
	ECertificateUnknown = 46,
	EIllegalParameter = 47,
	EUnknownCa = 48,
	EAccessDenied = 49,
	EDecodeError = 50,
	EDecryptError = 51,
	EExportRestriction = 60,
	EProtocolVersion = 70,
	EInsufficientSecurity = 71,
	EInternalError = 80,
	EUserCanceled = 90,
	ENoRenegotiation = 100,

	EMAX = 256
};
struct OneWay
{
	Chan	*c;		/* io channel */
	uchar	state;		/* state of connection */
	int	ref;		/* serialized by dslock for atomic destroy */
	QLock		q;		/* locks io access */
	QLock		ctlq;		/* locks one-way paramaters */

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

	uchar	encryptalg;	/* encryption algorithm */
	ushort	blocklen;	/* blocking length */
	int		protected;	/* cipher is enabled */
	RC4state	rc4;
	uchar		mackey[64];
};


typedef struct TlsRec TlsRec;
struct TlsRec
{
	Chan		*c;			/* io channel */
	int		ref;			/* serialized by dslock for atomic destroy */
	int		version;		/* version of the protocol we are speaking */
	int		verset;			/* version has been set */

	ushort	diglen;		/* length of digest */
	DigestState *(*hf)(uchar*, ulong, uchar*, DigestState*);	/* hash func */
	Lock		statelk;
	int		state;			/* must be set using setstate */

	/* for SSL format */
	int	max;		/* maximum unpadded data per msg */
	int	maxpad;		/* maximum padded data per msg */
	/* 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;

	/* input side */
	/* input side -- protected by in.q */
	OneWay	in;
	Block	*processed;
	Block	*unprocessed;
	Queue	*handq;				/* queue of handshake messages */
	Block	*processed;			/* next bunch of application data */
	Block	*unprocessed;			/* data read from c but not parsed into records */

	/* output side */
	OneWay	out;


@@ 73,7 126,7 @@ struct Dstate
static	Lock	dslock;
static	int	dshiwat;
static	int	maxdstate = 128;
static	Dstate** dstate;
static	TlsRec** dstate;
static	char	*encalgs;
static	char	*hashalgs;



@@ 90,8 143,7 @@ enum{
	Qconvdir,		/* directory for a conversation */
	Qdata,
	Qctl,
	Qsecretin,
	Qsecretout,
	Qhand,
	Qencalgs,
	Qhashalgs,
};


@@ 100,24 152,32 @@ enum{
#define CONV(x) 	(((x).path >> 5)&(Maxdstate-1))
#define QID(c, y) 	(((c)<<5) | (y))

static void	ensure(Dstate*, Block**, int);
static void	ensure(TlsRec*, Block**, int);
static void	consume(Block**, uchar*, int);
static void	setsecret(OneWay*, uchar*, int);
static Block*	encryptb(Dstate*, Block*, int);
static Block*	decryptb(Dstate*, Block*);
static Block*	digestb(Dstate*, Block*, int);
static void	checkdigestb(Dstate*, Block*);
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(Dstate*);
static Dstate*	dsclone(Chan *c);
static void	dsnew(Chan *c, Dstate **);
static void	tlshangup(TlsRec*);
static TlsRec*	dsclone(Chan *c);
static void	dsnew(Chan *c, TlsRec **);
static void	put64(uchar *p, vlong x);
static void	put32(uchar *p, u32int);
static void	put24(uchar *p, int);
static void	put16(uchar *p, int);
static u32int	get32(uchar *p);
static int	get16(uchar *p);
static void	tlsAlert(TlsRec *tr, int err);
static void	tlsError(TlsRec *tr, int err, char *msg, ...);
#pragma	varargck	argpos	tlsError	3

static char *tlsnames[] = {
[Qclonus]	"clone",
[Qdata]		"data",
[Qctl]		"ctl",
[Qsecretin]	"secretin",
[Qsecretout]	"secretout",
[Qhand]		"hand",
[Qencalgs]	"encalgs",
[Qhashalgs]	"hashalgs",
};


@@ 126,7 186,7 @@ static int
tlsgen(Chan *c, Dirtab *d, int nd, int s, Dir *dp)
{
	Qid q;
	Dstate *ds;
	TlsRec *ds;
	char name[16], *p, *nm;

	USED(nd);


@@ 189,18 249,14 @@ tlsgen(Chan *c, Dirtab *d, int nd, int s, Dir *dp)
			p = "data";
			break;
		case 2:
			q.path = QID(CONV(c->qid), Qsecretin);
			p = "secretin";
			q.path = QID(CONV(c->qid), Qhand);
			p = "hand";
			break;
		case 3:
			q.path = QID(CONV(c->qid), Qsecretout);
			p = "secretout";
			break;
		case 4:
			q.path = QID(CONV(c->qid), Qencalgs);
			p = "encalgs";
			break;
		case 5:
		case 4:
			q.path = QID(CONV(c->qid), Qhashalgs);
			p = "hashalgs";
			break;


@@ 223,7 279,7 @@ tlsattach(char *spec)
{
	Chan *c;

	c = devattach('D', spec);
	c = devattach('a', spec);
	c->qid.path = QID(0, Qtopdir)|CHDIR;
	c->qid.vers = 0;
	return c;


@@ 244,7 300,7 @@ tlsstat(Chan *c, char *db)
static Chan*
tlsopen(Chan *c, int omode)
{
	Dstate *s, **pp;
	TlsRec *s, **pp;
	int perm;

	perm = 0;


@@ 277,8 333,7 @@ tlsopen(Chan *c, int omode)
		break;
	case Qctl:
	case Qdata:
	case Qsecretin:
	case Qsecretout:
	case Qhand:
		if(waserror()) {
			unlock(&dslock);
			nexterror();


@@ 315,7 370,7 @@ static void
tlswstat(Chan *c, char *dp)
{
	Dir d;
	Dstate *s;
	TlsRec *s;

	convM2D(dp, &d);



@@ 332,13 387,12 @@ tlswstat(Chan *c, char *dp)
static void
tlsclose(Chan *c)
{
	Dstate *s;
	TlsRec *s;

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



@@ 357,10 411,6 @@ tlsclose(Chan *c)
		tlshangup(s);
		if(s->c)
			cclose(s->c);
		free(s->in.secret);
		free(s->out.secret);
		free(s->in.state);
		free(s->out.state);
		free(s);
	}
}


@@ 369,7 419,7 @@ tlsclose(Chan *c)
 *  make sure we have at least 'n' bytes in list 'l'
 */
static void
ensure(Dstate *s, Block **l, int n)
ensure(TlsRec *s, Block **l, int n)
{
	int sofar, i;
	Block *b, *bl;


@@ 429,7 479,7 @@ consume(Block **l, uchar *p, int n)
 *  give back n bytes
 */
static void
regurgitate(Dstate *s, uchar *p, int n)
regurgitate(TlsRec *s, uchar *p, int n)
{
	Block *b;



@@ 497,6 547,129 @@ qremove(Block **l, int n, int discard)
}

/*
 * read and process one tls record layer message
 * must be called with tr->in.q held
 */
static void
tlsrecread(TlsRec *tr)
{
	OneWay *volatile in;
	Block *volatile b;
	uchar *p, seq[8], header[RecHdrLen], hmac[MD5dlen];
	int volatile nconsumed;
	int len, type, ver;

	nconsumed = 0;
	if(waserror()){
		if(strcmp(up->error, Eintr) == 0)
			regurgitate(tr, header, nconsumed);
		nexterror();
	}
	ensure(tr, &tr->unprocessed, RecHdrLen);
	consume(&tr->unprocessed, header, RecHdrLen);
	nconsumed = RecHdrLen;
	type = header[0];
	ver = get16(header+1);
	len = get16(header+3);
	if(ver != tr->version && (tr->verset || ver < MinProtoVersion || ver > MaxProtoVersion))
		tlsError(tr, EProtocolVersion, "invalid version in record layer");
	if(len <= 0)
		tlsError(tr, EIllegalParameter, "invalid length in record layer");
	if(len > MaxRecLen)
		tlsError(tr, ERecordOverflow, "record message too long");
	ensure(tr, &tr->unprocessed, len);
	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
	 */
	b = qremove(&tr->unprocessed, len, 0);

	in = &tr->in;
	if(waserror()){
		qunlock(&in->ctlq);
		if(b != nil)
			freeb(b);
		nexterror();
	}
	qlock(&in->ctlq);
	b = pullupblock(b, len);
	p = b->rp;
	if(in->protected) {
		if(len <= tr->maclen)
			tlsError(tr, EDecodeError, "record message too short for mac");
		rc4(&in->rc4, p, len);
		len -= tr->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)
			tlsError(tr, EBadRecordMac, "record mac mismatch");
	}
	qunlock(&tr->in.ctlq);
	poperror();
	if(len <= 0)
		tlsError(tr, EDecodeError, "runt record message");

	switch(type) {
	default:
		tlsError(tr, EIllegalParameter, "invalid record message 0x%x", type);
		return;
	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);
			tlsError(tr, EUnexpectedMessage, "unexpected change cipher spec");
		}
		tr->in.keyed = 0;
		tr->in.protected = 1;
		tr->in.seq = 0;
		qunlock(&in->ctlq);
		break;
	case RAlert:
		if(len != 2)
			tlsError(tr, EDecodeError, "invalid alert");
		if(p[0] == 1) {
			if(p[1] == ECloseNotify) {
				tlsError(tr, ECloseNotify, "remote close");
				tlsSetState(tr, SRemoteClosed);
			}
		} else {
			tlsSetState(tr, SError);
			tlsAlert(tr, p[1]);
		}
		break;
	case RHandshake:
		/*
		 * don't worry about dropping the block
		 * qbwrite always queue it even if flow controlled and interrupted.
		 */
		if(tr->handq != nil){
			qbwrite(tr->handq, b);
			b = nil;
		}
		break;
	case RApplication:
//ZZZ race on state
		if(tr->state != SOpen)
			tlsError(tr, EUnexpectedMessage, "application message received before handshake completed");
		tr->processed = b;
		b = nil;
		break;
	}
	if(b != nil)
		freeb(b);
}

/*
 *  We can't let Eintr's lose data since the program
 *  doing the read may be able to handle it.  The only
 *  places Eintr is possible is during the read's in consume.


@@ 504,106 677,48 @@ qremove(Block **l, int n, int discard)
 *  consumed before the last ensure.
 */
static Block*
tlsbread(Chan *c, long n, ulong)
tlsbread(Chan *c, long n, ulong offset)
{
	Dstate *volatile s;
	TlsRec *volatile tr;
	Block *b;
	uchar consumed[3];
	int nconsumed;
	int len, pad;

	s = dstate[CONV(c->qid)];
	if(s == nil)
	switch(TYPE(c->qid)) {
	default:
		return devbread(c, n, offset);
	case Qhand:
	case Qdata:
		break;
	}

	tr = dstate[CONV(c->qid)];
	if(tr == nil)
		panic("tlsbread");
	if(s->state == Sincomplete)
		error(Ebadusefd);

	nconsumed = 0;
	if(waserror()){
		if(strcmp(up->error, Eintr) != 0)
			regurgitate(s, consumed, nconsumed);
		qunlock(&s->in.q);
		qunlock(&tr->in.q);
		nexterror();
	}
	qlock(&s->in.q);

	if(s->processed == 0){
		/* read in the whole message */
		ensure(s, &s->unprocessed, 2);
		consume(&s->unprocessed, consumed, 2);
		nconsumed = 2;
		if(consumed[0] & 0x80){
			len = ((consumed[0] & 0x7f)<<8) | consumed[1];
			ensure(s, &s->unprocessed, len);
			pad = 0;
		} else {
			len = ((consumed[0] & 0x3f)<<8) | consumed[1];
			ensure(s, &s->unprocessed, len+1);
			consume(&s->unprocessed, &consumed[2], 1);
			pad = consumed[2];
			if(pad > len){
				print("pad %d buf len %d\n", pad, len);
				error("bad pad in tls message");
			}
		}
		USED(nconsumed);
		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 */
		b = qremove(&s->unprocessed, len, 0);

		if(waserror()){
			qunlock(&s->in.ctlq);
			if(b != nil)
				freeb(b);
			nexterror();
		}
		qlock(&s->in.ctlq);
		switch(s->state){
		case Sencrypting:
			b = decryptb(s, b);
			break;
		case Sdigesting:
			b = pullupblock(b, s->diglen);
			if(b == nil)
				error("tls message too short");
			checkdigestb(s, b);
			b->rp += s->diglen;
			break;
		case Sdigenc:
			b = decryptb(s, b);
			b = pullupblock(b, s->diglen);
			if(b == nil)
				error("tls message too short");
			checkdigestb(s, b);
			b->rp += s->diglen;
			len -= s->diglen;
			break;
		}
	qlock(&tr->in.q);
	if(TYPE(c->qid) == Qdata){
//ZZZ race setting state
		if(tr->state != SOpen)
			error(Ebadusefd);
		while(tr->processed == nil)
			tlsrecread(tr);

		/* remove pad */
		if(pad)
			s->processed = qremove(&b, len - pad, 1);
		else
			s->processed = b;
		b = nil;
		s->in.mid++;
		qunlock(&s->in.ctlq);
		/* return at most what was asked for */
		b = qremove(&tr->processed, n, 0);
		qunlock(&tr->in.q);
		poperror();
	}else{
//ZZZ race setting state
		while(tr->state == SHandshake && !qcanread(tr->handq))
			tlsrecread(tr);
		qunlock(&tr->in.q);
		poperror();
		USED(nconsumed);
		b = qbread(tr->handq, n);
	}

	/* return at most what was asked for */
	b = qremove(&s->processed, n, 0);

	qunlock(&s->in.q);
	poperror();

	return b;
}



@@ 666,204 781,135 @@ randfill(uchar *buf, int len)
}

/*
 *  use SSL record format, add in count, digest and/or encrypt.
 *  the write is interruptable.  if it is interrupted, we'll
 *  get out of sync with the far side.  not much we can do about
 *  it since we don't know if any bytes have been written.
 *  write a block in tls records
 */
static long
tlsbwrite(Chan *c, Block *b, ulong offset)
static void
tlsrecwrite(TlsRec *tr, int type, Block *b)
{
	Dstate *volatile s;
	Block *volatile bb;
	Block *nb;
	int h, n, m, pad, rv;
	uchar *p;
	uchar *p, seq[8];
	OneWay *volatile out;
	int n, maclen;

	out = &tr->out;
	bb = b;
	s = dstate[CONV(c->qid)];
	if(s == nil)
		panic("tlsbwrite");
	if(s->state == Sincomplete){
		freeb(b);
		error(Ebadusefd);
	}

	if(waserror()){
		qunlock(&s->out.q);
		qunlock(&out->q);
		if(bb != nil)
			freeb(bb);
		nexterror();
	}
	qlock(&s->out.q);
	qlock(&out->q);

	rv = 0;
	while(bb){
		m = n = BLEN(bb);
		h = s->diglen + 2;

		/* trim to maximum block size */
		pad = 0;
		if(m > s->max){
			m = s->max;
		} else if(s->blocklen != 1){
			pad = (m + s->diglen)%s->blocklen;
			if(pad){
				if(m > s->maxpad){
					pad = 0;
					m = s->maxpad;
				} else {
					pad = s->blocklen - pad;
					h++;
				}
			}
		}
	while(bb != nil){
//ZZZ race on state
		if(tr->state != SHandshake && tr->state != SOpen)
			error(Ebadusefd);

		rv += m;
		if(m != n){
			nb = allocb(m + h + pad);
			memmove(nb->wp + h, bb->rp, m);
			nb->wp += m + h;
			bb->rp += m;
		} else {
			/* add header space */
			nb = padblock(bb, h);
			bb = nil;
		}
		m += s->diglen;

		/* SSL style count */
		if(pad){
			nb = padblock(nb, -pad);
			randfill(nb->wp, pad);
			nb->wp += pad;
			m += pad;

			p = nb->rp;
			p[0] = (m>>8);
			p[1] = m;
			p[2] = pad;
			offset = 3;
		} else {
			p = nb->rp;
			p[0] = (m>>8) | 0x80;
			p[1] = m;
			offset = 2;
		/*
		 * get at most one maximal record's input,
		 * with padding on the front for header and back for mac
		 */
		if(waserror()){
			qunlock(&out->ctlq);
			nexterror();
		}

		switch(s->state){
		case Sencrypting:
			nb = encryptb(s, nb, offset);
			break;
		case Sdigesting:
			nb = digestb(s, nb, offset);
			break;
		case Sdigenc:
			nb = digestb(s, nb, offset);
			nb = encryptb(s, nb, offset);
			break;
		qlock(&out->ctlq);
		maclen = 0;
		if(out->protected)
			maclen = tr->maclen;
		n = BLEN(bb);
		if(n > MaxRecLen){
			n = MaxRecLen;
			nb = allocb(n + RecHdrLen + maclen);
			memmove(nb->wp + RecHdrLen, bb->rp, n);
			nb->wp += n + RecHdrLen;
			bb->rp += n;
		}else{
			/*
			 * carefully reuse bb so it will get freed if we're out of memory
			 */
			bb = padblock(bb, RecHdrLen);
			if(maclen)
				nb = padblock(bb, -maclen);
			else
				nb = bb;
			bb = nil;
		}

		s->out.mid++;

		m = BLEN(nb);
		devtab[s->c->type]->bwrite(s->c, nb, s->c->offset);
		s->c->offset += m;
	}
	qunlock(&s->out.q);
	poperror();
		p = nb->rp;
		p[0] = type;
		put16(p+1, tr->version);
		put16(p+3, n);

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

static void
setsecret(OneWay *w, uchar *secret, int n)
{
	if(w->secret)
		free(w->secret);
			/* update length */
			put16(p+3, n);

	w->secret = smalloc(n);
	memmove(w->secret, secret, n);
	w->slen = n;
}
			/* encrypt */
			rc4(&out->rc4, p+RecHdrLen, n);
		}
		qunlock(&out->ctlq);
		poperror();

static void
initDESkey(OneWay *w)
{
	if(w->state){
		free(w->state);
		w->state = 0;
		/*
		 * if bwrite error's, we assume the block is queued.
		 * if not, we're out of sync with the receiver and will not recover.
		 */
		devtab[tr->c->type]->bwrite(tr->c, nb, 0);
	}

	w->state = smalloc(sizeof(DESstate));
	if(w->slen >= 16)
		setupDESstate(w->state, w->secret, w->secret+8);
	else if(w->slen >= 8)
		setupDESstate(w->state, w->secret, 0);
	else
		error("secret too short");
	qunlock(&out->q);
	poperror();
}

/*
 *  40 bit DES is the same as 56 bit DES.  However,
 *  16 bits of the key are masked to zero.
 */
static void
initDESkey_40(OneWay *w)
static long
tlsbwrite(Chan *c, Block *b, ulong offset)
{
	uchar key[8];
	TlsRec *tr;
	ulong n;

	if(w->state){
		free(w->state);
		w->state = 0;
	}
	n = BLEN(b);

	if(w->slen >= 8){
		memmove(key, w->secret, 8);
		key[0] &= 0x0f;
		key[2] &= 0x0f;
		key[4] &= 0x0f;
		key[6] &= 0x0f;
	}

	w->state = malloc(sizeof(DESstate));
	if(w->slen >= 16)
		setupDESstate(w->state, key, w->secret+8);
	else if(w->slen >= 8)
		setupDESstate(w->state, key, 0);
	else
		error("secret too short");
}
	tr = dstate[CONV(c->qid)];
	if(tr == nil)
		panic("tlsbread");

static void
initRC4key(OneWay *w)
{
	if(w->state){
		free(w->state);
		w->state = 0;
	switch(TYPE(c->qid)) {
	default:
		return devbwrite(c, b, offset);
	case Qhand:
//ZZZ race setting state
		if(tr->state != SHandshake && tr->state != SOpen)
			error(Ebadusefd);
		tlsrecwrite(tr, RHandshake, b);
		break;
	case Qdata:
//ZZZ race setting state
		if(tr->state != SOpen)
			error(Ebadusefd);
		tlsrecwrite(tr, RApplication, b);
		break;
	}

	w->state = smalloc(sizeof(RC4state));
	setupRC4state(w->state, w->secret, w->slen);
	return n;
}

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

	if(w->slen > 5)
		w->slen = 5;
	if(w->secret)
		free(w->secret);

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

/*


@@ 897,12 943,11 @@ struct Hashalg
Hashalg hashtab[] =
{
	{ "md5", MD5dlen, md5, },
	{ "sha1", SHA1dlen, sha1, },
	{ 0 }
};

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



@@ 927,34 972,14 @@ struct Encalg
	void	(*keyinit)(OneWay*);
};

#ifdef NOSPOOKS
Encalg encrypttab[] =
{
	{ "descbc", 8, DESCBC, initDESkey, },           /* DEPRECATED -- use des_56_cbc */
	{ "desecb", 8, DESECB, initDESkey, },           /* DEPRECATED -- use des_56_ecb */
	{ "des_56_cbc", 8, DESCBC, initDESkey, },
	{ "des_56_ecb", 8, DESECB, initDESkey, },
	{ "des_40_cbc", 8, DESCBC, initDESkey_40, },
	{ "des_40_ecb", 8, DESECB, initDESkey_40, },
	{ "rc4", 1, RC4, initRC4key_40, },              /* DEPRECATED -- use rc4_X      */
	{ "rc4_256", 1, RC4, initRC4key, },
	{ "rc4_128", 1, RC4, initRC4key_128, },
	{ "rc4_40", 1, RC4, initRC4key_40, },
	{ 0 }
};
#else
Encalg encrypttab[] =
{
	{ "des_40_cbc", 8, DESCBC, initDESkey_40, },
	{ "des_40_ecb", 8, DESECB, initDESkey_40, },
	{ "rc4", 1, RC4, initRC4key_40, },              /* DEPRECATED -- use rc4_X      */
	{ "rc4_40", 1, RC4, initRC4key_40, },
	{ 0 }
};
#endif NOSPOOKS

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



@@ 975,28 1000,25 @@ parseencryptalg(char *p, Dstate *s)
static long
tlswrite(Chan *c, void *a, long n, vlong off)
{
	Dstate *volatile s;
	TlsRec *volatile tr;
	Block *volatile b;
	int m, t;
	char *p, *np, *e, buf[128];
	uchar *x;
	ulong offset = off;

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

	t = TYPE(c->qid);
	if(t == Qdata){
		if(s->state == Sincomplete)
			error(Ebadusefd);

	if(t == Qdata || t == Qhand){
		p = a;
		e = p + n;
		do {
			m = e - p;
			if(m > s->max)
				m = s->max;
			if(m > MaxRecLen)
				m = MaxRecLen;

			b = allocb(m);
			if(waserror()){


@@ 1025,13 1047,7 @@ tlswrite(Chan *c, void *a, long n, vlong off)

	switch(t){
	default:
		panic("tlswrite");
	case Qsecretin:
		setsecret(&s->in, a, n);
		goto out;
	case Qsecretout:
		setsecret(&s->out, a, n);
		goto out;
		error(Ebadusefd);
	case Qctl:
		break;
	}


@@ 1048,13 1064,14 @@ tlswrite(Chan *c, void *a, long n, vlong off)
		*p++ = 0;

	if(strcmp(buf, "fd") == 0){
		if(s->c != nil)
			error(Einuse);
		s->c = buftochan(p);

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


@@ 1135,7 1152,7 @@ tlsinit(void)
	int n;
	char *cp;

	if((dstate = smalloc(sizeof(Dstate*) * maxdstate)) == 0)
	if((dstate = smalloc(sizeof(TlsRec*) * maxdstate)) == 0)
		panic("tlsinit");

	n = 1;


@@ 1168,7 1185,7 @@ tlsinit(void)
}

Dev tlsdevtab = {
	'D',
	'a',
	"tls",

	devreset,


@@ 1189,7 1206,7 @@ Dev tlsdevtab = {
};

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


@@ 1221,7 1238,7 @@ encryptb(Dstate *s, Block *b, int offset)
}

static Block*
decryptb(Dstate *s, Block *bin)
decryptb(TlsRec *s, Block *bin)
{
	Block *b, **l;
	uchar *p, *ep, *tp, *ip, *eip;


@@ 1273,7 1290,7 @@ decryptb(Dstate *s, Block *bin)
}

static Block*
digestb(Dstate *s, Block *b, int offset)
digestb(TlsRec *s, Block *b, int offset)
{
	uchar *p;
	DigestState ss;


@@ 1304,7 1321,7 @@ digestb(Dstate *s, Block *b, int offset)
}

static void
checkdigestb(Dstate *s, Block *bin)
checkdigestb(TlsRec *s, Block *bin)
{
	uchar *p;
	DigestState ss;


@@ 1362,7 1379,7 @@ buftochan(char *p)

/* hand up a digest connection */
static void
tlshangup(Dstate *s)
tlshangup(TlsRec *s)
{
	Block *b;



@@ 1371,18 1388,18 @@ tlshangup(Dstate *s)
		s->processed = b->next;
		freeb(b);
	}
	if(s->unprocessed){
	if(s->unprocessed != nil){
		freeb(s->unprocessed);
		s->unprocessed = 0;
		s->unprocessed = nil;
	}
	s->state = Sincomplete;
	qunlock(&s->in.q);
}

static Dstate*
static TlsRec*
dsclone(Chan *ch)
{
	Dstate **pp, **ep, **np;
	TlsRec **pp, **ep, **np;
	int newmax;

	if(waserror()) {


@@ 1392,7 1409,7 @@ dsclone(Chan *ch)
	lock(&dslock);
	ep = &dstate[maxdstate];
	for(pp = dstate; pp < ep; pp++) {
		if(*pp == 0) {
		if(*pp == nil) {
			dsnew(ch, pp);
			break;
		}


@@ 1401,18 1418,18 @@ dsclone(Chan *ch)
		if(maxdstate >= Maxdstate) {
			unlock(&dslock);
			poperror();
			return 0;
			return nil;
		}
		newmax = 2 * maxdstate;
		if(newmax > Maxdstate)
			newmax = Maxdstate;
		np = smalloc(sizeof(Dstate*) * newmax);
		if(np == 0)
		np = smalloc(sizeof(TlsRec*) * newmax);
		if(np == nil)
			error(Enomem);
		memmove(np, dstate, sizeof(Dstate*) * maxdstate);
		memmove(np, dstate, sizeof(TlsRec*) * maxdstate);
		dstate = np;
		pp = &dstate[maxdstate];
		memset(pp, 0, sizeof(Dstate*)*(newmax - maxdstate));
		memset(pp, 0, sizeof(TlsRec*)*(newmax - maxdstate));
		maxdstate = newmax;
		dsnew(ch, pp);
	}


@@ 1422,9 1439,9 @@ dsclone(Chan *ch)
}

static void
dsnew(Chan *ch, Dstate **pp)
dsnew(Chan *ch, TlsRec **pp)
{
	Dstate *s;
	TlsRec *s;
	int t;

	*pp = s = malloc(sizeof(*s));


@@ 1443,3 1460,38 @@ dsnew(Chan *ch, Dstate **pp)
	ch->qid.path = QID(pp - dstate, t);
	ch->qid.vers = 0;
}

static void
put32(uchar *p, u32int x)
{
	p[0] = x>>24;
	p[1] = x>>16;
	p[2] = x>>8;
	p[3] = x;
}

static void
put64(uchar *p, vlong x)
{
	put32(p, (u32int)(x >> 32));
	put32(p+4, (u32int)x);
}

static void
put16(uchar *p, int x)
{
	p[0] = x>>8;
	p[1] = x;
}

static u32int
get32(uchar *p)
{
	return (p[0]<<24)|(p[1]<<16)|(p[2]<<8)|p[3];
}

static int
get16(uchar *p)
{
	return (p[0]<<8)|p[1];
}

M port/parse.c => port/parse.c +8 -1
@@ 11,13 11,20 @@
Cmdbuf*
parsecmd(char *p, int n)
{
	Cmdbuf *cb;
	Cmdbuf *volatile cb;

	cb = smalloc(sizeof(*cb));
	
	if(n > sizeof(cb->buf)-1)
		n = sizeof(cb->buf)-1;

	if(waserror()){
		free(cb);
		nexterror();
	}
	memmove(cb->buf, p, n);
	poperror();

	if(n > 0 && cb->buf[n-1] == '\n')
		n--;
	cb->buf[n] = '\0';

M port/portclock.c => port/portclock.c +5 -5
@@ 69,18 69,18 @@ portclock(Ureg *ur)
		unlock(&clock0lock);
	}

	if(up == 0 || up->state != Running){
		m->inclockintr = 0;
	m->inclockintr = 0;
	if(up == 0 || up->state != Running)
		return;
	}

	if(anyready())
	if(anyready()){
		sched();
		splhi();
	}

	/* user profiling clock */
	if(userureg(ur)) {
		(*(ulong*)(USTKTOP-BY2WD)) += TK2MS(1);
		segclock(ur->pc);
	}
	m->inclockintr = 0;
}