#include <u.h>
#include <libc.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 */
/* 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,
};
enum
{
Bits5 = 0x1F,
Bits7 = 0x7F,
};
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;
}
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;
}
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;
}
void
pbshort(uchar* p, int v)
{
p[0]=2;
p[1]=2;
PSHORTB(p+2,v);
}
int
mcstype(uchar* p, uchar* ep)
{
if(tptype(p,ep) != Data){
werrstr("not an X.224 Data TPDU");
return -1;
}
p = tpdat(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
mcschan(uchar *p, uchar* ep)
{
if(mcstype(p,ep) != Msdi){
werrstr("not an MCS Send Data Indication: %r");
return -1;
}
if((p = tpdat(p, ep)) == nil)
return -1;
if(p+5 > ep){
werrstr(Eshort);
return -1;
}
return GSHORTB(p+3);
}
uchar*
mcsdat(uchar *p, uchar* ep)
{
if(mcstype(p,ep) != Msdi){
werrstr("not an MCS Send Data Indication: %r");
return nil;
}
if((p = tpdat(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;
}
/* 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
sizegccr(Msg* m)
{
int size, nv;
nv = m->nvc;
size = 9+14+216+12+12 + 8+12*nv;
return size; // should agree with the below
}
static uchar t124IdentifierKeyOid[7] = {0, 5, 0, 20, 124, 0, 1};
int
putgccr(uchar* buf, uint nb, Msg* m)
{
int i;
uchar *p, *ep;
long gccsize, earlyCapabilityFlags;
int ver, depth, width, height, sproto, wantconsole;
char* sysname;
Vchan *v;
int nv;
p = buf;
ep = buf+nb;
gccsize = sizegccr(m)-9;
if(p+gccsize+9 > ep){
werrstr(Eshort);
return -1;
}
ver = m->ver;
depth = m->depth;
width = m->xsz;
height = m->ysz;
sysname = m->sysname;
sproto = m->sproto;
wantconsole = m->wantconsole;
v = m->vctab;
nv = m->nvc;
earlyCapabilityFlags = CanErrinfo;
if(depth == 32)
earlyCapabilityFlags |= Want32bpp;
memcpy(p, t124IdentifierKeyOid, 7);
// 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
p[16] = 0;
memcpy(p+17, "Duca", 4); // H.221 nonstandard key (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, ver); // rdpVersion: RDP5=0x00080004
PSHORT(p+8, width); // desktopWidth ≤ 4096
PSHORT(p+10, height); // 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
memset(p+24, 32, 0); // clientName[32]
toutf16(p+24, 32, sysname, strlen(sysname)+1);
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(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, 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, (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*nv);
PLONG(p+4, nv);
for(i=0; i<nv; i++){
memcpy(p+8+12*i+0, v[i].name, 8);
PLONGB(p+8+12*i+8, v[i].flags);
}
p += 8+12*nv;
return p-buf;
}
int
getmcr(Msg* m, uchar* b, uint nb)
{
int len, tag, r, utype, ulen;
uchar *p, *ep;
p = b;
ep = b+nb;
m->type = Mconnected;
/* 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 */
m->ver = GLONG(p+4);
break;
/* BUG: exract channel IDs from SrvNet */
}
p += ulen;
}
return p-b;
}
/* MCS Send Data Request */
int
putmsdr(uchar* p, int nb, int ndata, int chanid, int mcsuid)
{
if(nb < 8){
werrstr(Esmall);
return -1;
}
p[0] = (Msdr<<2);
PSHORTB(p+1, mcsuid);
PSHORTB(p+3, chanid);
p[5] = 0x70;
PSHORTB(p+6, ndata|0x8000);
return 8;
}
|