ref: baf602582a840a0253900d41a2f1cce7156dd770
dir: /mcs.c/
/* 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;
}