shithub: rd

Download patch

ref: df634b2ca4bd940a61946a3bbda3881bed1a09d6
parent: baf602582a840a0253900d41a2f1cce7156dd770
author: Yaroslav Kolomiiets <yarikos@gmail.com>
date: Mon Mar 21 16:08:44 EDT 2016

* parsing and processing is largely separate
* most globals went into Rdp* context
* some functions merged
* some put/gets lined up to an extent to feel symmerical
* some cosmetic renames
* other minor tweaks

--- /dev/null
+++ b/README.md
@@ -1,0 +1,3 @@
+# README #
+
+*Rd* is a remote desktop client for Plan 9 operating system implementing RDP, the Microsoft's Remote Desktop Protocol. Initially started as a feature-cut fork of the X11 *rdesktop* client, it eventually grew into something different. It does not support pre-RDP5 protocol versions and requires the server to support an equivalent of *STARTTLS* (Windows XP SP2/2003 and up). The X.509 certs presented by servers are checked with `okThumbprint` against the list kept in `/sys/lib/tls/rdp` file.
\ No newline at end of file
--- a/cap.c
+++ b/cap.c
@@ -4,81 +4,162 @@
 #include "dat.h"
 #include "fns.h"
 
-/* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
-void
-scangencaps(uchar* p, uchar* ep)
+enum /* 2.2.7 Capability Sets; T.128 */
 {
-	int extraFlags, canrefresh, cansupress;
+	CapGeneral=	1,
+	CapBitmap=	2,
+	CapOrder=	3,
+	CapPointer=	8,
+	CapBitcache2=	19,
+	CapInput=	13,
+	CapSound=	12,
+	CapGlyph=	16,
 
-	if(p+22>ep)
-		sysfatal(Eshort);
-	extraFlags  = GSHORT(p+14);
-	USED(extraFlags);
-	canrefresh = p[22];
-	cansupress = p[23];
-	if(!canrefresh)
-		sysfatal("server lacks support for Refresh Rect PDU");
-	if(!cansupress)
-		sysfatal("server lacks support for Suppress Output PDU");
-}
+	/* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
+	CanFastpath=	0x0001,
+	NoBitcomphdr=	0x0400,
+	CanLongcred=	0x0004,
+};
 
-/* 2.2.7.1.2 Bitmap Capability Set (TS_BITMAP_CAPABILITYSET) */
-void
-scanbitcaps(uchar* p, uchar* ep)
+static int	putncap(uchar*,uint,Caps*);
+static int	putbitcaps(uchar*,uint,Caps*);
+static int	putgencaps(uchar*,uint,Caps*);
+static int	putordcaps(uchar*,uint,Caps*);
+static int	putbc2caps(uchar*,uint,Caps*);
+static int	putptrcaps(uchar*,uint,Caps*);
+static int	putinpcaps(uchar*,uint,Caps*);
+static int	putsndcaps(uchar*,uint,Caps*);
+static int	putglycaps(uchar*,uint,Caps*);
+
+static
+struct {
+	int	size;
+	int	(*putcap)(uchar*,uint,Caps*);
+} ctab[]=
 {
-	int w, h, depth;
+	{ 4,	putncap },
+	{ 24,	putgencaps },
+	{ 30,	putbitcaps },
+	{ 88,	putordcaps },
+	{ 40,	putbc2caps },
+	{ 8,	putptrcaps },
+	{ 88,	putinpcaps },
+	{ 8,	putsndcaps },
+	{ 52,	putglycaps },
+};
 
-	if(p+16> ep)
-		sysfatal(Eshort);
-	depth = GSHORT(p+4);
-	w = GSHORT(p+12);
-	h = GSHORT(p+14);
+int
+getcaps(Caps* caps, uchar* a, uint nb)
+{
+	int ncap, type, len;
+	uchar *p, *ep;
+	int extraFlags;
 
-	if(depth != rd.depth){
-		rd.depth = depth;
-		switch(depth){
-		case 8:
-			rd.chan = CMAP8;
+	p = a;
+	ep = p+nb;
+	memset(caps, sizeof(*caps), 0);
+
+	ncap = GSHORT(p);
+	p += 4;
+	for(; ncap>0 && p+4<ep; ncap--){
+		type = GSHORT(p+0);
+		len = GSHORT(p+2);
+		if(p+len > ep){
+			werrstr("bad length in server's capability set");
+			return -1;
+		}
+		switch(type){
+		case CapGeneral:
+			/* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
+			if(len < 24){
+				werrstr(Eshort);
+				return -1;
+			}
+			caps->general = 1;
+			extraFlags  = GSHORT(p+14);
+			caps->canrefresh = p[22];
+			caps->cansupress = p[23];
+			USED(extraFlags);
 			break;
-		case 15:
-			rd.chan = RGB15;
+		case CapBitmap:
+			/* 2.2.7.1.2 Bitmap Capability Set (TS_BITMAP_CAPABILITYSET) */
+			if(len < 16){
+				werrstr(Eshort);
+				return -1;
+			}
+			caps->bitmap = 1;
+			caps->depth = GSHORT(p+4);
+			caps->width = GSHORT(p+12);
+			caps->height = GSHORT(p+14);
 			break;
-		case 16:
-			rd.chan = RGB16;
-			break;
-		case 24:
-			rd.chan = RGB24;
-			break;
-		case 32:
-			rd.chan = XRGB32;
-			break;
-		default:
-			sysfatal("Unsupported server color depth: %uhd\n", depth);
 		}
+		p += len;
 	}
-	if(w != rd.dim.x || h != rd.dim.y){
-		rd.dim.x = w;
-		rd.dim.y = h;
-		rd.dim.x = (rd.dim.x + 3) & ~3;	/* ensure width divides by 4 */
+	return p-a;
+}
+
+int
+sizecaps(Caps*)
+{
+	int i, n;
+
+	n = 0;
+	for(i = 0; i < nelem(ctab); i++)
+		n += ctab[i].size;
+	return n;
+}
+
+int
+putcaps(uchar* a, uint nb, Caps* caps)
+{
+	uchar *p, *ep;
+	int i, n;
+
+	p = a;
+	ep = a+nb;
+
+	for(i = 0; i < nelem(ctab); i++){
+		n = ctab[i].putcap(p, ep-p, caps);
+		if(n < 0)
+			return -1;
+		p += n;
 	}
+	return p-a;
 }
 
+static int
+putncap(uchar *p, uint nb, Caps*)
+{
+	int ncap;
+	
+	ncap = 8;
+	
+	if(nb<4){
+		werrstr(Eshort);
+		return -1;
+	}
+	PSHORT(p, ncap);
+	PSHORT(p+2, 0);
+	return 4;
+}
+
 /* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
-uchar*
-putgencaps(uchar *p, uchar *ep)
+static int
+putgencaps(uchar *p, uint nb, Caps*)
 {
 	int extraFlags;
-
+	
 	extraFlags = 0
 		| CanFastpath
 		| NoBitcomphdr
 		| CanLongcred
-	;
-
-	if(p+24>ep)
-		sysfatal(Eshort);
+		;
+	if(nb<24){
+		werrstr(Eshort);
+		return -1;
+	}
 	PSHORT(p+0, CapGeneral);
-	PSHORT(p+2, GENCAPSIZE);
+	PSHORT(p+2, 24);	// size
 	PSHORT(p+4, 0);	// OSMAJORTYPE_UNSPECIFIED
 	PSHORT(p+6, 0);	// OSMINORTYPE_UNSPECIFIED
 	PSHORT(p+8, 0x200);	// TS_CAPS_PROTOCOLVERSION
@@ -89,24 +170,26 @@
 	PSHORT(p+20, 0);	// generalCompressionLevel
 	p[22] = 0;  	// refreshRectSupport - server only
 	p[23] = 0;  	// suppressOutputSupport - server only
-	return p+24;
+	return 24;
 }
 
 
 /* 2.2.7.1.2 Bitmap Capability Set (TS_BITMAP_CAPABILITYSET) */
-uchar*
-putbitcaps(uchar *p, uchar *ep)
+static int
+putbitcaps(uchar *p, uint nb, Caps* caps)
 {
-	if(p+30>ep)
-		sysfatal(Eshort);
+	if(nb < 30){
+		werrstr(Eshort);
+		return -1;
+	}
 	PSHORT(p+0, CapBitmap);
-	PSHORT(p+2, BITCAPSIZE);
-	PSHORT(p+4, rd.depth);	// preferredBitsPerPixel
+	PSHORT(p+2, 30);	// size
+	PSHORT(p+4, caps->depth);	// preferredBitsPerPixel
 	PSHORT(p+6, 1);	// receive1BitPerPixel
 	PSHORT(p+8, 1);	// receive4BitsPerPixel
 	PSHORT(p+10, 1);	// receive8BitsPerPixel
-	PSHORT(p+12, rd.dim.x);	// desktopWidth
-	PSHORT(p+14, rd.dim.y);	// desktopHeight
+	PSHORT(p+12, caps->xsz);	// desktopWidth
+	PSHORT(p+14, caps->ysz);	// desktopHeight
 	PSHORT(p+16, 0);	// pad2octets 
 	PSHORT(p+18, 1);	// desktopResizeFlag 
 	PSHORT(p+20, 1);	// bitmapCompressionFlag 
@@ -114,12 +197,12 @@
 	PSHORT(p+24, 1);	// drawingFlags 
 	PSHORT(p+26, 1);	// multipleRectangleSupport
 	PSHORT(p+26, 0);	// pad2octetsB
-	return p+30;
+	return 30;
 }
 
 /* 2.2.7.1.3 Order Capability Set (TS_ORDER_CAPABILITYSET) */
-uchar*
-putordcaps(uchar *p, uchar *ep)
+static int
+putordcaps(uchar *p, uint nb, Caps*)
 {
 	ushort orderFlags;
 	enum
@@ -135,11 +218,13 @@
 		| ZEROBOUNDSDELTASSUPPORT
 		| COLORINDEXSUPPORT
 		| SOLIDPATTERNBRUSHONLY
-	;
-	if(p+88>ep)
-		sysfatal(Eshort);
+		;
+	if(nb<88){
+		werrstr(Eshort);
+		return -1;
+	}
 	PSHORT(p+0, CapOrder);
-	PSHORT(p+2, ORDCAPSIZE);
+	PSHORT(p+2, 88);	// size
 	memset(p+4, 16, 0);	// terminalDescriptor
 	PLONG(p+20, 0);	// pad4octetsA 
 	PSHORT(p+24, 1);	// desktopSaveXGranularity 
@@ -157,18 +242,20 @@
 	PSHORT(p+82, 0);	// pad2octetsD
 	PSHORT(p+84, 0xe4);	// textANSICodePage
 	PSHORT(p+86, 0x04);	// pad2octetsE
-	return p+88;
+	return 88;
 }
 
 /* 2.2.7.1.4 Bitmap Cache Capability Set (TS_BITMAPCACHE_CAPABILITYSET) */
 /* 2.2.7.1.4.2 Revision 2 (TS_BITMAPCACHE_CAPABILITYSET_REV2) */
-uchar*
-putbc2caps(uchar *p, uchar *ep)
+static int
+putbc2caps(uchar *p, uint nb, Caps*)
 {
-	if(p+40>ep)
-		sysfatal(Eshort);
+	if(nb<40){
+		werrstr(Eshort);
+		return -1;
+	}
 	PSHORT(p+0, CapBitcache2);
-	PSHORT(p+2, BCACAPSIZE);
+	PSHORT(p+2, 40);	// size
 	PSHORT(p+4, 0);	// CacheFlags (2 bytes):  
 	p[6] = 0;	// pad2
 	p[7] = 3;	// NumCellCaches
@@ -178,25 +265,27 @@
 	PLONG(p+20, 0);	// BitmapCache3CellInfo
 	PLONG(p+24, 0);	// BitmapCache4CellInfo
 	memset(p+28, 12, 0); // Pad3
-	return p+40;
+	return 40;
 }
 
 /* 2.2.7.1.5 Pointer Capability Set (TS_POINTER_CAPABILITYSET) */
-uchar*
-putptrcaps(uchar *p, uchar *ep)
+static int
+putptrcaps(uchar *p, uint nb, Caps*)
 {
-	if(p+8>ep)
-		sysfatal(Eshort);
+	if(nb<8){
+		werrstr(Eshort);
+		return -1;
+	}
 	PSHORT(p+0, CapPointer);
-	PSHORT(p+2, PTRCAPSIZE);
+	PSHORT(p+2, 8);	// size
 	PSHORT(p+4, 0);	// colorPointerFlag  
 	PSHORT(p+6, 20);	// colorPointerCacheSize 
-	return p+8;
+	return 8;
 }
 
 /* 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */
-uchar*
-putinpcaps(uchar *p, uchar *ep)
+static int
+putinpcaps(uchar *p, uint nb, Caps*)
 {
 	long inputFlags;
 	enum
@@ -209,14 +298,15 @@
 	};
 
 	inputFlags = 0
-	| INPUT_FLAG_SCANCODES
-	| INPUT_FLAG_UNICODE
-	;
-
-	if(p+88>ep)
-		sysfatal(Eshort);
+		| INPUT_FLAG_SCANCODES
+		| INPUT_FLAG_UNICODE
+		;
+	if(nb<88){
+		werrstr(Eshort);
+		return -1;
+	}
 	PSHORT(p+0, CapInput);
-	PSHORT(p+2, INPCAPSIZE);
+	PSHORT(p+2, 88);	// size
 	PSHORT(p+4, inputFlags);	// inputFlags
 	PSHORT(p+6, 0);	// pad2octetsA
 
@@ -226,21 +316,23 @@
 	PLONG(p+16, 0);	// keyboardSubType
 	PLONG(p+20, 12);	// keyboardFunctionKey
 	memset(p+24, 64, 0);	// imeFileName
-	return p+88;
+	return 88;
 }
 
 /* 2.2.7.1.8 Glyph Cache Capability Set (TS_GLYPHCACHE_CAPABILITYSET) */
-uchar*
-putglycaps(uchar* p, uchar* ep)
+static int
+putglycaps(uchar *p, uint nb, Caps*)
 {
 	enum {
 		GLYPH_SUPPORT_NONE= 0,
 	};
 
-	if(p+52>ep)
-		sysfatal(Eshort);
+	if(nb<52){
+		werrstr(Eshort);
+		return -1;
+	}
 	PSHORT(p+0, CapGlyph);
-	PSHORT(p+2, GLYCAPSIZE);
+	PSHORT(p+2, 52);	// size
 	PLONG(p+4, 0x0400fe);	// GlyphCache 0
 	PLONG(p+8, 0x0400fe);	// GlyphCache 1
 	PLONG(p+12, 0x0800fe);	// GlyphCache 2
@@ -254,18 +346,20 @@
 	PLONG(p+44, 0x01000100);	// FragCache 
 	PSHORT(p+48, GLYPH_SUPPORT_NONE);	// GlyphSupportLevel
 	PSHORT(p+50, 0);	// pad2octets 
-	return p+52;
+	return 52;
 }
 
 /* 2.2.7.1.11 Sound Capability Set (TS_SOUND_CAPABILITYSET) */
-uchar*
-putsndcaps(uchar* p, uchar* ep)
+static int
+putsndcaps(uchar *p, uint nb, Caps*)
 {
-	if(p+8>ep)
-		sysfatal(Eshort);
+	if(nb<8){
+		werrstr(Eshort);
+		return -1;
+	}
 	PSHORT(p+0, CapSound);
-	PSHORT(p+2, SNDCAPSIZE);
+	PSHORT(p+2, 8);	// size
 	PSHORT(p+4, 0);	// soundFlags
 	PSHORT(p+6, 0);	// pad2octetsA
-	return p+8;
+	return 8;
 }
--- a/dat.h
+++ b/dat.h
@@ -1,6 +1,9 @@
 typedef	struct	Rdp Rdp;
 typedef	struct	Vchan Vchan;
-typedef	struct	Rdpnego Rdpnego;
+typedef	struct	Msg Msg;
+typedef	struct	Share Share;
+typedef	struct	Caps Caps;
+typedef	struct	Imgupd Imgupd;
 
 #define	GSHORT(p)	((p)[0]|((p)[1]<<8))
 #define	GSHORTB(p)	((p)[0]<<8|((p)[1]))
@@ -13,71 +16,237 @@
 
 #define	MIN(x,y)		(((x) < (y)) ? (x) : (y))
 
+enum
+{
+	MAXTPDU=	16386,	/* max TPDU size */
+};
+
 struct Rdp
 {
 	int		fd;			/* connection I/O descriptor */
-	long		sproto;		/* server-selected security protocol to replay back */
-	int		autologon;	/* enable auto logon */
+	long		sproto;		/* magic to bounce back to server */
 	char		*label;		/* window label */
 	char		*local;		/* local system name */
-	char		*user;		/* user name */
+	char		*user;		/* user name for auto logon  */
 	char		*windom;		/* domain for auto logon */
 	char		*passwd;		/* password for auto logon (sic) */
 	char		*shell;		/* remote shell override */
 	char		*rwd;		/* remote working directory */
+	int		xsz;			/* rfb dimensions */
+	int		ysz;			/* rfb dimensions */
+	int		depth;		/* rfb color depth */
 	ulong	chan;		/* remote graphics channel descriptor */
-	int		depth;		/* color depth as exposed by the protocol */
 	int		hupreason;	/* hangup reason as server explains */
 	int		mcsuid;		/* MCS [T.122] userId */
 	int		userchan;		/* MCS user channelId */
+	int		srvchan;		/* MCS server channel ID */
 	int		shareid;		/* share ID - [T128] section 8.4.2 */
-	int		licensed;		/* licensing sub-protocol completion */
 	int		active;		/* T.128 action state */
 	int		wantconsole;	/* attach to the console sesstion */
-	Point		dim;			/* rfb size */
+	Vchan	*vc;			/* static virtual channels table */
+	uint		nvc;			/* number of vctab entries */
+	uchar	cmap[256];	/* rfb color map for depths ≤ 8 */
+	uchar	rbuf[MAXTPDU];	/* read buffer */
 };
+int	starttls(Rdp*);
+int	rdphandshake(Rdp*);
+int	x224connect(Rdp*);
+int	x224disconnect(Rdp*);
+void	sendclientinfo(Rdp*);
+void	confirmactive(Rdp*);
+void	assync(Rdp*);
+void	asctl(Rdp*,int);
+void	asfontls(Rdp*);
+void	passinput(Rdp*,ulong,int,int,int,int);
+void	turnupdates(Rdp*, int);
+void	erectdom(Rdp*);
+void	readnet(Rdp*);
+void	clipannounce(Rdp*);
+void	clipvcfn(Rdp*, uchar*,uint);
+void	pollsnarf(Rdp*);
 
+void	readkbd(Rdp*);
+void	readdevmouse(Rdp*);
+void	eresized(Rdp*, int);
+
+
 struct Vchan
 {
 	int		mcsid;		/* MCS channelId */
 	int		flags;
 	char		name[8];
-	int		defragging;
-	uchar*	buf;			/* defragmentation buffer */
+	uchar	*buf;			/* defragmentation buffer */
 	int		nb;			/* sizeof buf */
 	int		pos;			/* next fragment offset */ 
-	void		(*fn)(uchar*,uchar*);
+	void		(*fn)(Rdp*,uchar*,uint);
 };
-Vchan*	lookupvc(int);
-Vchan*	namevc(char*);
-int		sendvc(char*,uchar*,int);
-void		scanvcpdu(uchar*,uchar*,int);
+void		initvc(Rdp*);
+int		sendvc(Rdp*,char*,uchar*,int);
 
-enum 
+struct Caps
 {
-	/* 2.2.7 Capability Sets; T.128 */
-	CapGeneral=	1,
-	CapBitmap=	2,
-	CapOrder=	3,
-	CapPointer=	8,
-	CapBitcache2=	19,
-	CapInput=	13,
-	CapSound=	12,
-	CapGlyph=	16,
-	GENCAPSIZE=	24,
-	BITCAPSIZE=	30,
-	ORDCAPSIZE=	88,
-	BCACAPSIZE=	40,
-	PTRCAPSIZE=	8,
-	INPCAPSIZE=	88,
-	SNDCAPSIZE=	8,
-	GLYCAPSIZE=	52,
+	int	general;
+	int	canrefresh;
+	int	cansupress;
 
-	/* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
-	CanFastpath	= 0x0001,
-	NoBitcomphdr	= 0x0400,
-	CanLongcred	= 0x0004,
+	int	bitmap;
+	int	depth;
+	int	xsz;
+	int	ysz;
+};
+int	getcaps(Caps*,uchar*,uint);
+int	sizecaps(Caps*);
+int	putcaps(uchar*,uint,Caps*);
 
+enum /* Msg.type */
+{
+	Xconnect=1, 	/* C: X.224 connect request */
+	Xconnected,	/* S: X.224 connection confirm */
+	Xhangup,  	/* C: X.224 disconnection request */
+	Mattach,   	/* C: MCS (T.125) Attach User Request */
+	Mattached,	/* S: MCS Attach User Confirm */
+	Mjoin,	/* C: MCS Channel Join Request */
+	Mjoined,		/* S: MCS Channel Join Confirm */
+	Merectdom,	/* C: MCS Erect Domain Request */
+	Mconnect,	/* C: MCS Connect Initial + GCC Confce Create Req */
+	Mconnected,	/* S: MCS Connect Response + GCC Confce Create Resp */
+	Mactivated,	/* C: MCS Confirm Active */
+	Mclosing,		/* S: Disconnect Provider Ultimatum */
+	Aflow,		/* S: T.128 Flow PDU */
+	Async,		/* C: MPAS 2.2.1.14 Client Synchronize PDU */
+	Actl,			/* C: MPAS 2.2.1.15 Control PDU */
+	Afontls,		/* C: MPAS 2.2.1.18 Client Font List PDU */
+	Ainput,      	/* C: MPAS (T.128) Input Event */
+	Dclientinfo,	/* C: RDP 2.2.1.11 Client Info */
+	Dsupress,		/* C: RDP 2.2.11.3 Suppress Output PDU */
+	Lneedlicense,	/* S: Licensing PDU */
+	Lreq,			/* C: Licensing PDU */
+	Lhavechal,	/* S: Licensing PDU */
+	Lnolicense,	/* C: Licensing PDU */
+	Ldone,		/* S: Licensing PDU */
+	Aupdate,		/* S: T.128 ASPDU or a "Fast-Path" RDP PDU */
+	Mvcdata,		/* S,C: MCS virtual channel data, raw */
+};
+
+enum /* Msg.negproto - for msg.c x224.c */
+{
+	ProtoTLS= 	1,
+	ProtoCSSP=	2,
+	ProtoUAUTH=	8,
+};
+
+struct Msg {
+	int	type;
+	int	negproto;	/* Xconnect, Xconnected */
+	int	mcsuid;	/* Mattached, Mjoin, Mactivated, Async & more */
+	int	chanid;	/* Mjoin, Mvcdata */
+	int	originid;	/* Mactivated, Async, Actl, Afontls, Ainput, Dsupress */
+	int	shareid;	/* Mactivated, Async, Actl, Afontls, Ainput, Dsupress */
+	int	ver;	/* Mconnect, Mconnected */
+	int	xsz;	/* Mconnect, Dsupress, Mactivated */
+	int	ysz;	/* Mconnect, Dsupress, Mactivated */
+	int	height;	/* Mconnect, Dsupress, Mactivated */
+	char	*sysname;	/* Mconnect, Dclientinfo, Lreq */
+	int	sproto;	/* Mconnect */
+	int	wantconsole;	/* Mconnect */
+	uint	nvc;	/* Mconnect */
+	Vchan	*vctab;	/* Mconnect */
+	char	*dom;	/* Dclientinfo*/
+	char	*user;	/* Dclientinfo, Lreq */
+	char	*pass;	/* Dclientinfo */
+	char	*rshell;	/* Dclientinfo */
+	char	*rwd;	/* Dclientinfo */
+	int	dologin;	/* Dclientinfo */
+	int	mtype;	/* Ainput */
+	ulong	msec;	/* Ainput */
+	ulong	flags;	/* Ainput, Mvcdata */
+	int	iarg1;	/* Ainput */
+	int	iarg2;	/* Ainput */
+	int	action;	/* Actl */
+	int	allow;	/* Dsupress */
+	uchar*	data;	/* Mvcdata, Aupdate */
+	uint	ndata;	/* Mvcdata, Aupdate */
+	uint	len;	/* Mvcdata */
+	int	(*getshare)(Share*, uchar*, uint);	/* Aupdate */
+};
+
+typedef	int	Msgget(Msg*,uchar*,uint);
+typedef	int	Msgput(uchar*,uint,Msg*);
+
+Msgget	getmsg;
+Msgput	putmsg;
+int	readmsg(Rdp*,Msg*);
+int	writemsg(Rdp*,Msg*);
+
+int	sizegccr(Msg*);
+Msgget	getmcr;
+Msgput	putgccr;
+Msgput	putclientinfo;
+Msgput	putconfirmactive;
+
+int	sizelicensemsg(Msg*);
+Msgget	getlicensemsg;
+Msgput	putlicensemsg;
+void 	respondlicense(Rdp*,Msg*);
+
+void	scanvcdata(Rdp*, Msg*);
+void	scanupdates(Rdp*, Msg*);
+
+enum /* Share.type */
+{
+	ShActivate=	1,
+	ShDeactivate,
+	ShUorders,
+	ShUimg,
+	ShUcmap,
+	ShUwarp,
+	ShSync,
+	ShCtl,
+	ShFmap,
+	ShEinfo,
+};
+
+struct Share
+{
+	int	type;
+	int	source;
+	int	shareid;
+	int	ncap;
+	int	nord;
+	int	nrect;
+	int	x;
+	int	y;
+	int	err;
+	uchar* data;
+	uint	ndata;
+};
+int	getshareT(Share*, uchar*, uint);	/* T.128 ASPDU updates */
+int	getshareF(Share*, uchar*, uint);	/* RDP Fast-Path updates */
+
+void	activating(Rdp*,Share*);
+void	deactivating(Rdp*,Share*);
+void	scanimgupdate(Rdp*,Share*);
+void	scancmap(Rdp*,Share*);
+void	scanorders(Rdp*,Share*);
+
+
+struct Imgupd {
+	int	x;
+	int	y;
+	int	xm;
+	int	ym;
+	int	xsz;
+	int	ysz;
+	int	depth;
+	int	iscompr;
+	int	nbytes;
+	uchar*	bytes;
+};
+int	getimgupd(Imgupd*, uchar*, uint);
+
+
+enum 
+{
 	/* 2.2.8.1.1.2.1 Basic (TS_SECURITY_HEADER) */
 	Scrypt			= 0x0008,
 	Sinfopk			= 0x0040,
@@ -89,23 +258,63 @@
 	InputUnicode=	5,
 	InputMouse=	0x8001,
 
+	/* 2.2.1.15.1 Control PDU Data */
+	CAreqctl=	1,
+	CAcooperate=	4,
+
+	GLOBALCHAN=	1003,	/* MCS global channel id */
+
 	TPKTFIXLEN=		4,
 	TPDATAFIXLEN=	(TPKTFIXLEN+3),
 	MCSCIFIXLEN=		(18+3*2+24*4),
 
-	MAXTPDU=	16386,	/* max TPDU size */
-	SRVCHAN=	1002,	/* server channel ID */
-	GLOBALCHAN=	1003,	/* MCS global channel's id */
+	SECHSIZE=	4,
+	SCHSIZE=		6,
+	SCDSIZE=		SCHSIZE+4+4+2*2,
+
 	NumOrders=		32,
 };
 
-extern Rdp	rd;
 extern uchar	orderSupport[NumOrders];
-extern uchar	cmap[256];	/* 8bpp translation table */
 
-extern Vchan	vctab[];		/* static virtual channels table */
-extern uint	nvc;			/* number of vctab entries */
-
 extern char	Eshort[];
 extern char	Ebignum[];
 extern char	Esmall[];
+
+
+enum /* X.224 PDU codes */
+{
+	ConReq=		0xE0, 		/* connection request */
+	ConCfrm=	0xD0,		/* connection confirm */
+	HupReq=		0x80,		/* disconnection request */
+	Data=		0xF0,		/* data */
+	Err=			0x70,		/* error */
+};
+
+enum /* ASN.1 tag numbers for MCS types */
+{
+	Mci=		101,		/* Connect Initial */
+	Mcr=	102,		/* Connect Response */
+	Medr=	1,		/* Erect Domain Request */
+	Maur=	10,		/* Attach User Request */
+	Mauc=	11,		/* Attach User Confirm */
+	Mcjr=	14,		/* Channel Join Request */
+	Mcjc=	15,		/* Channel Join Confirm */
+	Msdr=	25,		/* Send Data Request */
+	Msdi=	26,		/* Send Data Indication */
+	Mdpu=	8,		/* Disconnect Provider Ultimatum */
+};
+
+enum /* 2.2.8.1.1.1.2 Share Data Header (TS_SHAREDATAHEADER) */
+{
+	ADdraw=	2,
+	ADctl=	20,
+	ADcursor=	27,
+	ADinput=		28,
+	ADsync=	31,
+	ADrefresh=	33,
+	ADsupress=	35,
+	ADfontlist=	39,
+	ADfontmap=	40,
+	ADerrx=	47,
+};
--- a/eclip.c
+++ b/eclip.c
@@ -12,26 +12,19 @@
 #include "fns.h"
 
 static char	cliprdr[]				= "CLIPRDR";
-static void		cliprequest(uint);
 
 enum
 {
-	CFunicode	= 13,
-};
+	CFunicode=	13,
 
-enum
-{
-	FlagOk			= (1<<0),
-	FlagErr			= (1<<1),
-};
+	FlagOk=	(1<<0),
+	FlagErr=	(1<<1),
 
-enum
-{
-	ClipReady			= 1,
-	ClipAnnounce		= 2,
-	ClipNoted			= 3,
-	ClipReq			= 4,
-	ClipResp			= 5,
+	ClipReady=	1,
+	ClipAnnounce=	2,
+	ClipNoted=	3,
+	ClipReq=	4,
+	ClipResp=	5,
 };
 
 typedef	struct	Clipmsg Clipmsg;
@@ -43,16 +36,15 @@
 	uchar	*data;
 	uint		ndata;
 };
-
 static	int	clipputmsg(Clipmsg*,uchar*,int);
 static	int	clipgetmsg(Clipmsg*,uchar*,int);
 
-static	void	clipattached(Clipmsg*);
-static	void	clipnoted(Clipmsg*);
-static	void	cliprequested(Clipmsg*);
-static	void	clipprovided(Clipmsg*);
+static	void	clipattached(Rdp*,Clipmsg*);
+static	void	clipnoted(Rdp*,Clipmsg*);
+static	void	cliprequested(Rdp*,Clipmsg*);
+static	void	clipprovided(Rdp*,Clipmsg*);
 
-static	void	(*clipcall[])(Clipmsg*) =
+static	void	(*clipcall[])(Rdp*,Clipmsg*) =
 {
 	[ClipReady]=		clipattached,
 	[ClipAnnounce]=	clipnoted,
@@ -60,12 +52,14 @@
 	[ClipResp]=		clipprovided,
 };
 
+static void		cliprequest(Rdp*,uint);
+
 void
-clipvcfn(uchar* p, uchar* ep)
+clipvcfn(Rdp* c, uchar* p, uint nb)
 {
 	Clipmsg tx;
 
-	if(clipgetmsg(&tx, p, ep-p) < 0)
+	if(clipgetmsg(&tx, p, nb) < 0)
 		return;
 	if(tx.flags&FlagErr)
 		return;
@@ -73,11 +67,11 @@
 		return;
 	if(clipcall[tx.type] == nil)
 		return;
-	clipcall[tx.type](&tx);
+	clipcall[tx.type](c, &tx);
 }
 
 void
-clipannounce(void)
+clipannounce(Rdp* c)
 {
 	Clipmsg r;
 	uchar a[44];
@@ -87,12 +81,12 @@
 	r.flags = 0;
 	r.fmtid = CFunicode;
 	n = clipputmsg(&r, a, sizeof(a));
-	if(sendvc(cliprdr, a, n) < 0)
+	if(sendvc(c, cliprdr, a, n) < 0)
 		fprint(2, "clipannounce: %r\n");
 }
 
 static void
-cliprequest(uint fmtid)
+cliprequest(Rdp* c, uint fmtid)
 {
 	Clipmsg r;
 	uchar a[12];
@@ -102,18 +96,18 @@
 	r.flags = 0;
 	r.fmtid = fmtid;
 	n = clipputmsg(&r, a, sizeof(a));
-	if(sendvc(cliprdr, a, n) < 0)
+	if(sendvc(c, cliprdr, a, n) < 0)
 		fprint(2, "cliprequest: %r\n");
 }
 
 static void
-clipattached(Clipmsg*)
+clipattached(Rdp* c, Clipmsg*)
 {
-	clipannounce();
+	clipannounce(c);
 }
 
 static void
-clipnoted(Clipmsg *m)
+clipnoted(Rdp* c, Clipmsg *m)
 {
 	Clipmsg r;
 	uchar a[8];
@@ -120,17 +114,17 @@
 	int n;
 
 	if(m->fmtid)
-		cliprequest(m->fmtid);
+		cliprequest(c, m->fmtid);
 
 	r.type = ClipNoted;
 	r.flags = FlagOk;
 	n = clipputmsg(&r, a, sizeof(a));
-	if(sendvc(cliprdr, a, n) < 0)
+	if(sendvc(c, cliprdr, a, n) < 0)
 		fprint(2, "clipnoted: %r\n");
 }
 
 static void
-cliprequested(Clipmsg *m)
+cliprequested(Rdp* c, Clipmsg *m)
 {
 	Clipmsg r;
 	char* s;
@@ -158,13 +152,13 @@
 	r.data = b+8;
 	r.ndata = nb;
 	n = clipputmsg(&r, b, nb+8);
-	if(sendvc(cliprdr, b, n) < 0)
+	if(sendvc(c, cliprdr, b, n) < 0)
 		fprint(2, "cliprequested: %r\n");
 	free(b);
 }
 
 static void
-clipprovided(Clipmsg *m)
+clipprovided(Rdp*, Clipmsg *m)
 {
 	char *s;
 	int n, ns;
--- a/egdi.c
+++ b/egdi.c
@@ -85,14 +85,14 @@
 struct Order
 {
 	int	fsize;
-	uchar* (*fn)(uchar*,uchar*,int,int);
+	uchar* (*fn)(Rdp*, uchar*,uchar*,int,int);
 };
 
-static	uchar*	scrblt(uchar*,uchar*,int,int);
-static	uchar*	memblt(uchar*,uchar*,int,int);
-static	uchar*	cacheimage2(uchar*,uchar*,int,int);
-static	uchar*	cacheimage(uchar*,uchar*,int,int);
-static	uchar*	cachecmap(uchar*,uchar*,int,int);
+static	uchar*	scrblt(Rdp*, uchar*,uchar*,int,int);
+static	uchar*	memblt(Rdp*, uchar*,uchar*,int,int);
+static	uchar*	cacheimage2(Rdp*, uchar*,uchar*,int,int);
+static	uchar*	cacheimage(Rdp*, uchar*,uchar*,int,int);
+static	uchar*	cachecmap(Rdp*, uchar*,uchar*,int,int);
 
 Order ordtab[NumOrders] = {
 	[ScrBlt]= 		{ 1, scrblt },
@@ -132,11 +132,17 @@
 
 /* 2.2.2.2 Fast-Path Orders Update (TS_FP_UPDATE_ORDERS) */
 void
-scanorders(uchar* p, uchar* ep, int count)
+scanorders(Rdp* c, Share* as)
 {
+	int count;
+	uchar *p, *ep;
 	int ctl, fmask, fsize;
 	int size, opt, xorder;
 
+	count = as->nord;
+	p = as->data;
+	ep = as->data + as->ndata;
+
 	while(count-- > 0 && p<ep){	
 		fmask = 0;
 		ctl = *p;
@@ -144,10 +150,10 @@
 			goto ErrNstd;	// GDI+ or out of sync
 		if(ctl&Secondary){
 			if(p+6>ep)
-				sysfatal(Eshort);
+				sysfatal("scanorders: %s", Eshort);
 			size = ((short)GSHORT(p+1))+13;
 			if(size < 0 || p+size > ep)
-				sysfatal(Eshort);
+				sysfatal("scanorders: size: %s", Eshort);
 			opt = GSHORT(p+3);
 			xorder = p[5];
 			if(xorder >= nelem(auxtab) || auxtab[xorder].fn == nil){
@@ -156,7 +162,7 @@
 				continue;
 			}
 
-			auxtab[xorder].fn(p, p+size, xorder, opt);
+			auxtab[xorder].fn(c, p, p+size, xorder, opt);
 			p += size;
 			continue;
 		}
@@ -188,7 +194,7 @@
 
 		if(ordtab[gc.order].fn == nil)
 			goto ErrNotsup;
-		p = ordtab[gc.order].fn(p, ep, ctl, fmask);
+		p = ordtab[gc.order].fn(c, p, ep, ctl, fmask);
 		if(p == nil)
 			break;
 	}
@@ -239,7 +245,7 @@
 		p += 2;
 	}
 	if(p>ep)
-		sysfatal(Eshort);
+		sysfatal("getclipr: %s", Eshort);
 	return p;
 }
 
@@ -265,7 +271,7 @@
 		};
 	}
 	if(s > es)
-		sysfatal(Eshort);
+		sysfatal("getpt: %s", Eshort);
 	*pp = p;
 	return s;
 }
@@ -285,7 +291,7 @@
 
 /* 2.2.2.2.1.1.2.7 ScrBlt (SCRBLT_ORDER) */
 static uchar*
-scrblt(uchar* p, uchar* ep, int ctl, int fmask)
+scrblt(Rdp*, uchar* p, uchar* ep, int ctl, int fmask)
 {
 	static Rectangle r;
 	static Point pt;
@@ -317,7 +323,7 @@
 
 /* 2.2.2.2.1.1.2.9 MemBlt (MEMBLT_ORDER) */
 static uchar*
-memblt(uchar* p, uchar* ep, int ctl, int fmask)
+memblt(Rdp*, uchar* p, uchar* ep, int ctl, int fmask)
 {
 	static int cid;	/* cacheId */
 	static int coff;	/* cacheIndex */
@@ -339,7 +345,7 @@
 		p += 2;
 	}
 	if(p>ep)
-		sysfatal(Eshort);
+		sysfatal("memblt: %s", Eshort);
 
 	cid &= Bits8;
 	if(cid >= nelem(imgcache) || coff >= nelem(*imgcache)){
@@ -387,7 +393,7 @@
 
 /* 2.2.2.2.1.2.2 Cache Bitmap - Revision 1 (CACHE_BITMAP_ORDER) */
 static uchar*
-cacheimage(uchar* p, uchar* ep, int xorder, int opt)
+cacheimage(Rdp* c, uchar* p, uchar* ep, int xorder, int opt)
 {
 	int cid;	/* cacheId */
 	int coff;	/* cacheIndex */
@@ -400,7 +406,7 @@
 	Rectangle r;
 
 	if(p+15 >= ep)
-		sysfatal(Eshort);
+		sysfatal("cacheimage: %s", Eshort);
 	cid = p[6];
 	d.x = p[8];
 	d.y = p[9];
@@ -408,7 +414,7 @@
 	coff = GSHORT(p+13);
 	r.min = ZP;
 	r.max = d;
-	chan = rd.chan;
+	chan = c->chan;
 	zip = (xorder==CacheCompressed);
 
 	if(zip)
@@ -417,10 +423,10 @@
 		size -= 8;
 	}
 	if(p+size > ep)
-		sysfatal(Eshort);
+		sysfatal("cacheimage: size: %s", Eshort);
 	if((img = pickimage(cid, coff, r, chan)) == nil)
 		sysfatal("pickimage: %r");
-	err = (zip? loadrle : loadbmp)(img, r, p, size);
+	err = (zip? loadrle : loadbmp)(img, r, p, size, c->cmap);
 	if(err < 0)
 		sysfatal("%r");
 	return p+size;
@@ -428,9 +434,9 @@
 
 /* 2.2.2.2.1.2.3 Cache Bitmap - Revision 2 (CACHE_BITMAP_REV2_ORDER) */
 static uchar*
-cacheimage2(uchar* p,uchar* ep, int xorder, int opt)
+cacheimage2(Rdp* c, uchar* p,uchar* ep, int xorder, int opt)
 {
-	int n, c;
+	int n, g;
 	int zip;
 	int cid;	/* cacheId */
 	int coff;	/* cacheIndex */
@@ -442,10 +448,10 @@
 	Rectangle r;
 
 	if(p+9 >= ep)
-		sysfatal(Eshort);
+		sysfatal("cacheimage2: %s", Eshort);
 	p += 6;
 
-	chan = rd.chan;
+	chan = c->chan;
 	zip = (xorder==CacheCompressed2);
 	cid = opt&Bits3;
 	opt >>= 7;
@@ -452,35 +458,36 @@
 
 	if(opt&1<<1)
 		p += 8;	// persistent cache key
-	c = *p++;
-	if(c&1<<7)
-		c = ((c&Bits7)<<8) | *p++;
-	d.x = c;
+	g = *p++;
+	if(g&1<<7)
+		g = ((g&Bits7)<<8) | *p++;
+	d.x = g;
 	if(opt&1)
 		d.y = d.x;
 	else{
-		c = *p++;
-		if(c&1<<7)
-			c = ((c&Bits7)<<8) | *p++;
-		d.y = c;
+		g = *p++;
+		if(g&1<<7)
+			g = ((g&Bits7)<<8) | *p++;
+		d.y = g;
 	}
 	r.min = ZP;
 	r.max = d;
 
-	c = *p++;
-	n = c>>6;
-	c &= Bits6;
+	g = *p++;
+	n = g>>6;
+	g &= Bits6;
 	switch(n){
-	case 3:	c = (c<<8) | *p++;
-	case 2:	c = (c<<8) | *p++;
-	case 1:	c = (c<<8) | *p++;
+	default:	sysfatal("cacheimage2: integer too big");
+	case 3:	g = (g<<8) | *p++;
+	case 2:	g = (g<<8) | *p++;
+	case 1:	g = (g<<8) | *p++;
 	}
-	size = c;	// fixme: protect from integer overflow
+	size = g;
 
-	c = *p++;
-	if(c&1<<7)
-		c = ((c&Bits7)<<8) | *p++;
-	coff = c;
+	g = *p++;
+	if(g&1<<7)
+		g = ((g&Bits7)<<8) | *p++;
+	coff = g;
 
 	if(zip&& !(opt&1<<3)){
 		p += 8;	// bitmapComprHdr
@@ -487,10 +494,10 @@
 		size -= 8;
 	}
 	if(p+size > ep)
-		sysfatal(Eshort);
+		sysfatal("cacheimage2: size: %s", Eshort);
 	if((img = pickimage(cid, coff, r, chan)) == nil)
-		sysfatal("pullimage: %r");
-	err = (zip? loadrle : loadbmp)(img, r, p, size);
+		sysfatal("pickimage: %r");
+	err = (zip? loadrle : loadbmp)(img, r, p, size, c->cmap);
 	if(err < 0)
 		sysfatal("%r");
 	return p+size;
@@ -498,7 +505,7 @@
 
 /* 2.2.2.2.1.2.4 Cache Color Table (CACHE_COLOR_TABLE_ORDER) */
 static uchar*
-cachecmap(uchar* p,uchar* ep, int, int)
+cachecmap(Rdp*, uchar* p,uchar* ep, int, int)
 {
 	int cid, n;
 	
--- a/ele.c
+++ b/ele.c
@@ -23,9 +23,7 @@
 	SHaveChal=	2,
 	SHaveLicense=	3,
 	SNeedRenew=	4,
-	CLicenseInfo=	0x12,
  	CNeedLicense=	0x13,
-	CChalResp=	0x15,
 	Notify=	0xFF,
 
 	Brandom=	2,
@@ -39,106 +37,118 @@
 	NoTransition=	2,
 };
 
-static void reqlicense(char*,char*);
-static void	senderr(int,int);
-
-void
-scanlicensepdu(uchar* p, uchar* ep)
+int
+getlicensemsg(Msg* m, uchar* b, uint nb)
 {
-	uchar type;
+	uint type;
 
-	if(ep-p < 1)
+	if(nb < 1)
 		sysfatal(Eshort);
 
 	/* type[1] flags[1] size[2] */
-	type = p[0];
+	type = b[0];
 	switch(type){
+	default:
+		werrstr("unhandled license packet %ud", type);
+		m->type = 0;
+		return -1;
 	case SNeedLicense:
-		reqlicense(rd.user, rd.local);
+		m->type = Lneedlicense;
 		break;
 	case SHaveChal:
-		fprint(2, "unhandled SHaveChal PDU\n");
-		senderr(ErrCNoLicense, TotalAbort);
+		m->type = Lhavechal;
 		break;
 	case SNeedRenew:
 	case SHaveLicense:
 	case Notify:
-		rd.licensed = 1;
+		m->type = Ldone;
 		break;
 	}
+	return nb;
 }
 
-static void
-reqlicense(char* user, char *host)
+int
+sizelicensemsg(Msg* m)
 {
-	uchar buf[180], *p, *ep;
-	int nb, ndata, usersize, hostsize;
+	int usersize, hostsize;
 
-	usersize = strlen(user)+1;
-	hostsize = strlen(host)+1;
-	ndata = 24+usersize+hostsize+RandomSize+48;
-	nb = sizeof(buf);
+	switch(m->type){
+	default:
+		werrstr("sizelicensemsg: bad message type");
+		return -1;
+	case Lreq:
+		usersize = strlen(m->user)+1;
+		hostsize = strlen(m->sysname)+1;
+		return 24+usersize+hostsize+RandomSize+48;
+		break;
+	case Lnolicense:
+		return 16;
+	}
+}
 
-	p = prebuf(buf, nb, ndata, 0, Slicensepk);
-	if(p == nil)
-		sysfatal("reqlicense: %r");
-	ep = p+ndata;
+int
+putlicensemsg(uchar* buf, uint nb, Msg* m)
+{
+	uchar *p, *ep;
+	int ndata, usersize, hostsize;
+	int errcode, newstate;
 
-	/* 
-	 * type[1] flags[1] size[2]
-	 * kexalg[4] platfid[4] crandom[32]
-	 * premaster[blob] cuser[blob] chost[blob]
-	 * 
-	 * blob := type[2] len[2] data[len]
-	 */
-	p[0] = CNeedLicense;
-	p[1] = PreambleV3;
-	PSHORT(p+2, ndata);
+	p = buf;
+	ep = buf+nb;
+	ndata = nb;
 
-	PLONG(p+4, KeyExRSA);
-	PLONG(p+8, 0);
-	memset(p+12, RandomSize, 0);
-	p += 12+RandomSize;
+	switch(m->type){
+	default:
+		werrstr("putlicensemsg: bad message type");
+		return -1;
+	case Lreq:	
+		usersize = strlen(m->user)+1;
+		hostsize = strlen(m->sysname)+1;
 
-	PSHORT(p+0, Brandom);
-	PSHORT(p+2, 48);
-	memset(p+4, 48, 0);
-	p += 4+48;
+		/* 
+		 * type[1] flags[1] size[2] kexalg[4] platfid[4] crandom[32]
+		 * premaster[blob] cuser[blob] chost[blob]
+		 * (blob := type[2] len[2] data[len])
+		 */
+		p[0] = CNeedLicense;
+		p[1] = PreambleV3;
+		PSHORT(p+2, ndata);
+		PLONG(p+4, KeyExRSA);
+		PLONG(p+8, 0);
+		memset(p+12, RandomSize, 0);
+		p += 12+RandomSize;
+	
+		PSHORT(p+0, Brandom);
+		PSHORT(p+2, 48);
+		memset(p+4, 48, 0);
+		p += 4+48;
 
-	PSHORT(p+0, Bcuser);
-	PSHORT(p+2, usersize);
-	memcpy(p+4, user, usersize);
-	p+= 4+usersize;
+		PSHORT(p+0, Bcuser);
+		PSHORT(p+2, usersize);
+		memcpy(p+4, m->user, usersize);
+		p+= 4+usersize;
+	
+		PSHORT(p+0, Bchost);
+		PSHORT(p+2, hostsize);
+		memcpy(p+4, m->sysname, hostsize);
+		p+= 4+hostsize;
+		break;
 
-	PSHORT(p+0, Bchost);
-	PSHORT(p+2, hostsize);
-	memcpy(p+4, host, hostsize);
-	p+= 4+hostsize;
+	case Lnolicense:
+		errcode = ErrCNoLicense;
+		newstate = TotalAbort;
 
+		/* type[1] flags[1] size[2] errcode[4] newstate[4] blob.type[2] blob.len[2] ... */
+		p[0] = Notify;
+		p[1] = PreambleV3;
+		PSHORT(p+2, ndata);
+		PLONG(p+4, errcode);
+		PLONG(p+8, newstate);
+		PSHORT(p+12, Berror);
+		PSHORT(p+14, 0);
+		break;
+	}
 	assert(p == ep);
-	writen(rd.fd, buf, p-buf);
+	return p-buf;
 }
 
-static void
-senderr(int errcode, int newstate)
-{
-	uchar buf[512], *p;
-	int nb, ndata;
-
-	nb = sizeof(buf);
-	ndata = 16;
-	p = prebuf(buf, nb, ndata, 0, Slicensepk);
-	if(p == nil)
-		sysfatal("prebuf: %r");
-
-	/* type[1] flags[1] size[2] errcode[4] newstate[4] blob.type[2] blob.len[2] */
-	p[0] = Notify;
-	p[1] = PreambleV3;
-	PSHORT(p+2, ndata);
-	PLONG(p+4, errcode);
-	PLONG(p+8, newstate);
-	PSHORT(p+12, Berror);
-	PSHORT(p+14, 0);
-
-	writen(rd.fd, buf, p-buf);
-}
--- a/fns.h
+++ b/fns.h
@@ -1,71 +1,28 @@
-/* cap.c */
-void		scangencaps(uchar*,uchar*);
-void		scanbitcaps(uchar*,uchar*);
-uchar*	putgencaps(uchar*,uchar*);
-uchar*	putbitcaps(uchar*,uchar*);
-uchar*	putordcaps(uchar*,uchar*);
-uchar*	putbc2caps(uchar*,uchar*);
-uchar*	putptrcaps(uchar*,uchar*);
-uchar*	putinpcaps(uchar*,uchar*);
-uchar*	putsndcaps(uchar*,uchar*);
-uchar*	putglycaps(uchar*,uchar*);
-
 /* load.c */
-int		loadbmp(Image*,Rectangle,uchar*,int);
-int		loadrle(Image*,Rectangle,uchar*,int);
+int		loadbmp(Image*,Rectangle,uchar*,int,uchar*);
+int		loadrle(Image*,Rectangle,uchar*,int,uchar*);
 
 /* mcs.c */
-int		mcschanid(uchar*,uchar*);
+int		mcschan(uchar*,uchar*);
 int		mcstype(uchar*,uchar*);
 int		ismcshangup(uchar*,uchar*);
-uchar*	mcspayload(uchar*,uchar*);
+uchar*	mcsdat(uchar*,uchar*);
 int		mkmcsci(uchar*, int, int);
-int		mkmcssdr(uchar*,int,int,int);
-int		mcsconnect(int);
-void		erectdom(int);
-int		attachuser(int);
-int		joinchannel(int,int);
+int		putmsdr(uchar*,int,int,int,int);
 
 /* mpas.c */
-int		rdphandshake(int);
-void		readnet(int);
 int		isflowpdu(uchar*,uchar*);
-void		scanaspdu(uchar*, uchar*);
-void		scandatapdu(uchar*,uchar*);
-void		activating(uchar*,uchar*);
-void		passinput(ulong,int,int,int,int);
-void		turnupdates(int);
 int		sizesechdr(int);
-uchar*	prebuf(uchar*,int,int,int,int);
+uchar*	txprep(uchar*,int,int,int,int,int);
 
-/* draw.c */
-void		eresized(int);
-
-/* eclip.c */
-void		clipannounce(void);
-void		clipvcfn(uchar*,uchar*);
-
-/* egdi.c */
-void		scanorders(uchar*,uchar*,int);
-
-/* ele.c */
-void		scanlicensepdu(uchar*,uchar*);
-
 /* snarf.c */
 void		initsnarf(void);
-void		pollsnarf(void);
 char*	getsnarf(int*);
 void		putsnarf(char*,int);
 
 /* mouse.c */
-void		readdevmouse(void);
 void		warpmouse(int,int);
 
-/* kbd.c */
-void		readkbd(void);
-
-/* sec.c */
-
 /* mppc.c */
 uchar*	uncomp(uchar*,int,int,int*);
 
@@ -82,12 +39,9 @@
 int		mktpcr(uchar*,int,int);
 int		mktpdr(uchar*,int,int);
 int		istpkt(uchar*,uchar*);
-int		tpdutype(uchar*,uchar*);
-int		isdatatpdu(uchar*,uchar*);
-uchar*	tpdupayload(uchar*,uchar*);
-int		x224connect(int);
-int		x224disconnect(int);
-int		starttls(void);
+int		tptype(uchar*,uchar*);
+int		istpdat(uchar*,uchar*);
+uchar*	tpdat(uchar*,uchar*);
 
 /* rd.c */
 void		atexitkiller(void);
@@ -97,3 +51,8 @@
 char*	estrdup(char*);
 long		writen(int,void*,long);
 
+uchar*	gblen(uchar*,uchar*,int*);
+uchar*	gbtag(uchar*,uchar*,int*);
+void		pbshort(uchar*,int);
+
+uchar*	putsdh(uchar*,uchar*,int,int,int,int);
--- a/guide
+++ b/guide
@@ -3,7 +3,7 @@
 window -r 2 44 1026 812
 kill 5.out | rc
 src -s memcpy 5.out
-g 
+g rdphandshake
 h2d 
 d2h 
 dat.h fns.h mkfile /dist/replica/rd.proto
@@ -10,3 +10,13 @@
 diff -r . /sys/src/cmd/rd | sed '/^diff/!d; s//cp /'
 wc [~_]*.[ch] | sort -rn
 /n/dump/2015/0108/386/bin/rd	# legacy security
+B *.[ch]
+Edit X!/rd/.*\.[ch]! ,s/scanvcpdu/scanvcdata/g
+hg pull -u
+hg log -vl5
+hg diff
+hg ci -m'move remaining parsing from scanimgupdate to getshare[TF]'
+hg log -vl2
+hg up -rtip
+hg push
+dat.h
\ No newline at end of file
--- a/kbd.c
+++ b/kbd.c
@@ -106,7 +106,7 @@
 };
 
 void
-kbdsendscan(int sc, int mod)
+kbdsendscan(Rdp* c, int sc, int mod)
 {
 	long msec;
 	int f;
@@ -118,25 +118,25 @@
 
 	msec = time(nil);
 	if(mod != 0)
-		passinput(msec, InputKeycode, 0, mod, 0);
-	passinput(msec, InputKeycode, f|0, sc, 0);
-	passinput(msec, InputKeycode, f|KeyUp, sc, 0);
+		passinput(c, msec, InputKeycode, 0, mod, 0);
+	passinput(c, msec, InputKeycode, f|0, sc, 0);
+	passinput(c, msec, InputKeycode, f|KeyUp, sc, 0);
 	if(mod != 0)
-		passinput(msec, InputKeycode, KeyUp, mod, 0);
+		passinput(c, msec, InputKeycode, KeyUp, mod, 0);
 }
 
 void
-kbdsendrune(Rune r)
+kbdsendrune(Rdp* c, Rune r)
 {
 	long msec;
 
 	msec = time(nil);
-	passinput(msec, InputUnicode, 0, r, 0);
-	passinput(msec, InputUnicode, KeyUp, r, 0);
+	passinput(c, msec, InputUnicode, 0, r, 0);
+	passinput(c, msec, InputUnicode, KeyUp, r, 0);
 }
 
 void
-readkbd(void)
+readkbd(Rdp* c)
 {
 	char buf[256], k[10];
 	int ctlfd, fd, kr, kn, w;
@@ -164,46 +164,46 @@
 		if(r < nelem(rune2scan)){
 			sc = rune2scan[r].sc;
 			mod = rune2scan[r].mod;
-			kbdsendscan(sc, mod);
+			kbdsendscan(c, sc, mod);
 			continue;
 		}
 
 		switch(r){
 		case Kins:
-			kbdsendscan(Sins, 0);
+			kbdsendscan(c, Sins, 0);
 			break;
 		case Kdel:
-			kbdsendscan(Sdel, 0);
+			kbdsendscan(c, Sdel, 0);
 			break;
 		case Khome:
-			kbdsendscan(Shome, 0);
+			kbdsendscan(c, Shome, 0);
 			break;
 		case Kend:
-			kbdsendscan(Send, 0);
+			kbdsendscan(c, Send, 0);
 			break;
 		case Kpgup:
-			kbdsendscan(Spgup, 0);
+			kbdsendscan(c, Spgup, 0);
 			break;
 		case Kpgdown:
-			kbdsendscan(Spgdown, 0);
+			kbdsendscan(c, Spgdown, 0);
 			break;
 		case Kup:
-			kbdsendscan(Sup, 0);
+			kbdsendscan(c, Sup, 0);
 			break;
 		case Kdown:
-			kbdsendscan(Sdown,0 );
+			kbdsendscan(c, Sdown,0 );
 			break;
 		case Kleft:
-			kbdsendscan(Sleft, 0);
+			kbdsendscan(c, Sleft, 0);
 			break;
 		case Kright:
-			kbdsendscan(Sright, 0);
+			kbdsendscan(c, Sright, 0);
 			break;
 		case Kbrk:
 			exits("interrupt");
 			break;
 		case Kprint:
-			kbdsendscan(Sprint, 0);
+			kbdsendscan(c, Sprint, 0);
 			break;
 		case KF|1:
 		case KF|2:
@@ -215,16 +215,16 @@
 		case KF|8:
 		case KF|9:
 		case KF|10:
-			kbdsendscan(SF1+r-(KF|1), 0);
+			kbdsendscan(c, SF1+r-(KF|1), 0);
 			break;
 		case KF|11:
-			kbdsendscan(SF11, 0);
+			kbdsendscan(c, SF11, 0);
 			break;
 		case KF|12:
-			kbdsendscan(SF12, 0);
+			kbdsendscan(c, SF12, 0);
 			break;
 		default:
-			kbdsendrune(r);
+			kbdsendrune(c, r);
 			break;
 		}
 	}
--- a/load.c
+++ b/load.c
@@ -6,7 +6,7 @@
 
 /* like loadimage(2) but reverses scanline order and translates per cmap */
 int
-loadbmp(Image *i, Rectangle r, uchar *data, int ndata)
+loadbmp(Image *i, Rectangle r, uchar *data, int ndata, uchar *cmap)
 {
 	int n, bpl;
 	uchar *a;
@@ -46,7 +46,7 @@
 }
 
 int
-loadrle(Image *i, Rectangle r, uchar *data, int ndata)
+loadrle(Image *i, Rectangle r, uchar *data, int ndata, uchar *cmap)
 {
 	int nb, bpl;
 	uchar *buf;
@@ -60,7 +60,7 @@
 		free(buf);
 		return -1;
 	}
-	if(loadbmp(i, r, buf, nb) < 0){
+	if(loadbmp(i, r, buf, nb, cmap) < 0){
 		werrstr("loadrle: r=%R i->r=%R: %r", r, i->r);
 		free(buf);
 		return -1;
--- a/mcs.c
+++ b/mcs.c
@@ -1,4 +1,3 @@
-/* T.122 MCS, T.124 Generic Conference Control, T.125 MCS protocol  */
 #include <u.h>
 #include <libc.h>
 #include <draw.h>
@@ -14,20 +13,6 @@
 	TagEnum		= 10,
 	TagSeq		= 16,		/* also TagSeq OF */
 
-	/* ASN.1 tag numbers for MCS types */
-	Mci=		101,		/* Connect Initial */
-	Mcr=	102,		/* Connect Response */
-	Medr=	1,		/* Erect Domain Request */
-	Maur=	10,		/* Attach User Request */
-	Mauc=	11,		/* Attach User Confirm */
-	Mcjr=	14,		/* Channel Join Request */
-	Mcjc=	15,		/* Channel Join Confirm */
-	Msdr=	25,		/* Send Data Request */
-	Msdi=	26,		/* Send Data Indication */
-	Mdpu=	8,		/* Disconnect Provider Ultimatum */
-
-	Musrchanbase=	1001,
-
 	/* 2.2.1.3 Client MCS Connect Initial PDU with GCC Conference Create Request */
 	ClientCore=		0xC001,
 	ClientCluster=		0xC004,
@@ -39,12 +24,8 @@
 
 	/* 2.2.1.3.1 User Data Header (TS_UD_HEADER) */
 	SrvCore				= 0x0C01,
-
 };
 
-int	mkgcccr(uchar*,int);
-int	sizegcccr(void);
-
 enum
 {
 	Bits5	= 0x1F,
@@ -51,10 +32,6 @@
 	Bits7 = 0x7F,
 };
 
-static	uchar*	gblen(uchar*,uchar*,int*);
-static	uchar*	gbtag(uchar*,uchar*,int*);
-static	void		pbshort(uchar*,int);
-
 static uchar*
 gbuint7(uchar *p, uchar* ep, int* pv)
 {
@@ -80,7 +57,7 @@
 	return p;	
 }
 
-static uchar*
+uchar*
 gbtag(uchar *p, uchar* ep, int* ptag)
 {
 	if(p >= ep){
@@ -94,7 +71,7 @@
 	return p;
 }
 
-static uchar*
+uchar*
 gblen(uchar *p, uchar* ep, int* plen)
 {
 	int c,v;
@@ -125,7 +102,7 @@
 	return p+c;
 }
 
-static void
+void
 pbshort(uchar* p, int v)
 {
 	p[0]=2;
@@ -136,11 +113,11 @@
 int
 mcstype(uchar* p, uchar* ep)
 {
-	if(!isdatatpdu(p,ep)){
+	if(!istpdat(p,ep)){
 		werrstr("not an X.224 Data TPDU");
 		return -1;
 	}
-	p = tpdupayload(p, ep);
+	p = tpdat(p, ep);
 	if(p == nil)
 		return -1;
 	if(p >= ep){
@@ -157,13 +134,13 @@
 }
 
 int
-mcschanid(uchar *p, uchar* ep)
+mcschan(uchar *p, uchar* ep)
 {
 	if(mcstype(p,ep) != Msdi){
 		werrstr("not an MCS Send Data Indication: %r");
 		return -1;
 	}
-	if((p = tpdupayload(p, ep)) == nil)
+	if((p = tpdat(p, ep)) == nil)
 		return -1;
 	if(p+5 > ep){
 		werrstr(Eshort);
@@ -173,13 +150,13 @@
 }
 
 uchar*
-mcspayload(uchar *p, uchar* ep)
+mcsdat(uchar *p, uchar* ep)
 {
 	if(mcstype(p,ep) != Msdi){
 		werrstr("not an MCS Send Data Indication: %r");
 		return nil;
 	}
-	if((p = tpdupayload(p, ep)) == nil)
+	if((p = tpdat(p, ep)) == nil)
 		return nil;
 
 	if(p+6 > ep){
@@ -197,23 +174,6 @@
 	return p;
 }
 
-/* MCS Send Data Request */
-int
-mkmcssdr(uchar* p, int nb, int ndata, int chanid)
-{
-	if(nb < 8){
-		werrstr(Esmall);
-		return -1;
-	}
-	
-	p[0] = (Msdr<<2);
-	PSHORTB(p+1, rd.mcsuid);
-	PSHORTB(p+3, chanid);
-	p[5] = 0x70;
-	PSHORTB(p+6, ndata|0x8000);
-	return 8;
-}
-
 /* 2.2.1.3 Client MCS Connect Initial PDU with GCC Conference Create Request */
 int
 mkmcsci(uchar* buf, int nbuf, int ndata)
@@ -296,40 +256,52 @@
 
 /* GCC Conference Create Request  [T.124 section 8.7] in ASN.1 PER [X.691] */
 int
-sizegcccr(void)
+sizegccr(Msg* m)
 {
-	int size;
-	size = 9+14+216+12+12 + 8+12*nvc;
+	int size, nv;
+
+	nv = m->nvc;
+	size = 9+14+216+12+12 + 8+12*nv;
 	return size;	// should agree with the below
 }
 
+
+static uchar t124IdentifierKeyOid[7] = {0, 5, 0, 20, 124, 0, 1};
+
 int
-mkgcccr(uchar* buf, int nb)
+putgccr(uchar* buf, uint nb, Msg* m)
 {
 	int i;
 	uchar *p, *ep;
 	long gccsize, earlyCapabilityFlags;
+	int ver, depth, width, height, sproto, wantconsole;
+	char* sysname;
+	Vchan *v;
+	int nv;
 
 	p = buf;
 	ep = buf+nb;
-	gccsize = sizegcccr()-9;
+	gccsize = sizegccr(m)-9;
 	if(p+gccsize+9 > ep){
 		werrstr(Eshort);
 		return -1;
 	}
 
+	ver = m->ver;
+	depth = m->depth;
+	width = m->xsz;
+	height = m->ysz;
+	sysname = m->sysname;
+	sproto = m->sproto;
+	wantconsole = m->wantconsole;
+	v = m->vctab;
+	nv = m->nvc;
+
 	earlyCapabilityFlags = CanErrinfo;
-	if(rd.depth == 32)
+	if(depth == 32)
 		earlyCapabilityFlags |= Want32bpp;
 
-	// t124IdentifierKey: 0.0.20.124.0.1
-	p[0] = 0;
-	p[1] = 5;
-	p[2] = 0;
-	p[3] = 20;
-	p[4] = 124;
-	p[5] = 0;
-	p[6] = 1;
+	memcpy(p, t124IdentifierKeyOid, 7);
 
 	// connectPDU as a PER octet string
 	PSHORTB(p+7, (gccsize | 0x8000));	// connectPDU length
@@ -336,9 +308,9 @@
 	PSHORTB(p+9, 8);		// ConferenceCreateRequest
 	PSHORTB(p+11, 16);
 	p[13] = 0;
-	PSHORT(p+14, 0xC001);	// userData key: h221NonStandard. Yes, in LE.
+	PSHORT(p+14, 0xC001);	// userData key: h221NonStandard
 	p[16] = 0;
-	memcpy(p+17, "Duca", 4);	// H.221 nonstandard key (as mandated in 3.2.5.3.3)
+	memcpy(p+17, "Duca", 4);	// H.221 nonstandard key (3.2.5.3.3)
 	PSHORTB(p+21, ((gccsize-14) | 0x8000));		// userData length
 	p += 23;
 
@@ -345,14 +317,15 @@
 	// 2.2.1.3.2 Client Core Data
 	PSHORT(p+0, ClientCore);
 	PSHORT(p+2, 216);	// length of the data block
-	PLONG(p+4, 0x00080004);	// rdpVersion: RDP5=0x00080004
-	PSHORT(p+8, rd.dim.x);	// desktopWidth ≤ 4096
-	PSHORT(p+10, rd.dim.y);	// desktopHeight ≤ 2048
+	PLONG(p+4, ver);	// rdpVersion: RDP5=0x00080004
+	PSHORT(p+8, width);	// desktopWidth ≤ 4096
+	PSHORT(p+10, height);	// desktopHeight ≤ 2048
 	PSHORT(p+12, 0xCA01);	// colorDepth=8bpp, ignored
 	PSHORT(p+14, 0xAA03);	// SASSequence
 	PLONG(p+16, 0x409);	// keyboardLayout=us
 	PLONG(p+20, 2600); 	// clientBuild
-	toutf16(p+24, 32, rd.local, strlen(rd.local));	// clientName[32]
+	memset(p+24, 32, 0);	// clientName[32]
+	toutf16(p+24, 32, sysname, strlen(sysname));	
 	PSHORT(p+54, 0);		// zero-terminateclientName
 	PLONG(p+56, 4);	// keyboardType: 4="IBM enhanced (101-key or 102-key)"
 	PLONG(p+60, 0);	// keyboardSubType
@@ -361,13 +334,13 @@
 	PSHORT(p+132, 0xCA01);	// postBeta2ColorDepth=8bpp, ignored
 	PSHORT(p+134, 1);	// clientProductId
 	PLONG(p+136, 0);	// serialNumber
-	PSHORT(p+140, MIN(rd.depth, 24));	// highColorDepth: 4, 8, 15, 16, 24 bpp.
+	PSHORT(p+140, MIN(depth, 24));	// highColorDepth: 4, 8, 15, 16, 24 bpp.
 	PSHORT(p+142, 1+2+4+8);	// supportedColorDepths: 1=24, 2=16, 4=15, 8=32 bpp
 	PSHORT(p+144, earlyCapabilityFlags);	// earlyCapabilityFlags 
 	memset(p+146, 64, 0);	// clientDigProductId[64]
 	p[210] = 7;	// connectionType: 7=autodetect
 	p[211] = 0;	// pad1octet
-	PLONG(p+212, rd.sproto);	// serverSelectedProtocol
+	PLONG(p+212, sproto);	// serverSelectedProtocol
 	p += 216;
 	
 	// 2.2.1.3.3 Client Security Data
@@ -380,7 +353,7 @@
 	// 2.2.1.3.5 Client Cluster Data		*optional*
 	PSHORT(p+0, ClientCluster);
 	PSHORT(p+2, 12);	// length of the data block
-	PLONG(p+4, (rd.wantconsole? 11 : 9));	// Flags
+	PLONG(p+4, (wantconsole? 11 : 9));	// Flags
 	PLONG(p+8, 0);		// RedirectedSessionID
 	p += 12;
 
@@ -387,155 +360,28 @@
 	// 2.2.1.3.4 Client Network Data 	*optional*
 	// type[2] len[2] nchan[4] nchan*(name[8] options[4])
 	PSHORT(p+0, ClientNet);
-	PSHORT(p+2, 8+12*nvc);
-	PLONG(p+4, nvc);
-	for(i=0; i<nvc; i++){
-		memcpy(p+8+12*i+0, vctab[i].name, 8);
-		PLONGB(p+8+12*i+8, vctab[i].flags);
+	PSHORT(p+2, 8+12*nv);
+	PLONG(p+4, nv);
+	for(i=0; i<nv; i++){
+		memcpy(p+8+12*i+0, v[i].name, 8);
+		PLONGB(p+8+12*i+8, v[i].flags);
 	}
-	p += 8+12*nvc;
+	p += 8+12*nv;
 
 	return p-buf;
 }
 
-void
-erectdom(int fd)
-{
-	uchar buf[20], *p;
-	int len, nb;
-
-	p = buf;
-	nb = sizeof(buf);
-	len = mktpdat(buf, nb, 5);
-	if(len < 0)
-		sysfatal("mktpdat: %r");
-	p += TPDATAFIXLEN;
-	
-	p[0] = (Medr<<2);
-	PSHORTB(p+1, 1);
-	PSHORTB(p+3, 1);	
-	if(writen(fd, buf, len) != len)
-		sysfatal("Erect Domain: write: %r");
-}
-
 int
-attachuser(int fd)
+getmcr(Msg* m, uchar* b, uint nb)
 {
-	int len, tag, r, nb;
-	uchar buf[20], *p, *ep;
+	int len, tag, r, utype, ulen;
+	uchar *p, *ep;
 
-	nb = sizeof(buf);
-	len = mktpdat(buf, nb, 1);
-	if(len < 0)
-		sysfatal("mktpdat: %r");
-	buf[TPDATAFIXLEN] = (Maur<<2);
-	if(writen(fd, buf, len) != len)
-		sysfatal("Attach User: write: %r");
+	p = b;
+	ep = b+nb;
 
-	len = readpdu(fd, buf, nb);
-	if(len <= 0)
-		sysfatal("readpdu: %r");
-	p = buf;
-	ep = buf+len;
-	if(!isdatatpdu(p,ep))
-		sysfatal("MCS: expected Data TPDU\n");
-	p = tpdupayload(p, ep);
-	if(p+2 > ep)
-		sysfatal(Eshort);
+	m->type = Mconnected;
 
-	tag = p[0]>>2;
-	r = p[1];
-	if(tag != Mauc)
-		sysfatal("expected tag %d (Mauc), got %d", Mauc, tag);
-	if(r != 0)
-		sysfatal("Mauc error result: %d", r);
-	if((p[0])&2){
-		if(p+4 > ep)
-			sysfatal(Eshort);
-		rd.mcsuid = GSHORTB(p+2);
-		rd.userchan = rd.mcsuid+Musrchanbase;
-	}
-	return r;
-}
-
-int
-joinchannel(int fd, int chanid)
-{
-	uchar buf[32], *p, *ep;
-	int tag, len, r, nb;
-
-	p = buf;
-	nb = sizeof(buf);
-	len = mktpdat(buf, nb, 5);
-	if(len < 0)
-		sysfatal("mktpdat: %r");
-	p += TPDATAFIXLEN;
-	p[0] = (Mcjr << 2);
-	PSHORTB(p+1, rd.mcsuid);
-	PSHORTB(p+3, chanid);
-	if(writen(fd, buf, len) != len)
-		sysfatal("Channel Join: write: %r");
-
-	len = readpdu(fd, buf, nb);
-	if(len <= 0)
-		sysfatal("readpdu: %r");
-	p = buf;
-	ep = buf+len;
-	if(!isdatatpdu(p,ep))
-		sysfatal("MCS: expected Data TPDU\n");
-	p = tpdupayload(p, ep);
-	if(p+2 > ep)
-		sysfatal(Eshort);
-
-	tag = p[0]>>2;
-	r = p[1];
-	if(tag != Mcjc)
-		sysfatal("expected tag %d (Mcjc), got %d", Mcjc, tag);
-	if(r != 0)
-		sysfatal("Mcjc error result: %d", r);
-
-	return r;
-
-}
-
-int
-mcsconnect(int fd)
-{
-	uchar buf[MAXTPDU], *p, *ep;
-	int n, ndata, nb, len, tag, r, ver, utype, ulen;
-
-	/* 2.2.1.3 Client MCS Connect Initial PDU with GCC Conference Create Request */
-	nb = sizeof(buf);
-	ndata = sizegcccr();
-	len = mktpdat(buf, nb, ndata+MCSCIFIXLEN);
-	if(len < 0)
-		sysfatal("mktpdat: %r");
-	p = buf+TPDATAFIXLEN;
-	ep = buf+nb;
-	n = mkmcsci(p, ep-p, ndata);
-	if(n != ndata+MCSCIFIXLEN)
-		sysfatal("mkmcsci: %r");
-	n = mkgcccr(p+MCSCIFIXLEN, ndata);
-	if(n != ndata)
-		sysfatal("mkgcccr: %r");
-	if(writen(fd, buf, len) != len)
-		sysfatal("TPDUDT: write: %r");
-
-	/* 2.2.1.4 Server MCS Connect Response PDU with GCC Conference Create Response */
-	len = readpdu(fd, buf, nb);
-	if(len <= 0){
-		werrstr("read MCS Connect Response PDU: %r");
-		return -1;
-	}
-	p = buf;
-	ep = buf+len;
-
-	if(!isdatatpdu(p,ep)){
-		werrstr("MCS: expected Data TPDU\n");
-		return -1;
-	}
-	p = tpdupayload(p, ep);
-
 	/* at MCS Connect-Response ASN.1 BER-encoded structure */
 	if((p = gbtag(p, ep, &tag)) == nil || tag != Mcr || (p = gblen(p, ep, &len)) == nil)
 		return -1;
@@ -580,12 +426,29 @@
 		ulen = GSHORT(p+2);
 		switch(utype){
 		case SrvCore:		/* 2.2.1.4.2 Server Core Data */
-			ver = GLONG(p+4);
-			assert(ver >= 0x00080004);
+			m->ver = GLONG(p+4);
 			break;
+		/* BUG: exract channel IDs from SrvNet */
 		}
 		p += ulen;
 	}
+	return p-b;
+}
 
-	return r;
+
+/* MCS Send Data Request */
+int
+putmsdr(uchar* p, int nb, int ndata, int chanid, int mcsuid)
+{
+	if(nb < 8){
+		werrstr(Esmall);
+		return -1;
+	}
+	
+	p[0] = (Msdr<<2);
+	PSHORTB(p+1, mcsuid);
+	PSHORTB(p+3, chanid);
+	p[5] = 0x70;
+	PSHORTB(p+6, ndata|0x8000);
+	return 8;
 }
--- a/mkfile
+++ b/mkfile
@@ -4,7 +4,6 @@
 BIN=/$objtype/bin
 
 HFILES=fns.h dat.h
-CLEANFILES= x509.c
 OFILES=\
 	cap.$O\
 	eclip.$O\
@@ -17,7 +16,10 @@
 	mouse.$O\
 	mpas.$O\
 	mppc.$O\
+	msg.$O\
 	rd.$O\
+	rpc.$O\
+	tls.$O\
 	utf16.$O\
 	vchan.$O\
 	wsys.$O\
@@ -24,19 +26,5 @@
 	x224.$O\
 
 </sys/src/cmd/mkone
-
-x509.c:	/sys/src/libsec/port/x509.c
-	sed '
-	/^	ALG_sha1WithRSAEncryption,/a\
-		ALG_sha256WithRSAEncryption,\
-		ALG_shaWithRSASignatureOiw,
-	/^static Ints7 oid_sha1WithRSAEncryption =/a\
-	static Ints7 oid_sha256WithRSAEncryption ={7, 1, 2, 840, 113549, 1, 1, 11 };\
-	static Ints7 oid_shaWithRSASignatureOiw ={6, 1, 3, 14, 3, 2, 15 };
-	/^	\(Ints\*\)\&oid_sha1WithRSAEncryption,/a\
-		(Ints*)&oid_sha256WithRSAEncryption,\
-		(Ints*)&oid_shaWithRSASignatureOiw,
-	/^static DigestFun digestalg/ s/sha1,/sha1, sha2_256, sha1,/
-	' $prereq > $target
 
 $TARG: mkfile
--- a/mouse.c
+++ b/mouse.c
@@ -6,68 +6,63 @@
 #include "fns.h"
 
 enum {
-	MOUSE_FLAG_MOVE	= 0x0800,
-	MOUSE_FLAG_BUTTON1	= 0x1000,
-	MOUSE_FLAG_BUTTON2	= 0x2000,
-	MOUSE_FLAG_BUTTON3	= 0x4000,
-	MOUSE_FLAG_BUTTON4	= 0x0280,
-	MOUSE_FLAG_BUTTON5	= 0x0380,
-	MOUSE_FLAG_DOWN	= 0x8000,
+	MouseMove=	0x0800,
+	MouseB1=	0x1000,
+	MouseB2=	0x2000,
+	MouseB3=	0x4000,
+	MouseB4=	0x0280,
+	MouseB5=	0x0380,
+	MouseBdown=	0x8000,
 };
 
 static int mfd = -1;
 
 static void
-sendmouse(Mouse m, int flags)
+sendmouse(Rdp* c, Mouse m, int flags)
 {
-	passinput(m.msec, InputMouse, flags, m.xy.x, m.xy.y);
+	passinput(c, m.msec, InputMouse, flags, m.xy.x, m.xy.y);
 }
 
 static void
-mouseevent(Mouse m)
+mouseevent(Rdp* c, Mouse m)
 {
-	ushort flags;
-	int chg;
 	static Mouse o;
+	int down;
+	int chg;
 
+	m.xy = subpt(m.xy, screen->r.min);
 	switch(m.buttons){
 	case 8:
-		sendmouse(m, MOUSE_FLAG_BUTTON4|MOUSE_FLAG_DOWN);
-		sendmouse(m, MOUSE_FLAG_BUTTON4);
+		sendmouse(c, m, MouseB4|MouseBdown);
+		sendmouse(c, m, MouseB4);
 		return;
 	case 16:
-		sendmouse(m, MOUSE_FLAG_BUTTON5|MOUSE_FLAG_DOWN);
-		sendmouse(m, MOUSE_FLAG_BUTTON5);
+		sendmouse(c, m, MouseB5|MouseBdown);
+		sendmouse(c, m, MouseB5);
 		return;
 	}
 
 	if(!eqpt(m.xy, o.xy))
-		sendmouse(m, MOUSE_FLAG_MOVE);
+		sendmouse(c, m, MouseMove);
 
 	chg = m.buttons ^ o.buttons;
 	if(chg&1){
-		flags = MOUSE_FLAG_BUTTON1;
-		if(m.buttons&1)
-			flags |= MOUSE_FLAG_DOWN;
-		sendmouse(m, flags);
+		down = (m.buttons&1)? MouseBdown : 0;
+		sendmouse(c, m, MouseB1|down);
 	}
 	if(chg&2){
-		flags = MOUSE_FLAG_BUTTON3;
-		if(m.buttons&2)
-			flags |= MOUSE_FLAG_DOWN;
-		sendmouse(m, flags);
+		down = (m.buttons&2)? MouseBdown : 0;
+		sendmouse(c, m, MouseB3|down);
 	}
 	if(chg&4){
-		flags = MOUSE_FLAG_BUTTON2;
-		if(m.buttons&4)
-			flags |= MOUSE_FLAG_DOWN;
-		sendmouse(m, flags);
+		down = (m.buttons&4)? MouseBdown : 0;
+		sendmouse(c, m, MouseB2|down);
 	}
 	o = m;
 }
 
 void
-readdevmouse(void)
+readdevmouse(Rdp* c)
 {
 	Mouse m;
 	char ev[1+4*12];
@@ -79,14 +74,13 @@
 		if(read(mfd, ev, sizeof ev) != sizeof ev)
 			sysfatal("mouse eof");
 		if(*ev == 'm'){
-				m.xy.x = atoi(ev+1);
-				m.xy.y = atoi(ev+1+12);
-				m.buttons = atoi(ev+1+2*12) & 0x1F;
-				m.msec = atoi(ev+1+3*12);
-				m.xy = subpt(m.xy, screen->r.min);
-				mouseevent(m);
+			m.xy.x = atoi(ev+1+0*12);
+			m.xy.y = atoi(ev+1+1*12);
+			m.buttons = atoi(ev+1+2*12) & 0x1F;
+			m.msec = atoi(ev+1+3*12);
+			mouseevent(c, m);
 		}else
-			eresized(1);
+			eresized(c, 1);
 	}
 }
 
--- a/mpas.c
+++ b/mpas.c
@@ -1,673 +1,470 @@
-/*
- * Subset of: T.128 Multipoint application sharing
- * 
- * 2.2.8.1.1.1.1 Share Control Header (TS_SHARECONTROLHEADER)
- * http://msdn.microsoft.com/en-us/library/cc240576.aspx
- *
- * totalLen[2] pduType[2] PDUSource[2] 
- *
- * 2.2.9.1.1.3 says there may be many of these.
- */
+/* Based on: T.128 Multipoint application sharing */
 #include <u.h>
 #include <libc.h>
 #include <draw.h>
 #include "dat.h"
 #include "fns.h"
-#define DBG if(0)
-//#define DBG
 
-
-uchar	cmap[256];
-
 static const char	srcDesc[] = "Plan 9";	/* sourceDescriptor (T.128 section 8.4.1) */
 
-static void		scanfastpath(uchar*,uchar*);
-static void		scancursorpdu(uchar*, uchar*);
-static void		scangraphpdu(uchar*,uchar*);
-static void		scanimgupdate(uchar*,uchar*);
-static void		scanpalette(uchar*,uchar*);
-static void		scansrvcapset(uchar*,uchar*);
-static void		sendclientinfo(void);
-static void		confirmactive(void);
-static uchar*	putsdh(uchar*,uchar*,int,int);
-static void		assync(void);
-static void		asctl(int);
-static void		asfontls(void);
-
 enum
 {
 	Bits4=		0x0F,
-	SECHSIZE=	4,
-	SCHSIZE=		6,
-	SCDSIZE=		SCHSIZE+4+4+2*2,
-};
 
-enum /* 2.2.8.1.1.1.1 Share Control Header (TS_SHARECONTROLHEADER) */
-{
-	PDUTYPE_DEMANDACTIVEPDU	= 1,	/* Demand Active PDU (section 2.2.1.13.1) */
-	PDUTYPE_CONFIRMACTIVEPDU	= 3,	/* Confirm Active PDU (section 2.2.1.13.2) */
-	PDUTYPE_DEACTIVATEALLPDU	= 6,	/* Deactivate All PDU (section 2.2.3.1) */
-	PDUTYPE_DATAPDU				= 7,	/* Data PDU */
-	PDUTYPE_SERVER_REDIR_PKT		= 10,	/* Redirection PDU (section 2.2.13.3.1). */
-};
-
-enum /* 2.2.8.1.1.1.2 Share Data Header (TS_SHAREDATAHEADER) */
-{
-	PDUTYPE2_UPDATE=	2,
-	PDUTYPE2_CONTROL=	20,
-	PDUTYPE2_POINTER=	27,
-	PDUTYPE2_INPUT=		28,
-	PDUTYPE2_SYNCHRONIZE=	31,
-	PDUTYPE2_REFRESH_RECT=	33,
-	PDUTYPE2_SUPPRESS_OUTPUT=	35,
-	PDUTYPE2_FONTLIST=	39,
-	PDUTYPE2_FONTMAP=	40,
-	PDUTYPE2_SET_ERROR_INFO_PDU=	47,
-};
-
-enum /* 2.2.9.1.1.4 Server Pointer Update PDU (TS_POINTER_PDU) */
-{
-	TS_PTRMSGTYPE_SYSTEM=	1,
-	TS_PTRMSGTYPE_POSITION=	3,
-	TS_PTRMSGTYPE_COLOR=	6,
-	TS_PTRMSGTYPE_CACHED=	7,
-	TS_PTRMSGTYPE_POINTER=	8,
-};
-
-enum /* 2.2.9.1.1.3.1.2.2 Bitmap Data */
-{
+	/* 2.2.9.1.1.3.1.2.2 Bitmap Data */
 	Bcompress=	1,
 	Pcompress=	0x20,
-};
 
-enum /* 2.2.1.15.1 Control PDU Data */
-{
-	CTRLACTION_REQUEST_CONTROL= 	1,
-	CTRLACTION_GRANTED_CONTROL=	2,
-	CTRLACTION_DETACH=				3,
-	CTRLACTION_COOPERATE=			4,
-};
+	/* 2.2.8.1.1.1.1 Share Control Header */
+	PDUactivate	= 1,	/* Demand Active PDU (section 2.2.1.13.1) */
+	PDUactivated	= 3,	/* Confirm Active PDU (section 2.2.1.13.2) */
+	PDUdeactivate	= 6,	/* Deactivate All PDU (section 2.2.3.1) */
+	PDUdata		= 7,	/* Data PDU */
 
-enum /* 2.2.1.11.1.1 Info Packet (TS_INFO_PACKET) */
-{
-	INFO_MOUSE=			0x1,
-	INFO_DISABLECTRLALTDEL=	0x2,
-	INFO_AUTOLOGON=	0x8,
-	INFO_UNICODE=		0x10,
-	INFO_MAXIMIZESHELL=	0x20,
-	INFO_COMPRESSION=	0x80,
-	CompressionTypeMask=	0x1E00,
-		PACKET_COMPR_TYPE_8K=	0<<9,	// RDP 4.0 bulk compression ≡ MPPC
-		PACKET_COMPR_TYPE_64K=	1<<9,	// RDP 5.0 bulk compression (3.1.8.4.2)
-		PACKET_COMPR_TYPE_RDP6=	2<<9,	// RDP 6.0 bulk compression
-		PACKET_COMPR_TYPE_RDP61= 3<<9,	// RDP 6.1 bulk compression
-	INFO_ENABLEWINDOWSKEY=		0x100,
-	INFO_REMOTECONSOLEAUDIO=	0x2000,
-	INFO_FORCE_ENCRYPTED_CS_PDU=	0x4000,
-	INFO_RAIL=				0x8000,
-	INFO_LOGONERRORS=		0x10000,
-	INFO_MOUSE_HAS_WHEEL=	0x20000,
-	INFO_NOAUDIOPLAYBACK=	0x80000,
-	INFO_VIDEO_DISABLE=	0x400000,
+	/* 2.2.9.1.1.4 Server Pointer Update PDU (TS_POINTER_PDU) */
+	PDUcursorwarp=	3,
 
-	PERF_DISABLE_WALLPAPER=	1<<0,
-	PERF_DISABLE_FULLWINDOWDRAG=	1<<1,
-	PERF_DISABLE_MENUANIMATIONS=	1<<2,
-	PERF_DISABLE_THEMING=	1<<3,
-	PERF_DISABLE_CURSOR_SHADOW=		1<<5,
-	PERF_DISABLE_CURSORSETTINGS=		1<<6,
-	PERF_ENABLE_FONT_SMOOTHING=	1<<7,
-};
+	/* 2.2.1.11.1.1 Info Packet (TS_INFO_PACKET) */
+	InfMouse=	1<<0,
+	InfNoctlaltdel=	1<<1,
+	InfAutologon=	1<<3,
+	InfUnicode=	1<<4,
+	InfMaxshell=	1<<5,
+	InfCompress=	1<<7,
+	InfWinkey=	1<<8,
+	Compress64k=	1<<9,	// RDP 5.0 bulk compression (3.1.8.4.2)
 
-enum
-{
-	UPDATETYPE_ORDERS		= 0, 		/* [MS-RDPEGDI] section 2.2.2.2 */
- 	UPDATETYPE_BITMAP		= 1,		/* section 2.2.9.1.1.3.1.2 */
-	UPDATETYPE_PALETTE		= 2,		/* section 2.2.9.1.1.3.1.1 */
-	UPDATETYPE_SYNCHRONIZE	= 3,		/* section 2.2.9.1.1.3.1.3 */
-};
+	PerfNoDrag=	1<<1,
+	PerfNoAnim=	1<<2,
+	PerfNoTheming=	1<<3,
+	PerfNoCursorset=	1<<6,
+	PerfFontAA=	1<<7,
+	
+	/* 2.2.9.1.1.3.1 Slow-Path Graphics Update (TS_GRAPHICS_UPDATE) */
+	UpdOrders=	0, 		
+ 	UpdBitmap=	1,	/* section 2.2.9.1.1.3.1.2 */
+	UpdCmap=	2,	/* section 2.2.9.1.1.3.1.1 */
 
-enum /* 2.2.9.1.2.1 Fast-Path Update (TS_FP_UPDATE) */
-{
-	FASTPATH_UPDATETYPE_ORDERS		= 0,	/* [MS-RDPEGDI] section 2.2.2.2 */
-	FASTPATH_UPDATETYPE_BITMAP		= 1,
-	FASTPATH_UPDATETYPE_PALETTE		= 2,
-	FASTPATH_UPDATETYPE_SYNCHRONIZE	= 3,
-	FASTPATH_UPDATETYPE_SURFCMDS	= 4,
-	FASTPATH_UPDATETYPE_PTR_NULL	= 5,
-	FASTPATH_UPDATETYPE_PTR_DEFAULT	= 6,
-	FASTPATH_UPDATETYPE_PTR_POSITION	= 8,
-	FASTPATH_UPDATETYPE_COLOR		= 9,
-	FASTPATH_UPDATETYPE_CACHED		= 10,
-	FASTPATH_UPDATETYPE_POINTER		= 11,
+	/* 2.2.9.1.2.1 Fast-Path Update (TS_FP_UPDATE) */
+	FUpdOrders=	0,
+	FUpdBitmap=	1,
+	FUpdCmap=	2,
+	FUpdWarp=	8,
 
+	/* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
+	NoBitcomphdr	= 0x0400,
 };
 
+
+/* 2.2.9.1.2.1 Fast-Path Update (TS_FP_UPDATE)
+ *
+ * updateHeader[1] compressionFlags[1]? size[2] updateData[*]
+ */
 int
-rdphandshake(int)
+getshareF(Share* as, uchar* a, uint nbytes)
 {
-	int i;
+	int hd, nb, cflags, ulen;
+	uchar *p,  *ep, *q;
 
-	if(mcsconnect(rd.fd) < 0)
+	as->type = 0;
+	if(nbytes < 3){
+		werrstr(Eshort);
 		return -1;
-	erectdom(rd.fd);
-	if(attachuser(rd.fd) < 0)
-		return -1;
-	if(joinchannel(rd.fd, rd.userchan) < 0)
-		return -1;
-	if(joinchannel(rd.fd, GLOBALCHAN) < 0)
-		return -1;
-	for(i = 0; i < nvc; i++)
-		if(joinchannel(rd.fd, vctab[i].mcsid) < 0)
-			return -1;
+	}
 
-	sendclientinfo();
-	return rd.fd;
-}
+	p = a;
+	ep = a+nbytes;
 
-void
-readnet(int fd)
-{
-	int chanid, len, flags;
-	uchar *p, *ep, buf[MAXTPDU];
-	
-	for(;;){
-		len = readpdu(fd, buf, sizeof(buf));
-		if(len <= 0){
-			if(rd.active && !rd.hupreason)
-				fprint(2, "readpdu: %r\n");
-			return;
+	hd = *p++;
+	if(hd&(1<<7))
+		cflags = *p++;
+	else
+		cflags = 0;
+	if(p+2 > ep){
+		werrstr(Eshort);
+		return -1;
+	}
+	nb = GSHORT(p);
+	p += 2;
+	q = p+nb;
+	if(cflags&Pcompress){
+		if(p+nb > ep){
+			werrstr("bad length %d in Fast-Path PDU header", nb);
+			return -1;
 		}
-		p = buf;
-		ep = buf+len;
-	
-		if(istpkt(p,ep) == 0){
-			scanfastpath(p, ep);
-			continue;
+		if((p = uncomp(p, nb, cflags, &ulen)) == nil){
+			werrstr("fast-path packet de-compression failed: %r cflags=%#x", cflags);
+			return -1;
 		}
-		if(ismcshangup(p,ep)){
-			werrstr("Disconnect Provider Ultimatum");
-			return;
-		}
+		ep = p+ulen;
+	}
 
-		chanid = mcschanid(p,ep);
-		if(chanid < 0)
-			sysfatal("mcschanid: %r");
-
-		p = mcspayload(p, ep);
-
-		flags = GSHORT(p);
-		if(!rd.licensed && flags&Slicensepk){
-			/*
-			 * 2.2.8.1.1.2.1 Basic (TS_SECURITY_HEADER)
-			 * http://msdn.microsoft.com/en-us/library/cc240579.aspx
-			 */
-			p += SECHSIZE;
-			if(flags&Slicensepk){
-				scanlicensepdu(p, ep);
-				continue;
-			}
-			if(flags&Scrypt)
-				sysfatal("legacy encryption of a Slow-Path PDU");
+	switch(hd&Bits4){
+	case FUpdOrders:
+		if(p+2>ep){
+			werrstr(Eshort);
+			return -1;
 		}
-
-		if(chanid != GLOBALCHAN){
-			scanvcpdu(p, ep, chanid);
-			continue;
+		as->type = ShUorders;
+		as->nord = GSHORT(p);
+		as->data = p+2;
+		as->ndata = ep-(p+2);
+		break;
+	case FUpdBitmap:
+		if(p+4>ep){
+			werrstr(Eshort);
+			return -1;
 		}
-
-		if(isflowpdu(p,ep))
-			continue;
-
-		scanaspdu(p,ep);
+		as->type = ShUimg;
+		as->nrect = GSHORT(p+2);
+		as->data = p+4;
+		as->ndata = ep-(p+4);
+		break;
+	case FUpdCmap:
+		as->type = ShUcmap;
+		as->data = p;
+		as->ndata = ep-p;
+		break;
+	case FUpdWarp:
+		if(p+4>ep){
+			werrstr(Eshort);
+			return -1;
+		}
+		as->type = ShUwarp;
+		as->x = GSHORT(p+0);
+		as->y = GSHORT(p+2);
+		break;
 	}
+	return q-a;
 }
 
-/* T.128 FlowPDU */
+/* 
+ * T.128 ASPDU
+ *
+ * 2.2.8.1.1.1.1 Share Control Header
+ * https://msdn.microsoft.com/en-us/library/cc240576.aspx
+ * 
+ * totalLength[2] pduType[2] pduSource[2]
+ */
 int
-isflowpdu(uchar* p, uchar* ep)
+getshareT(Share* as, uchar* p, uint nb)
 {
-	int marker;
+	int len, nsrc, type;
+	int pduType2, ctype, mtype, uptype, clen, ulen, ulenr;
+	uchar *ep;
 
-	if(p+2 > ep){
+	as->type = 0;
+	if(nb < 6){
 		werrstr(Eshort);
 		return -1;
 	}
-	marker = GSHORT(p);
-	return marker == 0x8000;
-}
+	len = GSHORT(p);
+	if(len < SCHSIZE || len > nb){
+		werrstr("bad length in Share Control PDU header");
+		return -1;
+	}
+	type = GSHORT(p+2)&Bits4;
+	as->source = GSHORT(p+4);
 
-/* 2.2.9.1.2 Server Fast-Path Update PDU
- * enabled with CanFastpath in General Capability Set
- */
-static void
-scanfastpath(uchar *p, uchar* ep)
-{
-	int hd, nb, nord, cflags, ulen, x, y, enc;
-	uchar *q, *eq;
-
-	enc = p[0]&(1<<7);
-	if(enc)
-		sysfatal("legacy encryption in a Fast-Path PDU");
-	if(p[1]&(1<<7))
-		p += 3;
-	else
-		p += 2;
-
-	eq = ep;
-	while(p+3 < ep){
-		/* updateHeader[1] compressionFlags[1]? size[2] updateData[*] */
-		hd = *p++;
-		if(hd&(1<<7))
-			cflags = *p++;
-		else
-			cflags = 0;
-		if(p+2 > ep)
-			sysfatal(Eshort);
-		nb = GSHORT(p);
-		p += 2;
-		q = p+nb;
-
-		if(cflags&Pcompress){
-			if(p+nb > ep)
-				sysfatal(Eshort);
-			if((p = uncomp(p, nb, cflags, &ulen)) == nil)
-				sysfatal("fast-path packet de-compression failed: %r cflags=%#x", cflags);
-			ep = p+ulen;
+	switch(type){
+	case PDUactivate:
+		/*
+		 * 2.2.1.13.1 Server Demand Active PDU
+		 * http://msdn.microsoft.com/en-us/library/cc240484.aspx
+		 */
+		as->type = ShActivate;
+		if(len<14){
+			werrstr(Eshort);
+			return -1;
 		}
-
-		switch(hd&Bits4){
-		case FASTPATH_UPDATETYPE_ORDERS:
-			nord = GSHORT(p);
-			scanorders(p+2, ep, nord);
-			break;
-		case FASTPATH_UPDATETYPE_BITMAP:
-			scanimgupdate(p, ep);
-			break;
-		case FASTPATH_UPDATETYPE_PALETTE:
-			scanpalette(p, ep);
-			break;
-		case FASTPATH_UPDATETYPE_PTR_POSITION:
-			x = GSHORT(p+0);
-			y = GSHORT(p+2);
-			warpmouse(x, y);
-			break;
+		as->shareid = GLONG(p+6);
+		nsrc = GSHORT(p+10);
+		as->ndata = GSHORT(p+12);
+		if(len<14+nsrc+as->ndata){
+			werrstr(Eshort);
+			return -1;
 		}
-
-		p = q;
-		ep = eq;
-	}
-
-	lockdisplay(display);
-	flushimage(display, 1);
-	unlockdisplay(display);
-}
-
-/* T.128 ASPDU */
-void
-scanaspdu(uchar* p, uchar* ep)
-{
-	int pdutype, len;
-
-	while(p+SCHSIZE <= ep){
-		len = GSHORT(p);
-		if(len < SCHSIZE || p+len > ep)
-			sysfatal("bad length in Share Control PDU header");
+		as->data = p+14+nsrc;
+		as->ncap = GSHORT(p+14+nsrc);
+		break;
+	case PDUdeactivate:
+		as->type = ShDeactivate;
+		break;
+	case PDUdata:
+		/*
+		 * 2.2.8.1.1.1.2 Share Data Header (TS_SHAREDATAHEADER)
+		 * http://msdn.microsoft.com/en-us/library/cc240577.aspx
+		 *
+		 * shareId[4] pad1[1] streamId[1] uncomprLen[2]
+		 * pduType2[1] comprType[1] comprLen[2]
+		 */
+		ep = p+nb;
 	
-		pdutype = GSHORT(p+2)&Bits4;
+		if(nb < 18){
+			werrstr("%s (%ud<18)", Eshort, nb);
+			return -1;
+		}
+	
+		ulen = GSHORT(p+12);
+		pduType2 = p[14];
+		ctype = p[15];
+		clen = GSHORT(p+16) - SCDSIZE;
+		p += 18;
+	
+		if(ctype&(1<<5)){
+			if(p+clen > ep){
+				werrstr(Eshort);
+				return -1;
+			}
+			if((p = uncomp(p, clen, ctype, &ulenr)) == nil){
+				werrstr("decompression failed: %r");
+				return -1;
+			}
+			if(ulen != ulenr+SCDSIZE){
+				werrstr("bad length after decompression");
+				return -1;
+			}
+			ep = p+ulenr;
+		}
 
-		switch(pdutype){
-		case PDUTYPE_DEMANDACTIVEPDU:
-			activating(p+SCHSIZE, p+len);
-			rd.active = 1;
+		/* BUG add more boundary checks */
+	
+		switch (pduType2){
+		case ADsync:
+			as->type = ShSync;
 			break;
-		case PDUTYPE_DATAPDU:
-			scandatapdu(p+SCHSIZE, p+len);
+		case ADctl:
+			as->type = ShCtl;
 			break;
-		case PDUTYPE_DEACTIVATEALLPDU:
-			rd.active = 0;
+		case ADfontmap:	/* denotes completion of the connection sequence */
+			as->type = ShFmap;
 			break;
+		case ADerrx:
+			/* 2.2.5.1.1 Set Error Info PDU Data (TS_SET_ERROR_INFO_PDU) */
+			as->type = ShEinfo;
+			as->err = GLONG(p);
+			break;
+		case ADdraw:
+			/* 2.2.9.1.1.3.1 Slow-Path Graphics Update (TS_GRAPHICS_UPDATE) */
+			/* not when Fast-Path is in use */
+			if(p+2 > ep){
+				werrstr("ADdraw: %s", Eshort);
+				return -1;
+			}
+			uptype = GSHORT(p);
+			switch(uptype){
+			case UpdOrders:
+				if(p+8 > ep){
+					werrstr("ShUorders: %s", Eshort);
+					return -1;
+				}
+				as->type = ShUorders;
+				as->nord = GSHORT(p+4);
+				as->data = p+8;
+				as->ndata = ep-(p+8);
+				break;
+			case UpdBitmap:
+				if(p+4 > ep){
+					werrstr("ShUimg: %s", Eshort);
+					return -1;
+				}
+				as->type = ShUimg;
+				as->nrect = GSHORT(p+2);
+				as->data = p+4;
+				as->ndata = ep-(p+4);
+				break;
+			case UpdCmap:
+				as->type = ShUcmap;
+				as->data = p;
+				as->ndata = ep-p;
+				break;
+			}
+			break;
+		case ADcursor:
+			/* 2.2.9.1.1.4 Server Pointer Update PDU (TS_POINTER_PDU) */
+			if(p+2 > ep){
+				werrstr(Eshort);
+				return -1;
+			}
+			mtype = GSHORT(p);
+			if(mtype == PDUcursorwarp){
+				if(p+8 > ep){
+					werrstr(Eshort);
+					return -1;
+				}
+				as->type = ShUwarp;
+				as->x = GSHORT(p+4);
+				as->y = GSHORT(p+6);
+				break;
+			}
 		}
-		p += len;
-	}
-}
-
-/*
- * 2.2.8.1.1.1.2 Share Data Header (TS_SHAREDATAHEADER)
- * http://msdn.microsoft.com/en-us/library/cc240577.aspx
- *
- * shareId[4] pad1[1] streamId[1] uncomprLen[2]
- * pduType2[1] comprType[1] comprLen[2]
- */
-void
-scandatapdu(uchar *p, uchar* ep)
-{
-	int pduType2, ctype, clen, ulen, ulenr;
-
-	ulen = GSHORT(p+6);
-	pduType2 = p[8];
-	ctype = p[9];
-	clen = GSHORT(p+10);
-	p += 12;
-
-	if(ctype&(1<<5)){
-		clen -= SCDSIZE;
-		if(p+clen > ep)
-			sysfatal(Eshort);
-		if((p = uncomp(p, clen, ctype, &ulenr)) == nil)
-			sysfatal("decompression failed: %r");
-		if(ulen != ulenr+SCDSIZE)
-			sysfatal("bad length after decompression");
-		ep = p+ulenr;
-	}
-
-	switch (pduType2){
-	case PDUTYPE2_SYNCHRONIZE:
-	case PDUTYPE2_CONTROL:
-	case PDUTYPE2_FONTMAP:	/* denotes completion of the connection sequence */
 		break;
-	case PDUTYPE2_SET_ERROR_INFO_PDU:
-		/* 2.2.5.1.1 Set Error Info PDU Data (TS_SET_ERROR_INFO_PDU) */
-		rd.hupreason = GLONG(p);
-		break;
-	case PDUTYPE2_UPDATE:
-		scangraphpdu(p, ep);
-		break;
-	case PDUTYPE2_POINTER:
-		scancursorpdu(p, ep);
-		break;
 	}
+	return len;
 }
 
-/* 2.2.9.1.1.3.1 Slow-Path Graphics Update (TS_GRAPHICS_UPDATE) */
-static void
-scangraphpdu(uchar *p, uchar *ep)
+/* 2.2.9.1.1.3.1.2.2 Bitmap Data (TS_BITMAP_DATA) */
+int
+getimgupd(Imgupd* iu, uchar* a, uint nb)
 {
-	int uptype, nord;
+	uchar *p, *ep, *s;
+	int opt, len;
 
-	if(p+2 > ep)
-		sysfatal(Eshort);
-
-	uptype = GSHORT(p);
-	switch(uptype){
-	case UPDATETYPE_ORDERS:
-		if(p+8 > ep)
-			sysfatal(Eshort);
-		nord = GSHORT(p+4);
-		scanorders(p+8, ep, nord);
-		break;
-	case UPDATETYPE_BITMAP:
-		scanimgupdate(p, ep);
-		break;
-	case UPDATETYPE_PALETTE:
-		scanpalette(p, ep);
-		break;
+	p = a;
+	ep = a+nb;
+	if(nb < 18){
+		werrstr(Eshort);
+		return -1;
 	}
-	lockdisplay(display);
-	flushimage(display, 1);
-	unlockdisplay(display);
-}
-
-/* 2.2.9.1.1.4 Server Pointer Update PDU (TS_POINTER_PDU) */
-static void
-scancursorpdu(uchar* p, uchar* ep)
-{
-	int type, x, y;
-
-	if(p+2 > ep)
-		sysfatal(Eshort);
-	type = GSHORT(p);
-	switch(type){
-	case TS_PTRMSGTYPE_POSITION:
-		if(p+8 > ep)
-			sysfatal(Eshort);
-		x = GSHORT(p+4);
-		y = GSHORT(p+6);
-		warpmouse(x, y);
-		break;
+	iu->x = GSHORT(p+0);
+	iu->y = GSHORT(p+2);
+	iu->xm = GSHORT(p+4);
+	iu->ym = GSHORT(p+6);
+	iu->xsz = GSHORT(p+8);
+	iu->ysz = GSHORT(p+10);
+	iu->depth = GSHORT(p+12);
+	opt = GSHORT(p+14);
+	len = GSHORT(p+16);
+	p += 18;
+	s = p+len;
+	if(s > ep){
+		werrstr(Eshort);
+		return -1;
 	}
+	iu->iscompr = (opt&Bcompress);
+	if(opt&Bcompress && !(opt&NoBitcomphdr))
+		p += 8;
+	iu->bytes = p;
+	iu->nbytes = s-p;
+	return s-a;
 }
 
-/* 2.2.9.1.1.3.1.2.1 Bitmap Update Data (TS_UPDATE_BITMAP_DATA) */
-static void
-scanimgupdate(uchar* p, uchar* ep)
+/* T.128 FlowPDU */
+int
+isflowpdu(uchar* p, uchar* ep)
 {
-	uchar *s;
-	int err, nr, len, depth, chan, opt;
-	static Image* img;
-	Rectangle r, rs, d;
+	int marker;
 
-	if(p+4 > ep)
-		sysfatal(Eshort);
-	chan = rd.chan;
-	rs = rectaddpt(Rpt(ZP, rd.dim), screen->r.min);
-	nr = GSHORT(p+2);
-	p += 4;
-
-	lockdisplay(display);
-
-	if(img==nil || !eqrect(img->r, rs)){
-		if(img != nil)
-			freeimage(img);
-		img = allocimage(display, rs, chan, 0, DNofill);
-		if(img == nil)
-			sysfatal("%r");
-	}
-
-	for(; nr>0 && p+18<ep; nr--){
-		/* 2.2.9.1.1.3.1.2.2 Bitmap Data (TS_BITMAP_DATA) */
-		d.min.x =	GSHORT(p+0);
-		d.min.y =	GSHORT(p+2);
-		d.max.x =	GSHORT(p+4) + 1;
-		d.max.y =	GSHORT(p+6) + 1;
-		r.min = ZP;
-		r.max.x =	GSHORT(p+8);
-		r.max.y =	GSHORT(p+10);
-		depth = 	GSHORT(p+12);
-		opt = 	GSHORT(p+14);
-		len =   	GSHORT(p+16);
-		p +=    	18;
-		s = p+len;
-		if(s > ep)
-			sysfatal(Eshort);
-		if(depth != img->depth)
-			sysfatal("bad image depth");
-		r = rectaddpt(r, img->r.min);
-
-		if(opt&Bcompress)
-		if(!(opt&NoBitcomphdr))
-			p += 8;
-
-		err = (opt&Bcompress? loadrle : loadbmp)(img, r, p, s-p);
-		if(err < 0)
-			sysfatal("%r");
-		draw(screen, rectaddpt(d, screen->r.min), img, nil, img->r.min);
-		p = s;
-	}
-	unlockdisplay(display);
-}
-
-static void
-scanpalette(uchar* p, uchar* ep)
-{
-	int i, n;
-
-	n = GSHORT(p+4);
-	p += 8;
-	if(n > sizeof(cmap)){
-		fprint(2, "scanpalette: palette too big");
-		return;
-	}
-	if(p+3*n > ep)
-		sysfatal(Eshort);
-	for(i = 0; i<n; i++, p+=3)
-		cmap[i] = rgb2cmap(p[0], p[1], p[2]);
-}
-
-static void
-scansrvcapset(uchar *p, uchar *ep)
-{
-	int ncap, type, len;
-
-	ncap = GSHORT(p);
-	p += 4;
-	for(; ncap>0 && p+4<ep; ncap--){
-		type = GSHORT(p+0);
-		len = GSHORT(p+2);
-		if(p+len > ep)
-			sysfatal("bad length in server's capability set");
-		switch (type){
-		case CapGeneral:
-			scangencaps(p, p+len);
-			break;
-		case CapBitmap:
-			scanbitcaps(p, p+len);
-			break;
-		}
-		p += len;
-	}
-}
-
-/*
- * 2.2.1.13.1 Server Demand Active PDU
- * http://msdn.microsoft.com/en-us/library/cc240484.aspx
- */
-void
-activating(uchar* p, uchar* ep)
-{
-	int nsrc, ncaps;
-
-	rd.shareid = GLONG(p);
-	nsrc = GSHORT(p+4);
-	ncaps = GSHORT(p+6);
-	p += 8+nsrc;
-	if(p+ncaps >= ep){
+	if(p+2 > ep){
 		werrstr(Eshort);
-		return;
+		return -1;
 	}
-	scansrvcapset(p, p+ncaps);
-	confirmactive();
-	// server accepts input since this point
-	passinput(0, InputSync, 0, 0, 0);
-
-	assync();
-	asctl(CTRLACTION_COOPERATE);
-	asctl(CTRLACTION_REQUEST_CONTROL);
-	asfontls();	// unleashes the artist
+	marker = GSHORT(p);
+	return marker == 0x8000;
 }
 
 /* 2.2.1.13.2 Client Confirm Active PDU */
-static void
-confirmactive(void)
+int
+putconfirmactive(uchar* b, uint nb, Msg* m)
 {
-	int ncap, nsrc, capsize, calen, pdusize;
-	uchar buf[512], *p, *q, *ep;
+	uchar *p, *q, *ep;
+	int n, nsrc, capsize, ndata, pdusize;
+	int userchan, shareid, originid;
+	Caps caps;
 
-	ncap = 8;
+	assert(m->type == Mactivated);
+	userchan = m->mcsuid;
+	shareid = m->shareid;
+	originid = m->originid;
+
+	caps.depth = m->depth;
+	caps.xsz = m->xsz;
+	caps.ysz = m->ysz;
+	
 	nsrc = sizeof(srcDesc);
-	capsize = 0
-		+ GENCAPSIZE
-		+ BITCAPSIZE
-		+ ORDCAPSIZE
-		+ BCACAPSIZE
-		+ PTRCAPSIZE
-		+ INPCAPSIZE
-		+ SNDCAPSIZE
-		+ GLYCAPSIZE
-		;
-	calen = 20+nsrc+capsize;
+	capsize = sizecaps(&caps);
+	ndata = 16+nsrc+capsize;
 
-	p = prebuf(buf, sizeof(buf), calen, 0, 0);
+	p = txprep(b, nb, ndata, 0, userchan, 0);
 	if(p == nil)
 		sysfatal("buffer not prepared: %r");
-	ep = p+calen;
-	pdusize = ep-buf;
+	ep = p+ndata;
+	pdusize = ep-b;
 	q = p;
 
 	/* 2.2.8.1.1.1.1 Share Control Header */
 	/* totalLength[2] pduType[2] PDUSource[2] */
-	PSHORT(p+0, calen);
-	PSHORT(p+2, PDUTYPE_CONFIRMACTIVEPDU | (1<<4));
-	PSHORT(p+4, rd.userchan);
+	PSHORT(p+0, ndata);
+	PSHORT(p+2, PDUactivated | (1<<4));
+	PSHORT(p+4, userchan);
 
 	/* shareId[4] originatorId[2] sdlen[2] caplen[2] srcdesc[sdlen] ncap[2] pad[2] */
-	PLONG(p+6, rd.shareid);
-	PSHORT(p+10, SRVCHAN);
+	PLONG(p+6, shareid);
+	PSHORT(p+10, originid);
 	PSHORT(p+12, nsrc);
-	PSHORT(p+14, capsize+4);
+	PSHORT(p+14, capsize);
 	memcpy(p+16, srcDesc, nsrc);
-	PSHORT(p+16+nsrc, ncap);
-	PSHORT(p+18+nsrc, 0);
-	p += nsrc+20;
-	p = putgencaps(p, ep);
-	p = putbitcaps(p, ep);
-	p = putordcaps(p, ep);
-	p = putbc2caps(p, ep);
-	p = putptrcaps(p, ep);
-	p = putinpcaps(p, ep);
-	p = putsndcaps(p, ep);
-	p = putglycaps(p, ep);
-	assert(p-calen == q);
-
-	writen(rd.fd, buf, pdusize);
+	p += nsrc+16;
+	if((n = putcaps(p, ep-p, &caps)) < 0)
+		sysfatal("putcaps: %r");
+	p += n;
+	assert(p-ndata == q);
+	return pdusize;
 }
 
 /* 2.2.1.11 Client Info PDU */
-static void
-sendclientinfo(void)
+int
+putclientinfo(uchar* b, uint nb, Msg* m)
 {
-	uchar a[1024], *p, *q;
-	int ndata, secflags, usize;
+	uchar *p, *q;
+	int ndata, usize;
 	int opt, perfopt;
 	int ndom, nusr, npw, nsh, nwd;
 	uchar *wdom, *wusr, *wpw, *wsh, *wwd;
+		char *dom, *usr, *pw, *sh, *wd;
+		int dologin;
 
-	ndom = strlen(rd.windom)+1;
-	nusr = strlen(rd.user)+1;
-	npw = strlen(rd.passwd)+1;
-	nsh = strlen(rd.shell)+1;
-	nwd = strlen(rd.rwd)+1;
+	assert(m->type == Dclientinfo);
+	dom = m->dom;
+	usr = m->user;
+	pw = m->pass;
+	sh = m->rshell;
+	wd = m->rwd;
+	dologin = m->dologin;
+
+	ndom = strlen(dom)+1;
+	nusr = strlen(usr)+1;
+	npw = strlen(pw)+1;
+	nsh = strlen(sh)+1;
+	nwd = strlen(wd)+1;
 	wdom = emalloc(4*ndom);
 	wusr = emalloc(4*nusr);
 	wpw = emalloc(4*npw);
 	wsh = emalloc(4*nsh);
 	wwd = emalloc(4*nwd);
-	ndom = toutf16(wdom, 4*ndom, rd.windom, ndom);
-	nusr = toutf16(wusr, 4*nusr, rd.user, nusr);
-	npw = toutf16(wpw, 4*npw, rd.passwd, npw);
-	nsh = toutf16(wsh, 4*nsh, rd.shell, nsh);
-	nwd = toutf16(wwd, 4*nwd, rd.rwd, nwd);
+	ndom = toutf16(wdom, 4*ndom, dom, ndom);
+	nusr = toutf16(wusr, 4*nusr, usr, nusr);
+	npw = toutf16(wpw, 4*npw, pw, npw);
+	nsh = toutf16(wsh, 4*nsh, sh, nsh);
+	nwd = toutf16(wwd, 4*nwd, wd, nwd);
 
 	ndata = 18+ndom+nusr+npw+nsh+nwd+188;
 	
 	opt = 0
-	| INFO_MOUSE
-	| INFO_UNICODE
-	| INFO_DISABLECTRLALTDEL
-	| INFO_MAXIMIZESHELL
-	| INFO_ENABLEWINDOWSKEY
-	| INFO_FORCE_ENCRYPTED_CS_PDU
-	| INFO_COMPRESSION
-	| PACKET_COMPR_TYPE_8K
-	| PACKET_COMPR_TYPE_64K
-	;
+		| InfMouse
+		| InfUnicode
+		| InfNoctlaltdel
+		| InfMaxshell
+		| InfWinkey
+		| InfCompress
+		| Compress64k
+		;
+	if(dologin)
+		opt |= InfAutologon;
 	perfopt = 0
-	| PERF_DISABLE_FULLWINDOWDRAG
-	| PERF_DISABLE_MENUANIMATIONS
-	| PERF_DISABLE_CURSORSETTINGS
-	| PERF_DISABLE_THEMING
-	;
-	if(rd.autologon)
-		opt |= INFO_AUTOLOGON;
+		| PerfNoDrag
+		| PerfNoAnim
+		| PerfNoCursorset
+		| PerfNoTheming
+		;
 
-	secflags = Sinfopk;
-	p = prebuf(a, sizeof(a), ndata, 0, secflags);
+	p = txprep(b, nb, ndata, 0, m->mcsuid, Sinfopk);
 	if(p == nil)
-		sysfatal("sendclientinfo: %r");
-	usize = p+ndata-a;
+		return -1;
+	usize = p+ndata-b;
 	q = p;
 
-	PLONG(q+0, 0);	// codePage; langId when opt&INFO_UNICODE
+	PLONG(q+0, 0);	// codePage; langId when opt&InfUnicode
 	PLONG(q+4, opt);
 	PSHORT(q+8, ndom-2);
 	PSHORT(q+10, nusr-2);
@@ -703,182 +500,24 @@
 	free(wsh);
 	free(wwd);
 
-	writen(rd.fd, a, usize);
-	
+	return usize;
 }
 
 /* Share-Data Header (2.2.8.1.1.1.2 Share Data Header) */
-static uchar*
-putsdh(uchar* p, uchar* ep, int ndata, int pduType2)
+uchar*
+putsdh(uchar* p, uchar* ep, int ndata, int pduType2, int originid, int shareid)
 {
 	if(p+18>ep)
 		sysfatal(Eshort);
 	PSHORT(p+0, ndata);
-	PSHORT(p+2, (PDUTYPE_DATAPDU | 0x10));
-	PSHORT(p+4, rd.userchan);
-	PLONG(p+6, rd.shareid);
-	p[10] = 0;
-	p[11] = 1;
-	PSHORT(p+12, ndata);	// rdesktop used to put ndata-14...
+	PSHORT(p+2, (PDUdata | (1<<4)));
+	PSHORT(p+4, originid);
+	PLONG(p+6, shareid);
+	p[10] = 0;	// pad1
+	p[11] = 1;	// streamId: 1=Low, 2=Medium, 4=High
+	PSHORT(p+12, ndata);
 	p[14] = pduType2;
-	p[15] = 0; // ctype
-	PSHORT(p+16, 0); // clen
+	p[15] = 0; // compressedType 
+	PSHORT(p+16, 0); // compressedLength 
 	return p+18;
 }
-
-/* 2.2.1.14 Client Synchronize PDU */
-static void
-assync(void)
-{
-	uchar a[64], *p, *q;
-	int ndata, usize; 
-
-	ndata = 4+SCDSIZE;
-	p = prebuf(a, sizeof(a), ndata, 0, 0);
-	if(p == nil)
-		sysfatal("buffer not prepared: %r");
-	usize = p+ndata-a;
-
-	q = putsdh(p, p+ndata, ndata, PDUTYPE2_SYNCHRONIZE);
-	PSHORT(q+0, 1);
-	PSHORT(q+2, 1002);	// target MCS userId
-	writen(rd.fd, a, usize);
-}
-
-/* 2.2.1.15.1 Control PDU Data (TS_CONTROL_PDU) */
-static void
-asctl(int action)
-{
-	uchar a[64], *p, *q;
-	int ndata, usize; 
-
-	ndata = 8+SCDSIZE;
-	p = prebuf(a, sizeof(a), ndata, 0, 0);
-	if(p == nil)
-		sysfatal("buffer not prepared: %r");
-	usize = p+ndata-a;
-
-	q = putsdh(p, p+ndata, ndata, PDUTYPE2_CONTROL);
-	PSHORT(q+0, action);
-	PSHORT(q+2, 0);	// grantId[2]
-	PLONG(q+4, 0); 	// controlId[2]
-	writen(rd.fd, a, usize);
-}
-
-/* 2.2.1.18 Client Font List PDU */
-static void
-asfontls(void)
-{
-	uchar a[64], *p, *q;
-	int ndata, usize; 
-
-	ndata = 8+SCDSIZE;
-	p = prebuf(a, sizeof(a), ndata, 0, 0);
-	if(p == nil)
-		sysfatal("buffer not prepared: %r");
-	usize = p+ndata-a;
-
-	q = putsdh(p, p+ndata, ndata, PDUTYPE2_FONTLIST);
-	PSHORT(q+0, 0);	// numberFonts
-	PSHORT(q+2, 0);	// totalNumFonts
-	PSHORT(q+4, 2+1);	// listFlags: 1=first, 2=last
-	PSHORT(q+6, 50);	// entrySize
-
-	writen(rd.fd, a, usize);
-}
-
-/* 2.2.8.1.1.3.1.1 Slow-Path Input Event (TS_INPUT_EVENT) */
-void
-passinput(ulong msec, int mtype, int iflags, int iarg1, int iarg2)
-{
-	uchar a[64], *p, *q;
-	int ndata, usize; 
-
-	ndata = 16+SCDSIZE;
-	p = prebuf(a, sizeof(a), ndata, 0, 0);
-	if(p == nil)
-		sysfatal("buffer not prepared: %r");
-	usize = p+ndata-a;
-
-	q = putsdh(p, p+ndata, ndata, PDUTYPE2_INPUT);
-	PSHORT(q+0, 1); // numEvents
-	PSHORT(q+2, 0);
-	// 2.2.8.1.1.3.1.1 Slow-Path Input Event
-	PLONG(q+4, msec);
-	PSHORT(q+8, mtype);
-	// slowPathInputData[*]
-	PSHORT(q+10, iflags);
-	PSHORT(q+12, iarg1);
-	PSHORT(q+14, iarg2);
-
-	writen(rd.fd, a, usize);
-}
-
-/* 2.2.11.3.1 Suppress Output PDU Data (TS_SUPPRESS_OUTPUT_PDU) */
-void
-turnupdates(int allow)
-{
-	uchar a[64], *p, *q;
-	int ndata, usize; 
-
-	ndata = (allow?12:4)+SCDSIZE;
-	p = prebuf(a, sizeof(a), ndata, 0, 0);
-	if(p == nil)
-		sysfatal("buffer not prepared: %r");
-	usize = p+ndata-a;
-
-	q = putsdh(p, p+ndata, ndata, PDUTYPE2_SUPPRESS_OUTPUT);
-	q[0] = (allow?1:0); 
-	memset(q+1, 3, 0);
-	if(allow){
-		PSHORT(q+4, 0);	// left
-		PSHORT(q+6, 0);	// top
-		PSHORT(q+8, rd.dim.x-1);	// right
-		PSHORT(q+10, rd.dim.y-1);	// bottom
-	}
-	writen(rd.fd, a, usize);
-}
-
-int
-sizesechdr(int secflags)
-{
-	if(secflags&Scrypt)
-		return  12;	// flags[4] mac[8]
-	else if(secflags)
-		return 4;		// flags[4]
-	return 0;
-}
-
-uchar*
-prebuf(uchar* buf, int nb, int ndata, int chanid, int secflags)
-{
-	int n, len, shdsize;
-	uchar *p, *ep;
-
-	if(chanid==0)
-		chanid = GLOBALCHAN;
-
-	shdsize = sizesechdr(secflags);
-	len = TPDATAFIXLEN+8+shdsize+ndata;
-	if(len>nb){
-		werrstr("%s: provided %d, need %d, data %d", Esmall, nb, len, ndata);
-		return nil;
-	}
-	ep = buf+len;
-
-	ndata = len-TPDATAFIXLEN;
-	n = mktpdat(buf, nb, ndata);
-	if(n < 0)
-		sysfatal("mktpdat: %r");
-	p = buf+TPDATAFIXLEN;
-
-	ndata -= 8;
-	if(mkmcssdr(p, ep-p, ndata, chanid) < 0)
-		sysfatal("mkmcssdr: %r");
-	p += 8;
-
-	if(shdsize > 0)
-		PLONG(p, secflags);
-	return p + shdsize;
-}
-
--- /dev/null
+++ b/msg.c
@@ -1,0 +1,452 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+enum /* Rdpnego.type */
+{
+	Tnego=	1,
+	Rnego=	2,
+};
+
+typedef	struct	Rdpnego Rdpnego;
+struct Rdpnego
+{
+	int	type;
+	int	flags;
+	int	proto;
+};
+
+static int
+putnego(uchar* b, uint nb, Rdpnego* m)
+{
+	int len;
+
+	len = 8;
+	if(nb < 8){
+		werrstr(Esmall);
+		return -1;
+	}
+	b[0] = m->type;
+	b[1] = m->flags;
+	PSHORT(b+2, len);
+	PLONG(b+4, m->proto);
+
+	return len;
+}
+
+static int
+getnego(Rdpnego* m, uchar* b, uint nb)
+{
+	int len;
+
+	if(nb < 8){
+		werrstr(Eshort);
+		return -1;
+	}
+	m->type = b[0];
+	m->flags = b[1];
+	len = GSHORT(b+2);
+	m->proto = GLONG(b+4);
+	if(len != 8){
+		werrstr("bad length in RDP Nego Response");
+		return -1;
+	}
+	return len;
+}
+
+int
+sizesechdr(int secflags)
+{
+	if(secflags&Scrypt)
+		return  12;	// flags[4] mac[8]
+	else if(secflags)
+		return 4;		// flags[4]
+	return 0;
+}
+
+uchar*
+txprep(uchar* buf, int nb, int ndata, int chanid, int mcsuid, int secflags)
+{
+	int n, len, shdsize;
+	uchar *p, *ep;
+
+	if(chanid==0)
+		chanid = GLOBALCHAN;
+
+	shdsize = sizesechdr(secflags);
+	len = TPDATAFIXLEN+8+shdsize+ndata;
+	if(len>nb){
+		werrstr("%s: provided %d, need %d, data %d", Esmall, nb, len, ndata);
+		return nil;
+	}
+	ep = buf+len;
+
+	ndata = len-TPDATAFIXLEN;
+	n = mktpdat(buf, nb, ndata);
+	if(n < 0)
+		sysfatal("mktpdat: %r");
+	p = buf+TPDATAFIXLEN;
+
+	ndata -= 8;
+	if(putmsdr(p, ep-p, ndata, chanid, mcsuid) < 0)
+		sysfatal("putmsdr: %r");
+	p += 8;
+
+	if(shdsize > 0)
+		PLONG(p, secflags);
+	return p + shdsize;
+}
+
+int
+putmsg(uchar* b, uint nb, Msg* m)
+{
+	int n, nld, len;
+	uchar *p, *ep, *q;
+	Rdpnego neg;
+
+	switch(m->type){
+	case Xconnect:
+		/* 5.4.2.1 Negotiation-Based Approach */
+		nld = 25+8;
+		n = mktpcr(b, nb, nld);
+		if(n < 0)
+			return -1;
+		p = b+n-nld;
+		ep = b+n;
+	
+		memcpy(p, "Cookie: mstshash=eltons\r\n", 25);
+		neg = (Rdpnego){Tnego, 0, m->negproto};
+		if(putnego(p+25, ep-p, &neg) != 8){
+			werrstr("pnego failed: %r");
+			return -1;
+		}
+		return n;
+		
+	case Xhangup:
+		return mktpdr(b, nb, 0);
+
+	case Mattach:
+		n = mktpdat(b, nb, 1);
+		if(n < 0)
+			return -1;
+		b[TPDATAFIXLEN] = (Maur<<2);
+		return n;
+
+	case Mjoin:
+		n = mktpdat(b, nb, 5);
+		if(n < 0)
+			return -1;
+		p = b+TPDATAFIXLEN;
+		p[0] = (Mcjr << 2);
+		PSHORTB(p+1, m->mcsuid);
+		PSHORTB(p+3, m->chanid);
+		return n;
+
+	case Merectdom:
+		n = mktpdat(b, nb, 5);
+		if(n < 0)
+			return -1;
+		p = b+TPDATAFIXLEN;
+		p[0] = (Medr << 2);
+		PSHORTB(p+1, 1);
+		PSHORTB(p+3, 1);
+		return n;
+
+	case Mconnect:
+		/* 2.2.1.3 Client MCS Connect Initial PDU with GCC Conference Create Request */
+		nld = sizegccr(m);
+		len = mktpdat(b, nb, nld+MCSCIFIXLEN);
+		if(len < 0){
+			werrstr("mktpdat: %r");
+			return -1;
+		}
+		p = b+TPDATAFIXLEN;
+		ep = b+nb;
+		n = mkmcsci(p, ep-p, nld);
+		if(n != nld+MCSCIFIXLEN){
+			werrstr("mkmcsci: %r");
+			return -1;
+		}
+		n = putgccr(p+MCSCIFIXLEN, nld, m);
+		if(n != nld){
+			werrstr("putgccr: %r");
+			return -1;
+		}
+		return len;
+
+	case Dclientinfo:
+		/* 2.2.1.11 Client Info PDU */
+		return putclientinfo(b, nb, m);
+
+	case Mactivated:
+		/* 2.2.1.13.2 Client Confirm Active PDU */
+		return putconfirmactive(b, nb, m);
+
+	case Mvcdata:
+		nld = m->ndata+8;
+		p = txprep(b, nb, nld, m->chanid, m->originid, 0);
+		if(p == nil)
+			return -1;
+		PLONG(p+0, m->len);
+		PLONG(p+4, m->flags);
+		memcpy(p+8, m->data, m->ndata);
+		len = p+nld-b;
+		return len;
+
+	case Async:
+		/* 2.2.1.14 Client Synchronize PDU */
+		nld = 4+SCDSIZE;
+		if((p = txprep(b, nb, nld, 0, m->originid, 0)) == nil)
+			return -1;
+		len = p+nld-b;
+		q = putsdh(p, p+nld, nld, ADsync, m->originid, m->shareid);
+		PSHORT(q+0, 1);	// sync message type
+		PSHORT(q+2, m->mcsuid);	// target MCS userId
+		return len;
+
+	case Actl:
+		/* 2.2.1.15 Control PDU */
+		nld = 8+SCDSIZE;
+		if((p = txprep(b, nb, nld, 0, m->originid, 0)) == nil)
+			return -1;
+		len = p+nld-b;
+	
+		q = putsdh(p, p+nld, nld, ADctl, m->originid, m->shareid);
+		/* action[2] grantId[2] controlId[2] */
+		PSHORT(q+0, m->action);
+		PSHORT(q+2, 0);
+		PLONG(q+4, 0);
+		return len;
+
+	case Afontls:
+		/* 2.2.1.18 Client Font List PDU */
+		nld = 8+SCDSIZE;
+		if((p = txprep(b, nb, nld, 0, m->originid, 0)) == nil)
+			return -1;
+		len = p+nld-b;
+	
+		q = putsdh(p, p+nld, nld, ADfontlist, m->originid, m->shareid);
+		PSHORT(q+0, 0);	// numberFonts
+		PSHORT(q+2, 0);	// totalNumFonts
+		PSHORT(q+4, 2+1);	// listFlags: 1=first, 2=last
+		PSHORT(q+6, 50);	// entrySize
+		return len;
+
+	case Ainput:
+		/* 2.2.8.1.1.3.1.1 Slow-Path Input Event (TS_INPUT_EVENT) */
+		nld = 16+SCDSIZE;
+		if((p = txprep(b, nb, nld, 0, m->originid, 0)) == nil)
+			return -1;
+		len = p+nld-b;
+	
+		q = putsdh(p, p+nld, nld, ADinput, m->originid, m->shareid);
+		PSHORT(q+0, 1);	/* numEvents */
+		PSHORT(q+2, 0);
+		PLONG(q+4, m->msec);
+		PSHORT(q+8, m->mtype);
+		PSHORT(q+10, m->flags);
+		PSHORT(q+12, m->iarg1);
+		PSHORT(q+14, m->iarg2);
+		return len;
+
+	case Lreq:
+	case Lnolicense:
+		if((nld = sizelicensemsg(m)) < 0)
+			return -1;
+		if((p = txprep(b, nb, nld, 0, m->originid, Slicensepk)) == nil)
+			return -1;
+		len = p+nld-b;
+		if(putlicensemsg(p, nld, m) != nld){
+			werrstr("putlicensemsg: %r");
+			return -1;
+		}
+		return len;
+
+	case Dsupress:
+		/* 2.2.11.3 Suppress Output PDU */
+		nld = (m->allow?12:4)+SCDSIZE;
+		if((p = txprep(b, nb, nld, 0, m->originid, 0)) == nil)
+			return -1;
+		len = p-b+nld;
+	
+		q = putsdh(p, p+nld, nld, ADsupress, m->originid, m->shareid);
+		q[0] = (m->allow?1:0); 
+		memset(q+1, 3, 0);
+		if(m->allow){
+			PSHORT(q+4, 0);	// left
+			PSHORT(q+6, 0);	// top
+			PSHORT(q+8, m->xsz-1);	// right
+			PSHORT(q+10, m->ysz-1);	// bottom
+		}
+		return len;
+
+	default:
+		werrstr("putmsg: unsupported type");
+		return 0;
+	}
+};
+
+int
+getmsg(Msg* m, uchar* b, uint nb)
+{
+	uchar *p, *ep;
+	int type, mtag, btag, mr, secflg, sctlver;
+	Rdpnego neg;
+
+	p = b;
+	ep = b+nb;
+
+	if(istpkt(p, ep) == 0){
+		/*
+		 * 2.2.9.1.2 Server Fast-Path Update PDU
+		 * enabled with CanFastpath in General Capability Set
+		 */
+		if(p[0]&(1<<7)){
+			werrstr("legacy encryption in a Fast-Path PDU");
+			return -1;
+		}
+		if(p[1]&(1<<7))
+			p += 3;
+		else
+			p += 2;
+		m->type = Aupdate;
+		m->data = p;
+		m->ndata = ep-p;
+		m->getshare = getshareF;
+		return nb;
+	}
+
+	type = tptype(b, b+nb);
+	switch(type){
+	default:
+		werrstr("unknown TPDU type %d", type);
+		return -1;
+	case ConCfrm:
+		/* 5.4.2.1 Negotiation-Based Approach */
+		m->type = Xconnected;
+		m->negproto = 0;
+		if((p = tpdat(p, ep)) != nil && getnego(&neg, p, ep-p))
+			if(neg.type == Rnego)
+				m->negproto = neg.proto;
+		return nb;
+	case Data:
+		p = tpdat(p, ep);
+		if(p+2 > ep){
+			werrstr(Eshort);
+			return -1;
+		}
+
+		/* try ASN.1 PER: DomainMCSPDU are encoded this way */
+		mtag = p[0]>>2;
+		switch(mtag){
+		default:
+			werrstr("unknown MCS tag %d", mtag);
+			return -1;
+		case Mauc:
+			m->type = Mattached;
+			m->mcsuid = 0;
+			mr = p[1];
+			if(mr != 0){
+				werrstr("Mauc error result: %d", mr);
+				return -1;
+			}
+			if((p[0])&2){
+				if(p+4 > ep){
+					werrstr(Eshort);
+					return -1;
+				}
+				m->mcsuid = GSHORTB(p+2);
+			}
+			return nb;
+		case Mcjc:
+			m->type = Mjoined;
+			mr = p[1];
+			if(mr != 0){
+				werrstr("Mcjc error result: %d", mr);
+				return -1;
+			}
+			return nb;
+		case Mdpu:
+			m->type = Mclosing;
+			return nb;
+		case Msdi:
+			m->chanid = mcschan(b, b+nb);
+			if(m->chanid < 0){
+				werrstr("%d: bad MCS channel id", m->chanid);
+				return -1;
+			}
+			p = mcsdat(b, b+nb);
+			if(p == nil)
+				return -1;
+			if(m->chanid != GLOBALCHAN){
+				m->type = Mvcdata;
+				m->len = GLONG(p+0);
+				m->flags = GLONG(p+4);
+				m->data = p+8;
+				m->ndata = ep-p-8;
+				if(m->len > 8*1024*1024){
+					werrstr("bad length in virtual channel PDU header");
+					return -1;
+				}
+				return nb;
+			}
+			if(isflowpdu(p,ep)){
+				m->type = Aflow;
+				return nb;
+			}
+			secflg = GSHORT(p);
+			sctlver = GSHORT(p+2)>>4;
+			if(secflg&Slicensepk && sctlver != 1)
+				return getlicensemsg(m, p+4, ep-(p+4));
+			
+			m->type = Aupdate;
+			m->data = p;
+			m->ndata = ep-p;
+			m->getshare = getshareT;
+			return nb;
+		case 31:
+			/* try ANS.1 BER: T.125 ConnectMCSPDU are encoded this way */
+			gbtag(p, ep, &btag);
+			switch(btag){
+			case Mcr:
+				return getmcr(m, p, ep-p);
+			default:
+				werrstr("unknown MCS BER tag %d", btag);
+				return -1;
+			}
+		}
+		
+	}
+};
+
+int
+readmsg(Rdp* c, Msg* m)
+{
+	uchar* buf;
+	int fd, n;
+	uint nb;
+
+	fd = c->fd;
+	buf = c->rbuf;
+	nb = sizeof c->rbuf;
+
+	if((n = readpdu(fd, buf, nb)) < 0 || getmsg(m, buf, n) <= 0)
+		return -1;
+
+	return n;
+}
+
+int
+writemsg(Rdp* c, Msg* m)
+{
+	uchar buf[MAXTPDU];
+	int fd, n;
+
+	fd = c->fd;
+	if((n = putmsg(buf, sizeof buf, m)) < 0 || writen(fd, buf, n) != n)
+		return -1;
+
+	return n;
+}
--- a/rd.c
+++ b/rd.c
@@ -7,9 +7,7 @@
 
 Rdp rd = {
 	.fd = -1,
-	.chan = RGB16,
 	.depth = 16,
-	.dim = {800, 600},
 	.windom = "",
 	.passwd = "",
 	.shell = "",
@@ -56,7 +54,7 @@
 		return mpid;
 	}
 	atexit(atexitkiller);
-	readdevmouse();
+	readdevmouse(&rd);
 	exits("mouse eof");
 	return 0;
 }
@@ -74,7 +72,7 @@
 		return pid;
 	}
 	atexit(atexitkiller);
-	readkbd();
+	readkbd(&rd);
 	exits("kbd eof");
 	return 0;
 }
@@ -93,7 +91,8 @@
 		return pid;
 	}
 	atexit(atexitkiller);
-	pollsnarf();
+	initsnarf();
+	pollsnarf(&rd);
 	exits("snarf eof");
 	return 0;
 }
@@ -154,25 +153,6 @@
 		break;
 	case 'a':
 		rd.depth = atol(EARGF(usage()));
-		switch(rd.depth){
-		case 8:
-			rd.chan = CMAP8;
-			break;
-		case 15:
-			rd.chan = RGB15;
-			break;
-		case 16:
-			rd.chan = RGB16;
-			break;
-		case 24:
-			rd.chan = RGB24;
-			break;
-		case 32:
-			rd.chan = XRGB32;
-			break;
-		default:
-			sysfatal("bad color depth");
-		}
 		break;
 	case '0':
 		rd.wantconsole = 1;
@@ -199,9 +179,10 @@
 		else {
 			rd.user = creds->user;
 			rd.passwd = creds->passwd;
-			rd.autologon = 1;
 		}
-	}
+	}else
+		rd.user = "";
+	initvc(&rd);
 
 	addr = netmkaddr(server, "tcp", "3389");
 	fd = dial(addr, nil, nil, nil);
@@ -208,8 +189,8 @@
 	if(fd < 0)
 		sysfatal("dial %s: %r", addr);
 	rd.fd = fd;
-	if(x224connect(fd) < 0)
-		sysfatal("initial handshake failed: %r");
+	if(x224connect(&rd) < 0)
+		sysfatal("connect: %r");
 
 	if(initdraw(drawerror, nil, rd.label) < 0)
 		sysfatal("initdraw: %r");
@@ -216,34 +197,199 @@
 	display->locking = 1;
 	unlockdisplay(display);
 
-	rd.dim.y = Dy(screen->r);
-	rd.dim.x = Dx(screen->r);
-	rd.dim.x = (rd.dim.x + 3) & ~3;	/* ensure width divides by 4 */
+	rd.ysz = Dy(screen->r);
+	rd.xsz = (Dx(screen->r) +3) & ~3;
 
-	if(rdphandshake(fd) < 0)
-		sysfatal("handshake failed: %r");
+	if(rdphandshake(&rd) < 0)
+		sysfatal("handshake: %r");
 
 	atexit(atexitkiller);
 	atexitkill(getpid());
 	atexitkill(startmouseproc());
 	atexitkill(startkbdproc());
-	initsnarf();
 	atexitkill(startsnarfproc());
 
-	readnet(rd.fd);
+	readnet(&rd);
 
-	x224disconnect(rd.fd);
-	close(rd.fd);
-	lockdisplay(display);
-	closedisplay(display);
+	x224disconnect(&rd);
 
 	if(!rd.active)
 		exits(nil);
 	if(rd.hupreason)
-		sysfatal("hangup: %d", rd.hupreason);
+		sysfatal("disconnect reason code %d", rd.hupreason);
 	sysfatal("hangup");
 }
 
+void
+readnet(Rdp* c)
+{
+	Msg r;
+	
+	for(;;){
+		if(readmsg(c, &r) <= 0)
+			return;
+
+		switch(r.type){
+		case Mclosing:
+			return;
+		case Mvcdata:
+			scanvcdata(c, &r);
+			break;
+		case Aupdate:
+			scanupdates(c, &r);
+			break;
+		}
+	}
+}
+
+void
+scanupdates(Rdp* c, Msg* m)
+{
+	int n;
+	uchar *p, *ep;
+	Share u;
+
+	p = m->data;
+	ep = m->data + m->ndata;
+
+	for(; p < ep; p += n){
+		n = m->getshare(&u, p, ep-p);
+		if(n < 0)
+			sysfatal("scanupdates: %r");
+
+		switch(u.type){
+		case ShDeactivate:
+			deactivating(c, &u);
+			break;
+		case ShEinfo:
+			c->hupreason = u.err;
+			break;
+		case ShUorders:
+			scanorders(c, &u);
+			break;
+		case ShUimg:
+			scanimgupdate(c, &u);
+			break;
+		case ShUcmap:
+			scancmap(c, &u);
+			break;
+		case ShUwarp:
+			warpmouse(u.x, u.y);
+			break;
+		}
+	}
+}
+
+/* 2.2.1.13.1 Server Demand Active PDU */
+void
+activating(Rdp* c, Share* as)
+{
+	Caps rcaps;
+
+	if(getcaps(&rcaps, as->data, as->ndata) < 0)
+		sysfatal("getcaps: %r");
+	if(!rcaps.canrefresh)
+		sysfatal("server can not Refresh Rect PDU");
+	if(!rcaps.cansupress)
+		sysfatal("server can not Suppress Output PDU");
+	if(!rcaps.bitmap)
+		sysfatal("server concealed their Bitmap Capabilities");
+
+	switch(rcaps.depth){
+	default:	sysfatal("Unsupported server color depth: %uhd\n", rcaps.depth);
+	case 8:	c->chan = CMAP8; break;
+	case 15:	c->chan = RGB15; break;
+	case 16:	c->chan = RGB16; break;
+	case 24:	c->chan = RGB24; break;
+	case 32:	c->chan = XRGB32; break;
+	}
+	c->depth = rcaps.depth;
+	c->xsz = rcaps.xsz;
+	c->ysz = rcaps.ysz;
+	c->srvchan = as->source;
+	c->shareid = as->shareid;
+	c->active = 1;
+}
+
+void
+deactivating(Rdp* c, Share*)
+{
+	c->active = 0;
+}
+
+/* 2.2.9.1.1.3.1.2.1 Bitmap Update Data (TS_UPDATE_BITMAP_DATA) */
+void
+scanimgupdate(Rdp *c, Share* as)
+{
+	uchar* p, *ep;
+	int n, err, nr;
+	static Image* img;
+	Rectangle r, rs, d;
+	Imgupd iu;
+
+	assert(as->type == ShUimg);
+	p = as->data;
+	ep = as->data + as->ndata;
+	nr = as->nrect;
+
+	rs = rectaddpt(Rpt(ZP, Pt(c->xsz, c->ysz)), screen->r.min);
+
+	lockdisplay(display);
+
+	if(img==nil || !eqrect(img->r, rs)){
+		if(img != nil)
+			freeimage(img);
+		img = allocimage(display, rs, c->chan, 0, DNofill);
+		if(img == nil)
+			sysfatal("%r");
+	}
+
+	while(p<ep && nr>0){
+		/* 2.2.9.1.1.3.1.2.2 Bitmap Data (TS_BITMAP_DATA) */
+		if((n = getimgupd(&iu, p, ep-p)) < 0)
+			sysfatal("getimgupd: %r");
+		if(iu.depth != img->depth)
+			sysfatal("bad image depth");
+
+		d.min = Pt(iu.x, iu.y);
+		d.max = Pt(iu.xm+1, iu.ym+1);
+		r.min = ZP;
+		r.max = Pt(iu.xsz, iu.ysz);
+		r = rectaddpt(r, img->r.min);
+
+		err = (iu.iscompr? loadrle : loadbmp)(img, r, iu.bytes, iu.nbytes, c->cmap);
+		if(err < 0)
+			sysfatal("%r");
+		draw(screen, rectaddpt(d, screen->r.min), img, nil, img->r.min);
+		p += n;
+		nr--;
+	}
+	flushimage(display, 1);
+	unlockdisplay(display);
+}
+
+void
+scancmap(Rdp* c, Share* as)
+{
+	int i, n;
+	uchar *p,  *ep, *cmap;
+
+	p = as->data;
+	ep = as->data + as->ndata;
+	cmap = c->cmap;
+
+	n = GSHORT(p+4);
+	p += 8;
+	if(n > sizeof(c->cmap)){
+		fprint(2, "scancmap: data too big");
+		return;
+	}
+	if(p+3*n > ep)
+		sysfatal(Eshort);
+	for(i = 0; i<n; p+=3)
+		cmap[i++] = rgb2cmap(p[0], p[1], p[2]);
+}
+
 void*
 emalloc(ulong n)
 {
@@ -269,7 +415,7 @@
 }
 
 char*
-estrdup(const char *s)
+estrdup(char *s)
 {
 	char *b;
 
--- /dev/null
+++ b/rpc.c
@@ -1,0 +1,327 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+int	mcsconnect(Rdp*);
+int	attachuser(Rdp*);
+int	joinchannel(Rdp*,int,int);
+
+int
+x224connect(Rdp* c)
+{
+	Msg t, r;
+
+	t.type = Xconnect;
+	t.negproto = ProtoTLS;
+	if(writemsg(c, &t) <= 0)
+		return -1;
+	if(readmsg(c, &r) <= 0)
+		return -1;
+	if(r.type != Xconnected){
+		werrstr("X.224: protocol botch");
+		return -1;
+	}
+	if(r.negproto&ProtoTLS == 0){
+		werrstr("server refused STARTTLS");
+		return -1;
+	}
+
+	if(starttls(c) < 0)
+		return -1;
+
+	c->sproto = r.negproto;
+	return 0;
+}
+
+int
+x224disconnect(Rdp* c)
+{
+	Msg t;
+
+	t.type = Xhangup;
+	return writemsg(c, &t);
+}
+
+int
+mcsconnect(Rdp* c)
+{
+	Msg t, r;
+		
+	t.type = Mconnect;
+	t.ver = 0x80004;	/* RDP5 */
+	t.depth = c->depth;
+	t.xsz = c->xsz;
+	t.ysz = c->ysz;
+	t.sysname = c->local;
+	t.sproto = c->sproto;
+	t.wantconsole = c->wantconsole;
+	t.vctab = c->vc;
+	t.nvc = c->nvc;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("Connect Initial: writemsg: %r");
+
+	if(readmsg(c, &r) <= 0)
+		sysfatal("Connect Response: readmsg: %r");
+	if(r.type != Mconnected)
+		sysfatal("Connect Response: protocol botch");
+	if(r.ver < t.ver)
+		sysfatal("Connect Response: unsupported RDP protocol version %x", r.ver);
+
+	return 0;
+}
+
+void
+erectdom(Rdp* c)
+{
+	Msg t;
+	
+	t.type = Merectdom;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("Erect Domain: writemsg: %r");
+}
+
+int
+attachuser(Rdp* c)
+{
+	Msg t, r;
+
+	t.type = Mattach;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("attachuser: writemsg: %r");
+	if(readmsg(c, &r) <= 0)
+		sysfatal("attachuser: readmsg: %r");
+	if(r.type != Mattached)
+		sysfatal("attachuser: protocol botch");
+
+	c->mcsuid = r.mcsuid;
+	c->userchan = r.mcsuid;
+	return 0;
+}
+
+int
+joinchannel(Rdp* c, int mcsuid, int chanid)
+{
+	Msg t, r;
+	
+	t.type = Mjoin;
+	t.mcsuid = mcsuid;
+	t.chanid = chanid;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("Channel Join: writemsg: %r");
+	if(readmsg(c, &r) <= 0)
+		sysfatal("Channel Join: readmsg: %r");
+	if(r.type != Mjoined)
+		sysfatal("Channel Join: protocol botch");
+
+	/* BUG: ensure the returned and requested chanids match */
+
+	return 0;
+}
+
+int
+rdphandshake(Rdp* c)
+{
+	int i;
+	int mcsuid, userchan;
+	Vchan* v;
+	int nv;
+	Msg r;
+	Share u;
+
+	v = c->vc;
+	nv = c->nvc;
+
+	if(mcsconnect(c) < 0)
+		return -1;
+	erectdom(c);
+	if(attachuser(c) < 0)
+		return -1;
+
+	mcsuid = c->mcsuid;
+	userchan = c->userchan;
+
+	if(joinchannel(c, mcsuid, userchan) < 0)
+		return -1;
+	if(joinchannel(c, mcsuid, GLOBALCHAN) < 0)
+		return -1;
+	for(i = 0; i < nv; i++)
+		if(joinchannel(c, mcsuid, v[i].mcsid) < 0)
+			return -1;
+
+	sendclientinfo(c);
+	for(;;){
+		if(readmsg(c, &r) <= 0)
+			return -1;
+		switch(r.type){
+		case Mclosing:
+			werrstr("Disconnect Provider Ultimatum");
+			return -1;
+		case Aflow:
+		case Ldone:
+			break;
+		case Lneedlicense:
+		case Lhavechal:
+			respondlicense(c, &r);
+			break;
+		case Aupdate:
+			if(r.getshare(&u, r.data, r.ndata) < 0)
+				return -1;
+			switch(u.type){
+			case ShEinfo:
+				c->hupreason = u.err;
+				break;
+			case ShActivate:
+				activating(c, &u);
+				confirmactive(c);
+				passinput(c, 0, InputSync, 0, 0, 0);
+				assync(c);
+				asctl(c, CAcooperate);
+				asctl(c, CAreqctl);
+				asfontls(c);
+				break;
+			case ShDeactivate:
+				deactivating(c, &u);
+				break;
+			case ShSync:
+			case ShCtl:
+				break;
+			case ShFmap:
+				return 0;
+				break;
+			}
+		}
+	}
+}
+
+void
+sendclientinfo(Rdp* c)
+{
+	Msg t;
+
+	t.type = Dclientinfo;
+	t.mcsuid = c->mcsuid;
+	t.dom = c->windom;
+	t.user = c->user;
+	t.pass = c->passwd;
+	t.rshell = c->shell;
+	t.rwd = c->rwd;
+	t.dologin = (strlen(c->user) > 0);
+
+	if(writemsg(c, &t) <= 0)
+		sysfatal("sendclientinfo: %r");
+}
+
+void
+confirmactive(Rdp* c)
+{
+	Msg	t;
+
+	t.type = Mactivated;
+	t.originid = c->srvchan;
+	t.mcsuid = c->userchan;
+	t.shareid = c->shareid;
+	t.xsz = c->xsz;
+	t.ysz = c->ysz;
+	t.depth = c->depth;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("confirmactive: %r");
+}
+
+void
+respondlicense(Rdp *c, Msg *r)
+{
+	Msg t;
+
+	switch(r->type){
+	default:
+		return;
+	case Lneedlicense:
+		t.type = Lreq;
+		t.sysname = c->local;
+		t.user = c->user;
+		t.originid = c->userchan;
+		break;
+	case Lhavechal:
+			fprint(2, "unhandled Lhavechal\n");
+		t.type = Lnolicense;
+		t.originid = c->userchan;
+		break;
+	}
+
+	if(writemsg(c, &t) < 0)
+		sysfatal("respondlicense: writemsg failed: %r");
+}
+
+
+void
+assync(Rdp *c)
+{
+	Msg t;
+
+	t.type = Async;
+	t.mcsuid = c->srvchan;
+	t.originid = c->userchan;
+	t.shareid = c->shareid;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("assync: %r");
+}
+
+void
+asctl(Rdp* c, int action)
+{
+	Msg t;
+
+	t.type = Actl;
+	t.originid = c->userchan;
+	t.shareid = c->shareid;
+	t.action = action;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("asctl: %r");
+}
+
+void
+asfontls(Rdp* c)
+{
+	Msg t;
+
+	t.type = Afontls;
+	t.originid = c->userchan;
+	t.shareid = c->shareid;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("asfontls: %r");
+}
+
+void
+passinput(Rdp* c, ulong msec, int mtype, int iflags, int iarg1, int iarg2)
+{
+	Msg t;
+
+	t.type = Ainput;
+	t.originid = c->userchan;
+	t.shareid = c->shareid;
+	t.mtype = mtype;
+	t.msec = msec;
+	t.flags = iflags;
+	t.iarg1 = iarg1;
+	t.iarg2 = iarg2;
+	if(writemsg(c, &t) <= 0)
+		sysfatal("passinput: %r");
+}
+
+void
+turnupdates(Rdp* c, int allow)
+{
+	Msg t;
+
+	t.type = Dsupress;
+	t.originid = c->userchan;
+	t.shareid = c->shareid;
+	t.xsz = c->xsz;
+	t.ysz = c->ysz;
+	t.allow = allow;
+	writemsg(c, &t);
+}
+
+
--- /dev/null
+++ b/tls.c
@@ -1,0 +1,54 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <mp.h>
+#include <libsec.h>
+#include "dat.h"
+#include "fns.h"
+
+int
+istrusted(uchar* cert, int certlen)
+{
+	uchar digest[SHA1dlen];
+	Thumbprint *table;
+
+	fmtinstall('H', encodefmt);
+	if(cert==nil || certlen <= 0) {
+		werrstr("server did not provide TLS certificate");
+		return 0;
+	}
+	sha1(cert, certlen, digest, nil);
+	table = initThumbprints("/sys/lib/tls/rdp", "/sys/lib/tls/rdp.exclude");
+	if(!table || !okThumbprint(digest, table)){
+		werrstr("server certificate %.*H not recognized", SHA1dlen, digest);
+		return 0;
+	}
+	freeThumbprints(table);
+	return 1;
+}
+
+/* lifted from /sys/src/cmd/upas/fs/imap4.c:/^starttls */
+int
+starttls(Rdp* r)
+{
+	TLSconn c;
+	int fd, sfd;
+
+	fd = r->fd;
+
+	memset(&c, 0, sizeof c);
+	sfd = tlsClient(fd, &c);
+	if(sfd < 0){
+		werrstr("tlsClient: %r");
+		return -1;
+	}
+	if(!istrusted(c.cert, c.certlen)){
+		close(sfd);
+		return -1;
+	}
+	/* BUG: free c.cert? */
+
+	close(r->fd);
+	r->fd = sfd;
+	return sfd;
+}
--- a/vchan.c
+++ b/vchan.c
@@ -10,19 +10,17 @@
 
 enum
 {
-	/* 2.2.1.3.4.1 Channel Definition Structure */
-	ChanInited		= 0x80000000,
-	ChanShowproto	= 0x00200000,
+	ChanInited=	0x80000000,	/* 2.2.1.3.4.1 Channel Definition Structure */
+	ChanShowproto=	0x00200000,
+	
+	ChanChunkLen=	1600,	/* 2.2.6.1 Virtual Channel PDU */
 
-	/* 2.2.6.1 Virtual Channel PDU */
-	ChanChunkLen			= 1600,
-
-	/* 2.2.6.1.1 Channel PDU Header */
-	CFfirst=	0x01,
+	CFfirst=	0x01,	/* 2.2.6.1.1 Channel PDU Header */
 	CFlast=	0x02,
 	CFshowproto=	0x10,
 };
 
+static
 Vchan vctab[] =
 {
 	{
@@ -32,9 +30,17 @@
 		.flags = ChanInited | ChanShowproto,
 	},
 };
+static
 uint nvc = nelem(vctab);
 
-Vchan*
+void
+initvc(Rdp* c)
+{
+	c->vc = vctab;
+	c->nvc = nvc;
+}
+
+static Vchan*
 lookupvc(int mcsid)
 {
 	int i;
@@ -44,7 +50,7 @@
 	return nil;
 }
 
-Vchan*
+static Vchan*
 namevc(char* name)
 {
 	int i;
@@ -55,78 +61,58 @@
 }
 
 void
-scanvcpdu(uchar *p, uchar* ep, int chanid)
+scanvcdata(Rdp* c, Msg* m)
 {
-	int flags, len, rem;
-	uchar *q, *eq;
 	Vchan* vc;
-
-	vc = lookupvc(chanid);
+	int n;
+	
+	vc = lookupvc(m->chanid);
 	if(vc == nil)
 		return;
 
-	len = GLONG(p+0);
-	flags = GLONG(p+4);
-	p += 8;
-	if(len < 0 || len > 8*1024*1024){
-		werrstr("bad length in virtual channel PDU header");
-		fprint(2, "scanvcpdu: %r\n");
-		return;
-	}
-	if(flags&CFfirst){
-		vc->defragging = 1;
+	if(m->flags&CFfirst)
 		vc->pos = 0;
-	}
 
-	if(!vc->defragging){
-		vc->fn(p, ep);
-		return;
-	}
+	vc->buf = erealloc(vc->buf, m->len);
+	vc->nb = m->len;
+	n = vc->nb - vc->pos;
+	if(n > m->ndata)
+		n = m->ndata;
+	memcpy(vc->buf+vc->pos, m->data, n);
+	vc->pos += n;
 
-	vc->buf = erealloc(vc->buf, len);
-	vc->nb = len;
-	q = vc->buf + vc->pos;
-	eq = vc->buf + len;
-	rem = ep-p;
-	if(rem > eq-q)
-		rem = eq-q;
-	memcpy(q, p, rem);
-	vc->pos += rem;
-	if(flags&CFlast){
-		q = vc->buf;
-		vc->fn(q, eq);
+	if(m->flags&CFlast){
+		vc->fn(c, vc->buf, vc->nb);
 		free(vc->buf);
 		vc->buf = nil;
 		vc->nb = 0;
-		vc->defragging = 0;
 	}
 }
 
 int
-sendvc(char* cname, uchar* a, int n)
+sendvc(Rdp* c, char* cname, uchar* a, int n)
 {
 	int sofar, chunk;
-	int flags;
-	int nb, len, ndata;
-	uchar buf[40+ChanChunkLen];
-	uchar *p, *q;
 	Vchan* vc;
-	int chanid;
+	Msg t;
 	
+	if(n < 0)
+		return -1;
+
 	vc = namevc(cname);
 	if(vc == nil){
 		werrstr("%s: no such vchannel", cname);
 		return -1;
 	}
-	chanid = vc->mcsid;
-	if(chanid < 0)
+	if(vc->mcsid < 0)
 		return -1;
-	if(n < 0)
-		return -1;
 
-	p = a;
-	nb = sizeof(buf);
-	flags = CFfirst | CFshowproto;
+	t.type = Mvcdata;
+	t.originid = c->userchan;
+	t.chanid = vc->mcsid;
+	t.flags = CFfirst | CFshowproto;
+	t.len = n;
+	t.data = a;
 
 	for(sofar=0; sofar<n; sofar += chunk){
 		chunk = n-sofar;
@@ -133,17 +119,14 @@
 		if(chunk > ChanChunkLen)
 			chunk = ChanChunkLen;
 		else
-			flags |= CFlast;
-		ndata = chunk+8;
-		q = prebuf(buf, nb, ndata, chanid, 0);
-		if(q == nil)
-			return -1;
-		PLONG(q+0, n);
-		PLONG(q+4, flags);
-		memcpy(q+8, p+sofar, chunk);
-		len = q-buf+ndata;
-		writen(rd.fd, buf, len);
-		flags &= ~CFfirst;
+			t.flags |= CFlast;
+
+		t.data = a+sofar;
+		t.ndata = chunk;
+		writemsg(c, &t);
+
+		t.flags &= ~CFfirst;
 	}
 	return n;
 }
+
--- a/wsys.c
+++ b/wsys.c
@@ -35,7 +35,7 @@
 }
 
 void
-eresized(int)
+eresized(Rdp* c, int)
 {
 	int fd;
 	Point d;
@@ -45,8 +45,7 @@
 		sysfatal("resize failed: %r");
 
 	/* lifted from /sys/src/cmd/vnc/wsys.c */
-	d = addpt(rd.dim, Pt(2*Borderwidth, 2*Borderwidth));
-	rd.dim.x = (rd.dim.x + 3) & ~3;	/* ensure width divides by 4 */
+	d = addpt(Pt(c->xsz, c->ysz), Pt(2*Borderwidth, 2*Borderwidth));
 	if(d.x < Dx(screen->r) || d.y < Dy(screen->r)){
 		fd = open("/dev/wctl", OWRITE);
 		if(fd >= 0){
@@ -54,8 +53,8 @@
 			close(fd);
 		}
 	}
-	turnupdates(0);
-	turnupdates(!ishidden());
+	turnupdates(c, 0);
+	turnupdates(c, !ishidden());
 	unlockdisplay(display);
 }
 void
@@ -113,7 +112,7 @@
 
 /* lifted from /sys/src/cmd/vnc/wsys.c */
 void
-pollsnarf(void)
+pollsnarf(Rdp* c)
 {
 	Dir *dir;
 
@@ -130,7 +129,7 @@
 		if(dir == nil)	/* old drawterm */
 			continue;
 		if(dir->qid.vers > snarfvers){
-			clipannounce();
+			clipannounce(c);
 			snarfvers = dir->qid.vers;
 		}
 		free(dir);
--- a/x224.c
+++ b/x224.c
@@ -1,41 +1,11 @@
 #include <u.h>
 #include <libc.h>
 #include <draw.h>
-#include <mp.h>
-#include <libsec.h>
 #include "dat.h"
 #include "fns.h"
 
-enum
-{
-	/* X.224 PDU codes */
-	ConReq=		0xE0, 		/* connection request */
-	ConCfrm=	0xD0,		/* connection confirm */
-	HupReq=		0x80,		/* disconnection request */
-	Data=		0xF0,		/* data */
-	Err=			0x70,		/* error */
-
-	/* Rdpnego.type */
-	Tnego=	1,
-	Rnego=	2,
-
-	/* Rdpnego.proto */
-	ProtoTLS= 	1,
-	ProtoCSSP=	2,
-	ProtoUAUTH=	8,
-};
-
-struct Rdpnego
-{
-	int	type;
-	int	flags;
-	int	proto;
-};
-int	getnego(Rdpnego*, uchar*, uint);
-int	putnego(uchar*, uint, Rdpnego*);
-
 /*
- * examine a packet header at between p and ep
+ * examine a packet header
  * returns 1 if it's a TPKT-encapsulated TPDU (T.123 clause 8; RFC 1006)
  * returns 0 if not - likely a Fast-Path Update PDU ([MS-RDPBCGR] 5.3.8 and 5.4.4)
  */
@@ -54,7 +24,7 @@
 }
 
 int
-tpdutype(uchar* p, uchar* ep)
+tptype(uchar* p, uchar* ep)
 {
 	if(p+5 >= ep){
 		werrstr(Eshort);
@@ -64,9 +34,9 @@
 }
 
 int
-isdatatpdu(uchar* p, uchar* ep)
+istpdat(uchar* p, uchar* ep)
 {
-	return (tpdutype(p,ep) == Data);
+	return (tptype(p,ep) == Data);
 }
 
 
@@ -113,7 +83,7 @@
 }
 
 uchar*
-tpdupayload(uchar* p, uchar* ep)
+tpdat(uchar* p, uchar* ep)
 {
 	uchar* q;
 
@@ -121,7 +91,7 @@
 		werrstr("Fast-Path Update PDU is not expected");
 		return nil;
 	}
-	if(tpdutype(p,ep) == Data)
+	if(tptype(p,ep) == Data)
 		q = p+7;
 	else
 		q = p+11;
@@ -146,12 +116,14 @@
 		return -1;
 	}
 
-	/* TPKT header: version[1] unused[1] len[2] */
+	/*
+	 * TPKT header: version[1] unused[1] len[2]
+	 * ConReq: hdlen[1] type[1] dstref[2] srcref[2] class[1]
+	 */
 	p[0] = 0x03;
 	p[1] = 0;
 	PSHORTB(p+2, size);
 
-	/* ConReq: hdlen[1] type[1] dstref[2] srcref[2] class[1] */
 	p[4+0] = 7-1+ndata;
 	p[4+1] = ConReq;
 	PSHORTB(p+4+2, 0);
@@ -212,153 +184,4 @@
 	p[5] = HupReq;
 	p[6] = (1<<7);		/* seqno (0 in Class 0) + EOT mark (1<<7) */
 	return size;
-}
-
-
-int
-putnego(uchar* b, uint nb, Rdpnego* m)
-{
-	int len;
-
-	len = 8;
-	if(nb < 8){
-		werrstr(Esmall);
-		return -1;
-	}
-	b[0] = m->type;
-	b[1] = m->flags;
-	PSHORT(b+2, len);
-	PLONG(b+4, m->proto);
-
-	return len;
-}
-
-int
-getnego(Rdpnego* m, uchar* b, uint nb)
-{
-	int len;
-
-	if(nb < 8){
-		werrstr(Eshort);
-		return -1;
-	}
-	m->type = b[0];
-	m->flags = b[1];
-	len = GSHORT(b+2);
-	m->proto = GLONG(b+4);
-	if(len != 8){
-		werrstr("bad length in RDP Nego Response");
-		return -1;
-	}
-	return len;
-}
-
-int
-istrusted(uchar* cert, int certlen)
-{
-	uchar digest[SHA1dlen];
-	Thumbprint *table;
-
-	if(cert==nil || certlen <= 0) {
-		werrstr("server did not provide TLS certificate");
-		return 0;
-	}
-	sha1(cert, certlen, digest, nil);
-	table = initThumbprints("/sys/lib/tls/rdp", "/sys/lib/tls/rdp.exclude");
-	if(!table || !okThumbprint(digest, table)){
-		werrstr("server certificate %.*H not recognized", SHA1dlen, digest);
-		return 0;
-	}
-	freeThumbprints(table);
-	return 1;
-}
-
-/* lifted from /sys/src/cmd/upas/fs/imap4.c:/^starttls */
-int
-starttls(void)
-{
-	TLSconn conn;
-	int sfd;
-
-	fmtinstall('H', encodefmt);
-	memset(&conn, 0, sizeof conn);
-	sfd = tlsClient(rd.fd, &conn);
-	if(sfd < 0){
-		werrstr("tlsClient: %r");
-		return -1;
-	}
-	if(!istrusted(conn.cert, conn.certlen)){
-		close(sfd);
-		return -1;
-	}
-
-	close(rd.fd);
-	rd.fd = sfd;
-
-	return sfd;
-}
-
-/* 5.4.2.1 Negotiation-Based Approach */
-int
-x224connect(int fd)
-{
-	int n, ndata;
-	uchar buf[4+7+25+8], *p, *ep;
-	Rdpnego t, r;
-
-	ndata = 25+8;
-	n = mktpcr(buf, sizeof buf, ndata);
-	if(n < 0)
-		return -1;
-
-	p = buf+n-ndata;
-	ep = buf+n;
-
-	memcpy(p, "Cookie: mstshash=eltons\r\n", 25);
-	p += 25;
-
-	t = (Rdpnego){Tnego, 0, ProtoTLS};
-	if(putnego(p, ep-p, &t) != 8){
-		werrstr("pnego failed: %r");
-		return -1;
-	}
-	if(writen(fd, buf, n) != n)
-		return -1;
-
-	n = readpdu(fd, buf, sizeof buf);
-	if(n < 6){
-		werrstr("X.224: ConCfrm: %r");
-		return -1;
-	}
-	ep = buf+n;
-
-	if(!istpkt(buf, ep) || tpdutype(buf, ep) != ConCfrm){
-		werrstr("X.224: protocol botch");
-		return -1;
-	}
-	if((p = tpdupayload(buf, ep)) == nil)
-		return -1;
-	if(getnego(&r, p, ep-p) < 0 || r.type != Rnego || !(r.proto&ProtoTLS)){
-		werrstr("server refused STARTTLS");
-		return -1;
-	}
-
-	fd = starttls();
-	if(fd < 0)
-		return -1;
-
-	rd.sproto = r.proto;
-
-	return fd;
-}
-
-int
-x224disconnect(int fd)
-{
-	int n, m;
-	uchar buf[12];
-
-	n = mktpdr(buf, sizeof buf, 0);
-	m = write(fd, buf, n);
-	return m;
 }