/* Based on: T.128 Multipoint application sharing */ #include #include #include "dat.h" #include "fns.h" static const char srcDesc[] = "Plan 9"; /* sourceDescriptor (T.128 section 8.4.1) */ enum { Bits4= 0x0F, /* 2.2.9.1.1.3.1.2.2 Bitmap Data */ Bcompress= 1, Pcompress= 0x20, /* 2.2.8.1.1.1.1 Share Control Header */ PDUactivate = 1, /* Demand Active PDU (section 2.2.1.13.1) */ PDUactivated = 3, /* Confirm Active PDU (section 2.2.1.13.2) */ PDUdeactivate = 6, /* Deactivate All PDU (section 2.2.3.1) */ PDUdata = 7, /* Data PDU */ /* 2.2.9.1.1.4 Server Pointer Update PDU (TS_POINTER_PDU) */ PDUcursorwarp= 3, /* 2.2.1.11.1.1 Info Packet (TS_INFO_PACKET) */ InfMouse= 1<<0, InfNoctlaltdel= 1<<1, InfAutologon= 1<<3, InfUnicode= 1<<4, InfMaxshell= 1<<5, InfCompress= 1<<7, InfWinkey= 1<<8, Compress64k= 1<<9, // RDP 5.0 bulk compression (3.1.8.4.2) PerfNoDrag= 1<<1, PerfNoAnim= 1<<2, PerfNoTheming= 1<<3, PerfNoCursorset= 1<<6, PerfFontAA= 1<<7, /* 2.2.9.1.1.3.1 Slow-Path Graphics Update (TS_GRAPHICS_UPDATE) */ UpdOrders= 0, UpdBitmap= 1, /* section 2.2.9.1.1.3.1.2 */ UpdCmap= 2, /* section 2.2.9.1.1.3.1.1 */ /* 2.2.9.1.2.1 Fast-Path Update (TS_FP_UPDATE) */ FUpdOrders= 0, FUpdBitmap= 1, FUpdCmap= 2, FUpdWarp= 8, /* 2.2.7.1.1 General Capability Set (TS_GENERAL_CAPABILITYSET) */ NoBitcomphdr = 0x0400, }; /* 2.2.9.1.2.1 Fast-Path Update (TS_FP_UPDATE) * * updateHeader[1] compressionFlags[1]? size[2] updateData[*] */ int getshareF(Share* as, uchar* a, uint nbytes) { int hd, nb, cflags, ulen; uchar *p, *ep, *q; as->type = 0; if(nbytes < 3){ werrstr(Eshort); return -1; } p = a; ep = a+nbytes; hd = *p++; if(hd&(1<<7)) cflags = *p++; else cflags = 0; if(p+2 > ep){ werrstr(Eshort); return -1; } nb = GSHORT(p); p += 2; q = p+nb; if(cflags&Pcompress){ if(p+nb > ep){ werrstr("bad length %d in Fast-Path PDU header", nb); return -1; } if((p = uncomp(p, nb, cflags, &ulen)) == nil){ werrstr("fast-path packet de-compression failed: %r cflags=%#x", cflags); return -1; } ep = p+ulen; } switch(hd&Bits4){ case FUpdOrders: if(p+2>ep){ werrstr(Eshort); return -1; } as->type = ShUorders; as->nr = GSHORT(p); as->data = p+2; as->ndata = ep-(p+2); break; case FUpdBitmap: if(p+4>ep){ werrstr(Eshort); return -1; } as->type = ShUimg; as->nr = GSHORT(p+2); as->data = p+4; as->ndata = ep-(p+4); break; case FUpdCmap: as->type = ShUcmap; as->data = p; as->ndata = ep-p; break; case FUpdWarp: if(p+4>ep){ werrstr(Eshort); return -1; } as->type = ShUwarp; as->x = GSHORT(p+0); as->y = GSHORT(p+2); break; } return q-a; } /* * T.128 ASPDU * * 2.2.8.1.1.1.1 Share Control Header * https://msdn.microsoft.com/en-us/library/cc240576.aspx * * totalLength[2] pduType[2] pduSource[2] */ int getshareT(Share* as, uchar* p, uint nb) { int len, nsrc, type; int pduType2, ctype, mtype, uptype, clen, ulen, ulenr; uchar *ep; as->type = 0; if(nb < 6){ werrstr(Eshort); return -1; } len = GSHORT(p); if(len < SCHSIZE || len > nb){ werrstr("bad length in Share Control PDU header"); return -1; } type = GSHORT(p+2)&Bits4; as->source = GSHORT(p+4); switch(type){ case PDUactivate: /* * 2.2.1.13.1 Server Demand Active PDU * http://msdn.microsoft.com/en-us/library/cc240484.aspx */ as->type = ShActivate; if(len<14){ werrstr(Eshort); return -1; } as->shareid = GLONG(p+6); nsrc = GSHORT(p+10); as->ndata = GSHORT(p+12); if(len<14+nsrc+as->ndata){ werrstr(Eshort); return -1; } as->data = p+14+nsrc; as->ncap = GSHORT(p+14+nsrc); break; case PDUdeactivate: as->type = ShDeactivate; break; case PDUdata: /* * 2.2.8.1.1.1.2 Share Data Header (TS_SHAREDATAHEADER) * http://msdn.microsoft.com/en-us/library/cc240577.aspx * * shareId[4] pad1[1] streamId[1] uncomprLen[2] * pduType2[1] comprType[1] comprLen[2] */ ep = p+nb; if(nb < 18){ werrstr("%s (%ud<18)", Eshort, nb); return -1; } ulen = GSHORT(p+12); pduType2 = p[14]; ctype = p[15]; clen = GSHORT(p+16) - SCDSIZE; p += 18; if(ctype&(1<<5)){ if(p+clen > ep){ werrstr(Eshort); return -1; } if((p = uncomp(p, clen, ctype, &ulenr)) == nil){ werrstr("decompression failed: %r"); return -1; } if(ulen != ulenr+SCDSIZE){ werrstr("bad length after decompression"); return -1; } ep = p+ulenr; } switch (pduType2){ case ADsync: as->type = ShSync; break; case ADctl: as->type = ShCtl; break; case ADfontmap: /* denotes completion of the connection sequence */ as->type = ShFmap; break; case ADerrx: /* 2.2.5.1.1 Set Error Info PDU Data (TS_SET_ERROR_INFO_PDU) */ if(p+4 > ep){ werrstr("ADdraw: %s", Eshort); return -1; } as->type = ShEinfo; as->err = GLONG(p); break; case ADdraw: /* 2.2.9.1.1.3.1 Slow-Path Graphics Update (TS_GRAPHICS_UPDATE) */ /* not when Fast-Path is in use */ if(p+2 > ep){ werrstr("ADdraw: %s", Eshort); return -1; } uptype = GSHORT(p); switch(uptype){ case UpdOrders: if(p+8 > ep){ werrstr("ShUorders: %s", Eshort); return -1; } as->type = ShUorders; as->nr = GSHORT(p+4); as->data = p+8; as->ndata = ep-(p+8); break; case UpdBitmap: if(p+4 > ep){ werrstr("ShUimg: %s", Eshort); return -1; } as->type = ShUimg; as->nr = GSHORT(p+2); as->data = p+4; as->ndata = ep-(p+4); break; case UpdCmap: as->type = ShUcmap; as->data = p; as->ndata = ep-p; break; } break; case ADcursor: /* 2.2.9.1.1.4 Server Pointer Update PDU (TS_POINTER_PDU) */ if(p+2 > ep){ werrstr(Eshort); return -1; } mtype = GSHORT(p); if(mtype == PDUcursorwarp){ if(p+8 > ep){ werrstr(Eshort); return -1; } as->type = ShUwarp; as->x = GSHORT(p+4); as->y = GSHORT(p+6); break; } } break; } return len; } /* 2.2.9.1.1.3.1.2.2 Bitmap Data (TS_BITMAP_DATA) */ int getimgupd(Imgupd* iu, uchar* a, uint nb) { uchar *p, *ep, *s; int opt, len; p = a; ep = a+nb; if(nb < 18){ werrstr(Eshort); return -1; } iu->type = Ubitmap; iu->x = GSHORT(p+0); iu->y = GSHORT(p+2); iu->xm = GSHORT(p+4); iu->ym = GSHORT(p+6); iu->xsz = GSHORT(p+8); iu->ysz = GSHORT(p+10); iu->depth = GSHORT(p+12); opt = GSHORT(p+14); len = GSHORT(p+16); p += 18; s = p+len; if(s > ep){ werrstr(Eshort); return -1; } iu->iscompr = (opt&Bcompress); if(opt&Bcompress && !(opt&NoBitcomphdr)) p += 8; iu->bytes = p; iu->nbytes = s-p; return s-a; } /* 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.1.13.2 Client Confirm Active PDU */ int putconfirmactive(uchar* b, uint nb, Msg* m) { uchar *p, *q, *ep; int n, nsrc, capsize, ndata, pdusize; int userchan, shareid, originid; Caps caps; assert(m->type == Mactivated); userchan = m->mcsuid; shareid = m->shareid; originid = m->originid; caps.depth = m->depth; caps.xsz = m->xsz; caps.ysz = m->ysz; nsrc = sizeof(srcDesc); capsize = sizecaps(&caps); ndata = 16+nsrc+capsize; p = txprep(b, nb, ndata, 0, userchan, 0); if(p == nil) sysfatal("buffer not prepared: %r"); ep = p+ndata; pdusize = ep-b; q = p; /* 2.2.8.1.1.1.1 Share Control Header */ /* totalLength[2] pduType[2] PDUSource[2] */ PSHORT(p+0, ndata); PSHORT(p+2, PDUactivated | (1<<4)); PSHORT(p+4, userchan); /* shareId[4] originatorId[2] sdlen[2] caplen[2] srcdesc[sdlen] ncap[2] pad[2] */ PLONG(p+6, shareid); PSHORT(p+10, originid); PSHORT(p+12, nsrc); PSHORT(p+14, capsize); memcpy(p+16, srcDesc, nsrc); p += nsrc+16; if((n = putcaps(p, ep-p, &caps)) < 0) sysfatal("putcaps: %r"); p += n; assert(p-ndata == q); return pdusize; } /* 2.2.1.11 Client Info PDU */ int putclientinfo(uchar* b, uint nb, Msg* m) { uchar *p, *q; int ndata, usize; int opt, perfopt; int ndom, nusr, npw, nsh, nwd; uchar *wdom, *wusr, *wpw, *wsh, *wwd; char *dom, *usr, *pw, *sh, *wd; int dologin; assert(m->type == Dclientinfo); dom = m->dom; usr = m->user; pw = m->pass; sh = m->rshell; wd = m->rwd; dologin = m->dologin; ndom = strlen(dom)+1; nusr = strlen(usr)+1; npw = strlen(pw)+1; nsh = strlen(sh)+1; nwd = strlen(wd)+1; wdom = emalloc(4*ndom); wusr = emalloc(4*nusr); wpw = emalloc(4*npw); wsh = emalloc(4*nsh); wwd = emalloc(4*nwd); ndom = toutf16(wdom, 4*ndom, dom, ndom); nusr = toutf16(wusr, 4*nusr, usr, nusr); npw = toutf16(wpw, 4*npw, pw, npw); nsh = toutf16(wsh, 4*nsh, sh, nsh); nwd = toutf16(wwd, 4*nwd, wd, nwd); ndata = 18+ndom+nusr+npw+nsh+nwd+188; opt = 0 | InfMouse | InfUnicode | InfNoctlaltdel | InfMaxshell | InfWinkey | InfCompress | Compress64k ; if(dologin) opt |= InfAutologon; perfopt = 0 | PerfNoDrag | PerfNoAnim | PerfNoCursorset | PerfNoTheming | PerfFontAA ; p = txprep(b, nb, ndata, 0, m->mcsuid, Sinfopk); if(p == nil) return -1; usize = p+ndata-b; q = p; PLONG(q+0, 0); // codePage; langId when opt&InfUnicode 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); return usize; } /* Share-Data Header (2.2.8.1.1.1.2 Share Data Header) */ uchar* putsdh(uchar* p, uchar* ep, int ndata, int pduType2, int originid, int shareid) { if(p+18>ep) sysfatal(Eshort); PSHORT(p+0, ndata); PSHORT(p+2, (PDUdata | (1<<4))); PSHORT(p+4, originid); PLONG(p+6, shareid); p[10] = 0; // pad1 p[11] = 1; // streamId: 1=Low, 2=Medium, 4=High PSHORT(p+12, ndata); p[14] = pduType2; p[15] = 0; // compressedType PSHORT(p+16, 0); // compressedLength return p+18; }