ref: baf602582a840a0253900d41a2f1cce7156dd770
author: Yaroslav Kolomiiets <yarikos@gmail.com>
date: Mon Mar 7 16:32:43 EST 2016
rd: initial import
--- /dev/null
+++ b/cap.c
@@ -1,0 +1,271 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+/* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
+void
+scangencaps(uchar* p, uchar* ep)
+{
+ int extraFlags, canrefresh, cansupress;
+
+ 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.2 Bitmap Capability Set (TS_BITMAP_CAPABILITYSET) */
+void
+scanbitcaps(uchar* p, uchar* ep)
+{
+ int w, h, depth;
+
+ if(p+16> ep)
+ sysfatal(Eshort);
+ depth = GSHORT(p+4);
+ w = GSHORT(p+12);
+ h = GSHORT(p+14);
+
+ if(depth != rd.depth){
+ rd.depth = depth;
+ switch(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("Unsupported server color depth: %uhd\n", depth);
+ }
+ }
+ 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 */
+ }
+}
+
+/* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
+uchar*
+putgencaps(uchar *p, uchar *ep)
+{
+ int extraFlags;
+
+ extraFlags = 0
+ | CanFastpath
+ | NoBitcomphdr
+ | CanLongcred
+ ;
+
+ if(p+24>ep)
+ sysfatal(Eshort);
+ PSHORT(p+0, CapGeneral);
+ PSHORT(p+2, GENCAPSIZE);
+ PSHORT(p+4, 0); // OSMAJORTYPE_UNSPECIFIED
+ PSHORT(p+6, 0); // OSMINORTYPE_UNSPECIFIED
+ PSHORT(p+8, 0x200); // TS_CAPS_PROTOCOLVERSION
+ PSHORT(p+12, 0); // generalCompressionTypes
+ PSHORT(p+14, extraFlags);
+ PSHORT(p+16, 0); // updateCapabilityFlag
+ PSHORT(p+18, 0); // remoteUnshareFlag
+ PSHORT(p+20, 0); // generalCompressionLevel
+ p[22] = 0; // refreshRectSupport - server only
+ p[23] = 0; // suppressOutputSupport - server only
+ return p+24;
+}
+
+
+/* 2.2.7.1.2 Bitmap Capability Set (TS_BITMAP_CAPABILITYSET) */
+uchar*
+putbitcaps(uchar *p, uchar *ep)
+{
+ if(p+30>ep)
+ sysfatal(Eshort);
+ PSHORT(p+0, CapBitmap);
+ PSHORT(p+2, BITCAPSIZE);
+ PSHORT(p+4, rd.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+16, 0); // pad2octets
+ PSHORT(p+18, 1); // desktopResizeFlag
+ PSHORT(p+20, 1); // bitmapCompressionFlag
+ PSHORT(p+22, 0); // highColorFlags
+ PSHORT(p+24, 1); // drawingFlags
+ PSHORT(p+26, 1); // multipleRectangleSupport
+ PSHORT(p+26, 0); // pad2octetsB
+ return p+30;
+}
+
+/* 2.2.7.1.3 Order Capability Set (TS_ORDER_CAPABILITYSET) */
+uchar*
+putordcaps(uchar *p, uchar *ep)
+{
+ ushort orderFlags;
+ enum
+ {
+ NEGOTIATEORDERSUPPORT= 0x02,
+ ZEROBOUNDSDELTASSUPPORT= 0x08,
+ COLORINDEXSUPPORT= 0x20,
+ SOLIDPATTERNBRUSHONLY= 0x40,
+ };
+
+ orderFlags = 0
+ | NEGOTIATEORDERSUPPORT
+ | ZEROBOUNDSDELTASSUPPORT
+ | COLORINDEXSUPPORT
+ | SOLIDPATTERNBRUSHONLY
+ ;
+ if(p+88>ep)
+ sysfatal(Eshort);
+ PSHORT(p+0, CapOrder);
+ PSHORT(p+2, ORDCAPSIZE);
+ memset(p+4, 16, 0); // terminalDescriptor
+ PLONG(p+20, 0); // pad4octetsA
+ PSHORT(p+24, 1); // desktopSaveXGranularity
+ PSHORT(p+26, 20); // desktopSaveYGranularity
+ PSHORT(p+28, 0); // pad2octetsA
+ PSHORT(p+30, 1); // maximumOrderLevel
+ PSHORT(p+32, 0); // numberFonts
+ PSHORT(p+34, orderFlags);
+ memcpy(p+36, orderSupport, 32);
+ PSHORT(p+68, 0x6a1); // textFlags
+ PSHORT(p+70, 0); // orderSupportExFlags
+ PLONG(p+72, 0); // pad4octetsB
+ PLONG(p+76, 480*480); // desktopSaveSize
+ PSHORT(p+80, 0); // pad2octetsC
+ PSHORT(p+82, 0); // pad2octetsD
+ PSHORT(p+84, 0xe4); // textANSICodePage
+ PSHORT(p+86, 0x04); // pad2octetsE
+ return p+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)
+{
+ if(p+40>ep)
+ sysfatal(Eshort);
+ PSHORT(p+0, CapBitcache2);
+ PSHORT(p+2, BCACAPSIZE);
+ PSHORT(p+4, 0); // CacheFlags (2 bytes):
+ p[6] = 0; // pad2
+ p[7] = 3; // NumCellCaches
+ PLONG(p+8, 120); // BitmapCache0CellInfo
+ PLONG(p+12, 120); // BitmapCache1CellInfo
+ PLONG(p+16, 336); // BitmapCache2CellInfo
+ PLONG(p+20, 0); // BitmapCache3CellInfo
+ PLONG(p+24, 0); // BitmapCache4CellInfo
+ memset(p+28, 12, 0); // Pad3
+ return p+40;
+}
+
+/* 2.2.7.1.5 Pointer Capability Set (TS_POINTER_CAPABILITYSET) */
+uchar*
+putptrcaps(uchar *p, uchar *ep)
+{
+ if(p+8>ep)
+ sysfatal(Eshort);
+ PSHORT(p+0, CapPointer);
+ PSHORT(p+2, PTRCAPSIZE);
+ PSHORT(p+4, 0); // colorPointerFlag
+ PSHORT(p+6, 20); // colorPointerCacheSize
+ return p+8;
+}
+
+/* 2.2.7.1.6 Input Capability Set (TS_INPUT_CAPABILITYSET) */
+uchar*
+putinpcaps(uchar *p, uchar *ep)
+{
+ long inputFlags;
+ enum
+ {
+ INPUT_FLAG_SCANCODES= 0x0001,
+ INPUT_FLAG_MOUSEX= 0x0004,
+ INPUT_FLAG_FASTPATH_INPUT= 0x0008,
+ INPUT_FLAG_UNICODE= 0x0010,
+ INPUT_FLAG_FASTPATH_INPUT2= 0x0020,
+ };
+
+ inputFlags = 0
+ | INPUT_FLAG_SCANCODES
+ | INPUT_FLAG_UNICODE
+ ;
+
+ if(p+88>ep)
+ sysfatal(Eshort);
+ PSHORT(p+0, CapInput);
+ PSHORT(p+2, INPCAPSIZE);
+ PSHORT(p+4, inputFlags); // inputFlags
+ PSHORT(p+6, 0); // pad2octetsA
+
+ // the below SHOULD be the same as in the Client Core Data (section 2.2.1.3.2).
+ PLONG(p+8, 0x409); // keyboardLayout
+ PLONG(p+12, 4); // keyboardType: IBM enhanced (101- or 102-key)
+ PLONG(p+16, 0); // keyboardSubType
+ PLONG(p+20, 12); // keyboardFunctionKey
+ memset(p+24, 64, 0); // imeFileName
+ return p+88;
+}
+
+/* 2.2.7.1.8 Glyph Cache Capability Set (TS_GLYPHCACHE_CAPABILITYSET) */
+uchar*
+putglycaps(uchar* p, uchar* ep)
+{
+ enum {
+ GLYPH_SUPPORT_NONE= 0,
+ };
+
+ if(p+52>ep)
+ sysfatal(Eshort);
+ PSHORT(p+0, CapGlyph);
+ PSHORT(p+2, GLYCAPSIZE);
+ PLONG(p+4, 0x0400fe); // GlyphCache 0
+ PLONG(p+8, 0x0400fe); // GlyphCache 1
+ PLONG(p+12, 0x0800fe); // GlyphCache 2
+ PLONG(p+16, 0x0800fe); // GlyphCache 3
+ PLONG(p+20, 0x1000fe); // GlyphCache 4
+ PLONG(p+24, 0x2000fe); // GlyphCache 5
+ PLONG(p+28, 0x4000fe); // GlyphCache 6
+ PLONG(p+32, 0x8000fe); // GlyphCache 7
+ PLONG(p+36, 0x10000fe); // GlyphCache 8
+ PLONG(p+40, 0x8000040); // GlyphCache 9
+ PLONG(p+44, 0x01000100); // FragCache
+ PSHORT(p+48, GLYPH_SUPPORT_NONE); // GlyphSupportLevel
+ PSHORT(p+50, 0); // pad2octets
+ return p+52;
+}
+
+/* 2.2.7.1.11 Sound Capability Set (TS_SOUND_CAPABILITYSET) */
+uchar*
+putsndcaps(uchar* p, uchar* ep)
+{
+ if(p+8>ep)
+ sysfatal(Eshort);
+ PSHORT(p+0, CapSound);
+ PSHORT(p+2, SNDCAPSIZE);
+ PSHORT(p+4, 0); // soundFlags
+ PSHORT(p+6, 0); // pad2octetsA
+ return p+8;
+}
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,111 @@
+typedef struct Rdp Rdp;
+typedef struct Vchan Vchan;
+typedef struct Rdpnego Rdpnego;
+
+#define GSHORT(p) ((p)[0]|((p)[1]<<8))
+#define GSHORTB(p) ((p)[0]<<8|((p)[1]))
+#define GLONG(p) ((p)[0]|((p)[1]<<8)|((p)[2]<<16)|((p)[3]<<24))
+#define GLONGB(p) (((p)[0]<<24)|((p)[1]<<16)|((p)[2]<<8)|(p)[3])
+#define PSHORT(p,v) (p)[0]=(v);(p)[1]=(v)>>8
+#define PSHORTB(p,v) (p)[0]=(v)>>8;(p)[1]=(v)
+#define PLONG(p,v) (p)[0]=(v);(p)[1]=(v)>>8;(p)[2]=(v)>>16;(p)[3]=(v)>>24
+#define PLONGB(p,v) (p)[0]=(v)>>24;(p)[1]=(v)>>16;(p)[2]=(v)>>8;(p)[3]=(v)
+
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+
+struct Rdp
+{
+ int fd; /* connection I/O descriptor */
+ long sproto; /* server-selected security protocol to replay back */
+ int autologon; /* enable auto logon */
+ char *label; /* window label */
+ char *local; /* local system name */
+ char *user; /* user name */
+ char *windom; /* domain for auto logon */
+ char *passwd; /* password for auto logon (sic) */
+ char *shell; /* remote shell override */
+ char *rwd; /* remote working directory */
+ 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 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 */
+};
+
+struct Vchan
+{
+ int mcsid; /* MCS channelId */
+ int flags;
+ char name[8];
+ int defragging;
+ uchar* buf; /* defragmentation buffer */
+ int nb; /* sizeof buf */
+ int pos; /* next fragment offset */
+ void (*fn)(uchar*,uchar*);
+};
+Vchan* lookupvc(int);
+Vchan* namevc(char*);
+int sendvc(char*,uchar*,int);
+void scanvcpdu(uchar*,uchar*,int);
+
+enum
+{
+ /* 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,
+
+ /* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */
+ CanFastpath = 0x0001,
+ NoBitcomphdr = 0x0400,
+ CanLongcred = 0x0004,
+
+ /* 2.2.8.1.1.2.1 Basic (TS_SECURITY_HEADER) */
+ Scrypt = 0x0008,
+ Sinfopk = 0x0040,
+ Slicensepk = 0x0080,
+
+ /* 2.2.8.1.1.3.1.1 Slow-Path Input Event (TS_INPUT_EVENT) */
+ InputSync= 0,
+ InputKeycode= 4,
+ InputUnicode= 5,
+ InputMouse= 0x8001,
+
+ 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 */
+ 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[];
--- /dev/null
+++ b/eclip.c
@@ -1,0 +1,266 @@
+/*
+ * [MS-RDPECLIP]: Remote Desktop Protocol: Clipboard Virtual Channel Extension
+ * http://msdn.microsoft.com/en-us/library/cc241066.aspx
+ *
+ * Standard Clipboard Formats
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/ff729168(v=vs.85).aspx
+ */
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+static char cliprdr[] = "CLIPRDR";
+static void cliprequest(uint);
+
+enum
+{
+ CFunicode = 13,
+};
+
+enum
+{
+ FlagOk = (1<<0),
+ FlagErr = (1<<1),
+};
+
+enum
+{
+ ClipReady = 1,
+ ClipAnnounce = 2,
+ ClipNoted = 3,
+ ClipReq = 4,
+ ClipResp = 5,
+};
+
+typedef struct Clipmsg Clipmsg;
+struct Clipmsg
+{
+ uint type;
+ uint flags;
+ uint fmtid;
+ 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 (*clipcall[])(Clipmsg*) =
+{
+ [ClipReady]= clipattached,
+ [ClipAnnounce]= clipnoted,
+ [ClipReq]= cliprequested,
+ [ClipResp]= clipprovided,
+};
+
+void
+clipvcfn(uchar* p, uchar* ep)
+{
+ Clipmsg tx;
+
+ if(clipgetmsg(&tx, p, ep-p) < 0)
+ return;
+ if(tx.flags&FlagErr)
+ return;
+ if(tx.type >= nelem(clipcall))
+ return;
+ if(clipcall[tx.type] == nil)
+ return;
+ clipcall[tx.type](&tx);
+}
+
+void
+clipannounce(void)
+{
+ Clipmsg r;
+ uchar a[44];
+ int n;
+
+ r.type = ClipAnnounce;
+ r.flags = 0;
+ r.fmtid = CFunicode;
+ n = clipputmsg(&r, a, sizeof(a));
+ if(sendvc(cliprdr, a, n) < 0)
+ fprint(2, "clipannounce: %r\n");
+}
+
+static void
+cliprequest(uint fmtid)
+{
+ Clipmsg r;
+ uchar a[12];
+ int n;
+
+ r.type = ClipReq;
+ r.flags = 0;
+ r.fmtid = fmtid;
+ n = clipputmsg(&r, a, sizeof(a));
+ if(sendvc(cliprdr, a, n) < 0)
+ fprint(2, "cliprequest: %r\n");
+}
+
+static void
+clipattached(Clipmsg*)
+{
+ clipannounce();
+}
+
+static void
+clipnoted(Clipmsg *m)
+{
+ Clipmsg r;
+ uchar a[8];
+ int n;
+
+ if(m->fmtid)
+ cliprequest(m->fmtid);
+
+ r.type = ClipNoted;
+ r.flags = FlagOk;
+ n = clipputmsg(&r, a, sizeof(a));
+ if(sendvc(cliprdr, a, n) < 0)
+ fprint(2, "clipnoted: %r\n");
+}
+
+static void
+cliprequested(Clipmsg *m)
+{
+ Clipmsg r;
+ char* s;
+ uchar *b;
+ int n, ns, nb;
+
+ b = emalloc(8);
+ nb = 0;
+
+ r.type = ClipResp;
+ r.flags = FlagOk;
+ if(m->fmtid != CFunicode){
+ r.flags = FlagErr;
+ goto Respond;
+ }
+
+ s = getsnarf(&ns);
+ if(s == nil)
+ goto Respond;
+ nb = ns*4;
+ b = erealloc(b, nb+8);
+ nb = toutf16(b+8, nb, s, ns);
+ free(s);
+ Respond:
+ r.data = b+8;
+ r.ndata = nb;
+ n = clipputmsg(&r, b, nb+8);
+ if(sendvc(cliprdr, b, n) < 0)
+ fprint(2, "cliprequested: %r\n");
+ free(b);
+}
+
+static void
+clipprovided(Clipmsg *m)
+{
+ char *s;
+ int n, ns;
+
+ ns = m->ndata*UTFmax/2;
+ s = emalloc(ns);
+ n = fromutf16(s, ns, m->data, m->ndata);
+ putsnarf(s, n);
+ free(s);
+ return;
+}
+
+static int
+clipputmsg(Clipmsg *m, uchar *a, int n)
+{
+ if(n < 8){
+ werrstr(Esmall);
+ return -1;
+ }
+ PSHORT(a+0, m->type);
+ PSHORT(a+2, m->flags);
+ switch(m->type){
+ case ClipAnnounce:
+ m->data = a+8;
+ m->ndata = 4+32;
+ if(8 + m->ndata > n){
+ werrstr(Esmall);
+ return -1;
+ }
+ PLONG(a+8, m->fmtid);
+ memset(a+12, 0, 32); /* fmt name - who cares? */
+ break;
+ case ClipReq:
+ m->data = a+8;
+ m->ndata = 4;
+ if(8 + m->ndata > n){
+ werrstr(Esmall);
+ return -1;
+ }
+ PLONG(a+8, m->fmtid);
+ break;
+ case ClipNoted:
+ m->ndata = 0;
+ m->data = a+8;
+ break;
+ }
+ if(8 + m->ndata > n){
+ werrstr(Esmall);
+ return -1;
+ }
+ PLONG(a+4, m->ndata);
+ memcpy(a+8, m->data, m->ndata);
+ return 8+m->ndata;
+}
+
+static int
+clipgetmsg(Clipmsg *m, uchar *p, int n)
+{
+ uint len, fmtid;
+ uchar *ep;
+
+ if(8 > n){
+ werrstr(Eshort);
+ return -1;
+ }
+ m->type = GSHORT(p);
+ m->flags = GSHORT(p+2);
+ len = GLONG(p+4);
+ if(8+len > n){
+ werrstr(Eshort);
+ return -1;
+ }
+ m->ndata = len;
+ m->data = p+8;
+
+ switch(m->type){
+ case ClipReq:
+ if(len < 4){
+ werrstr(Eshort);
+ return -1;
+ }
+ m->fmtid = GLONG(m->data);
+ break;
+ case ClipAnnounce:
+ m->fmtid = 0;
+ p += 8;
+ ep = p+len;
+ while(p < ep){
+ fmtid = GLONG(p);
+ if(fmtid == CFunicode){
+ m->fmtid = fmtid;
+ break;
+ }
+ p += 4+32*1;
+ }
+ break;
+ }
+ return 8+len;
+}
--- /dev/null
+++ b/egdi.c
@@ -1,0 +1,518 @@
+/*
+ * [MS-RDPEGDI]: Graphics Device Interface (GDI) Acceleration Extensions
+ * http://msdn.microsoft.com/en-us/library/cc241537.aspx
+ */
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+ Bits2= 3,
+ Bits3= 7,
+ Bits6= 63,
+ Bits7= 127,
+ Bits8= 255,
+};
+enum /* 2.2.2.2.1 Drawing Order (DRAWING_ORDER) */
+{
+ Standard= 1<<0,
+ Secondary= 1<<1,
+};
+enum /* 2.2.2.2.1.1.2 Primary Drawing Order (PRIMARY_DRAWING_ORDER) */
+{
+ Clipped= 1<<2,
+ NewOrder= 1<<3,
+ Diff= 1<<4,
+ SameClipping= 1<<5,
+ ZeroFieldBit0= 1<<6,
+ ZeroFieldBit1= 1<<7,
+};
+enum
+{
+ ROP2_COPY = 0xcc,
+};
+enum
+{
+ /* orderSupport indices for capability negotiation */
+ CanDstBlt = 0,
+ CanPatBlt, /* also OpaqueRect */
+ CanScrBlt,
+ CanMemBlt = 3,
+ CanMem3Blt,
+ CanDrawNineGrid = 7,
+ CanLineTo,
+ CanMultiDrawNineGrid,
+ CanSaveBitmap = 0x0B,
+ CanMultiDstBlt = 0x0F,
+ CanMultiPatBlt = 0x10,
+ CanMultiScrBlt,
+ CanMultiOpaqueRect,
+ CanFastIndex,
+ CanPolygonSC, /* also PolygonCB */
+ CanPolygonCB, /* also PolygonCB */
+ CanPolyline,
+ CanFastGlyph = 0x18,
+ CanEllipseSC, /* also EllipseCB */
+ CanEllipseCB, /* also EllipseSC */
+ CanGlyphIndex,
+};
+enum
+{
+ /* 2.2.2.2.1.1.2 Primary Drawing Order (PRIMARY_DRAWING_ORDER) */
+ PatBlt=1,
+ ScrBlt=2,
+ OpaqueRect=10,
+ MemBlt=13,
+ MultiOpaqueRect=18,
+};
+enum
+{
+ /* 2.2.2.2.1.2.1.1 Secondary Drawing Order Header */
+ CacheImage = 0,
+ CacheCmap,
+ CacheCompressed,
+ CacheGlyph,
+ CacheImage2,
+ CacheCompressed2,
+ CacheBrush = 7,
+ CacheCompressed3,
+};
+
+typedef struct Order Order;
+struct Order
+{
+ int fsize;
+ uchar* (*fn)(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);
+
+Order ordtab[NumOrders] = {
+ [ScrBlt]= { 1, scrblt },
+ [MemBlt]= { 2, memblt },
+ [PatBlt]= { 2, nil },
+ [OpaqueRect]= { 1, nil },
+ [MultiOpaqueRect]= { 2, nil },
+};
+
+Order auxtab[8] = {
+ [CacheImage2]= { 0, cacheimage2 },
+ [CacheCompressed2]= { 0, cacheimage2 },
+ [CacheImage]= { 0, cacheimage },
+ [CacheCompressed]= { 0, cacheimage },
+ [CacheCmap]= { 0, cachecmap },
+};
+
+uchar
+orderSupport[NumOrders] =
+{
+ [CanScrBlt] 1,
+ [CanMemBlt] 1,
+// [CanMultiOpaqueRect] 1,
+// [CanPatBlt] 1, /* and OpaqueRect */
+};
+
+static struct GdiContext
+{
+ int order;
+ Rectangle clipr;
+} gc = {PatBlt};
+
+static Image* imgcache[3][600];
+
+static uchar* getclipr(Rectangle*,uchar*,uchar*);
+static uchar* getpt(Point*,uchar*,uchar*,int,int);
+
+/* 2.2.2.2 Fast-Path Orders Update (TS_FP_UPDATE_ORDERS) */
+void
+scanorders(uchar* p, uchar* ep, int count)
+{
+ int ctl, fmask, fsize;
+ int size, opt, xorder;
+
+ while(count-- > 0 && p<ep){
+ fmask = 0;
+ ctl = *p;
+ if(!(ctl&Standard))
+ goto ErrNstd; // GDI+ or out of sync
+ if(ctl&Secondary){
+ if(p+6>ep)
+ sysfatal(Eshort);
+ size = ((short)GSHORT(p+1))+13;
+ if(size < 0 || p+size > ep)
+ sysfatal(Eshort);
+ opt = GSHORT(p+3);
+ xorder = p[5];
+ if(xorder >= nelem(auxtab) || auxtab[xorder].fn == nil){
+ fprint(2, "egdi: unsupported secondary order %d\n", xorder);
+ p += size;
+ continue;
+ }
+
+ auxtab[xorder].fn(p, p+size, xorder, opt);
+ p += size;
+ continue;
+ }
+ p++;
+ if(ctl&NewOrder){
+ gc.order = *p++;
+ if(gc.order >= NumOrders) // paranoia
+ gc.order = PatBlt;
+ }
+ fsize = ordtab[gc.order].fsize - ((ctl>>6)&Bits2);
+ switch(fsize){
+ default:
+ goto ErrFsize;
+ case 3:
+ fmask = p[0]|(p[1]<<8)|(p[2]<<16);
+ break;
+ case 2:
+ fmask = GSHORT(p);
+ break;
+ case 1:
+ fmask = p[0];
+ case 0:
+ break;
+ }
+ p += fsize;
+
+ if(ctl&Clipped && !(ctl&SameClipping))
+ p = getclipr(&gc.clipr, p, ep);
+
+ if(ordtab[gc.order].fn == nil)
+ goto ErrNotsup;
+ p = ordtab[gc.order].fn(p, ep, ctl, fmask);
+ if(p == nil)
+ break;
+ }
+ lockdisplay(display);
+ flushimage(display, 1);
+ unlockdisplay(display);
+ return;
+
+ErrNstd:
+ fprint(2, "egdi: non-standard order\n");
+ return;
+ErrFsize:
+ fprint(2, "egdi: bad field encoding bytes count for order %d\n", gc.order);
+ return;
+ErrNotsup:
+ fprint(2, "egdi: unsupported order %d\n", gc.order);
+ return;
+}
+
+static uchar*
+getclipr(Rectangle* pr, uchar* p, uchar* ep)
+{
+ int bctl;
+
+ bctl = *p++;
+ if(bctl&1<<4)
+ pr->min.x += (char)*p++;
+ else if(bctl&1<<0){
+ pr->min.x = GSHORT(p);
+ p += 2;
+ }
+ if(bctl&1<<5)
+ pr->min.y += (char)*p++;
+ else if(bctl&1<<1){
+ pr->min.y = GSHORT(p);
+ p += 2;
+ }
+ if(bctl&1<<6)
+ pr->max.x += (char)*p++;
+ else if(bctl&1<<2){
+ pr->max.x = GSHORT(p)+1;
+ p += 2;
+ }
+ if(bctl&1<<7)
+ pr->max.y += (char)*p++;
+ else if(bctl&1<<3){
+ pr->max.y = GSHORT(p)+1;
+ p += 2;
+ }
+ if(p>ep)
+ sysfatal(Eshort);
+ return p;
+}
+
+static uchar*
+getpt(Point* pp, uchar* s, uchar *es, int ctl, int fmask)
+{
+ Point p;
+
+ p = *pp;
+ if(ctl&Diff){
+ if(fmask&1<<0)
+ p.x += (char)*s++;
+ if(fmask&1<<1)
+ p.y += (char)*s++;
+ }else{
+ if(fmask&1<<0){
+ p.x = GSHORT(s);
+ s += 2;
+ };
+ if(fmask&1<<1){
+ p.y = GSHORT(s);
+ s += 2;
+ };
+ }
+ if(s > es)
+ sysfatal(Eshort);
+ *pp = p;
+ return s;
+}
+
+static uchar*
+getoffrect(Rectangle* pr, uchar* p, uchar* ep, int ctl, int fmask){
+ Rectangle r;
+
+ r = *pr;
+ r.max = subpt(r.max, r.min);
+ p = getpt(&r.min, p, ep, ctl, fmask);
+ p = getpt(&r.max, p, ep, ctl, fmask>>2);
+ r.max = addpt(r.max, r.min);
+ *pr = r;
+ return p;
+}
+
+/* 2.2.2.2.1.1.2.7 ScrBlt (SCRBLT_ORDER) */
+static uchar*
+scrblt(uchar* p, uchar* ep, int ctl, int fmask)
+{
+ static Rectangle r;
+ static Point pt;
+ static int rop3;
+ Rectangle rs;
+ Point ps;
+
+ p = getoffrect(&r, p, ep, ctl, fmask);
+ if(fmask&1<<4)
+ rop3 = *p++;
+ p = getpt(&pt, p, ep, ctl, fmask>>5);
+
+ if(rop3 != ROP2_COPY){
+ fprint(2, "scrblt: rop3 %#hhux is not supported\n", rop3);
+ return p;
+ }
+ rs = r;
+ if(ctl&Clipped)
+ rectclip(&rs, gc.clipr); // not replclipr: need to clip dst only
+ rs = rectaddpt(rs, screen->r.min);
+ ps = addpt(pt, screen->r.min);
+
+ lockdisplay(display);
+ draw(screen, rs, screen, nil, ps);
+ unlockdisplay(display);
+
+ return p;
+}
+
+/* 2.2.2.2.1.1.2.9 MemBlt (MEMBLT_ORDER) */
+static uchar*
+memblt(uchar* p, uchar* ep, int ctl, int fmask)
+{
+ static int cid; /* cacheId */
+ static int coff; /* cacheIndex */
+ static int rop3;
+ static Rectangle r;
+ static Point pt;
+ Image* img;
+
+ if(fmask&1<<0){
+ cid = GSHORT(p);
+ p += 2;
+ }
+ p = getoffrect(&r, p, ep, ctl, fmask>>1);
+ if(fmask&1<<5)
+ rop3 = *p++;
+ p = getpt(&pt, p, ep, ctl, fmask>>6);
+ if(fmask&1<<8){
+ coff = GSHORT(p);
+ p += 2;
+ }
+ if(p>ep)
+ sysfatal(Eshort);
+
+ cid &= Bits8;
+ if(cid >= nelem(imgcache) || coff >= nelem(*imgcache)){
+ fprint(2, "memblt: bad image cache spec [%d %d]\n", cid, coff);
+ return p;
+ }
+ img = imgcache[cid][coff];
+ if(img == nil){
+ fprint(2, "memblt: empty cache entry cid %d coff %d\n", cid, coff);
+ return p;
+ }
+
+ lockdisplay(display);
+ if(ctl&Clipped)
+ replclipr(screen, screen->repl, rectaddpt(gc.clipr, screen->r.min));
+ draw(screen, rectaddpt(r, screen->r.min), img, nil, pt);
+ if(ctl&Clipped)
+ replclipr(screen, screen->repl, screen->r);
+ unlockdisplay(display);
+
+ return p;
+}
+
+static Image*
+pickimage(int cid, int coff, Rectangle r, ulong chan)
+{
+ Image* img;
+
+ if(cid >= nelem(imgcache) || coff >= nelem(*imgcache)){
+ werrstr("bad image cache spec [%d %d]", cid, coff);
+ return nil;
+ }
+
+ img = imgcache[cid][coff];
+ if(img==nil || !eqrect(img->r, r)){
+ if(img != nil)
+ freeimage(img);
+ img = allocimage(display, r, chan, 0, DNofill);
+ if(img == nil)
+ sysfatal("%r");
+ imgcache[cid][coff] = img;
+ }
+ return img;
+}
+
+/* 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)
+{
+ int cid; /* cacheId */
+ int coff; /* cacheIndex */
+ int chan;
+ int zip;
+ int err;
+ int size;
+ Image* img;
+ Point d; /* width, height */
+ Rectangle r;
+
+ if(p+15 >= ep)
+ sysfatal(Eshort);
+ cid = p[6];
+ d.x = p[8];
+ d.y = p[9];
+ size = GSHORT(p+11);
+ coff = GSHORT(p+13);
+ r.min = ZP;
+ r.max = d;
+ chan = rd.chan;
+ zip = (xorder==CacheCompressed);
+
+ if(zip)
+ if(opt&1<<10){
+ p += 8; // bitmapComprHdr
+ size -= 8;
+ }
+ if(p+size > ep)
+ sysfatal(Eshort);
+ if((img = pickimage(cid, coff, r, chan)) == nil)
+ sysfatal("pickimage: %r");
+ err = (zip? loadrle : loadbmp)(img, r, p, size);
+ if(err < 0)
+ sysfatal("%r");
+ return p+size;
+}
+
+/* 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)
+{
+ int n, c;
+ int zip;
+ int cid; /* cacheId */
+ int coff; /* cacheIndex */
+ int chan;
+ int size;
+ int err;
+ Image* img;
+ Point d;
+ Rectangle r;
+
+ if(p+9 >= ep)
+ sysfatal(Eshort);
+ p += 6;
+
+ chan = rd.chan;
+ zip = (xorder==CacheCompressed2);
+ cid = opt&Bits3;
+ opt >>= 7;
+
+ if(opt&1<<1)
+ p += 8; // persistent cache key
+ c = *p++;
+ if(c&1<<7)
+ c = ((c&Bits7)<<8) | *p++;
+ d.x = c;
+ if(opt&1)
+ d.y = d.x;
+ else{
+ c = *p++;
+ if(c&1<<7)
+ c = ((c&Bits7)<<8) | *p++;
+ d.y = c;
+ }
+ r.min = ZP;
+ r.max = d;
+
+ c = *p++;
+ n = c>>6;
+ c &= Bits6;
+ switch(n){
+ case 3: c = (c<<8) | *p++;
+ case 2: c = (c<<8) | *p++;
+ case 1: c = (c<<8) | *p++;
+ }
+ size = c; // fixme: protect from integer overflow
+
+ c = *p++;
+ if(c&1<<7)
+ c = ((c&Bits7)<<8) | *p++;
+ coff = c;
+
+ if(zip&& !(opt&1<<3)){
+ p += 8; // bitmapComprHdr
+ size -= 8;
+ }
+ if(p+size > ep)
+ sysfatal(Eshort);
+ if((img = pickimage(cid, coff, r, chan)) == nil)
+ sysfatal("pullimage: %r");
+ err = (zip? loadrle : loadbmp)(img, r, p, size);
+ if(err < 0)
+ sysfatal("%r");
+ return p+size;
+}
+
+/* 2.2.2.2.1.2.4 Cache Color Table (CACHE_COLOR_TABLE_ORDER) */
+static uchar*
+cachecmap(uchar* p,uchar* ep, int, int)
+{
+ int cid, n;
+
+ cid = p[6];
+ n = GSHORT(p+7);
+ if(n != 256){
+ fprint(2, "cachecmap: %d != 256\n", n);
+ return nil;
+ }
+ if(p+9+4*256>ep){
+ werrstr(Eshort);
+ return nil;
+ }
+ /* fixed cmap here */
+ USED(cid);
+ return p+9+4*256;
+}
--- /dev/null
+++ b/ele.c
@@ -1,0 +1,144 @@
+/*
+ * [MS-RDPELE] 2.2.2 Licensing PDU (TS_LICENSING_PDU)
+ * http://msdn.microsoft.com/en-us/library/cc241913.aspx
+ *
+ * 2.2.1.12.1.1 Licensing Preamble [MS-RDPBCGR]
+ * 2.2.1.12.1.2 Licensing Binary Blob [MS-RDPBCGR]
+ * 2.2.2.2 Client New License Request [MS-RDPELE]
+ */
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+ RandomSize= 32,
+
+ PreambleV3= 3, /* RDP 5.0+ */
+ KeyExRSA= 1,
+
+ SNeedLicense= 1,
+ SHaveChal= 2,
+ SHaveLicense= 3,
+ SNeedRenew= 4,
+ CLicenseInfo= 0x12,
+ CNeedLicense= 0x13,
+ CChalResp= 0x15,
+ Notify= 0xFF,
+
+ Brandom= 2,
+ Berror= 4,
+ Bcuser= 15,
+ Bchost= 16,
+
+ ErrCNoLicense= 2,
+
+ TotalAbort= 1,
+ NoTransition= 2,
+};
+
+static void reqlicense(char*,char*);
+static void senderr(int,int);
+
+void
+scanlicensepdu(uchar* p, uchar* ep)
+{
+ uchar type;
+
+ if(ep-p < 1)
+ sysfatal(Eshort);
+
+ /* type[1] flags[1] size[2] */
+ type = p[0];
+ switch(type){
+ case SNeedLicense:
+ reqlicense(rd.user, rd.local);
+ break;
+ case SHaveChal:
+ fprint(2, "unhandled SHaveChal PDU\n");
+ senderr(ErrCNoLicense, TotalAbort);
+ break;
+ case SNeedRenew:
+ case SHaveLicense:
+ case Notify:
+ rd.licensed = 1;
+ break;
+ }
+}
+
+static void
+reqlicense(char* user, char *host)
+{
+ uchar buf[180], *p, *ep;
+ int nb, ndata, usersize, hostsize;
+
+ usersize = strlen(user)+1;
+ hostsize = strlen(host)+1;
+ ndata = 24+usersize+hostsize+RandomSize+48;
+ nb = sizeof(buf);
+
+ p = prebuf(buf, nb, ndata, 0, Slicensepk);
+ if(p == nil)
+ sysfatal("reqlicense: %r");
+ ep = p+ndata;
+
+ /*
+ * 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, Bchost);
+ PSHORT(p+2, hostsize);
+ memcpy(p+4, host, hostsize);
+ p+= 4+hostsize;
+
+ assert(p == ep);
+ writen(rd.fd, buf, 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);
+}
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,99 @@
+/* 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);
+
+/* mcs.c */
+int mcschanid(uchar*,uchar*);
+int mcstype(uchar*,uchar*);
+int ismcshangup(uchar*,uchar*);
+uchar* mcspayload(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);
+
+/* 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);
+
+/* 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*);
+
+/* rle.c */
+uchar* unrle(uchar*,int, uchar*,int,int,int);
+
+/* utf16.c */
+int fromutf16(char*,int,uchar*,int);
+int toutf16(uchar*,int,char*,int);
+
+/* x224.c */
+int mktpdat(uchar*,int,int);
+int readpdu(int,uchar*,uint);
+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);
+
+/* rd.c */
+void atexitkiller(void);
+void atexitkill(int pid);
+void* emalloc(ulong);
+void* erealloc(void*,ulong);
+char* estrdup(char*);
+long writen(int,void*,long);
+
--- /dev/null
+++ b/guide
@@ -1,0 +1,12 @@
+mk clean
+cpu -c 'objtype='$cputype mk
+window -r 2 44 1026 812
+kill 5.out | rc
+src -s memcpy 5.out
+g
+h2d
+d2h
+dat.h fns.h mkfile /dist/replica/rd.proto
+diff -r . /sys/src/cmd/rd | sed '/^diff/!d; s//cp /'
+wc [~_]*.[ch] | sort -rn
+/n/dump/2015/0108/386/bin/rd # legacy security
--- /dev/null
+++ b/kbd.c
@@ -1,0 +1,231 @@
+#include <u.h>
+#include <libc.h>
+#include <keyboard.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+
+enum
+{
+ Kbrk= Spec|0x61,
+ KeyEx= 0x0100,
+ KeyUp= 0x8000,
+};
+
+enum
+{
+ Sext= (1<<16),
+ Sesc= 1,
+ Sbspace= 14,
+ Stab= 15,
+ Sq= 16,
+ Sw= 17,
+ Se= 18,
+ Sr= 19,
+ St= 20,
+ Sy= 21,
+ Su= 22,
+ Si= 23,
+ So= 24,
+ Sp= 25,
+ Srbrace= 27,
+ Sret= 28,
+ Slctrl= 29,
+ Sa= 30,
+ Ss= 31,
+ Sd= 32 ,
+ Sf= 33,
+ Sg= 34,
+ Sh= 35,
+ Sj= 36,
+ Sk= 37,
+ Sl= 38,
+ Slshift= 42,
+ Sbslash= 43,
+ Sz= 44,
+ Sx= 45,
+ Sc= 46,
+ Sv= 47,
+ Sb= 48,
+ Sn= 49,
+ Sm= 50,
+ Speriod= 52,
+ Sslash= 53,
+ Sprint= Sext|55,
+ SF1= 59,
+ SF11= 87,
+ SF12= 88,
+ Shome= 199,
+ Sup= 200,
+ Spgup= 201,
+ Sleft= 203,
+ Sright= 205,
+ Sdown= 208,
+ Spgdown= 209,
+ Send= 207,
+ Sins= 210,
+ Sdel= 211,
+};
+
+struct {
+ uchar sc, mod;
+} rune2scan[] = {
+ Sdel, Slshift,
+ Sa, Slctrl,
+ Sb, Slctrl,
+ Sc, Slctrl,
+ Sd, Slctrl,
+ Se, Slctrl,
+ Sf, Slctrl,
+ Sg, Slctrl,
+ Sbspace, 0,
+ Stab, 0,
+ Sret, 0,
+ Sk, Slctrl,
+ Sl, Slctrl,
+ Sm, Slctrl,
+ Sn, Slctrl,
+ So, Slctrl,
+ Sp, Slctrl,
+ Sq, Slctrl,
+ Sr, Slctrl,
+ Ss, Slctrl,
+ St, Slctrl,
+ Su, Slctrl,
+ Sv, Slctrl,
+ Sw, Slctrl,
+ Sx, Slctrl,
+ Sy, Slctrl,
+ Sz, Slctrl,
+ Sesc, 0,
+ Sbslash, Slctrl,
+ Srbrace, Slctrl,
+ Speriod, Slctrl,
+ Sslash, Slctrl,
+};
+
+void
+kbdsendscan(int sc, int mod)
+{
+ long msec;
+ int f;
+
+ f = 0;
+ if(sc&Sext)
+ f = KeyEx;
+ sc &= ~Sext;
+
+ 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);
+ if(mod != 0)
+ passinput(msec, InputKeycode, KeyUp, mod, 0);
+}
+
+void
+kbdsendrune(Rune r)
+{
+ long msec;
+
+ msec = time(nil);
+ passinput(msec, InputUnicode, 0, r, 0);
+ passinput(msec, InputUnicode, KeyUp, r, 0);
+}
+
+void
+readkbd(void)
+{
+ char buf[256], k[10];
+ int ctlfd, fd, kr, kn, w;
+ uchar mod, sc;
+ Rune r;
+
+ if((fd = open("/dev/cons", OREAD)) < 0)
+ sysfatal("open %s: %r", buf);
+ if((ctlfd = open("/dev/consctl", OWRITE)) < 0)
+ sysfatal("open %s: %r", buf);
+ write(ctlfd, "rawon", 5);
+
+ kn = 0;
+ for(;;){
+ while(!fullrune(k, kn)){
+ kr = read(fd, k+kn, sizeof k - kn);
+ if(kr <= 0)
+ sysfatal("bad read from kbd");
+ kn += kr;
+ }
+ w = chartorune(&r, k);
+ kn -= w;
+ memmove(k, &k[w], kn);
+
+ if(r < nelem(rune2scan)){
+ sc = rune2scan[r].sc;
+ mod = rune2scan[r].mod;
+ kbdsendscan(sc, mod);
+ continue;
+ }
+
+ switch(r){
+ case Kins:
+ kbdsendscan(Sins, 0);
+ break;
+ case Kdel:
+ kbdsendscan(Sdel, 0);
+ break;
+ case Khome:
+ kbdsendscan(Shome, 0);
+ break;
+ case Kend:
+ kbdsendscan(Send, 0);
+ break;
+ case Kpgup:
+ kbdsendscan(Spgup, 0);
+ break;
+ case Kpgdown:
+ kbdsendscan(Spgdown, 0);
+ break;
+ case Kup:
+ kbdsendscan(Sup, 0);
+ break;
+ case Kdown:
+ kbdsendscan(Sdown,0 );
+ break;
+ case Kleft:
+ kbdsendscan(Sleft, 0);
+ break;
+ case Kright:
+ kbdsendscan(Sright, 0);
+ break;
+ case Kbrk:
+ exits("interrupt");
+ break;
+ case Kprint:
+ kbdsendscan(Sprint, 0);
+ break;
+ case KF|1:
+ case KF|2:
+ case KF|3:
+ case KF|4:
+ case KF|5:
+ case KF|6:
+ case KF|7:
+ case KF|8:
+ case KF|9:
+ case KF|10:
+ kbdsendscan(SF1+r-(KF|1), 0);
+ break;
+ case KF|11:
+ kbdsendscan(SF11, 0);
+ break;
+ case KF|12:
+ kbdsendscan(SF12, 0);
+ break;
+ default:
+ kbdsendrune(r);
+ break;
+ }
+ }
+}
--- /dev/null
+++ b/load.c
@@ -1,0 +1,70 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+/* like loadimage(2) but reverses scanline order and translates per cmap */
+int
+loadbmp(Image *i, Rectangle r, uchar *data, int ndata)
+{
+ int n, bpl;
+ uchar *a;
+
+ bpl = bytesperline(r, i->depth);
+ n = bpl*Dy(r);
+ if(n > ndata){
+ werrstr("loadbmp: insufficient data");
+ return -1;
+ }
+
+ if(i->depth == 8)
+ for(a = data; a <data+ndata; a++)
+ *a = cmap[*a];
+
+ n = bpl;
+ while(r.max.y > r.min.y){
+ a = bufimage(i->display, 21+n);
+ if(a == nil){
+ werrstr("bufimage failed");
+ return -1;
+ }
+ a[0] = 'y';
+ BPLONG(a+1, i->id);
+ BPLONG(a+5, r.min.x);
+ BPLONG(a+9, r.max.y-1);
+ BPLONG(a+13, r.max.x);
+ BPLONG(a+17, r.max.y);
+ memmove(a+21, data, n);
+ ndata += n;
+ data += n;
+ r.max.y--;
+ }
+ if(flushimage(i->display, 0) < 0)
+ return -1;
+ return ndata;
+}
+
+int
+loadrle(Image *i, Rectangle r, uchar *data, int ndata)
+{
+ int nb, bpl;
+ uchar *buf;
+
+ bpl = bytesperline(r, i->depth);
+ nb = bpl*Dy(r);
+ buf = emalloc(nb);
+
+ if(unrle(buf, nb, data, ndata, bpl, bpl/Dx(r)) == nil){
+ werrstr("loadrle: decompression failed");
+ free(buf);
+ return -1;
+ }
+ if(loadbmp(i, r, buf, nb) < 0){
+ werrstr("loadrle: r=%R i->r=%R: %r", r, i->r);
+ free(buf);
+ return -1;
+ }
+ free(buf);
+ return nb;
+}
--- /dev/null
+++ b/mcs.c
@@ -1,0 +1,591 @@
+/* T.122 MCS, T.124 Generic Conference Control, T.125 MCS protocol */
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+ /* ASN.1 Universal tags */
+ TagBool = 1,
+ TagInt = 2,
+ TagOctetString = 4,
+ 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,
+ ClientSec= 0xC002,
+ ClientNet= 0xC003,
+
+ CanErrinfo= 1,
+ Want32bpp= 2,
+
+ /* 2.2.1.3.1 User Data Header (TS_UD_HEADER) */
+ SrvCore = 0x0C01,
+
+};
+
+int mkgcccr(uchar*,int);
+int sizegcccr(void);
+
+enum
+{
+ Bits5 = 0x1F,
+ 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)
+{
+ uint u,v,go;
+
+ v = 0;
+ go = 1;
+ while(go){
+ if(p >= ep){
+ werrstr(Eshort);
+ return nil;
+ }
+ u = *p;
+ v = (v<<7) | (u&Bits7);
+ if(v&(Bits7<<24)){
+ werrstr(Ebignum);
+ return nil;
+ }
+ go = u&(1<<7);
+ p++;
+ }
+ *pv = v;
+ return p;
+}
+
+static uchar*
+gbtag(uchar *p, uchar* ep, int* ptag)
+{
+ if(p >= ep){
+ werrstr(Eshort);
+ return nil;
+ }
+ *ptag = p[0] & Bits5;
+ p += 1;
+ if(*ptag == Bits5)
+ p = gbuint7(p, ep, ptag);
+ return p;
+}
+
+static uchar*
+gblen(uchar *p, uchar* ep, int* plen)
+{
+ int c,v;
+
+ if(p >= ep){
+ werrstr(Eshort);
+ return nil;
+ }
+
+ v = *p++;
+ if(v < (1<<7)){
+ *plen = v;
+ return p;
+ }
+ c = v&Bits7;
+ if(p+c >= ep){
+ werrstr(Eshort);
+ return nil;
+ }
+ switch(c){
+ default: werrstr(Ebignum); return nil;
+ case 0: *plen = 0; break;
+ case 1: *plen = p[0]; break;
+ case 2: *plen = GSHORTB(p); break;
+ case 3: *plen = (GSHORTB(p)<<8)|p[2]; break;
+ case 4: *plen = GLONGB(p); break;
+ }
+ return p+c;
+}
+
+static void
+pbshort(uchar* p, int v)
+{
+ p[0]=2;
+ p[1]=2;
+ PSHORTB(p+2,v);
+}
+
+int
+mcstype(uchar* p, uchar* ep)
+{
+ if(!isdatatpdu(p,ep)){
+ werrstr("not an X.224 Data TPDU");
+ return -1;
+ }
+ p = tpdupayload(p, ep);
+ if(p == nil)
+ return -1;
+ if(p >= ep){
+ werrstr(Eshort);
+ return -1;
+ }
+ return p[0]>>2;
+}
+
+int
+ismcshangup(uchar* p, uchar* ep)
+{
+ return (mcstype(p,ep) == Mdpu);
+}
+
+int
+mcschanid(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)
+ return -1;
+ if(p+5 > ep){
+ werrstr(Eshort);
+ return -1;
+ }
+ return GSHORTB(p+3);
+}
+
+uchar*
+mcspayload(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)
+ return nil;
+
+ if(p+6 > ep){
+ werrstr(Eshort);
+ return nil;
+ }
+ if(p[6] & 0x80)
+ p += 8;
+ else
+ p += 7;
+ if(p > ep){
+ werrstr(Eshort);
+ return nil;
+ }
+ 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)
+{
+ uchar* p;
+ p = buf;
+ if(nbuf < ndata+MCSCIFIXLEN){
+ werrstr("buffer too small");
+ return -1;
+ }
+
+ PSHORTB(p, 0x7f65); /* Connect-Initial tag */
+ p[2] = 0x82; /* length in next 2 bytes */
+ PSHORTB(p+3, ndata+MCSCIFIXLEN-2*2-1);
+ p += 5;
+
+ /* BER callingDomainSelector */
+ p[0] = TagOctetString;
+ p[1] = 1; /* len */
+ p[2] = 1;
+ p += 3;
+ /* BER calledDomainSelector */
+ p[0] = TagOctetString;
+ p[1] = 1; /* len */
+ p[2] = 1;
+ p += 3;
+ /* BER upwardFlag */
+ p[0] = TagBool;
+ p[1] = 1; /* len */
+ p[2] = 0xff;
+ p += 3;
+
+ /* BER MCS DomainParamaters: targetParameters */
+ p[0] = 0x30; /* tag */
+ p[1] = 8*4; /* len */
+ pbshort(p+2, 34); /* maxChannelIds */
+ pbshort(p+6, 2); /* maxUserIds */
+ pbshort(p+10, 0); /* maxTokenIds */
+ pbshort(p+14, 1); /* maxPriorities */
+ pbshort(p+18, 0); /* minThroughput */
+ pbshort(p+22, 1); /* maxHeight (of a MCS provider) */
+ pbshort(p+26, 65535); /* maxMCSPDUsize */
+ pbshort(p+30, 2); /* (MCS) protocolVersion */
+ p += 34;
+
+ /* BER MCS DomainParamaters: minimumParameters */
+ p[0] = 0x30; /* tag */
+ p[1] = 8*4; /* len */
+ pbshort(p+2, 1); /* maxChannelIds */
+ pbshort(p+6, 1); /* maxUserIds */
+ pbshort(p+10, 1); /* maxTokenIds */
+ pbshort(p+14, 1); /* maxPriorities */
+ pbshort(p+18, 0); /* minThroughput */
+ pbshort(p+22, 1); /* maxHeight (of a MCS provider) */
+ pbshort(p+26, 1056); /* maxMCSPDUsize */
+ pbshort(p+30, 2); /* (MCS) protocolVersion */
+ p += 34;
+
+ /* BER MCS DomainParamaters: maximumParameters */
+ p[0] = 0x30; /* tag */
+ p[1] = 8*4; /* len */
+ pbshort(p+2, 65535); /* maxChannelIds */
+ pbshort(p+6, 65535); /* maxUserIds */
+ pbshort(p+10, 65535); /* maxTokenIds */
+ pbshort(p+14, 1); /* maxPriorities */
+ pbshort(p+18, 0); /* minThroughput */
+ pbshort(p+22, 1); /* maxHeight (of a MCS provider) */
+ pbshort(p+26, 65535); /* maxMCSPDUsize */
+ pbshort(p+30, 2); /* (MCS) protocolVersion */
+ p += 34;
+
+ /* BER userData */
+ p[0] = TagOctetString;
+ p[1] = 0x82; /* length in next 2 bytes */
+ PSHORTB(p+2, ndata);
+ /* userData should follow */
+
+ return MCSCIFIXLEN+ndata;
+}
+
+/* GCC Conference Create Request [T.124 section 8.7] in ASN.1 PER [X.691] */
+int
+sizegcccr(void)
+{
+ int size;
+ size = 9+14+216+12+12 + 8+12*nvc;
+ return size; // should agree with the below
+}
+
+int
+mkgcccr(uchar* buf, int nb)
+{
+ int i;
+ uchar *p, *ep;
+ long gccsize, earlyCapabilityFlags;
+
+ p = buf;
+ ep = buf+nb;
+ gccsize = sizegcccr()-9;
+ if(p+gccsize+9 > ep){
+ werrstr(Eshort);
+ return -1;
+ }
+
+ earlyCapabilityFlags = CanErrinfo;
+ if(rd.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;
+
+ // connectPDU as a PER octet string
+ PSHORTB(p+7, (gccsize | 0x8000)); // connectPDU length
+ PSHORTB(p+9, 8); // ConferenceCreateRequest
+ PSHORTB(p+11, 16);
+ p[13] = 0;
+ PSHORT(p+14, 0xC001); // userData key: h221NonStandard. Yes, in LE.
+ p[16] = 0;
+ memcpy(p+17, "Duca", 4); // H.221 nonstandard key (as mandated in 3.2.5.3.3)
+ PSHORTB(p+21, ((gccsize-14) | 0x8000)); // userData length
+ p += 23;
+
+ // 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
+ 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]
+ PSHORT(p+54, 0); // zero-terminateclientName
+ PLONG(p+56, 4); // keyboardType: 4="IBM enhanced (101-key or 102-key)"
+ PLONG(p+60, 0); // keyboardSubType
+ PLONG(p+64, 12); // keyboardFunctionKey
+ memset(p+68, 64, 0); // imeFileName[64]
+ 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+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
+ p += 216;
+
+ // 2.2.1.3.3 Client Security Data
+ PSHORT(p+0, ClientSec);
+ PSHORT(p+2, 12); // length of the data block
+ PLONG(p+4, 0); // (legacy) encryptionMethods
+ PLONG(p+8, 0); // extEncryptionMethods
+ p += 12;
+
+ // 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+8, 0); // RedirectedSessionID
+ p += 12;
+
+ // 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);
+ }
+ p += 8+12*nvc;
+
+ 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)
+{
+ int len, tag, r, nb;
+ uchar buf[20], *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");
+
+ 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 != 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;
+
+ /* result */
+ if((p = gbtag(p, ep, &tag)) == nil || tag != TagEnum
+ || (p = gblen(p, ep, &len)) == nil || len < 0 || p+len > ep)
+ return -1;
+ r = p[0];
+ if(r != 0){
+ werrstr("MCS Connect-Response: %d", r);
+ return -1;
+ }
+ p += len;
+
+ /* calledConnectId */
+ if((p = gbtag(p, ep, &tag)) == nil || tag != TagInt
+ || (p = gblen(p, ep, &len)) == nil || len < 0 || p+len > ep)
+ return -1;
+ p += len;
+
+ /* domainParamaters */
+ if((p = gbtag(p, ep, &tag)) == nil || tag != TagSeq
+ || (p = gblen(p, ep, &len)) == nil || len < 0 || p+len > ep)
+ return -1;
+ p += len;
+
+ /* Mcr userData */
+ if((p = gbtag(p, ep, &tag)) == nil || tag != TagOctetString
+ || (p = gblen(p, ep, &len)) == nil || len < 0 || p+len > ep)
+ return -1;
+
+ /* GCC ConferenceCreateResponse [T.124] sect 8.7 */
+ if(p[21]&(1<<7))
+ p += 23;
+ else
+ p += 22;
+
+ while(p<ep){
+ /* 2.2.1.3.1 User Data Header (TS_UD_HEADER) */
+ utype = GSHORT(p+0);
+ ulen = GSHORT(p+2);
+ switch(utype){
+ case SrvCore: /* 2.2.1.4.2 Server Core Data */
+ ver = GLONG(p+4);
+ assert(ver >= 0x00080004);
+ break;
+ }
+ p += ulen;
+ }
+
+ return r;
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,42 @@
+</$objtype/mkfile
+
+TARG=rd
+BIN=/$objtype/bin
+
+HFILES=fns.h dat.h
+CLEANFILES= x509.c
+OFILES=\
+ cap.$O\
+ eclip.$O\
+ egdi.$O\
+ ele.$O\
+ kbd.$O\
+ rle.$O\
+ load.$O\
+ mcs.$O\
+ mouse.$O\
+ mpas.$O\
+ mppc.$O\
+ rd.$O\
+ utf16.$O\
+ vchan.$O\
+ wsys.$O\
+ 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
--- /dev/null
+++ b/mouse.c
@@ -1,0 +1,100 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <mouse.h>
+#include "dat.h"
+#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,
+};
+
+static int mfd = -1;
+
+static void
+sendmouse(Mouse m, int flags)
+{
+ passinput(m.msec, InputMouse, flags, m.xy.x, m.xy.y);
+}
+
+static void
+mouseevent(Mouse m)
+{
+ ushort flags;
+ int chg;
+ static Mouse o;
+
+ switch(m.buttons){
+ case 8:
+ sendmouse(m, MOUSE_FLAG_BUTTON4|MOUSE_FLAG_DOWN);
+ sendmouse(m, MOUSE_FLAG_BUTTON4);
+ return;
+ case 16:
+ sendmouse(m, MOUSE_FLAG_BUTTON5|MOUSE_FLAG_DOWN);
+ sendmouse(m, MOUSE_FLAG_BUTTON5);
+ return;
+ }
+
+ if(!eqpt(m.xy, o.xy))
+ sendmouse(m, MOUSE_FLAG_MOVE);
+
+ chg = m.buttons ^ o.buttons;
+ if(chg&1){
+ flags = MOUSE_FLAG_BUTTON1;
+ if(m.buttons&1)
+ flags |= MOUSE_FLAG_DOWN;
+ sendmouse(m, flags);
+ }
+ if(chg&2){
+ flags = MOUSE_FLAG_BUTTON3;
+ if(m.buttons&2)
+ flags |= MOUSE_FLAG_DOWN;
+ sendmouse(m, flags);
+ }
+ if(chg&4){
+ flags = MOUSE_FLAG_BUTTON2;
+ if(m.buttons&4)
+ flags |= MOUSE_FLAG_DOWN;
+ sendmouse(m, flags);
+ }
+ o = m;
+}
+
+void
+readdevmouse(void)
+{
+ Mouse m;
+ char ev[1+4*12];
+
+ if((mfd = open("/dev/mouse", ORDWR)) < 0)
+ sysfatal("open /dev/mouse: %r");
+
+ for(;;){
+ 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);
+ }else
+ eresized(1);
+ }
+}
+
+void
+warpmouse(int x, int y)
+{
+ if(mfd < 0)
+ return;
+
+ fprint(mfd, "m%d %d", x, y);
+}
--- /dev/null
+++ b/mpas.c
@@ -1,0 +1,884 @@
+/*
+ * 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.
+ */
+#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 */
+{
+ 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,
+};
+
+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,
+
+ 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,
+};
+
+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 */
+};
+
+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,
+
+};
+
+int
+rdphandshake(int)
+{
+ int i;
+
+ if(mcsconnect(rd.fd) < 0)
+ 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;
+}
+
+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;
+ }
+ p = buf;
+ ep = buf+len;
+
+ if(istpkt(p,ep) == 0){
+ scanfastpath(p, ep);
+ continue;
+ }
+ if(ismcshangup(p,ep)){
+ werrstr("Disconnect Provider Ultimatum");
+ return;
+ }
+
+ 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");
+ }
+
+ if(chanid != GLOBALCHAN){
+ scanvcpdu(p, ep, chanid);
+ continue;
+ }
+
+ if(isflowpdu(p,ep))
+ continue;
+
+ scanaspdu(p,ep);
+ }
+}
+
+/* T.128 FlowPDU */
+int
+isflowpdu(uchar* p, uchar* ep)
+{
+ int marker;
+
+ if(p+2 > ep){
+ werrstr(Eshort);
+ return -1;
+ }
+ marker = GSHORT(p);
+ return marker == 0x8000;
+}
+
+/* 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(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;
+ }
+
+ 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");
+
+ pdutype = GSHORT(p+2)&Bits4;
+
+ switch(pdutype){
+ case PDUTYPE_DEMANDACTIVEPDU:
+ activating(p+SCHSIZE, p+len);
+ rd.active = 1;
+ break;
+ case PDUTYPE_DATAPDU:
+ scandatapdu(p+SCHSIZE, p+len);
+ break;
+ case PDUTYPE_DEACTIVATEALLPDU:
+ rd.active = 0;
+ 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;
+ }
+}
+
+/* 2.2.9.1.1.3.1 Slow-Path Graphics Update (TS_GRAPHICS_UPDATE) */
+static void
+scangraphpdu(uchar *p, uchar *ep)
+{
+ int uptype, nord;
+
+ 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;
+ }
+ 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;
+ }
+}
+
+/* 2.2.9.1.1.3.1.2.1 Bitmap Update Data (TS_UPDATE_BITMAP_DATA) */
+static void
+scanimgupdate(uchar* p, uchar* ep)
+{
+ uchar *s;
+ int err, nr, len, depth, chan, opt;
+ static Image* img;
+ Rectangle r, rs, d;
+
+ 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){
+ werrstr(Eshort);
+ return;
+ }
+ 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
+}
+
+/* 2.2.1.13.2 Client Confirm Active PDU */
+static void
+confirmactive(void)
+{
+ int ncap, nsrc, capsize, calen, pdusize;
+ uchar buf[512], *p, *q, *ep;
+
+ ncap = 8;
+ nsrc = sizeof(srcDesc);
+ capsize = 0
+ + GENCAPSIZE
+ + BITCAPSIZE
+ + ORDCAPSIZE
+ + BCACAPSIZE
+ + PTRCAPSIZE
+ + INPCAPSIZE
+ + SNDCAPSIZE
+ + GLYCAPSIZE
+ ;
+ calen = 20+nsrc+capsize;
+
+ p = prebuf(buf, sizeof(buf), calen, 0, 0);
+ if(p == nil)
+ sysfatal("buffer not prepared: %r");
+ ep = p+calen;
+ pdusize = ep-buf;
+ 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);
+
+ /* shareId[4] originatorId[2] sdlen[2] caplen[2] srcdesc[sdlen] ncap[2] pad[2] */
+ PLONG(p+6, rd.shareid);
+ PSHORT(p+10, SRVCHAN);
+ PSHORT(p+12, nsrc);
+ PSHORT(p+14, capsize+4);
+ 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);
+}
+
+/* 2.2.1.11 Client Info PDU */
+static void
+sendclientinfo(void)
+{
+ uchar a[1024], *p, *q;
+ int ndata, secflags, usize;
+ int opt, perfopt;
+ int ndom, nusr, npw, nsh, nwd;
+ uchar *wdom, *wusr, *wpw, *wsh, *wwd;
+
+ 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;
+ 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);
+
+ 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
+ ;
+ perfopt = 0
+ | PERF_DISABLE_FULLWINDOWDRAG
+ | PERF_DISABLE_MENUANIMATIONS
+ | PERF_DISABLE_CURSORSETTINGS
+ | PERF_DISABLE_THEMING
+ ;
+ if(rd.autologon)
+ opt |= INFO_AUTOLOGON;
+
+ secflags = Sinfopk;
+ p = prebuf(a, sizeof(a), ndata, 0, secflags);
+ if(p == nil)
+ sysfatal("sendclientinfo: %r");
+ usize = p+ndata-a;
+ q = p;
+
+ PLONG(q+0, 0); // codePage; langId when opt&INFO_UNICODE
+ PLONG(q+4, opt);
+ PSHORT(q+8, ndom-2);
+ PSHORT(q+10, nusr-2);
+ PSHORT(q+12, npw-2);
+ PSHORT(q+14, nsh-2);
+ PSHORT(q+16, nwd-2);
+ q += 18;
+ memcpy(q, wdom, ndom);
+ q += ndom;
+ memcpy(q, wusr, nusr);
+ q += nusr;
+ memcpy(q, wpw, npw);
+ q += npw;
+ memcpy(q, wsh, nsh);
+ q += nsh;
+ memcpy(q, wwd, nwd);
+ q += nwd;
+
+ PSHORT(q+0, 2); // cbClientAddress
+ PSHORT(q+2, 0); // clientAddress
+ PSHORT(q+4, 2); // cbClientDir
+ PSHORT(q+6, 0); // clientDir
+ memset(q+8, 172, 0); // clientTimeZone
+ PLONG(q+180, 0); // clientSessionId
+ PLONG(q+184, perfopt); // performanceFlags
+ q += 188;
+
+ assert(q == p+ndata);
+
+ free(wdom);
+ free(wusr);
+ free(wpw);
+ free(wsh);
+ free(wwd);
+
+ writen(rd.fd, a, 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)
+{
+ 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...
+ p[14] = pduType2;
+ p[15] = 0; // ctype
+ PSHORT(p+16, 0); // clen
+ 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/mppc.c
@@ -1,0 +1,263 @@
+/*
+ * lifted from /sys/src/cmd/ip/ppp/mppc.c (RFC 2118)
+ * plus the RDP 5.0 64K history (3.1.8.4.2 RDP 5.0)
+ */
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+#define DBG if(0)
+//#define DBG
+
+enum
+{
+ MaxHistorySize= 64*1024,
+
+ Preset= 0x80, /* reset history */
+ Pfront= 0x40, /* move packet to front of history */
+ Pcompress= 0x20, /* packet is compressed */
+ Pbulk64= 0x01, /* RPD5 bulk compression (64K history) */
+};
+
+enum
+{
+ Bits4= 0xf,
+ Bits5= 0x1f,
+ Bits6= 0x3f,
+ Bits7= 0x7f,
+ Bits8= 0xff,
+ Bits11= 0x7ff,
+ Bits13= 0x1fff,
+ Bits16= 0xffff,
+};
+
+enum
+{
+ Lit7, /* seven bit literal */
+ Lit8, /* eight bit literal */
+ Off6, /* six bit offset */
+ Off8, /* eight bit offset */
+ Off11, /* eleven bit offset (64K history) */
+ Off13, /* thirteen bit offset (8K history) */
+ Off16, /* sixteen bit offset (64K history) */
+};
+
+/* decode first four bits (8K history) */
+static int decode8[16]=
+{
+ Lit7, Lit7, Lit7, Lit7,
+ Lit7, Lit7, Lit7, Lit7,
+ Lit8, Lit8, Lit8, Lit8,
+ Off13, Off13, Off8, Off6,
+};
+
+/* decode first five bits (64K history) */
+static int decode64[32]=
+{
+ Lit7, Lit7, Lit7, Lit7,
+ Lit7, Lit7, Lit7, Lit7,
+ Lit7, Lit7, Lit7, Lit7,
+ Lit7, Lit7, Lit7, Lit7,
+ Lit8, Lit8, Lit8, Lit8,
+ Lit8, Lit8, Lit8, Lit8,
+ Off16, Off16, Off16, Off16,
+ Off11, Off11, Off8, Off6,
+};
+
+typedef struct Uncstate Uncstate;
+struct Uncstate
+{
+ uchar his[MaxHistorySize];
+ int indx; /* current indx in history */
+ int size; /* current history size */
+};
+
+static Uncstate uncstate;
+
+
+#define NEXTBYTE sreg = (sreg<<8) | *p++; n--; bits += 8
+uchar*
+uncomp(uchar* buf, int nbytes, int flags, int* psize)
+{
+ int n, bits, off, len, ones, t;
+ int *decode, lookbits, lookmask, maxhis, maxones;
+ ulong sreg;
+ uchar *p, c, *hp, *hs, *he, *hq;
+ Uncstate *s;
+
+ s = &uncstate;
+ p = buf;
+ n = nbytes;
+
+ if(flags&Pbulk64){
+ maxhis = 64*1024;
+ maxones = 14;
+ decode = decode64;
+ lookbits = 5;
+ lookmask = Bits5;
+ }else{
+ maxhis = 8*1024;
+ maxones = 11;
+ decode = decode8;
+ lookbits = 4;
+ lookmask = Bits4;
+ }
+
+ if(flags&Preset){
+ s->indx = 0;
+ s->size = 0;
+ memset(s->his, maxhis, 0);
+ }
+ if(flags&Pfront){
+ s->indx = 0;
+DBG fprint(2, "mppc: front flag set\n");
+ }
+ if(!(flags&Pcompress)){
+ *psize = n;
+ return buf;
+ }
+
+ bits = 0;
+ sreg = 0;
+ hs = s->his; /* history start */
+ hp = hs+s->indx; /* write pointer in history */
+ he = hs+maxhis; /* history end */
+ for(;;){
+ if(bits<lookbits){
+ if(n==0) goto Done;
+ NEXTBYTE;
+ }
+ t = decode[(sreg>>(bits-lookbits))&lookmask];
+ switch(t){
+ default:
+ sysfatal("mppc: bad decode %d!", t);
+ case Lit7:
+ bits -= 1;
+ if(bits<7){
+ if(n==0) goto Done;
+ NEXTBYTE;
+ }
+ c = (sreg>>(bits-7))&Bits7;
+ bits -= 7;
+ if(hp >= he) goto His;
+ *hp++ = c;
+ continue;
+ case Lit8:
+ bits -= 2;
+ if(bits<7) {
+ if(n==0) goto Eof;
+ NEXTBYTE;
+ }
+ c = 0x80 | ((sreg>>(bits-7))&Bits7);
+ bits -= 7;
+ if(hp >= he) goto His;
+ *hp++ = c;
+ continue;
+ case Off6:
+ bits -= lookbits;
+ if(bits<6){
+ if(n==0) goto Eof;
+ NEXTBYTE;
+ }
+ off = (sreg>>(bits-6))&Bits6;
+ bits -= 6;
+ break;
+ case Off8:
+ bits -= lookbits;
+ if(bits<8){
+ if(n==0) goto Eof;
+ NEXTBYTE;
+ }
+ off = ((sreg>>(bits-8))&Bits8)+64;
+ bits -= 8;
+ break;
+ case Off13: /* (8K history) */
+ bits -= 3;
+ while(bits<13){
+ if(n==0) goto Eof;
+ NEXTBYTE;
+ }
+ off = ((sreg>>(bits-13))&Bits13)+320;
+ bits -= 13;
+ break;
+ case Off11: /* (64K history) */
+ bits -= 4;
+ while(bits<11){
+ if(n==0) goto Eof;
+ NEXTBYTE;
+ }
+ off = ((sreg>>(bits-11))&Bits11)+320;
+ bits -= 11;
+ break;
+ case Off16: /* (64K history) */
+ bits -= 3;
+ while(bits<16){
+ if(n==0) goto Eof;
+ NEXTBYTE;
+ }
+ off = ((sreg>>(bits-16))&Bits16)+2368;
+ bits -= 16;
+ break;
+ }
+ for(ones=0;;ones++) {
+ if(bits == 0) {
+ if(n==0) goto Eof;
+ NEXTBYTE;
+ }
+ bits--;
+ if(!(sreg&(1<<bits)))
+ break;
+ }
+ if(ones>maxones){
+ werrstr("bad length %d\n", ones);
+ return nil;
+ }
+ if(ones == 0) {
+ len = 3;
+ } else {
+ ones++;
+ while(bits<ones) {
+ if(n==0) goto Eof;
+ NEXTBYTE;
+ }
+ len = (1<<ones) | ((sreg>>(bits-ones))&((1<<ones)-1));
+ bits -= ones;
+ }
+
+ hq = hp-off;
+ if(hq < hs) {
+ hq += maxhis;
+ if(hq-hs+len > s->size){
+// goto His;
+fprint(2, "mppc: reference past valid history\n");
+ }
+ }
+ if(hp+len > he)
+ goto His;
+ while(len) {
+ *hp++ = *hq++;
+ len--;
+ }
+ }
+
+Done:
+ hq = hs+s->indx;
+ len = hp-hq;
+DBG fprint(2, "mppc: len %d bits = %d n=%d\n", len, bits, n);
+
+ s->indx += len;
+ if(s->indx > s->size)
+ s->size = s->indx;
+
+ *psize = len;
+ return hq;
+
+Eof:
+ werrstr("unexpected end of data");
+ return nil;
+His:
+ werrstr("bad history reference");
+ return nil;
+}
--- /dev/null
+++ b/rd.c
@@ -1,0 +1,281 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+Rdp rd = {
+ .fd = -1,
+ .chan = RGB16,
+ .depth = 16,
+ .dim = {800, 600},
+ .windom = "",
+ .passwd = "",
+ .shell = "",
+ .rwd = "",
+};
+
+char Eshort[]= "short data";
+char Esmall[]= "buffer too small";
+char Ebignum[]= "number too big";
+
+static void
+usage(void)
+{
+ fprint(2, "usage: rd [-0A] [-T title] [-a depth] [-c wdir] [-d dom] [-k keyspec] [-n term] [-s shell] [net!]server[!port]\n");
+ exits("usage");
+}
+
+long
+writen(int fd, void* buf, long nbytes)
+{
+ long n, sofar;
+
+ sofar = 0;
+ while(sofar < nbytes){
+ n = write(fd, buf, nbytes-sofar);
+ if(n <= 0)
+ break;
+ sofar += n;
+ }
+ return sofar;
+}
+
+static int
+startmouseproc(void)
+{
+ int mpid;
+
+ switch(mpid = rfork(RFPROC|RFMEM)){
+ case -1:
+ sysfatal("rfork: %r");
+ case 0:
+ break;
+ default:
+ return mpid;
+ }
+ atexit(atexitkiller);
+ readdevmouse();
+ exits("mouse eof");
+ return 0;
+}
+
+static int
+startkbdproc(void)
+{
+ int pid;
+ switch(pid = rfork(RFPROC|RFMEM)){
+ case -1:
+ sysfatal("rfork: %r");
+ case 0:
+ break;
+ default:
+ return pid;
+ }
+ atexit(atexitkiller);
+ readkbd();
+ exits("kbd eof");
+ return 0;
+}
+
+static int
+startsnarfproc(void)
+{
+ int pid;
+
+ switch(pid = rfork(RFPROC|RFMEM)){
+ case -1:
+ sysfatal("rfork: %r");
+ case 0:
+ break;
+ default:
+ return pid;
+ }
+ atexit(atexitkiller);
+ pollsnarf();
+ exits("snarf eof");
+ return 0;
+}
+
+static int killpid[32];
+static int nkillpid;
+
+void
+atexitkiller(void)
+{
+ int i, pid;
+
+ pid = getpid();
+ for(i=0; i<nkillpid; i++)
+ if(pid != killpid[i])
+ postnote(PNPROC, killpid[i], "kill");
+}
+void
+atexitkill(int pid)
+{
+ killpid[nkillpid++] = pid;
+}
+
+void
+main(int argc, char *argv[])
+{
+ int doauth, fd;
+ char *server, *addr, *keyspec;
+ UserPasswd *creds;
+
+ rd.local = getenv("sysname");
+ rd.user = getenv("user");
+
+ keyspec = "";
+ doauth = 1;
+
+ ARGBEGIN {
+ case 'A':
+ doauth = 0;
+ break;
+ case 'k':
+ keyspec = EARGF(usage());
+ break;
+ case 'T':
+ rd.label = strdup(EARGF(usage()));
+ break;
+ case 'd':
+ rd.windom = strdup(EARGF(usage()));
+ break;
+ case 's':
+ rd.shell = strdup(EARGF(usage()));
+ break;
+ case 'c':
+ rd.rwd = strdup(EARGF(usage()));
+ break;
+ case 'n':
+ rd.local = estrdup(EARGF(usage()));
+ 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;
+ break;
+ default:
+ usage();
+ } ARGEND
+
+ if (argc != 1)
+ usage();
+
+ server = argv[0];
+ if(rd.local == nil)
+ sysfatal("set $sysname or use -n\n");
+ if(rd.user == nil)
+ sysfatal("set $user");
+ if (rd.label == nil)
+ rd.label = smprint("rd %s", server);
+
+ if(doauth){
+ creds = auth_getuserpasswd(auth_getkey, "proto=pass service=rdp %s", keyspec);
+ if(creds == nil)
+ fprint(2, "factotum: %r\n");
+ else {
+ rd.user = creds->user;
+ rd.passwd = creds->passwd;
+ rd.autologon = 1;
+ }
+ }
+
+ addr = netmkaddr(server, "tcp", "3389");
+ fd = dial(addr, nil, nil, nil);
+ if(fd < 0)
+ sysfatal("dial %s: %r", addr);
+ rd.fd = fd;
+ if(x224connect(fd) < 0)
+ sysfatal("initial handshake failed: %r");
+
+ if(initdraw(drawerror, nil, rd.label) < 0)
+ sysfatal("initdraw: %r");
+ 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 */
+
+ if(rdphandshake(fd) < 0)
+ sysfatal("handshake failed: %r");
+
+ atexit(atexitkiller);
+ atexitkill(getpid());
+ atexitkill(startmouseproc());
+ atexitkill(startkbdproc());
+ initsnarf();
+ atexitkill(startsnarfproc());
+
+ readnet(rd.fd);
+
+ x224disconnect(rd.fd);
+ close(rd.fd);
+ lockdisplay(display);
+ closedisplay(display);
+
+ if(!rd.active)
+ exits(nil);
+ if(rd.hupreason)
+ sysfatal("hangup: %d", rd.hupreason);
+ sysfatal("hangup");
+}
+
+void*
+emalloc(ulong n)
+{
+ void *b;
+
+ b = mallocz(n, 1);
+ if(b == nil)
+ sysfatal("out of memory allocating %lud: %r", n);
+ setmalloctag(b, getcallerpc(&n));
+ return b;
+}
+
+void*
+erealloc(void *a, ulong n)
+{
+ void *b;
+
+ b = realloc(a, n);
+ if(b == nil)
+ sysfatal("out of memory re-allocating %lud: %r", n);
+ setrealloctag(b, getcallerpc(&a));
+ return b;
+}
+
+char*
+estrdup(const char *s)
+{
+ char *b;
+
+ b = strdup(s);
+ if(b == nil)
+ sysfatal("strdup: %r");
+ setmalloctag(b, getcallerpc(&s));
+ return b;
+}
--- /dev/null
+++ b/rle.c
@@ -1,0 +1,256 @@
+/*
+ * 2.2.9.1.1.3.1.2.4 RLE Compressed Bitmap Stream (RLE_BITMAP_STREAM)
+ * http://msdn.microsoft.com/en-us/library/cc240895.aspx
+ * 3.1.9 Interleaved RLE-Based Bitmap Compression
+ * http://msdn.microsoft.com/en-us/library/dd240593.aspx
+*/
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+ Bits4= 15,
+ Bits5= 31,
+};
+
+enum
+{
+ Bg,
+ Fg,
+ FgS,
+ Dith,
+ Fill,
+ Mix,
+ MixS,
+ Lit,
+ Spec,
+ Mix3,
+ Mix5,
+ Wpix,
+ Bpix,
+};
+
+enum
+{
+ Std= 0,
+ Ext= 1,
+};
+
+static int
+decode[2][16] =
+{
+[Std] {
+ Bg, Bg, Fg, Fg, Mix, Mix, Fill, Fill,
+ Lit, Lit, -1, -1, FgS, MixS, Dith, -1,
+ },
+[Ext] {
+ Bg, Fg, Mix, Fill, Lit, -1, FgS, MixS,
+ Dith, Mix3, Mix5, -1, -1, Wpix, Bpix, -1,
+ }
+};
+
+static void*
+memfill(void *a1, ulong n1, void *a2, ulong n2)
+{
+ char *s1, *s2, *e1, *e2;
+
+ if((long)n1 < 0 || (long)n2 <= 0)
+ abort();
+ s1 = a1;
+ s2 = a2;
+ e1 = s1+n1;
+ e2 = s2+n2;
+ while(s1 < e1){
+ *s1++ = *s2++;
+ if(s2 >= e2)
+ s2 = a2;
+ }
+ return a1;
+}
+
+static void*
+memxor(void *a1, void *a2, ulong n)
+{
+ char *s1, *s2;
+
+ if((long)n < 0)
+ abort();
+ s1 = a1;
+ s2 = a2;
+ while(n > 0){
+ *s1++ ^= *s2++;
+ n--;
+ }
+ return a1;
+}
+
+uchar*
+unrle(uchar* d, int nd, uchar* s, int ns, int bpl, int pixelsize)
+{
+ int t, hdr, code, bits, len, wasline1, wasbg;
+ uchar pen[4], dpen[8], wpen[4], *p, *ep, *wp, *we;
+ uint sreg;
+
+ p = s;
+ ep = s+ns;
+ wp = d;
+ we = d+nd;
+ wasbg = 0;
+ wasline1 = 1;
+ PLONG(pen, DWhite);
+ PLONG(wpen, DWhite);
+
+ while(p < ep){
+ hdr = *p++;
+ code = hdr>>4;
+ if(code != Bits4){
+ t = decode[Std][code];
+ if(code>>2 == 3)
+ bits = Bits4;
+ else
+ bits = Bits5;
+ len = hdr&bits;
+ if(t==Mix || t==MixS){
+ if(len == 0)
+ len = 1+*p++;
+ else
+ len *= 8;
+ }else{
+ if(len == 0)
+ len = 1+bits+*p++;
+ }
+ }else{
+ code = hdr&Bits4;
+ if(code < 9){
+ len = GSHORT(p);
+ p += 2;
+ }else
+ len = 0;
+ t = decode[Ext][code];
+ }
+ len *= pixelsize;
+ if(wp+len > we){
+ Overrun:
+ werrstr("unrle: output buffer is %uld bytes short", wp+len-we);
+ return nil;
+ }
+
+ if(t != Bg)
+ wasbg = 0;
+ if(wasline1 && wp-bpl >= d){
+ wasline1 = 0;
+ wasbg = 0;
+ }
+
+ switch(t){
+ default:
+ werrstr("unrle: bad decode");
+ return nil;
+ case Lit:
+ memmove(wp, p, len);
+ p += len;
+ break;
+ case Bg:
+ if(wasbg){
+ memmove(wp, pen, pixelsize);
+ if(wp-bpl >= d)
+ memxor(wp, wp-bpl, pixelsize);
+ wp += pixelsize;
+ len -= pixelsize;
+ }
+ if(wp-bpl >= d){
+ while(len>bpl){
+ memmove(wp, wp-bpl, bpl);
+ wp += bpl;
+ len -= bpl;
+ }
+ memmove(wp, wp-bpl, len);
+ }else
+ memset(wp, len, 0);
+ wasbg = 1;
+ break;
+ case FgS:
+ memmove(pen, p, pixelsize);
+ p += pixelsize;
+ /* fall through */
+ case Fg:
+ memfill(wp, len, pen, pixelsize);
+ if(wp-bpl >= d)
+ memxor(wp, wp-bpl, len);
+ break;
+ case Mix3:
+ sreg = 3;
+ bits = 8;
+ len = 8*pixelsize;
+ goto case_MixAll;
+ case Mix5:
+ sreg = 5;
+ bits = 8;
+ len = 8*pixelsize;
+ goto case_MixAll;
+ case MixS:
+ memmove(pen, p, pixelsize);
+ p += pixelsize;
+ /* fall through */
+ case Mix:
+ sreg = 0;
+ bits = 0;
+ /* fall through */
+ case_MixAll:
+ if(wp+len > we)
+ goto Overrun;
+ while(len > 0){
+ if(bits == 0){
+ sreg = *p++;
+ bits = 8;
+ }
+ if(sreg&1){
+ memmove(wp, pen, pixelsize);
+ if(wp-bpl >= d)
+ memxor(wp, wp-bpl, pixelsize);
+ }else{
+ if(wp-bpl >= d)
+ memmove(wp, wp-bpl, pixelsize);
+ else
+ memset(wp, pixelsize, 0);
+ }
+ wp += pixelsize;
+ len -= pixelsize;
+ sreg >>= 1;
+ bits--;
+ }
+ break;
+ case Fill:
+ memmove(dpen, p, pixelsize);
+ p += pixelsize;
+ memfill(wp, len, dpen, pixelsize);
+ break;
+ case Dith:
+ len *= 2;
+ if(wp+len > we)
+ goto Overrun;
+ memmove(dpen, p, pixelsize);
+ memmove(dpen+pixelsize, p+pixelsize, pixelsize);
+ p += 2*pixelsize;
+ memfill(wp, len, dpen, 2*pixelsize);
+ break;
+ case Wpix:
+ len = pixelsize;
+ if(wp+len > we)
+ goto Overrun;
+ memmove(wp, wpen, pixelsize);
+ break;
+ case Bpix:
+ len = pixelsize;
+ if(wp+len > we)
+ goto Overrun;
+ memset(wp, pixelsize, 0);
+ break;
+ }
+ wp += len;
+ }
+ return wp;
+}
--- /dev/null
+++ b/utf16.c
@@ -1,0 +1,105 @@
+/* /lib/rfc/rfc2781, also translates \n to \r\n */
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+ Bits10 = 0x03FF,
+ Bits16 = 0xFFFF,
+ Bits20 = 0x0FFFFF,
+ HHalfZoneS = 0xD800, HHalfZoneE = 0xDBFF,
+ LHalfZoneS = 0xDC00, LHalfZoneE = 0xDFFF,
+};
+
+int
+toutf16(uchar* buf, int nb, char* s, int ns)
+{
+ uchar *b, *eb;
+ char *es;
+ Rune r;
+
+ b = buf;
+ eb = b+nb;
+ es = s+ns;
+
+ while(s < es){
+ if(*s == '\n'){
+ if(b+2 > eb)
+ break;
+ PSHORT(b, '\r');
+ b+=2;
+ }
+ s += chartorune(&r, s);
+ if(b+2 > eb)
+ break;
+ if(r <= Bits16){
+ PSHORT(b, r);
+ b+=2;
+ continue;
+ }
+ r -= Bits16+1;
+ if(r > Bits20){
+ PSHORT(b, Runeerror);
+ b+=2;
+ continue;
+ }
+ if(b+4 > eb)
+ break;
+ PSHORT(b+0, HHalfZoneS | (r >> 10));
+ PSHORT(b+2, LHalfZoneS | (r & Bits10));
+ b+=4;
+ }
+ return b-buf;
+}
+
+int
+fromutf16(char* str, int ns, uchar* ws, int nw)
+{
+ char *s, *es, buf[UTFmax];
+ uchar *q, *eq;
+ ushort w1, w2;
+ Rune r;
+ int n;
+
+ s = str;
+ es = str + ns;
+ q = ws;
+ eq = ws + nw;
+
+ while(q+2 <= eq){
+ w1 = GSHORT(q);
+ q += 2;
+ if(w1<HHalfZoneS || w1>LHalfZoneE){
+ r = w1;
+ goto Convert;
+ }
+ if(w1>HHalfZoneE){
+ r = Runeerror;
+ goto Convert;
+ }
+ if(q+2 > eq){
+ r = Runeerror;
+ goto Convert;
+ }
+ w2 = GSHORT(q);
+ q += 2;
+ if(w2<LHalfZoneS || w2>LHalfZoneE){
+ r = Runeerror;
+ goto Convert;
+ }
+ r = (w1 & Bits10)<<10 | (w2 & Bits10) + Bits16 + 1;
+ Convert:
+ n = runetochar(buf, &r);
+ if(buf[0] == '\r')
+ continue;
+ if(s+n > es)
+ break;
+ memmove(s, buf, n);
+ s += n;
+ }
+ return s-str;
+}
+
--- /dev/null
+++ b/vchan.c
@@ -1,0 +1,149 @@
+/*
+ * [MS-RDPBCGR] 3.1.5.2 Static Virtual Channels
+ * http://msdn.microsoft.com/en-us/library/cc240926.aspx
+ */
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+enum
+{
+ /* 2.2.1.3.4.1 Channel Definition Structure */
+ ChanInited = 0x80000000,
+ ChanShowproto = 0x00200000,
+
+ /* 2.2.6.1 Virtual Channel PDU */
+ ChanChunkLen = 1600,
+
+ /* 2.2.6.1.1 Channel PDU Header */
+ CFfirst= 0x01,
+ CFlast= 0x02,
+ CFshowproto= 0x10,
+};
+
+Vchan vctab[] =
+{
+ {
+ .mcsid = GLOBALCHAN + 1, /* iota */
+ .name = "CLIPRDR",
+ .fn = clipvcfn,
+ .flags = ChanInited | ChanShowproto,
+ },
+};
+uint nvc = nelem(vctab);
+
+Vchan*
+lookupvc(int mcsid)
+{
+ int i;
+ for(i=0; i<nvc; i++)
+ if(vctab[i].mcsid == mcsid)
+ return &vctab[i];
+ return nil;
+}
+
+Vchan*
+namevc(char* name)
+{
+ int i;
+ for(i=0; i<nvc; i++)
+ if(strcmp(vctab[i].name, name) == 0)
+ return &vctab[i];
+ return nil;
+}
+
+void
+scanvcpdu(uchar *p, uchar* ep, int chanid)
+{
+ int flags, len, rem;
+ uchar *q, *eq;
+ Vchan* vc;
+
+ vc = lookupvc(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;
+ vc->pos = 0;
+ }
+
+ if(!vc->defragging){
+ vc->fn(p, ep);
+ return;
+ }
+
+ 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);
+ free(vc->buf);
+ vc->buf = nil;
+ vc->nb = 0;
+ vc->defragging = 0;
+ }
+}
+
+int
+sendvc(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;
+
+ vc = namevc(cname);
+ if(vc == nil){
+ werrstr("%s: no such vchannel", cname);
+ return -1;
+ }
+ chanid = vc->mcsid;
+ if(chanid < 0)
+ return -1;
+ if(n < 0)
+ return -1;
+
+ p = a;
+ nb = sizeof(buf);
+ flags = CFfirst | CFshowproto;
+
+ for(sofar=0; sofar<n; sofar += chunk){
+ chunk = n-sofar;
+ 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;
+ }
+ return n;
+}
--- /dev/null
+++ b/wsys.c
@@ -1,0 +1,138 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include "dat.h"
+#include "fns.h"
+
+static int snarffd;
+static ulong snarfvers;
+
+#define MAXSNARF 100*1024
+#ifndef BUFSIZE
+#define BUFSIZE 4*1024
+#endif
+
+static int
+ishidden(void)
+{
+ int wctl;
+ long n;
+ uchar buf[80];
+ char *p;
+
+ wctl = open("/dev/wctl", OREAD);
+ if(wctl < 0)
+ return 0;
+ n = read(wctl, buf, sizeof(buf));
+ close(wctl);
+ if(n < 12*4)
+ return 0;
+ buf[sizeof(buf)-1] = 0;
+ p = (char*)buf;
+ if(strstr(p, "hidden") != nil)
+ return 1;
+ return 0;
+}
+
+void
+eresized(int)
+{
+ int fd;
+ Point d;
+
+ lockdisplay(display);
+ if(getwindow(display, Refnone) < 0)
+ 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 */
+ if(d.x < Dx(screen->r) || d.y < Dy(screen->r)){
+ fd = open("/dev/wctl", OWRITE);
+ if(fd >= 0){
+ fprint(fd, "resize -dx %d -dy %d", d.x, d.y);
+ close(fd);
+ }
+ }
+ turnupdates(0);
+ turnupdates(!ishidden());
+ unlockdisplay(display);
+}
+void
+initsnarf(void)
+{
+ snarffd = open("/dev/snarf", OREAD);
+}
+
+/*
+ * /dev/snarf updates when the file is closed, so we must open our own
+ * fd here rather than use snarffd
+ */
+void
+putsnarf(char* s, int nb)
+{
+ int fd, i;
+
+ if(nb==0 || nb > MAXSNARF)
+ return;
+ fd = open("/dev/snarf", OWRITE);
+ if(fd < 0)
+ return;
+ snarfvers++;
+ while(nb > 0){
+ i = write(fd, s, nb);
+ if(i < 0)
+ break;
+ s += i;
+ nb -= i;
+ }
+ close(fd);
+}
+
+char*
+getsnarf(int *pnb)
+{
+ int i, n;
+ char *s, buf[BUFSIZE];
+
+ if(snarffd < 0)
+ return nil;
+
+ s = nil;
+ i = 0;
+ seek(snarffd, 0, 0);
+ while((n = read(snarffd, buf, sizeof(buf))) > 0){
+ s = erealloc(s, i+n+1);
+ memmove(s+i, buf, n);
+ i += n;
+ s[i] = 0;
+ }
+ *pnb = i+1; // for terminating zero
+ return s;
+}
+
+/* lifted from /sys/src/cmd/vnc/wsys.c */
+void
+pollsnarf(void)
+{
+ Dir *dir;
+
+ while(snarffd < 0){
+ snarffd = open("/dev/snarf", OREAD);
+ if(snarffd < 0)
+ sleep(1000*60);
+ }
+
+ for(;;){
+ sleep(1000);
+
+ dir = dirstat("/dev/snarf");
+ if(dir == nil) /* old drawterm */
+ continue;
+ if(dir->qid.vers > snarfvers){
+ clipannounce();
+ snarfvers = dir->qid.vers;
+ }
+ free(dir);
+ }
+}
--- /dev/null
+++ b/x224.c
@@ -1,0 +1,364 @@
+#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
+ * 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)
+ */
+int
+istpkt(uchar* p, uchar* ep)
+{
+ int magic;
+
+ if(p+1>ep){
+ werrstr(Eshort);
+ return -1;
+ }
+
+ magic = p[0];
+ return (magic == 3);
+}
+
+int
+tpdutype(uchar* p, uchar* ep)
+{
+ if(p+5 >= ep){
+ werrstr(Eshort);
+ return -1;
+ }
+ return p[5];
+}
+
+int
+isdatatpdu(uchar* p, uchar* ep)
+{
+ return (tpdutype(p,ep) == Data);
+}
+
+
+/*
+ * read a PDU: either TPKT-encapsulated TPDU or Fast-Path Update PDU
+ */
+int
+readpdu(int fd, uchar *buf, uint nbuf)
+{
+ int n, len;
+ uchar *p;
+
+ p = buf;
+
+ n = readn(fd, p, TPKTFIXLEN);
+ if(n != TPKTFIXLEN){
+ werrstr("short read: %r");
+ return -1;
+ }
+
+ switch(istpkt(p, p+n)){
+ case -1:
+ return -1;
+ case 0:
+ /* Fast-Path Update PDU */
+ len = p[1];
+ if(len&(1<<7))
+ len = ((len^(1<<7))<<8) | p[2];
+ break;
+ default:
+ /* TPKT-encapsulated TPDU */
+ len = GSHORTB(p+2);
+ }
+
+ if(len <= n || len > nbuf){
+ werrstr("bad length in PDU header: %d", len);
+ return -1;
+ }
+
+ n += readn(fd, p+n, len-n);
+ if(n != len)
+ return -1;
+ return n;
+}
+
+uchar*
+tpdupayload(uchar* p, uchar* ep)
+{
+ uchar* q;
+
+ if(istpkt(p, ep) == 0){
+ werrstr("Fast-Path Update PDU is not expected");
+ return nil;
+ }
+ if(tpdutype(p,ep) == Data)
+ q = p+7;
+ else
+ q = p+11;
+ if(q > ep){
+ werrstr(Eshort);
+ return nil;
+ }
+ return q;
+}
+
+/* connect request */
+int
+mktpcr(uchar* buf, int nbuf, int ndata)
+{
+ int size;
+ uchar *p;
+
+ p = buf;
+ size = TPKTFIXLEN+7+ndata;
+ if(size > nbuf){
+ werrstr(Esmall);
+ return -1;
+ }
+
+ /* TPKT header: version[1] unused[1] len[2] */
+ 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);
+ PSHORTB(p+4+4, 0);
+ p[4+6] = 0;
+
+ return size;
+}
+
+/* data transfer */
+int
+mktpdat(uchar* buf, int nbuf, int ndata)
+{
+ int size;
+ uchar *p;
+
+ p = buf;
+ size = TPDATAFIXLEN+ndata;
+ if(size > nbuf){
+ werrstr("buffer too small: provided %d need %d", nbuf, size);
+ return -1;
+ }
+
+ /* TPKT header: version[1] unused[1] len[2] */
+ p[0] = 0x03;
+ p[1] = 0;
+ PSHORTB(p+2, size);
+
+ /* TPDU: hdlen[1] type[1] seqno[1] */
+ p[4] = 2;
+ p[5] = Data;
+ p[6] = (1<<7); /* seqno (0 in Class 0) + EOT mark (1<<7) */
+
+ return size;
+}
+
+/* disconnection request */
+int
+mktpdr(uchar* buf, int nbuf, int ndata)
+{
+ int size;
+ uchar *p;
+
+ p = buf;
+ size = TPDATAFIXLEN+ndata;
+ if(size > nbuf){
+ werrstr("buffer too small");
+ return -1;
+ }
+
+ /* TPKT header: version[1] unused[1] len[2] */
+ p[0] = 0x03;
+ p[1] = 0;
+ PSHORTB(p+2, size);
+
+ /* HupReq: hdlen[1] type[1] seqno[1] */
+ p[4] = 2;
+ 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;
+}