~kris/9p

9hist

5fe022b3c50305980b4a2f7d377798d733f360bd — David du Colombier 25 years ago 5e14454
Plan 9 from Bell Labs 2001-04-04
6 files changed, 515 insertions(+), 72 deletions(-)

M bitsy/etherwavelan.c
M pc/etherga620.c
M pc/etherwavelan.c
M pc/mouse.c
A pc/vganvidia.c
M port/devtls.c
M bitsy/etherwavelan.c => bitsy/etherwavelan.c +10 -1
@@ 115,6 115,7 @@ enum
	WTyp_Keys	= 0xfcb0,
	WTyp_TxKey	= 0xfcb1,
	WTyp_CurName	= 0xfd41,
	WTyp_BaseID	= 0xfd42,	// ID of the currently connected-to base station
	WTyp_CurTxRate	= 0xfd44,	// Current TX rate
	WTyp_HasCrypt	= 0xfd4f,
};


@@ 940,8 941,16 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
	PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
	if(i == 3)
		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
	else
	else {
		Wltv ltv;
		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
		ltv.type = WTyp_BaseID;
		ltv.len = 4;
		if (w_inltv(ctlr, &ltv))
			print("#l%d: unable to read base station mac addr\n", ether->ctlrno);
		l += snprint(p+l, READSTR-l, "Base station: %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
			ltv.addr[0], ltv.addr[1], ltv.addr[2], ltv.addr[3], ltv.addr[4], ltv.addr[5]);
	}
	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
	if (ltv_ins(ctlr, WTyp_HasCrypt) == 0)

M pc/etherga620.c => pc/etherga620.c +3 -4
@@ 228,7 228,7 @@ enum {					/* Host/NIC Interface ring sizes */
};

enum {
	NrsrHI		= 256,		/* (WAS 128) Fill-level of Rsr (m.b. < Nrsr) */
	NrsrHI		= 128,		/* Fill-level of Rsr (m.b. < Nrsr) */
	NrsrLO		= 64,		/* Level at which to top-up ring */
	NrjrHI		= 0,		/* Fill-level of Rjr (m.b. < Nrjr) */
	NrjrLO		= 0,		/* Level at which to top-up ring */


@@ 665,11 665,11 @@ ga620init(Ether* edev)
	 * These defaults are based on the tuning hints in the Alteon
	 * Host/NIC Software Interface Definition and example software.
	 */
	csr32w(ctlr, Rct, 1000);	/* was 100 */
	csr32w(ctlr, Rct, 100);
	csr32w(ctlr, Sct, 0);
	csr32w(ctlr, St, 100000);
	csr32w(ctlr, SmcBD, Nsr/4);
	csr32w(ctlr, RmcBD, 100);	/* was 6 */
	csr32w(ctlr, RmcBD, 6);

	/*
	 * Enable DMA Assist Logic.


@@ 993,7 993,6 @@ ga620pnp(Ether* edev)
	edev->irq = ctlr->pcidev->intl;
	edev->tbdf = ctlr->pcidev->tbdf;
	edev->mbps = 1000;
	edev->oq = qopen(512*1024, 1, 0, 0);

	/*
	 * Check if the adapter's station address is to be overridden.

M pc/etherwavelan.c => pc/etherwavelan.c +4 -1
@@ 115,6 115,7 @@ enum
	WTyp_Keys	= 0xfcb0,
	WTyp_TxKey	= 0xfcb1,
	WTyp_CurName	= 0xfd41,
	WTyp_BaseID	= 0xfd42,	// ID of the currently connected-to base station
	WTyp_CurTxRate	= 0xfd44,	// Current TX rate
	WTyp_HasCrypt	= 0xfd4f,
};


@@ 940,8 941,10 @@ ifstat(Ether* ether, void* a, long n, ulong offset)
	PRINTSTAT("Promiscuous mode: %d\n", ltv_ins(ctlr, WTyp_Prom));
	if(i == 3)
		PRINTSTAT("SSID name: %s\n", ltv_inname(ctlr, WTyp_NetName));
	else
	else {
		PRINTSTAT("Current name: %s\n", ltv_inname(ctlr, WTyp_CurName));
//		PRINTSTAT("Base station: %s\n", ltv_inname(ctlr, WTyp_BaseID));
	}
	PRINTSTAT("Net name: %s\n", ltv_inname(ctlr, WTyp_WantName));
	PRINTSTAT("Node name: %s\n", ltv_inname(ctlr, WTyp_NodeName));
	if (ltv_ins(ctlr, WTyp_HasCrypt) == 0)

M pc/mouse.c => pc/mouse.c +16 -0
@@ 69,6 69,12 @@ serialmouse(int port, char *type, int setspeed)
 * Also on laptops with AccuPoint AND external mouse, the
 * controller may deliver 3 or 4 bytes according to the type
 * of the external mouse; code must adapt.
 *
 * On the NEC Versa series (and perhaps others?) we seem to
 * lose a byte from the packet every once in a while, which
 * means we lose where we are in the instruction stream.
 * To resynchronize, if we get a byte more than two seconds
 * after the previous byte, we assume it's the first in a packet.
 */
static void
ps2mouseputc(int c, int shift)


@@ 76,8 82,18 @@ ps2mouseputc(int c, int shift)
	static short msg[4];
	static int nb;
	static uchar b[] = {0, 1, 4, 5, 2, 3, 6, 7, 0, 1, 2, 3, 2, 3, 6, 7 };
	static ulong lasttick;
	ulong m;
	int buttons, dx, dy;

	/*
	 * Resynchronize in stream with timing; see comment above.
	 */
	m = MACHP(0)->ticks;
	if(TK2SEC(m - lasttick) > 2)
		nb = 0;
	lasttick = m;

	/* 
	 *  check byte 0 for consistency
	 */

A pc/vganvidia.c => pc/vganvidia.c +248 -0
@@ 0,0 1,248 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

enum {
	Pramin = 0x00710000,
	Pramdac = 0x00680000
};

enum {
	hwCurPos = Pramdac + 0x0300,
	hwCurImage = Pramin + (0x00010000 - 0x0800),
};

static ushort nvidiadid[] = {
	0x0020,		/* Riva TNT */
	0x0028,		/* Riva TNT2 */
	0x0029,		/* Riva TNT2 (Ultra)*/
	0x002C,		/* Riva TNT2 (Vanta) */
	0x002D,		/* Riva TNT2 M64 */
	0x00A0,		/* Riva TNT2 (Integrated) */
	0x0100,		/* GeForce 256 */
	0x0101,		/* GeForce DDR */
	0x0103,		/* Quadro */
	0x0110,		/* GeForce2 MX */
	0x0111,		/* GeForce2 MX DDR */
	0x0112,		/* GeForce 2 Go */
	0x0113,		/* Quadro 2 MXR */
	0x0150,		/* GeForce2 GTS */
	0x0151,		/* GeForce2 GTS (rev 1) */
	0x0152,		/* GeForce2 Ultra */
	0x0153,		/* Quadro 2 Pro */
	0,
};

static Pcidev*
nvidiapci(void)
{
	Pcidev *p;
	ushort *did;

	if((p = pcimatch(nil, 0x10DE, 0)) == nil)
		return nil;
	for(did = nvidiadid; *did; did++){
		if(*did == p->did)
			return p;
	}

	return nil;
}


static ulong
nvidialinear(VGAscr* scr, int* size, int* align)
{
	Pcidev *p;
	int oapsize, wasupamem;
	ulong aperture, oaperture;

	oaperture = scr->aperture;
	oapsize = scr->apsize;
	wasupamem = scr->isupamem;

	aperture = 0;
	if(p = nvidiapci()){
		aperture = p->mem[1].bar & ~0x0F;
		*size = p->mem[1].size;
	}

	if(wasupamem) {
		if(oaperture == aperture)
			return oaperture;
		upafree(oaperture, oapsize);
	}
	scr->isupamem = 0;

	aperture = upamalloc(aperture, *size, *align);
	if(aperture == 0){
		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
			aperture = oaperture;
			scr->isupamem = 1;
		}
		else
			scr->isupamem = 0;
	}
	else
		scr->isupamem = 1;

	return aperture;
}

static void
nvidiaenable(VGAscr* scr)
{
	Pcidev *p;
	Physseg seg;
	ulong aperture;
	int align, size;

	/*
	 * Only once, can't be disabled for now.
	 * scr->io holds the physical address of
	 * the MMIO registers.
	 */
	if(scr->io)
		return;
	p = nvidiapci();
	if(p == nil)
		return;

	scr->io = upamalloc(p->mem[0].bar & ~0x0F, p->mem[0].size, 0);
	if (scr->io == 0)
		return;

	memset(&seg, 0, sizeof(seg));
	seg.attr = SG_PHYSICAL;
	seg.name = smalloc(NAMELEN);
	snprint(seg.name, NAMELEN, "nvidiammio");
	seg.pa = scr->io;
	seg.size = p->mem[0].size;
	addphysseg(&seg);

	size = p->mem[1].size;
	align = 0;
	aperture = nvidialinear(scr, &size, &align);
	if(aperture) {
		scr->aperture = aperture;
		scr->apsize = size;
		memset(&seg, 0, sizeof(seg));
		seg.attr = SG_PHYSICAL;
		seg.name = smalloc(NAMELEN);
		snprint(seg.name, NAMELEN, "nvidiascreen");
		seg.pa = aperture;
		seg.size = size;
		addphysseg(&seg);
	}
}

static void
nvidiacurdisable(VGAscr* scr)
{
	if(scr->io == 0)
		return;

	vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01);
}

static void
nvidiacurload(VGAscr* scr, Cursor* curs)
{
	ulong*	p;
	int		i,j;
	ushort	c,s;
	ulong	tmp;

	if(scr->io == 0)
		return;

	vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) & ~0x01);

	p = KADDR(scr->io + hwCurImage);

	for (i=0; i<16; i++) {
		c = (curs->clr[2 * i] << 8) | curs->clr[2 * i+1];
		s = (curs->set[2 * i] << 8) | curs->set[2 * i+1];
		tmp = 0;
		for (j=0; j<16; j++) {
			if(s&0x8000)
				tmp |= 0x80000000;
			else if(c&0x8000)
				tmp |= 0xFFFF0000;
			if (j&0x1) {
				*p++ = tmp;
				tmp = 0;
			} else {
				tmp>>=16;
			}
			c<<=1;
			s<<=1;
		}
		for (j=0; j<8; j++)
			*p++ = 0;
	}
	for (i=0; i<256; i++)
		*p++ = 0;

	scr->offset = curs->offset;
	vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01);

	return;
}

static int
nvidiacurmove(VGAscr* scr, Point p)
{
	ulong*	cursorpos;

	if(scr->io == 0)
		return 1;

	cursorpos = KADDR(scr->io + hwCurPos);
	*cursorpos = ((p.y+scr->offset.y)<<16)|((p.x+scr->offset.x) & 0xFFFF);

	return 0;
}

static void
nvidiacurenable(VGAscr* scr)
{
	nvidiaenable(scr);
	if(scr->io == 0)
		return;

	vgaxo(Crtx, 0x1F, 0x57);

	nvidiacurload(scr, &arrow);
	nvidiacurmove(scr, ZP);

	vgaxo(Crtx, 0x31, vgaxi(Crtx, 0x31) | 0x01);
}

VGAdev vganvidiadev = {
	"nvidia",

	nvidiaenable,
	nil,
	nil,
	nvidialinear,
};

VGAcur vganvidiacur = {
	"nvidiahwgc",

	nvidiacurenable,
	nvidiacurdisable,
	nvidiacurload,
	nvidiacurmove,
};

M port/devtls.c => port/devtls.c +234 -66
@@ 115,6 115,67 @@ struct TlsRec
	int	perm;
};

typedef struct TlsErrs	TlsErrs;
struct TlsErrs{
	int	err;
	int	sslerr;
	int	tlserr;
	int	fatal;
	char	*msg;
};

static TlsErrs tlserrs[] = {
	{ECloseNotify,			ECloseNotify,			ECloseNotify,
		0, "remote close"},
	{EUnexpectedMessage,		EUnexpectedMessage,		EUnexpectedMessage,
		1, "unexpected message"},
	{EBadRecordMac,			EBadRecordMac,			EBadRecordMac,
		1, "bad record MAC"},
	{EDecryptionFailed,		EIllegalParameter,		EDecryptionFailed,
		1, "decryption failed"},
	{ERecordOverflow,		EIllegalParameter,		ERecordOverflow,
		1, "record too long"},
	{EDecompressionFailure,		EDecompressionFailure,		EDecompressionFailure,
		1, "decompression failed"},
	{EHandshakeFailure,		EHandshakeFailure,		EHandshakeFailure,
		1, "could not negotiate acceptable security paramters"},
	{ENoCertificate,		ENoCertificate,			ECertificateUnknown,
		1, "no appropriate certificate available"},
	{EBadCertificate,		EBadCertificate,		EBadCertificate,
		1, "corrupted or invalid certificate"},
	{EUnsupportedCertificate,	EUnsupportedCertificate,	EUnsupportedCertificate,
		1, "unsupported certificate type"},
	{ECertificateRevoked,		ECertificateRevoked,		ECertificateRevoked,
		1, "revoked certificate"},
	{ECertificateExpired,		ECertificateExpired,		ECertificateExpired,
		1, "expired certificate"},
	{ECertificateUnknown,		ECertificateUnknown,		ECertificateUnknown,
		1, "unacceptable certificate"},
	{EIllegalParameter,		EIllegalParameter,		EIllegalParameter,
		1, "illegal parameter"},
	{EUnknownCa,			EHandshakeFailure,		EUnknownCa,
		1, "unknown certificate authority"},
	{EAccessDenied,			EHandshakeFailure,		EAccessDenied,
		1, "access denied"},
	{EDecodeError,			EIllegalParameter,		EDecodeError,
		1, "error decoding message"},
	{EDecryptError,			EIllegalParameter,		EDecryptError,
		1, "error decrypting message"},
	{EExportRestriction,		EHandshakeFailure,		EExportRestriction,
		1, "export restriction violated"},
	{EProtocolVersion,		EIllegalParameter,		EProtocolVersion,
		1, "protocol version not supported"},
	{EInsufficientSecurity,		EHandshakeFailure,		EInsufficientSecurity,
		1, "stronger security routines required"},
	{EInternalError,		EHandshakeFailure,		EInternalError,
		1, "internal error"},
	{EUserCanceled,			ECloseNotify,		EUserCanceled,
		0, "handshake canceled by user"},
	{ENoRenegotiation,		EUnexpectedMessage,		ENoRenegotiation,
		0, "renegotiation not supported"},
	{-1},
};

static	Lock	dslock;
static	int	dshiwat;
static	int	maxdstate = 128;


@@ 124,6 185,7 @@ static	char	*hashalgs;

enum
{
//ZZZ
	Maxdmsg=	1<<16,
	Maxdstate=	64
};


@@ 159,9 221,11 @@ 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 void	tlsSetState(TlsRec *tr, int newstate);
static void	rcvAlert(TlsRec *tr, int err);
static void	sendAlert(TlsRec *tr, int err);
static void	rcvError(TlsRec *tr, int err, char *msg, ...);
#pragma	varargck	argpos	rcvError	3

static char *tlsnames[] = {
[Qclonus]	"clone",


@@ 373,7 437,8 @@ tlsopen(Chan *c, int omode)
			qlock(&tr->in.io);
			if(tr->handq != nil)
				error(Einuse);
			tr->handq = qopen(MaxRecLen, 0, nil, nil);
//ZZZ what is the correct buffering here?
			tr->handq = qopen(2 * MaxRecLen, 0, nil, nil);
			if(tr->handq == nil)
				error("can't allocate handshake queue");
			qunlock(&tr->in.io);


@@ 598,13 663,14 @@ tlsrecread(TlsRec *tr)
	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");
		rcvError(tr, EProtocolVersion, "invalid version in record layer");
	if(len <= 0)
		tlsError(tr, EIllegalParameter, "invalid length in record layer");
		rcvError(tr, EIllegalParameter, "invalid length in record layer");
	if(len > MaxRecLen)
		tlsError(tr, ERecordOverflow, "record message too long");
		rcvError(tr, ERecordOverflow, "record message too long");
	ensure(tr, &tr->unprocessed, len);
	nconsumed = 0;
	poperror();

	/*
	 * If an Eintr happens after this, we'll get out of sync.


@@ 616,7 682,7 @@ tlsrecread(TlsRec *tr)

	in = &tr->in;
	if(waserror()){
//ZZZ kill the connection
//ZZZ kill the connection?
		qunlock(&in->seclock);
		if(b != nil)
			freeb(b);


@@ 627,7 693,7 @@ tlsrecread(TlsRec *tr)
	p = b->rp;
	if(in->sec != nil) {
		if(len <= in->sec->maclen)
			tlsError(tr, EDecodeError, "record message too short for mac");
			rcvError(tr, EDecodeError, "record message too short for mac");
		rc4(&in->sec->rc4, p, len);
		len -= in->sec->maclen;



@@ 637,58 703,65 @@ tlsrecread(TlsRec *tr)
		in->seq++;
		(*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");
			rcvError(tr, EBadRecordMac, "record mac mismatch");
	}
	qunlock(&tr->in.seclock);
	qunlock(&in->seclock);
	poperror();
	if(len <= 0)
		tlsError(tr, EDecodeError, "runt record message");
		rcvError(tr, EDecodeError, "runt record message");

	switch(type) {
	default:
		tlsError(tr, EIllegalParameter, "invalid record message 0x%x", type);
		rcvError(tr, EIllegalParameter, "invalid record message 0x%x", type);
		return;
	case RChangeCipherSpec:
		if(len != 1 || p[0] != 1)
			tlsError(tr, EHandshakeFailure, "invalid change cipher spec");
			rcvError(tr, EDecodeError, "invalid change cipher spec");
		qlock(&in->seclock);
		if(in->new == nil){
			qunlock(&in->seclock);
			tlsError(tr, EUnexpectedMessage, "unexpected change cipher spec");
			rcvError(tr, EUnexpectedMessage, "unexpected change cipher spec");
		}
		free(tr->in.sec);
		tr->in.sec = tr->in.new;
		tr->in.new = nil;
		tr->in.seq = 0;
		free(in->sec);
		in->sec = in->new;
		in->new = nil;
		in->seq = 0;
		qunlock(&in->seclock);
		break;
	case RAlert:
		if(len != 2)
			tlsError(tr, EDecodeError, "invalid alert");
			rcvError(tr, EDecodeError, "invalid alert");
		if(p[0] == 1) {
			if(p[1] == ECloseNotify) {
				tlsError(tr, ECloseNotify, "remote close");
				rcvError(tr, ECloseNotify, "remote close");
				tlsSetState(tr, SRemoteClosed);
			}
			/*
			 * ignore EUserCancelled, it's meaningless
			 * need to handle ENoRenegotiation
			 */
		} else {
			tlsSetState(tr, SError);
			tlsAlert(tr, p[1]);
			rcvAlert(tr, p[1]);
		}
		break;
	case RHandshake:
		/*
		 * don't worry about dropping the block
		 * qbwrite always queue it even if flow controlled and interrupted.
		 * qbwrite always queues even if flow controlled and interrupted.
		 *
		 * if there isn't any handshaker, ignore the request,
		 * but notify the other side we are doing so.
		 */
		if(tr->handq != nil){
			qbwrite(tr->handq, b);
			b = nil;
		}
		}else if(tr->verset && tr->version != SSL3Version)
			sendAlert(tr, ENoRenegotiation);
		break;
	case RApplication:
//ZZZ race on state
		if(tr->state != SOpen)
			tlsError(tr, EUnexpectedMessage, "application message received before handshake completed");
			rcvError(tr, EUnexpectedMessage, "application message received before handshake completed");
		tr->processed = b;
		b = nil;
		break;


@@ 770,6 843,7 @@ tlsread(Chan *c, void *a, long n, vlong off)
		snprint(buf, sizeof(buf), "%lud", CONV(c->qid));
		return readstr(offset, a, n, buf);
	case Qdata:
	case Qhand:
		b = tlsbread(c, n, offset);
		break;
	case Qencalgs:


@@ 914,8 988,8 @@ tlsbwrite(Chan *c, Block *b, ulong offset)
		return devbwrite(c, b, offset);
	case Qhand:
//ZZZ race setting state
		if(tr->state != SHandshake && tr->state != SOpen)
			error(Ebadusefd);
//		if(tr->state != SHandshake && tr->state != SOpen)
//			error(Ebadusefd);
		tlsrecwrite(tr, RHandshake, b);
		break;
	case Qdata:


@@ 948,7 1022,7 @@ initmd5key(Hashalg *ha, int version, Secret *s, uchar *p)
	memmove(s->mackey, p, ha->maclen);
}

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


@@ 982,7 1056,7 @@ initRC4key(Encalg *ea, Secret *s, uchar *p, uchar *)
	setupRC4state(&s->rc4, p, ea->keylen);
}

Encalg encrypttab[] =
static Encalg encrypttab[] =
{
	{ "clear" },
	{ "rc4_128", 128/8, 0, initRC4key, },


@@ 1007,11 1081,12 @@ tlswrite(Chan *c, void *a, long n, vlong off)
	Encalg *ea;
	Hashalg *ha;
	TlsRec *volatile tr;
	Secret *tos, *toc;
	Block *volatile b;
	Cmdbuf *volatile cb;
	int m;
	char *p, *e, buf[128];
	uchar *x;
	char *p, *e;
	uchar *volatile x;
	ulong offset = off;

	tr = dstate[CONV(c->qid)];


@@ 1049,7 1124,7 @@ tlswrite(Chan *c, void *a, long n, vlong off)
		return -1;
	}

	cb = parsecmd(buf, n);
	cb = parsecmd(a, n);
	if(waserror()){
		free(cb);
		nexterror();


@@ 1072,11 1147,12 @@ tlswrite(Chan *c, void *a, long n, vlong off)
			error("usage: fd n version");
		if(tr->c != nil)
			error(Einuse);
		n = strtol(cb->f[2], nil, 0);
		if(n < MinProtoVersion || n > MinProtoVersion)
		m = strtol(cb->f[2], nil, 0);
		if(m < MinProtoVersion || m > MaxProtoVersion)
			error("unsupported version");
		tr->c = buftochan(cb->f[1]);
		tr->version = n;
		tr->version = m;
		tlsSetState(tr, SHandshake);
	}else if(strcmp(cb->f[0], "version") == 0){
		if(cb->nf != 2)
			error("usage: version n");


@@ 1084,49 1160,63 @@ tlswrite(Chan *c, void *a, long n, vlong off)
			error("must set fd before version");
		if(tr->verset)
			error("version already set");
		n = strtol(cb->f[1], nil, 0);
		if(n == SSL3Version)
		m = strtol(cb->f[1], nil, 0);
		if(m == SSL3Version)
			tr->packMac = sslPackMac;
		else if(n == TLSVersion)
		else if(m == TLSVersion)
			tr->packMac = tlsPackMac;
		else
			error("unsupported version");
		tr->verset = 1;
		tr->version = n;
		tr->version = m;
	}else if(strcmp(cb->f[0], "opened") == 0){
		if(cb->nf != 1)
			error("usage: opened");
		tlsSetState(tr, SOpen);
	}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");
		m = strtol(cb->f[1], nil, 0);

		qunlock(&tr->in.seclock);
		qunlock(&tr->out.seclock);
		poperror();
		free(cb);
		poperror();

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

		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);
		return n;
	}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");

		toc = tr->out.new;
		tr->out.new = nil;

//ZZZ minor race; worth fixing?
		qunlock(&tr->in.seclock);
		qunlock(&tr->out.seclock);
		poperror();
		free(cb);
		poperror();
	
		b = allocb(1);
		*b->wp++ = 1;
		tlsrecwrite(tr, RChangeCipherSpec, b);
		freeb(b);

		qlock(&tr->out.seclock);
		free(tr->out.sec);
		tr->out.sec = tr->out.new;
		tr->out.new = nil;
		tr->out.sec = toc;
		qunlock(&tr->out.seclock);
		return n;
	}else if(strcmp(cb->f[0], "secret") == 0){
		if(cb->nf != 4)
			error("usage: secret hashalg encalg secretdata");
		if(cb->nf != 5)
			error("usage: secret hashalg encalg isclient secretdata");
		if(tr->c == nil || !tr->verset)
			error("must set fd and version before secrets");



@@ 1142,25 1232,33 @@ tlswrite(Chan *c, void *a, long n, vlong off)
		ha = parsehashalg(cb->f[1]);
		ea = parseencalg(cb->f[2]);

		m = (strlen(cb->f[3])*3)/2;
		p = cb->f[4];
		m = (strlen(p)*3)/2;
		x = smalloc(m);
		if(waserror()){
			free(x);
			poperror();
			nexterror();
		}
		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");
		m = dec64(x, m, p, strlen(p));
		if(m < 2 * ha->maclen + 2 * ea->keylen + 2 * ea->ivlen)
			error("not enough secret data provided");

		tr->out.new = smalloc(sizeof(Secret));
		tr->in.new = smalloc(sizeof(Secret));
		tos = smalloc(sizeof(Secret));
		toc = 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]);
			(*ha->initkey)(ha, tr->version, tos, &x[0]);
			(*ha->initkey)(ha, tr->version, toc, &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]);
			(*ea->initkey)(ea, tos, &x[2 * ha->maclen], &x[2 * ha->maclen + 2 * ea->keylen]);
			(*ea->initkey)(ea, toc, &x[2 * ha->maclen + ea->keylen], &x[2 * ha->maclen + 2 * ea->keylen + ea->ivlen]);
		}
		if(strtol(cb->f[3], nil, 0) == 0){
			tr->in.new = tos;
			tr->out.new = toc;
		}else{
			tr->in.new = toc;
			tr->out.new = tos;
		}

		free(x);


@@ 1254,6 1352,76 @@ buftochan(char *p)
	return c;
}

static void
sendAlert(TlsRec *tr, int err)
{
	Block *b;
	int i, fatal;

	fatal = 1;
	for(i=0; i < nelem(tlserrs); i++) {
		if(tlserrs[i].err == err) {
			if(tr->version == SSL3Version)
				err = tlserrs[i].sslerr;
			else
				err = tlserrs[i].tlserr;
			fatal = tlserrs[i].fatal;
			break;
		}
	}

	b = allocb(2);
	*b->wp++ = fatal + 1;
	*b->wp++ = err;
	tlsrecwrite(tr, RAlert, b);
//ZZZ race on state
	if(fatal)
		tlsSetState(tr, SError);
}

static void
rcvAlert(TlsRec *tr, int err)
{
	char *s;
	int i, fatal;

	s = "unknown error";
	fatal = 1;
	for(i=0; i < nelem(tlserrs); i++){
		if(tlserrs[i].err == err){
			s = tlserrs[i].msg;
			fatal = tlserrs[i].fatal;
			break;
		}
	}
//ZZZ need to kill session if fatal error
	if(fatal)
		tlsSetState(tr, SError);
	error(s);
}

static void
rcvError(TlsRec *tr, int err, char *fmt, ...)
{
	char msg[ERRLEN];
	va_list arg;

	sendAlert(tr, err);
	va_start(arg, fmt);
	strcpy(msg, "tls local %s");
	doprint(strchr(msg, '\0'), msg+sizeof(msg), fmt, arg);
	va_end(arg);
	error(msg);
}

static void
tlsSetState(TlsRec *tr, int newstate)
{
	lock(&tr->statelk);
	tr->state = newstate;
	unlock(&tr->statelk);
}

/* hand up a digest connection */
static void
tlshangup(TlsRec *s)