#include #include #include "dat.h" #include "fns.h" static void applyvc(Rdp*, Msg*); static void applyupdates(Rdp*, Msg*); extern int mcsconnect(Rdp*); int attachuser(Rdp*); int joinchannel(Rdp*,int,int); int defragvc(Rdp*,Msg*); void callvcfunc(Rdp*,Msg*); int x224handshake(Rdp* c) { Msg t, r; t.type = Xconnect; t.negproto = ProtoTLS; if(writemsg(c, &t) <= 0) return -1; if(readmsg(c, &r) <= 0) return -1; if(r.type != Xconnected){ werrstr("X.224: protocol botch"); return -1; } if(r.negproto&ProtoTLS == 0){ werrstr("server refused STARTTLS"); return -1; } c->sproto = r.negproto; if(starttls(c) < 0) return -1; return 0; } int x224hangup(Rdp* c) { Msg t; t.type = Xhangup; return writemsg(c, &t); } int mcsconnect(Rdp* c) { Msg t, r; t.type = Mconnect; t.ver = 0x80004; /* RDP5 */ t.depth = c->depth; t.xsz = c->xsz; t.ysz = c->ysz; t.sysname = c->local; t.sproto = c->sproto; t.wantconsole = c->wantconsole; t.vctab = c->vc; t.nvc = c->nvc; if(writemsg(c, &t) <= 0) sysfatal("Connect Initial: writemsg: %r"); if(readmsg(c, &r) <= 0) sysfatal("Connect Response: readmsg: %r"); if(r.type != Mconnected) sysfatal("Connect Response: protocol botch"); if(r.ver < t.ver) sysfatal("Connect Response: unsupported RDP protocol version %x", r.ver); return 0; } void erectdom(Rdp* c) { Msg t; t.type = Merectdom; if(writemsg(c, &t) <= 0) sysfatal("Erect Domain: writemsg: %r"); } int attachuser(Rdp* c) { Msg t, r; t.type = Mattach; if(writemsg(c, &t) <= 0) sysfatal("attachuser: writemsg: %r"); if(readmsg(c, &r) <= 0) sysfatal("attachuser: readmsg: %r"); if(r.type != Mattached) sysfatal("attachuser: protocol botch"); c->mcsuid = r.mcsuid; c->userchan = r.mcsuid; return 0; } int joinchannel(Rdp* c, int mcsuid, int chanid) { Msg t, r; t.type = Mjoin; t.mcsuid = mcsuid; t.chanid = chanid; if(writemsg(c, &t) <= 0) sysfatal("Channel Join: writemsg: %r"); if(readmsg(c, &r) <= 0) sysfatal("Channel Join: readmsg: %r"); if(r.type != Mjoined) sysfatal("Channel Join: protocol botch"); /* BUG: ensure the returned and requested chanids match */ return 0; } int rdphandshake(Rdp* c) { int i, nv; Vchan* v; Msg r; Share u; v = c->vc; nv = c->nvc; if(mcsconnect(c) < 0) return -1; erectdom(c); if(attachuser(c) < 0) return -1; if(joinchannel(c, c->mcsuid, c->userchan) < 0) return -1; if(joinchannel(c, c->mcsuid, GLOBALCHAN) < 0) return -1; for(i = 0; i < nv; i++) if(joinchannel(c, c->mcsuid, v[i].mcsid) < 0) return -1; sendclientinfo(c); for(;;){ if(readmsg(c, &r) <= 0) return -1; switch(r.type){ case Mclosing: werrstr("Disconnect Provider Ultimatum"); return -1; case Ldone: break; case Lneedlicense: case Lhavechal: respondlicense(c, &r); break; case Aupdate: if(r.getshare(&u, r.data, r.ndata) < 0) return -1; switch(u.type){ default: fprint(2, "handshake: unhandled %d\n", u.type); break; case ShEinfo: /* do we really expect this here? */ c->hupreason = u.err; break; case ShActivate: activate(c, &u); return 0; } } } } /* 2.2.1.13.1 Server Demand Active PDU */ void activate(Rdp* c, Share* as) { Caps rcaps; if(getcaps(&rcaps, as->data, as->ndata) < 0) sysfatal("getcaps: %r"); if(!rcaps.canrefresh) sysfatal("server can not Refresh Rect PDU"); if(!rcaps.cansupress) sysfatal("server can not Suppress Output PDU"); if(!rcaps.bitmap) sysfatal("server concealed their Bitmap Capabilities"); c->depth = rcaps.depth; c->xsz = rcaps.xsz; c->ysz = rcaps.ysz; c->srvchan = as->source; c->shareid = as->shareid; c->active = 1; confirmactive(c); finalhandshake(c); act(c, 0, InputSync, 0, 0, 0); } void deactivate(Rdp* c, Share* as) { USED(as); c->active = 0; } void finalhandshake(Rdp* c) { Msg r; Share u; assync(c); asctl(c, CAcooperate); asctl(c, CAreqctl); asfontls(c); for(;;){ if(readmsg(c, &r) <= 0) sysfatal("activate: readmsg: %r"); switch(r.type){ default: fprint(2, "activate: unhandled PDU type %d\n", u.type); break; case Mclosing: fprint(2, "disconnecting early"); return; case Aupdate: if(r.getshare(&u, r.data, r.ndata) < 0) sysfatal("activate: r.getshare: %r"); switch(u.type){ default: fprint(2, "activate: unhandled ASPDU type %d\n", u.type); break; case ShSync: case ShCtl: /* responses to the assync(). asctl() calls above */ break; case ShFmap: /* finalized - we're good */ return; } } } } void sendclientinfo(Rdp* c) { Msg t; t.type = Dclientinfo; t.mcsuid = c->mcsuid; t.dom = c->windom; t.user = c->user; t.pass = c->passwd; t.rshell = c->shell; t.rwd = c->rwd; t.dologin = (strlen(c->user) > 0); if(writemsg(c, &t) <= 0) sysfatal("sendclientinfo: %r"); } void confirmactive(Rdp* c) { Msg t; t.type = Mactivated; t.originid = c->srvchan; t.mcsuid = c->userchan; t.shareid = c->shareid; t.xsz = c->xsz; t.ysz = c->ysz; t.depth = c->depth; if(writemsg(c, &t) <= 0) sysfatal("confirmactive: %r"); } void respondlicense(Rdp *c, Msg *r) { Msg t; switch(r->type){ default: return; case Lneedlicense: t.type = Lreq; t.sysname = c->local; t.user = c->user; t.originid = c->userchan; break; case Lhavechal: fprint(2, "unhandled Lhavechal\n"); t.type = Lnolicense; t.originid = c->userchan; break; } if(writemsg(c, &t) < 0) sysfatal("respondlicense: writemsg failed: %r"); } void assync(Rdp *c) { Msg t; t.type = Async; t.mcsuid = c->srvchan; t.originid = c->userchan; t.shareid = c->shareid; if(writemsg(c, &t) <= 0) sysfatal("assync: %r"); } void asctl(Rdp* c, int action) { Msg t; t.type = Actl; t.originid = c->userchan; t.shareid = c->shareid; t.action = action; if(writemsg(c, &t) <= 0) sysfatal("asctl: %r"); } void asfontls(Rdp* c) { Msg t; t.type = Afontls; t.originid = c->userchan; t.shareid = c->shareid; if(writemsg(c, &t) <= 0) sysfatal("asfontls: %r"); } void act(Rdp* c, ulong msec, int typ, int f, int a, int b) { Msg t; t.type = Ainput; t.originid = c->userchan; t.shareid = c->shareid; t.msec = msec; t.mtype = typ; t.flags = f; t.iarg[0] = a; t.iarg[1] = b; if(writemsg(c, &t) <= 0) sysfatal("act: %r"); } void turnupdates(Rdp* c, int allow) { Msg t; t.type = Dsupress; t.originid = c->userchan; t.shareid = c->shareid; t.xsz = c->xsz; t.ysz = c->ysz; t.allow = allow; writemsg(c, &t); } void apply(Rdp* c, Msg* m) { switch(m->type){ default: fprint(2, "type %d is not expected\n", m->type); break; case 0: fprint(2, "unsupported PDU\n"); break; case Mvchan: applyvc(c, m); break; case Aupdate: applyupdates(c, m); break; } } static void applyvc(Rdp* c, Msg* m) { if(defragvc(c, m) > 0) callvcfunc(c, m); } static void applyupdates(Rdp* c, Msg* m) { int n; uchar *p, *ep; Share u; p = m->data; ep = m->data + m->ndata; for(; p < ep; p += n){ n = m->getshare(&u, p, ep-p); if(n < 0) sysfatal("applyupdates: %r"); switch(u.type){ default: if(u.type != 0) fprint(2, "applyupdates: unhandled %d\n", u.type); break; case ShDeactivate: deactivate(c, &u); break; case ShActivate: // server may initiate capability re-exchange activate(c, &u); break; case ShEinfo: c->hupreason = u.err; break; case ShUorders: case ShUimg: drawimgupdate(c, &u); break; case ShUcmap: loadcmap(c, &u); break; case ShUwarp: warpmouse(u.x, u.y); break; case Aflow: break; } } }