shithub: rd

ref: 358c45c5326b463845e900fd8f16475c85a8d681
dir: rd/mpas.c

View raw version
/* Based on: T.128 Multipoint application sharing */
#include <u.h>
#include <libc.h>
#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 = igets(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 = igets(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 = igets(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 = igets(p+0);
		as->y = igets(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 = igets(p);
	if(len < SCHSIZE || len > nb){
		werrstr("bad length in Share Control PDU header");
		return -1;
	}
	type = igets(p+2)&Bits4;
	as->source = igets(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 = igetl(p+6);
		nsrc = igets(p+10);
		as->ndata = igets(p+12);
		if(len<14+nsrc+as->ndata){
			werrstr(Eshort);
			return -1;
		}
		as->data = p+14+nsrc;
		as->ncap = igets(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 = igets(p+12);
		pduType2 = p[14];
		ctype = p[15];
		clen = igets(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 = igetl(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 = igets(p);
			switch(uptype){
			case UpdOrders:
				if(p+8 > ep){
					werrstr("ShUorders: %s", Eshort);
					return -1;
				}
				as->type = ShUorders;
				as->nr = igets(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 = igets(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 = igets(p);
			if(mtype == PDUcursorwarp){
				if(p+8 > ep){
					werrstr(Eshort);
					return -1;
				}
				as->type = ShUwarp;
				as->x = igets(p+4);
				as->y = igets(p+6);
				break;
			}
		}
		break;
	}
	return len;
}

/* 2.2.9.1.1.3.1.2.2 Bitmap Data (TS_BITMAP_DATA) */
int
getimgupd(Imgupd* u, uchar* a, uint nb)
{
	uchar *p, *ep, *s;
	int opt, len;

	p = a;
	ep = a+nb;
	if(nb < 18){
		werrstr(Eshort);
		return -1;
	}
	u->type = Ubitmap;
	u->x = igets(p+0);
	u->y = igets(p+2);
	u->xm = igets(p+4);
	u->ym = igets(p+6);
	u->xsz = igets(p+8);
	u->ysz = igets(p+10);
	u->depth = igets(p+12);
	opt = igets(p+14);
	len = igets(p+16);
	p += 18;
	s = p+len;
	if(s > ep){
		werrstr(Eshort);
		return -1;
	}
	u->compressed = (opt&Bcompress);
	if(opt&Bcompress && !(opt&NoBitcomphdr))
		p += 8;
	u->bytes = p;
	u->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 = igets(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] */
	iputs(p+0, ndata);
	iputs(p+2, PDUactivated | (1<<4));
	iputs(p+4, userchan);

	/* shareId[4] originatorId[2] sdlen[2] caplen[2] srcdesc[sdlen] ncap[2] pad[2] */
	iputl(p+6, shareid);
	iputs(p+10, originid);
	iputs(p+12, nsrc);
	iputs(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;

	iputl(q+0, 0);	// codePage; langId when opt&InfUnicode
	iputl(q+4, opt);
	iputs(q+8, ndom-2);
	iputs(q+10, nusr-2);
	iputs(q+12, npw-2);
	iputs(q+14, nsh-2);
	iputs(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;

	iputs(q+0, 2);	// cbClientAddress 
	iputs(q+2, 0);	// clientAddress
	iputs(q+4, 2);	// cbClientDir
	iputs(q+6, 0);	// clientDir
	memset(q+8, 172, 0);	// clientTimeZone
	iputl(q+180, 0);	// clientSessionId
	iputl(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);
	iputs(p+0, ndata);
	iputs(p+2, (PDUdata | (1<<4)));
	iputs(p+4, originid);
	iputl(p+6, shareid);
	p[10] = 0;	// pad1
	p[11] = 1;	// streamId: 1=Low, 2=Medium, 4=High
	iputs(p+12, ndata);
	p[14] = pduType2;
	p[15] = 0; // compressedType 
	iputs(p+16, 0); // compressedLength 
	return p+18;
}