/* * 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 #include #include #include "dat.h" #include "fns.h" #define DBG if(0) //#define DBG uchar cmap[256]; static const char srcDesc[] = "Plan 9"; 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, 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 fd) { int i; if(x224connect(fd) < 0) return -1; if(mcsconnect(fd) < 0) return -1; if(rd.opt&OEncrypt) erectdom(fd); if(attachuser(fd) < 0) return -1; if(joinchannel(fd, rd.userchan) < 0) return -1; if(joinchannel(fd, GLOBALCHAN) < 0) return -1; if(rd.opt&OVersion5) for(i = 0; i < nvc; i++) if(joinchannel(fd, vctab[i].mcsid) < 0) return -1; sendsecxchg(); sendclientinfo(); return 1; } 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); if(rd.opt&OEncrypt || !rd.licensed){ /* * 2.2.8.1.1.2.1 Basic (TS_SECURITY_HEADER) * http://msdn.microsoft.com/en-us/library/cc240579.aspx */ flags = GSHORT(p); p += SECHSIZE; if(flags&SEC_ENCRYPT){ // fixme: prevent un-encrypted sessions p = rdpdecryptv(p, ep-p); if(p == nil){ sysfatal("error decrypting a Slow-Path PDU: %r"); continue; } } if(flags&SEC_LICENSE_PKT){ scanlicensepdu(p, ep); continue; } } if(chanid != GLOBALCHAN){ scanvcpdu(p, ep, chanid); continue; } if(isflowpdu(p,ep)) continue; /* T.128 FlowPDU */ scanaspdu(p,ep); /* T.128 ASPDU */ } } 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 FASTPATH_OUTPUT_SUPPORTED 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(p[1]&(1<<7)) p += 3; else p += 2; if(enc){ p = rdpdecryptv(p, ep-p); if(p == nil) sysfatal("Fast-Path PDU: %r"); } 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); } 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) sysfatal(Eshort); if(depth != img->depth) sysfatal("bad image depth"); r = rectaddpt(r, img->r.min); if(opt&Bcompress) if(!(opt&NO_BITMAP_COMPRESSION_HDR)) 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; i0 && p+4 ep) sysfatal("bad length in server's capability set"); switch (type){ case CAPSTYPE_GENERAL: scangencaps(p, p+len); break; case CAPSTYPE_BITMAP: 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, INPUT_EVENT_SYNC, 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 secflags, 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; secflags = SEC_RESET_SEQNO | SEC_IGNORE_SEQNO; if(rd.opt&OEncrypt) secflags |= SEC_ENCRYPT; p = prebuf(buf, sizeof(buf), calen, 0, secflags); 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 // shareControlHeader [6] PSHORT(p+0, calen); // totalLength[2] PSHORT(p+2, PDUTYPE_CONFIRMACTIVEPDU | (1<<4)); // pduType[2] PSHORT(p+4, rd.userchan); // PDUSource[2] PLONG(p+6, rd.shareid); // shareId[4] PSHORT(p+10, 0x03ea); // originatorId[2] PSHORT(p+12, nsrc); // lengthSourceDescriptor[2] PSHORT(p+14, capsize+4); // lengthCombinedCapabilities[2] memcpy(p+16, srcDesc, nsrc); // sourceDescriptor[*]; T.128 section 8.4.1 PSHORT(p+16+nsrc, ncap); // numberCapabilities[2] PSHORT(p+18+nsrc, 0); // pad2Octets[2] p += nsrc+20; p = putgencaps(p, ep); p = putbitcaps(p, ep); p = putordcaps(p, ep); if(rd.opt&OVersion5) p = putbc2caps(p, ep); else p = putbcacaps(p, ep); p = putptrcaps(p, ep); p = putinpcaps(p, ep); p = putsndcaps(p, ep); p = putglycaps(p, ep); assert(p-calen == q); if(secflags&SEC_ENCRYPT){ rdpmac(q-8, 8, q, calen); rdpencrypt(q, calen); } 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_COMPRESSION | PACKET_COMPR_TYPE_8K ; perfopt = 0 | PERF_DISABLE_FULLWINDOWDRAG | PERF_DISABLE_MENUANIMATIONS | PERF_DISABLE_CURSORSETTINGS | PERF_DISABLE_THEMING ; if(rd.autologon) opt |= INFO_AUTOLOGON; secflags = SEC_INFO_PKT | (rd.opt&OEncrypt? SEC_ENCRYPT : 0); 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); if(secflags&SEC_ENCRYPT){ rdpmac(p-8, 8, p, ndata); rdpencrypt(p, ndata); } 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, secflags, usize; ndata = 4+SCDSIZE; secflags = (rd.opt&OEncrypt? SEC_ENCRYPT : 0); p = prebuf(a, sizeof(a), ndata, 0, secflags); 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 if(secflags&SEC_ENCRYPT){ rdpmac(p-8, 8, p, ndata); rdpencrypt(p, ndata); } 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, secflags, usize; ndata = 8+SCDSIZE; secflags = (rd.opt&OEncrypt? SEC_ENCRYPT : 0); p = prebuf(a, sizeof(a), ndata, 0, secflags); 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] if(secflags&SEC_ENCRYPT){ rdpmac(p-8, 8, p, ndata); rdpencrypt(p, ndata); } writen(rd.fd, a, usize); } /* 2.2.1.18 Client Font List PDU */ static void asfontls(void) { uchar a[64], *p, *q; int ndata, secflags, usize; ndata = 8+SCDSIZE; secflags = (rd.opt&OEncrypt? SEC_ENCRYPT : 0); p = prebuf(a, sizeof(a), ndata, 0, secflags); 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 if(secflags&SEC_ENCRYPT){ rdpmac(p-8, 8, p, ndata); rdpencrypt(p, ndata); } 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, secflags, usize; ndata = 16+SCDSIZE; secflags = (rd.opt&OEncrypt? SEC_ENCRYPT : 0); p = prebuf(a, sizeof(a), ndata, 0, secflags); 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); if(secflags&SEC_ENCRYPT){ rdpmac(p-8, 8, p, ndata); rdpencrypt(p, ndata); } 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, secflags, usize; ndata = (allow?12:4)+SCDSIZE; secflags = (rd.opt&OEncrypt? SEC_ENCRYPT : 0); p = prebuf(a, sizeof(a), ndata, 0, secflags); 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 } if(secflags&SEC_ENCRYPT){ rdpmac(p-8, 8, p, ndata); rdpencrypt(p, ndata); } writen(rd.fd, a, usize); }