shithub: rd

ref: baf602582a840a0253900d41a2f1cce7156dd770
dir: /mcs.c/

View raw version
/* 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;
}