1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317 |
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "../port/netif.h"
- #include "../port/error.h"
- #include <libsec.h>
- #include "../port/thwack.h"
- /*
- * sdp - secure datagram protocol
- */
- typedef struct Sdp Sdp;
- typedef struct Conv Conv;
- typedef struct OneWay OneWay;
- typedef struct Stats Stats;
- typedef struct AckPkt AckPkt;
- typedef struct Algorithm Algorithm;
- typedef struct CipherRc4 CipherRc4;
- enum
- {
- Qtopdir= 1, /* top level directory */
- Qsdpdir, /* sdp directory */
- Qclone,
- Qlog,
- Qconvdir, /* directory per conversation */
- Qctl,
- Qdata, /* unreliable packet channel */
- Qcontrol, /* reliable control channel */
- Qstatus,
- Qstats,
- Qrstats,
- MaxQ,
- Maxconv= 256, // power of 2
- Nfs= 4, // number of file systems
- MaxRetries= 12,
- KeepAlive = 300, // keep alive in seconds - should probably be about 60 but is higher to avoid linksys bug
- SecretLength= 32, // a secret per direction
- SeqMax = (1<<24),
- SeqWindow = 32,
- NCompStats = 8,
- };
- #define TYPE(x) (((ulong)(x).path) & 0xff)
- #define CONV(x) ((((ulong)(x).path) >> 8)&(Maxconv-1))
- #define QID(x, y) (((x)<<8) | (y))
- struct Stats
- {
- ulong outPackets;
- ulong outDataPackets;
- ulong outDataBytes;
- ulong outCompDataBytes;
- ulong outCompBytes;
- ulong outCompStats[NCompStats];
- ulong inPackets;
- ulong inDataPackets;
- ulong inDataBytes;
- ulong inCompDataBytes;
- ulong inMissing;
- ulong inDup;
- ulong inReorder;
- ulong inBadComp;
- ulong inBadAuth;
- ulong inBadSeq;
- ulong inBadOther;
- };
- struct OneWay
- {
- Rendez statsready;
- ulong seqwrap; // number of wraps of the sequence number
- ulong seq;
- ulong window;
- uchar secret[SecretLength];
- QLock controllk;
- Rendez controlready;
- Block *controlpkt; // control channel
- ulong controlseq;
- void *cipherstate; // state cipher
- int cipherivlen; // initial vector length
- int cipherblklen; // block length
- int (*cipher)(OneWay*, uchar *buf, int len);
- void *authstate; // auth state
- int authlen; // auth data length in bytes
- int (*auth)(OneWay*, uchar *buf, int len);
- void *compstate;
- int (*comp)(Conv*, int subtype, ulong seq, Block **);
- };
- // conv states
- enum {
- CFree,
- CInit,
- CDial,
- CAccept,
- COpen,
- CLocalClose,
- CRemoteClose,
- CClosed,
- };
- struct Conv {
- QLock;
- Sdp *sdp;
- int id;
- int ref; // holds conv up
- int state;
- int dataopen; // ref count of opens on Qdata
- int controlopen; // ref count of opens on Qcontrol
- int reader; // reader proc has been started
- Stats lstats;
- Stats rstats;
-
- ulong lastrecv; // time last packet was received
- ulong timeout;
- int retries;
- // the following pair uniquely define conversation on this port
- ulong dialid;
- ulong acceptid;
- QLock readlk; // protects readproc
- Proc *readproc;
- Chan *chan; // packet channel
- char *channame;
- char owner[KNAMELEN]; /* protections */
- int perm;
- Algorithm *auth;
- Algorithm *cipher;
- Algorithm *comp;
- int drop;
- OneWay in;
- OneWay out;
- };
- struct Sdp {
- QLock;
- Log;
- int nconv;
- Conv *conv[Maxconv];
- int ackproc;
- };
- enum {
- TConnect,
- TControl,
- TData,
- TCompData,
- };
- enum {
- ControlMesg,
- ControlAck,
- };
- enum {
- ThwackU,
- ThwackC,
- };
- enum {
- ConOpenRequest,
- ConOpenAck,
- ConOpenAckAck,
- ConClose,
- ConCloseAck,
- ConReset,
- };
- struct AckPkt
- {
- uchar cseq[4];
- uchar outPackets[4];
- uchar outDataPackets[4];
- uchar outDataBytes[4];
- uchar outCompDataBytes[4];
- uchar outCompStats[4*NCompStats];
- uchar inPackets[4];
- uchar inDataPackets[4];
- uchar inDataBytes[4];
- uchar inCompDataBytes[4];
- uchar inMissing[4];
- uchar inDup[4];
- uchar inReorder[4];
- uchar inBadComp[4];
- uchar inBadAuth[4];
- uchar inBadSeq[4];
- uchar inBadOther[4];
- };
- struct Algorithm
- {
- char *name;
- int keylen; // in bytes
- void (*init)(Conv*);
- };
- enum {
- RC4forward = 10*1024*1024, // maximum skip forward
- RC4back = 100*1024, // maximum look back
- };
- struct CipherRc4
- {
- ulong cseq; // current byte sequence number
- RC4state current;
- int ovalid; // old is valid
- ulong lgseq; // last good sequence
- ulong oseq; // old byte sequence number
- RC4state old;
- };
- static Dirtab sdpdirtab[]={
- "log", {Qlog}, 0, 0666,
- "clone", {Qclone}, 0, 0666,
- };
- static Dirtab convdirtab[]={
- "ctl", {Qctl}, 0, 0666,
- "data", {Qdata}, 0, 0666,
- "control", {Qcontrol}, 0, 0666,
- "status", {Qstatus}, 0, 0444,
- "stats", {Qstats}, 0, 0444,
- "rstats", {Qrstats}, 0, 0444,
- };
- static int m2p[] = {
- [OREAD] 4,
- [OWRITE] 2,
- [ORDWR] 6
- };
- enum {
- Logcompress= (1<<0),
- Logauth= (1<<1),
- Loghmac= (1<<2),
- };
- static Logflag logflags[] =
- {
- { "compress", Logcompress, },
- { "auth", Logauth, },
- { "hmac", Loghmac, },
- { nil, 0, },
- };
- static Dirtab *dirtab[MaxQ];
- static Sdp sdptab[Nfs];
- static char *convstatename[] = {
- [CFree] "Free",
- [CInit] "Init",
- [CDial] "Dial",
- [CAccept] "Accept",
- [COpen] "Open",
- [CLocalClose] "LocalClose",
- [CRemoteClose] "RemoteClose",
- [CClosed] "Closed",
- };
- static int sdpgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp);
- static Conv *sdpclone(Sdp *sdp);
- static void sdpackproc(void *a);
- static void onewaycleanup(OneWay *ow);
- static int readready(void *a);
- static int controlread();
- static void convsetstate(Conv *c, int state);
- static Block *readcontrol(Conv *c, int n);
- static void writecontrol(Conv *c, void *p, int n, int wait);
- static Block *readdata(Conv *c, int n);
- static long writedata(Conv *c, Block *b);
- static void convderef(Conv *c);
- static Block *conviput(Conv *c, Block *b, int control);
- static void conviconnect(Conv *c, int op, Block *b);
- static void convicontrol(Conv *c, int op, Block *b);
- static Block *convicomp(Conv *c, int op, ulong, Block *b);
- static void convoput(Conv *c, int type, int subtype, Block *b);
- static void convoconnect(Conv *c, int op, ulong dialid, ulong acceptid);
- static void convopenchan(Conv *c, char *path);
- static void convstats(Conv *c, int local, char *buf, int n);
- static void convreader(void *a);
- static void setalg(Conv *c, char *name, Algorithm *tab, Algorithm **);
- static void setsecret(OneWay *cc, char *secret);
- static void nullcipherinit(Conv*c);
- static void descipherinit(Conv*c);
- static void rc4cipherinit(Conv*c);
- static void nullauthinit(Conv*c);
- static void shaauthinit(Conv*c);
- static void md5authinit(Conv*c);
- static void nullcompinit(Conv*c);
- static void thwackcompinit(Conv*c);
- static Algorithm cipheralg[] =
- {
- "null", 0, nullcipherinit,
- "des_56_cbc", 7, descipherinit,
- "rc4_128", 16, rc4cipherinit,
- "rc4_256", 32, rc4cipherinit,
- nil, 0, nil,
- };
- static Algorithm authalg[] =
- {
- "null", 0, nullauthinit,
- "hmac_sha1_96", 16, shaauthinit,
- "hmac_md5_96", 16, md5authinit,
- nil, 0, nil,
- };
- static Algorithm compalg[] =
- {
- "null", 0, nullcompinit,
- "thwack", 0, thwackcompinit,
- nil, 0, nil,
- };
- static void
- sdpinit(void)
- {
- int i;
- Dirtab *dt;
-
- // setup dirtab with non directory entries
- for(i=0; i<nelem(sdpdirtab); i++) {
- dt = sdpdirtab + i;
- dirtab[TYPE(dt->qid)] = dt;
- }
- for(i=0; i<nelem(convdirtab); i++) {
- dt = convdirtab + i;
- dirtab[TYPE(dt->qid)] = dt;
- }
- }
- static Chan*
- sdpattach(char* spec)
- {
- Chan *c;
- int dev;
- char buf[100];
- Sdp *sdp;
- int start;
- dev = atoi(spec);
- if(dev<0 || dev >= Nfs)
- error("bad specification");
- c = devattach('E', spec);
- c->qid = (Qid){QID(0, Qtopdir), 0, QTDIR};
- c->dev = dev;
- sdp = sdptab + dev;
- qlock(sdp);
- start = sdp->ackproc == 0;
- sdp->ackproc = 1;
- qunlock(sdp);
- if(start) {
- snprint(buf, sizeof(buf), "sdpackproc%d", dev);
- kproc(buf, sdpackproc, sdp);
- }
-
- return c;
- }
- static Walkqid*
- sdpwalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, 0, 0, sdpgen);
- }
- static int
- sdpstat(Chan* c, uchar* db, int n)
- {
- return devstat(c, db, n, nil, 0, sdpgen);
- }
- static Chan*
- sdpopen(Chan* ch, int omode)
- {
- int perm;
- Sdp *sdp;
- Conv *c;
- omode &= 3;
- perm = m2p[omode];
- USED(perm);
- sdp = sdptab + ch->dev;
- switch(TYPE(ch->qid)) {
- default:
- break;
- case Qtopdir:
- case Qsdpdir:
- case Qconvdir:
- if(omode != OREAD)
- error(Eperm);
- break;
- case Qlog:
- logopen(sdp);
- break;
- case Qclone:
- c = sdpclone(sdp);
- if(c == nil)
- error(Enodev);
- ch->qid.path = QID(c->id, Qctl);
- break;
- case Qdata:
- case Qctl:
- case Qstatus:
- case Qcontrol:
- case Qstats:
- case Qrstats:
- c = sdp->conv[CONV(ch->qid)];
- qlock(c);
- if(waserror()) {
- qunlock(c);
- nexterror();
- }
- if((perm & (c->perm>>6)) != perm)
- if(strcmp(up->user, c->owner) != 0 || (perm & c->perm) != perm)
- error(Eperm);
- c->ref++;
- if(TYPE(ch->qid) == Qdata) {
- c->dataopen++;
- // kill reader if Qdata is opened for the first time
- if(c->dataopen == 1)
- if(c->readproc != nil)
- postnote(c->readproc, 1, "interrupt", 0);
- } else if(TYPE(ch->qid) == Qcontrol) {
- c->controlopen++;
- }
- qunlock(c);
- poperror();
- break;
- }
- ch->mode = openmode(omode);
- ch->flag |= COPEN;
- ch->offset = 0;
- return ch;
- }
- static void
- sdpclose(Chan* ch)
- {
- Sdp *sdp = sdptab + ch->dev;
- Conv *c;
- if(!(ch->flag & COPEN))
- return;
- switch(TYPE(ch->qid)) {
- case Qlog:
- logclose(sdp);
- break;
- case Qctl:
- case Qstatus:
- case Qstats:
- case Qrstats:
- c = sdp->conv[CONV(ch->qid)];
- qlock(c);
- convderef(c);
- qunlock(c);
- break;
- case Qdata:
- c = sdp->conv[CONV(ch->qid)];
- qlock(c);
- c->dataopen--;
- convderef(c);
- if(c->dataopen == 0)
- if(c->reader == 0)
- if(c->chan != nil)
- if(!waserror()) {
- kproc("convreader", convreader, c);
- c->reader = 1;
- c->ref++;
- poperror();
- }
- qunlock(c);
- break;
- case Qcontrol:
- c = sdp->conv[CONV(ch->qid)];
- qlock(c);
- c->controlopen--;
- convderef(c);
- if(c->controlopen == 0 && c->ref != 0) {
- switch(c->state) {
- default:
- convsetstate(c, CClosed);
- break;
- case CAccept:
- case COpen:
- convsetstate(c, CLocalClose);
- break;
- }
- }
- qunlock(c);
- break;
- }
- }
- static long
- sdpread(Chan *ch, void *a, long n, vlong off)
- {
- char buf[256];
- char *s;
- Sdp *sdp = sdptab + ch->dev;
- Conv *c;
- Block *b;
- int rv;
- USED(off);
- switch(TYPE(ch->qid)) {
- default:
- error(Eperm);
- case Qtopdir:
- case Qsdpdir:
- case Qconvdir:
- return devdirread(ch, a, n, 0, 0, sdpgen);
- case Qlog:
- return logread(sdp, a, off, n);
- case Qstatus:
- c = sdp->conv[CONV(ch->qid)];
- qlock(c);
- n = readstr(off, a, n, convstatename[c->state]);
- qunlock(c);
- return n;
- case Qctl:
- sprint(buf, "%lud", CONV(ch->qid));
- return readstr(off, a, n, buf);
- case Qcontrol:
- b = readcontrol(sdp->conv[CONV(ch->qid)], n);
- if(b == nil)
- return 0;
- if(BLEN(b) < n)
- n = BLEN(b);
- memmove(a, b->rp, n);
- freeb(b);
- return n;
- case Qdata:
- b = readdata(sdp->conv[CONV(ch->qid)], n);
- if(b == nil)
- return 0;
- if(BLEN(b) < n)
- n = BLEN(b);
- memmove(a, b->rp, n);
- freeb(b);
- return n;
- case Qstats:
- case Qrstats:
- c = sdp->conv[CONV(ch->qid)];
- s = smalloc(1000);
- convstats(c, TYPE(ch->qid) == Qstats, s, 1000);
- rv = readstr(off, a, n, s);
- free(s);
- return rv;
- }
- }
- static Block*
- sdpbread(Chan* ch, long n, ulong offset)
- {
- Sdp *sdp = sdptab + ch->dev;
- if(TYPE(ch->qid) != Qdata)
- return devbread(ch, n, offset);
- return readdata(sdp->conv[CONV(ch->qid)], n);
- }
- static long
- sdpwrite(Chan *ch, void *a, long n, vlong off)
- {
- Sdp *sdp = sdptab + ch->dev;
- Cmdbuf *cb;
- char *arg0;
- char *p;
- Conv *c;
- Block *b;
-
- USED(off);
- switch(TYPE(ch->qid)) {
- default:
- error(Eperm);
- case Qctl:
- c = sdp->conv[CONV(ch->qid)];
- cb = parsecmd(a, n);
- qlock(c);
- if(waserror()) {
- qunlock(c);
- free(cb);
- nexterror();
- }
- if(cb->nf == 0)
- error("short write");
- arg0 = cb->f[0];
- if(strcmp(arg0, "accept") == 0) {
- if(cb->nf != 2)
- error("usage: accept file");
- convopenchan(c, cb->f[1]);
- } else if(strcmp(arg0, "dial") == 0) {
- if(cb->nf != 2)
- error("usage: dial file");
- convopenchan(c, cb->f[1]);
- convsetstate(c, CDial);
- } else if(strcmp(arg0, "drop") == 0) {
- if(cb->nf != 2)
- error("usage: drop permil");
- c->drop = atoi(cb->f[1]);
- } else if(strcmp(arg0, "cipher") == 0) {
- if(cb->nf != 2)
- error("usage: cipher alg");
- setalg(c, cb->f[1], cipheralg, &c->cipher);
- } else if(strcmp(arg0, "auth") == 0) {
- if(cb->nf != 2)
- error("usage: auth alg");
- setalg(c, cb->f[1], authalg, &c->auth);
- } else if(strcmp(arg0, "comp") == 0) {
- if(cb->nf != 2)
- error("usage: comp alg");
- setalg(c, cb->f[1], compalg, &c->comp);
- } else if(strcmp(arg0, "insecret") == 0) {
- if(cb->nf != 2)
- error("usage: insecret secret");
- setsecret(&c->in, cb->f[1]);
- if(c->cipher)
- c->cipher->init(c);
- if(c->auth)
- c->auth->init(c);
- } else if(strcmp(arg0, "outsecret") == 0) {
- if(cb->nf != 2)
- error("usage: outsecret secret");
- setsecret(&c->out, cb->f[1]);
- if(c->cipher)
- c->cipher->init(c);
- if(c->auth)
- c->auth->init(c);
- } else
- error("unknown control request");
- poperror();
- qunlock(c);
- free(cb);
- return n;
- case Qlog:
- cb = parsecmd(a, n);
- p = logctl(sdp, cb->nf, cb->f, logflags);
- free(cb);
- if(p != nil)
- error(p);
- return n;
- case Qcontrol:
- writecontrol(sdp->conv[CONV(ch->qid)], a, n, 0);
- return n;
- case Qdata:
- b = allocb(n);
- memmove(b->wp, a, n);
- b->wp += n;
- return writedata(sdp->conv[CONV(ch->qid)], b);
- }
- }
- long
- sdpbwrite(Chan *ch, Block *bp, ulong offset)
- {
- Sdp *sdp = sdptab + ch->dev;
- if(TYPE(ch->qid) != Qdata)
- return devbwrite(ch, bp, offset);
- return writedata(sdp->conv[CONV(ch->qid)], bp);
- }
- static int
- sdpgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
- {
- Sdp *sdp = sdptab + c->dev;
- int type = TYPE(c->qid);
- Dirtab *dt;
- Qid qid;
- if(s == DEVDOTDOT){
- switch(TYPE(c->qid)){
- case Qtopdir:
- case Qsdpdir:
- snprint(up->genbuf, sizeof(up->genbuf), "#E%ld", c->dev);
- mkqid(&qid, Qtopdir, 0, QTDIR);
- devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
- break;
- case Qconvdir:
- snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
- mkqid(&qid, Qsdpdir, 0, QTDIR);
- devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
- break;
- default:
- panic("sdpwalk %llux", c->qid.path);
- }
- return 1;
- }
- switch(type) {
- default:
- // non directory entries end up here
- if(c->qid.type & QTDIR)
- panic("sdpgen: unexpected directory");
- if(s != 0)
- return -1;
- dt = dirtab[TYPE(c->qid)];
- if(dt == nil)
- panic("sdpgen: unknown type: %lud", TYPE(c->qid));
- devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp);
- return 1;
- case Qtopdir:
- if(s != 0)
- return -1;
- mkqid(&qid, QID(0, Qsdpdir), 0, QTDIR);
- devdir(c, qid, "sdp", 0, eve, 0555, dp);
- return 1;
- case Qsdpdir:
- if(s<nelem(sdpdirtab)) {
- dt = sdpdirtab+s;
- devdir(c, dt->qid, dt->name, dt->length, eve, dt->perm, dp);
- return 1;
- }
- s -= nelem(sdpdirtab);
- if(s >= sdp->nconv)
- return -1;
- mkqid(&qid, QID(s, Qconvdir), 0, QTDIR);
- snprint(up->genbuf, sizeof(up->genbuf), "%d", s);
- devdir(c, qid, up->genbuf, 0, eve, 0555, dp);
- return 1;
- case Qconvdir:
- if(s>=nelem(convdirtab))
- return -1;
- dt = convdirtab+s;
- mkqid(&qid, QID(CONV(c->qid),TYPE(dt->qid)), 0, QTFILE);
- devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp);
- return 1;
- }
- }
- static Conv*
- sdpclone(Sdp *sdp)
- {
- Conv *c, **pp, **ep;
- c = nil;
- ep = sdp->conv + nelem(sdp->conv);
- qlock(sdp);
- if(waserror()) {
- qunlock(sdp);
- nexterror();
- }
- for(pp = sdp->conv; pp < ep; pp++) {
- c = *pp;
- if(c == nil){
- c = malloc(sizeof(Conv));
- if(c == nil)
- error(Enomem);
- memset(c, 0, sizeof(Conv));
- qlock(c);
- c->sdp = sdp;
- c->id = pp - sdp->conv;
- *pp = c;
- sdp->nconv++;
- break;
- }
- if(c->ref == 0 && canqlock(c)){
- if(c->ref == 0)
- break;
- qunlock(c);
- }
- }
- poperror();
- qunlock(sdp);
- if(pp >= ep)
- return nil;
- assert(c->state == CFree);
- // set ref to 2 - 1 ref for open - 1 ref for channel state
- c->ref = 2;
- c->state = CInit;
- c->in.window = ~0;
- strncpy(c->owner, up->user, sizeof(c->owner));
- c->perm = 0660;
- qunlock(c);
- return c;
- }
- // assume c is locked
- static void
- convretryinit(Conv *c)
- {
- c->retries = 0;
- // +2 to avoid rounding effects.
- c->timeout = TK2SEC(m->ticks) + 2;
- }
- // assume c is locked
- static int
- convretry(Conv *c, int reset)
- {
- c->retries++;
- if(c->retries > MaxRetries) {
- if(reset)
- convoconnect(c, ConReset, c->dialid, c->acceptid);
- convsetstate(c, CClosed);
- return 0;
- }
- c->timeout = TK2SEC(m->ticks) + (c->retries+1);
- return 1;
- }
- // assumes c is locked
- static void
- convtimer(Conv *c, ulong sec)
- {
- Block *b;
- if(c->timeout > sec)
- return;
- switch(c->state) {
- case CInit:
- break;
- case CDial:
- if(convretry(c, 1))
- convoconnect(c, ConOpenRequest, c->dialid, 0);
- break;
- case CAccept:
- if(convretry(c, 1))
- convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
- break;
- case COpen:
- b = c->out.controlpkt;
- if(b != nil) {
- if(convretry(c, 1))
- convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
- break;
- }
- c->timeout = c->lastrecv + KeepAlive;
- if(c->timeout > sec)
- break;
- // keepalive - randomly spaced between KeepAlive and 2*KeepAlive
- if(c->timeout + KeepAlive > sec && nrand(c->lastrecv + 2*KeepAlive - sec) > 0)
- break;
- // can not use writecontrol
- b = allocb(4);
- c->out.controlseq++;
- hnputl(b->wp, c->out.controlseq);
- b->wp += 4;
- c->out.controlpkt = b;
- convretryinit(c);
- if(!waserror()) {
- convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
- poperror();
- }
- break;
- case CLocalClose:
- if(convretry(c, 0))
- convoconnect(c, ConClose, c->dialid, c->acceptid);
- break;
- case CRemoteClose:
- case CClosed:
- break;
- }
- }
- static void
- sdpackproc(void *a)
- {
- Sdp *sdp = a;
- ulong sec;
- int i;
- Conv *c;
- for(;;) {
- tsleep(&up->sleep, return0, 0, 1000);
- sec = TK2SEC(m->ticks);
- qlock(sdp);
- for(i=0; i<sdp->nconv; i++) {
- c = sdp->conv[i];
- if(c->ref == 0)
- continue;
- qunlock(sdp);
- qlock(c);
- if(c->ref > 0 && !waserror()) {
- convtimer(c, sec);
- poperror();
- }
- qunlock(c);
- qlock(sdp);
- }
- qunlock(sdp);
- }
- }
- Dev sdpdevtab = {
- 'E',
- "sdp",
- devreset,
- sdpinit,
- devshutdown,
- sdpattach,
- sdpwalk,
- sdpstat,
- sdpopen,
- devcreate,
- sdpclose,
- sdpread,
- devbread,
- sdpwrite,
- devbwrite,
- devremove,
- devwstat,
- };
- // assume hold lock on c
- static void
- convsetstate(Conv *c, int state)
- {
- if(0)print("convsetstate %d: %s -> %s\n", c->id, convstatename[c->state], convstatename[state]);
- switch(state) {
- default:
- panic("setstate: bad state: %d", state);
- case CDial:
- assert(c->state == CInit);
- c->dialid = (rand()<<16) + rand();
- convretryinit(c);
- convoconnect(c, ConOpenRequest, c->dialid, 0);
- break;
- case CAccept:
- assert(c->state == CInit);
- c->acceptid = (rand()<<16) + rand();
- convretryinit(c);
- convoconnect(c, ConOpenAck, c->dialid, c->acceptid);
- break;
- case COpen:
- assert(c->state == CDial || c->state == CAccept);
- c->lastrecv = TK2SEC(m->ticks);
- if(c->state == CDial) {
- convretryinit(c);
- convoconnect(c, ConOpenAckAck, c->dialid, c->acceptid);
- hnputl(c->in.secret, c->acceptid);
- hnputl(c->in.secret+4, c->dialid);
- hnputl(c->out.secret, c->dialid);
- hnputl(c->out.secret+4, c->acceptid);
- } else {
- hnputl(c->in.secret, c->dialid);
- hnputl(c->in.secret+4, c->acceptid);
- hnputl(c->out.secret, c->acceptid);
- hnputl(c->out.secret+4, c->dialid);
- }
- setalg(c, "hmac_md5_96", authalg, &c->auth);
- break;
- case CLocalClose:
- assert(c->state == CAccept || c->state == COpen);
- convretryinit(c);
- convoconnect(c, ConClose, c->dialid, c->acceptid);
- break;
- case CRemoteClose:
- wakeup(&c->in.controlready);
- wakeup(&c->out.controlready);
- break;
- case CClosed:
- wakeup(&c->in.controlready);
- wakeup(&c->out.controlready);
- if(c->readproc)
- postnote(c->readproc, 1, "interrupt", 0);
- if(c->state != CClosed)
- convderef(c);
- break;
- }
- c->state = state;
- }
- //assumes c is locked
- static void
- convderef(Conv *c)
- {
- c->ref--;
- if(c->ref > 0) {
- return;
- }
- assert(c->ref == 0);
- assert(c->dataopen == 0);
- assert(c->controlopen == 0);
- if(0)print("convderef: %d: ref == 0!\n", c->id);
- c->state = CFree;
- if(c->chan) {
- cclose(c->chan);
- c->chan = nil;
- }
- if(c->channame) {
- free(c->channame);
- c->channame = nil;
- }
- c->cipher = nil;
- c->auth = nil;
- c->comp = nil;
- strcpy(c->owner, "network");
- c->perm = 0660;
- c->dialid = 0;
- c->acceptid = 0;
- c->timeout = 0;
- c->retries = 0;
- c->drop = 0;
- onewaycleanup(&c->in);
- onewaycleanup(&c->out);
- memset(&c->lstats, 0, sizeof(Stats));
- memset(&c->rstats, 0, sizeof(Stats));
- }
- static void
- onewaycleanup(OneWay *ow)
- {
- if(ow->controlpkt)
- freeb(ow->controlpkt);
- if(ow->authstate)
- free(ow->authstate);
- if(ow->cipherstate)
- free(ow->cipherstate);
- if(ow->compstate)
- free(ow->compstate);
- memset(ow, 0, sizeof(OneWay));
- }
- // assumes conv is locked
- static void
- convopenchan(Conv *c, char *path)
- {
- if(c->state != CInit || c->chan != nil)
- error("already connected");
- c->chan = namec(path, Aopen, ORDWR, 0);
- c->channame = smalloc(strlen(path)+1);
- strcpy(c->channame, path);
- if(waserror()) {
- cclose(c->chan);
- c->chan = nil;
- free(c->channame);
- c->channame = nil;
- nexterror();
- }
- kproc("convreader", convreader, c);
- assert(c->reader == 0 && c->ref > 0);
- // after kproc in case it fails
- c->reader = 1;
- c->ref++;
- poperror();
- }
- static void
- convstats(Conv *c, int local, char *buf, int n)
- {
- Stats *stats;
- char *p, *ep;
- int i;
- if(local) {
- stats = &c->lstats;
- } else {
- if(!waserror()) {
- writecontrol(c, 0, 0, 1);
- poperror();
- }
- stats = &c->rstats;
- }
- qlock(c);
- p = buf;
- ep = buf + n;
- p += snprint(p, ep-p, "outPackets: %lud\n", stats->outPackets);
- p += snprint(p, ep-p, "outDataPackets: %lud\n", stats->outDataPackets);
- p += snprint(p, ep-p, "outDataBytes: %lud\n", stats->outDataBytes);
- p += snprint(p, ep-p, "outCompDataBytes: %lud\n", stats->outCompDataBytes);
- for(i=0; i<NCompStats; i++) {
- if(stats->outCompStats[i] == 0)
- continue;
- p += snprint(p, ep-p, "outCompStats[%d]: %lud\n", i, stats->outCompStats[i]);
- }
- p += snprint(p, ep-p, "inPackets: %lud\n", stats->inPackets);
- p += snprint(p, ep-p, "inDataPackets: %lud\n", stats->inDataPackets);
- p += snprint(p, ep-p, "inDataBytes: %lud\n", stats->inDataBytes);
- p += snprint(p, ep-p, "inCompDataBytes: %lud\n", stats->inCompDataBytes);
- p += snprint(p, ep-p, "inMissing: %lud\n", stats->inMissing);
- p += snprint(p, ep-p, "inDup: %lud\n", stats->inDup);
- p += snprint(p, ep-p, "inReorder: %lud\n", stats->inReorder);
- p += snprint(p, ep-p, "inBadComp: %lud\n", stats->inBadComp);
- p += snprint(p, ep-p, "inBadAuth: %lud\n", stats->inBadAuth);
- p += snprint(p, ep-p, "inBadSeq: %lud\n", stats->inBadSeq);
- p += snprint(p, ep-p, "inBadOther: %lud\n", stats->inBadOther);
- USED(p);
- qunlock(c);
- }
- // c is locked
- static void
- convack(Conv *c)
- {
- Block *b;
- AckPkt *ack;
- Stats *s;
- int i;
- b = allocb(sizeof(AckPkt));
- ack = (AckPkt*)b->wp;
- b->wp += sizeof(AckPkt);
- s = &c->lstats;
- hnputl(ack->cseq, c->in.controlseq);
- hnputl(ack->outPackets, s->outPackets);
- hnputl(ack->outDataPackets, s->outDataPackets);
- hnputl(ack->outDataBytes, s->outDataBytes);
- hnputl(ack->outCompDataBytes, s->outCompDataBytes);
- for(i=0; i<NCompStats; i++)
- hnputl(ack->outCompStats+i*4, s->outCompStats[i]);
- hnputl(ack->inPackets, s->inPackets);
- hnputl(ack->inDataPackets, s->inDataPackets);
- hnputl(ack->inDataBytes, s->inDataBytes);
- hnputl(ack->inCompDataBytes, s->inCompDataBytes);
- hnputl(ack->inMissing, s->inMissing);
- hnputl(ack->inDup, s->inDup);
- hnputl(ack->inReorder, s->inReorder);
- hnputl(ack->inBadComp, s->inBadComp);
- hnputl(ack->inBadAuth, s->inBadAuth);
- hnputl(ack->inBadSeq, s->inBadSeq);
- hnputl(ack->inBadOther, s->inBadOther);
- convoput(c, TControl, ControlAck, b);
- }
- // assume we hold lock for c
- static Block *
- conviput(Conv *c, Block *b, int control)
- {
- int type, subtype;
- ulong seq, seqwrap;
- long seqdiff;
- int pad;
- c->lstats.inPackets++;
- if(BLEN(b) < 4) {
- c->lstats.inBadOther++;
- freeb(b);
- return nil;
- }
-
- type = b->rp[0] >> 4;
- subtype = b->rp[0] & 0xf;
- b->rp += 1;
- if(type == TConnect) {
- conviconnect(c, subtype, b);
- return nil;
- }
- switch(c->state) {
- case CInit:
- case CDial:
- c->lstats.inBadOther++;
- convoconnect(c, ConReset, c->dialid, c->acceptid);
- convsetstate(c, CClosed);
- break;
- case CAccept:
- case CRemoteClose:
- case CLocalClose:
- c->lstats.inBadOther++;
- freeb(b);
- return nil;
- }
- seq = (b->rp[0]<<16) + (b->rp[1]<<8) + b->rp[2];
- b->rp += 3;
- seqwrap = c->in.seqwrap;
- seqdiff = seq - c->in.seq;
- if(seqdiff < -(SeqMax*3/4)) {
- seqwrap++;
- seqdiff += SeqMax;
- } else if(seqdiff > SeqMax*3/4) {
- seqwrap--;
- seqdiff -= SeqMax;
- }
- if(seqdiff <= 0) {
- if(seqdiff <= -SeqWindow) {
- if(0)print("old sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff);
- c->lstats.inBadSeq++;
- freeb(b);
- return nil;
- }
- if(c->in.window & (1<<-seqdiff)) {
- if(0)print("dup sequence number: %ld (%ld %ld)\n", seq, c->in.seqwrap, seqdiff);
- c->lstats.inDup++;
- freeb(b);
- return nil;
- }
- c->lstats.inReorder++;
- }
- // ok the sequence number looks ok
- if(0) print("coniput seq=%ulx\n", seq);
- if(c->in.auth != 0) {
- if(!(*c->in.auth)(&c->in, b->rp-4, BLEN(b)+4)) {
- if(0)print("bad auth %ld\n", BLEN(b)+4);
- c->lstats.inBadAuth++;
- freeb(b);
- return nil;
- }
- b->wp -= c->in.authlen;
- }
- if(c->in.cipher != 0) {
- if(!(*c->in.cipher)(&c->in, b->rp, BLEN(b))) {
- if(0)print("bad cipher\n");
- c->lstats.inBadOther++;
- freeb(b);
- return nil;
- }
- b->rp += c->in.cipherivlen;
- if(c->in.cipherblklen > 1) {
- pad = b->wp[-1];
- if(pad > BLEN(b)) {
- if(0)print("pad too big\n");
- c->lstats.inBadOther++;
- freeb(b);
- return nil;
- }
- b->wp -= pad;
- }
- }
- // ok the packet is good
- if(seqdiff > 0) {
- while(seqdiff > 0 && c->in.window != 0) {
- if((c->in.window & (1<<(SeqWindow-1))) == 0) {
- c->lstats.inMissing++;
- }
- c->in.window <<= 1;
- seqdiff--;
- }
- if(seqdiff > 0) {
- c->lstats.inMissing += seqdiff;
- seqdiff = 0;
- }
- c->in.seq = seq;
- c->in.seqwrap = seqwrap;
- }
- c->in.window |= 1<<-seqdiff;
- c->lastrecv = TK2SEC(m->ticks);
- switch(type) {
- case TControl:
- convicontrol(c, subtype, b);
- return nil;
- case TData:
- c->lstats.inDataPackets++;
- c->lstats.inDataBytes += BLEN(b);
- if(control)
- break;
- return b;
- case TCompData:
- c->lstats.inDataPackets++;
- c->lstats.inCompDataBytes += BLEN(b);
- b = convicomp(c, subtype, seq, b);
- if(b == nil) {
- c->lstats.inBadComp++;
- return nil;
- }
- c->lstats.inDataBytes += BLEN(b);
- if(control)
- break;
- return b;
- }
- if(0)print("dropping packet id=%d: type=%d n=%ld control=%d\n", c->id, type, BLEN(b), control);
- c->lstats.inBadOther++;
- freeb(b);
- return nil;
- }
- // assume hold conv lock
- static void
- conviconnect(Conv *c, int subtype, Block *b)
- {
- ulong dialid;
- ulong acceptid;
- if(BLEN(b) != 8) {
- freeb(b);
- return;
- }
- dialid = nhgetl(b->rp);
- acceptid = nhgetl(b->rp + 4);
- freeb(b);
- if(0)print("sdp: conviconnect: %s: %d %uld %uld\n", convstatename[c->state], subtype, dialid, acceptid);
- if(subtype == ConReset) {
- convsetstate(c, CClosed);
- return;
- }
- switch(c->state) {
- default:
- panic("unknown state: %d", c->state);
- case CInit:
- break;
- case CDial:
- if(dialid != c->dialid)
- goto Reset;
- break;
- case CAccept:
- case COpen:
- case CLocalClose:
- case CRemoteClose:
- if(dialid != c->dialid
- || subtype != ConOpenRequest && acceptid != c->acceptid)
- goto Reset;
- break;
- case CClosed:
- goto Reset;
- }
- switch(subtype) {
- case ConOpenRequest:
- switch(c->state) {
- case CInit:
- c->dialid = dialid;
- convsetstate(c, CAccept);
- return;
- case CAccept:
- case COpen:
- // duplicate ConOpenRequest that we ignore
- return;
- }
- break;
- case ConOpenAck:
- switch(c->state) {
- case CDial:
- c->acceptid = acceptid;
- convsetstate(c, COpen);
- return;
- case COpen:
- // duplicate that we have to ack
- convoconnect(c, ConOpenAckAck, acceptid, dialid);
- return;
- }
- break;
- case ConOpenAckAck:
- switch(c->state) {
- case CAccept:
- convsetstate(c, COpen);
- return;
- case COpen:
- case CLocalClose:
- case CRemoteClose:
- // duplicate that we ignore
- return;
- }
- break;
- case ConClose:
- switch(c->state) {
- case COpen:
- convoconnect(c, ConCloseAck, dialid, acceptid);
- convsetstate(c, CRemoteClose);
- return;
- case CRemoteClose:
- // duplicate ConClose
- convoconnect(c, ConCloseAck, dialid, acceptid);
- return;
- }
- break;
- case ConCloseAck:
- switch(c->state) {
- case CLocalClose:
- convsetstate(c, CClosed);
- return;
- }
- break;
- }
- Reset:
- // invalid connection message - reset to sender
- if(1)print("sdp: invalid conviconnect - sending reset\n");
- convoconnect(c, ConReset, dialid, acceptid);
- convsetstate(c, CClosed);
- }
- static void
- convicontrol(Conv *c, int subtype, Block *b)
- {
- ulong cseq;
- AckPkt *ack;
- int i;
- if(BLEN(b) < 4)
- return;
- cseq = nhgetl(b->rp);
-
- switch(subtype){
- case ControlMesg:
- if(cseq == c->in.controlseq) {
- if(0)print("duplicate control packet: %ulx\n", cseq);
- // duplicate control packet
- freeb(b);
- if(c->in.controlpkt == nil)
- convack(c);
- return;
- }
- if(cseq != c->in.controlseq+1)
- return;
- c->in.controlseq = cseq;
- b->rp += 4;
- if(BLEN(b) == 0) {
- // just a ping
- freeb(b);
- convack(c);
- } else {
- c->in.controlpkt = b;
- if(0) print("recv %ld size=%ld\n", cseq, BLEN(b));
- wakeup(&c->in.controlready);
- }
- return;
- case ControlAck:
- if(cseq != c->out.controlseq)
- return;
- if(BLEN(b) < sizeof(AckPkt))
- return;
- ack = (AckPkt*)(b->rp);
- c->rstats.outPackets = nhgetl(ack->outPackets);
- c->rstats.outDataPackets = nhgetl(ack->outDataPackets);
- c->rstats.outDataBytes = nhgetl(ack->outDataBytes);
- c->rstats.outCompDataBytes = nhgetl(ack->outCompDataBytes);
- for(i=0; i<NCompStats; i++)
- c->rstats.outCompStats[i] = nhgetl(ack->outCompStats + 4*i);
- c->rstats.inPackets = nhgetl(ack->inPackets);
- c->rstats.inDataPackets = nhgetl(ack->inDataPackets);
- c->rstats.inDataBytes = nhgetl(ack->inDataBytes);
- c->rstats.inCompDataBytes = nhgetl(ack->inCompDataBytes);
- c->rstats.inMissing = nhgetl(ack->inMissing);
- c->rstats.inDup = nhgetl(ack->inDup);
- c->rstats.inReorder = nhgetl(ack->inReorder);
- c->rstats.inBadComp = nhgetl(ack->inBadComp);
- c->rstats.inBadAuth = nhgetl(ack->inBadAuth);
- c->rstats.inBadSeq = nhgetl(ack->inBadSeq);
- c->rstats.inBadOther = nhgetl(ack->inBadOther);
- freeb(b);
- freeb(c->out.controlpkt);
- c->out.controlpkt = nil;
- c->timeout = c->lastrecv + KeepAlive;
- wakeup(&c->out.controlready);
- return;
- }
- }
- static Block*
- convicomp(Conv *c, int subtype, ulong seq, Block *b)
- {
- if(c->in.comp == nil) {
- freeb(b);
- return nil;
- }
- if(!(*c->in.comp)(c, subtype, seq, &b))
- return nil;
- return b;
- }
- // c is locked
- static void
- convwriteblock(Conv *c, Block *b)
- {
- // simulated errors
- if(c->drop && nrand(c->drop) == 0)
- return;
- if(waserror()) {
- convsetstate(c, CClosed);
- nexterror();
- }
- devtab[c->chan->type]->bwrite(c->chan, b, 0);
- poperror();
- }
- // assume hold conv lock
- static void
- convoput(Conv *c, int type, int subtype, Block *b)
- {
- int pad;
-
- c->lstats.outPackets++;
- /* Make room for sdp trailer */
- if(c->out.cipherblklen > 1)
- pad = c->out.cipherblklen - (BLEN(b) + c->out.cipherivlen) % c->out.cipherblklen;
- else
- pad = 0;
- b = padblock(b, -(pad+c->out.authlen));
- if(pad) {
- memset(b->wp, 0, pad-1);
- b->wp[pad-1] = pad;
- b->wp += pad;
- }
- /* Make space to fit sdp header */
- b = padblock(b, 4 + c->out.cipherivlen);
- b->rp[0] = (type << 4) | subtype;
- c->out.seq++;
- if(c->out.seq == (1<<24)) {
- c->out.seq = 0;
- c->out.seqwrap++;
- }
- b->rp[1] = c->out.seq>>16;
- b->rp[2] = c->out.seq>>8;
- b->rp[3] = c->out.seq;
-
- if(c->out.cipher)
- (*c->out.cipher)(&c->out, b->rp+4, BLEN(b)-4);
- // auth
- if(c->out.auth) {
- b->wp += c->out.authlen;
- (*c->out.auth)(&c->out, b->rp, BLEN(b));
- }
-
- convwriteblock(c, b);
- }
- // assume hold conv lock
- static void
- convoconnect(Conv *c, int op, ulong dialid, ulong acceptid)
- {
- Block *b;
- c->lstats.outPackets++;
- assert(c->chan != nil);
- b = allocb(9);
- b->wp[0] = (TConnect << 4) | op;
- hnputl(b->wp+1, dialid);
- hnputl(b->wp+5, acceptid);
- b->wp += 9;
- if(!waserror()) {
- convwriteblock(c, b);
- poperror();
- }
- }
- static Block *
- convreadblock(Conv *c, int n)
- {
- Block *b;
- Chan *ch;
- qlock(&c->readlk);
- if(waserror()) {
- c->readproc = nil;
- qunlock(&c->readlk);
- nexterror();
- }
- qlock(c);
- if(c->state == CClosed) {
- qunlock(c);
- error("closed");
- }
- c->readproc = up;
- ch = c->chan;
- assert(c->ref > 0);
- qunlock(c);
- b = devtab[ch->type]->bread(ch, n, 0);
- c->readproc = nil;
- poperror();
- qunlock(&c->readlk);
- return b;
- }
- static int
- readready(void *a)
- {
- Conv *c = a;
- return c->in.controlpkt != nil || (c->state == CClosed) || (c->state == CRemoteClose);
- }
- static Block *
- readcontrol(Conv *c, int n)
- {
- Block *b;
- USED(n);
- qlock(&c->in.controllk);
- if(waserror()) {
- qunlock(&c->in.controllk);
- nexterror();
- }
- qlock(c); // this lock is not held during the sleep below
- for(;;) {
- if(c->chan == nil || c->state == CClosed) {
- qunlock(c);
- if(0)print("readcontrol: return error - state = %s\n", convstatename[c->state]);
- error("conversation closed");
- }
- if(c->in.controlpkt != nil)
- break;
- if(c->state == CRemoteClose) {
- qunlock(c);
- if(0)print("readcontrol: return nil - state = %s\n", convstatename[c->state]);
- poperror();
- return nil;
- }
- qunlock(c);
- sleep(&c->in.controlready, readready, c);
- qlock(c);
- }
- convack(c);
- b = c->in.controlpkt;
- c->in.controlpkt = nil;
- qunlock(c);
- poperror();
- qunlock(&c->in.controllk);
- return b;
- }
- static int
- writeready(void *a)
- {
- Conv *c = a;
- return c->out.controlpkt == nil || (c->state == CClosed) || (c->state == CRemoteClose);
- }
- // c is locked
- static void
- writewait(Conv *c)
- {
- for(;;) {
- if(c->state == CFree || c->state == CInit ||
- c->state == CClosed || c->state == CRemoteClose)
- error("conversation closed");
- if(c->state == COpen && c->out.controlpkt == nil)
- break;
- qunlock(c);
- if(waserror()) {
- qlock(c);
- nexterror();
- }
- sleep(&c->out.controlready, writeready, c);
- poperror();
- qlock(c);
- }
- }
- static void
- writecontrol(Conv *c, void *p, int n, int wait)
- {
- Block *b;
- qlock(&c->out.controllk);
- qlock(c);
- if(waserror()) {
- qunlock(c);
- qunlock(&c->out.controllk);
- nexterror();
- }
- writewait(c);
- b = allocb(4+n);
- c->out.controlseq++;
- hnputl(b->wp, c->out.controlseq);
- memmove(b->wp+4, p, n);
- b->wp += 4+n;
- c->out.controlpkt = b;
- convretryinit(c);
- convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b)));
- if(wait)
- writewait(c);
- poperror();
- qunlock(c);
- qunlock(&c->out.controllk);
- }
- static Block *
- readdata(Conv *c, int n)
- {
- Block *b;
- int nn;
- for(;;) {
- // some slack for tunneling overhead
- nn = n + 100;
- // make sure size is big enough for control messages
- if(nn < 1000)
- nn = 1000;
- b = convreadblock(c, nn);
- if(b == nil)
- return nil;
- qlock(c);
- if(waserror()) {
- qunlock(c);
- return nil;
- }
- b = conviput(c, b, 0);
- poperror();
- qunlock(c);
- if(b != nil) {
- if(BLEN(b) > n)
- b->wp = b->rp + n;
- return b;
- }
- }
- }
- static long
- writedata(Conv *c, Block *b)
- {
- int n;
- ulong seq;
- int subtype;
- qlock(c);
- if(waserror()) {
- qunlock(c);
- nexterror();
- }
- if(c->state != COpen) {
- freeb(b);
- error("conversation not open");
- }
- n = BLEN(b);
- c->lstats.outDataPackets++;
- c->lstats.outDataBytes += n;
- if(c->out.comp != nil) {
- // must generate same value as convoput
- seq = (c->out.seq + 1) & (SeqMax-1);
- subtype = (*c->out.comp)(c, 0, seq, &b);
- c->lstats.outCompDataBytes += BLEN(b);
- convoput(c, TCompData, subtype, b);
- } else
- convoput(c, TData, 0, b);
- poperror();
- qunlock(c);
- return n;
- }
- static void
- convreader(void *a)
- {
- Conv *c = a;
- Block *b;
- qlock(c);
- assert(c->reader == 1);
- while(c->dataopen == 0 && c->state != CClosed) {
- qunlock(c);
- b = nil;
- if(!waserror()) {
- b = convreadblock(c, 2000);
- poperror();
- }
- qlock(c);
- if(b == nil) {
- if(strcmp(up->errstr, Eintr) != 0) {
- convsetstate(c, CClosed);
- break;
- }
- } else if(!waserror()) {
- conviput(c, b, 1);
- poperror();
- }
- }
- c->reader = 0;
- convderef(c);
- qunlock(c);
- pexit("hangup", 1);
- }
- /* ciphers, authenticators, and compressors */
- static void
- setalg(Conv *c, char *name, Algorithm *alg, Algorithm **p)
- {
- for(; alg->name; alg++)
- if(strcmp(name, alg->name) == 0)
- break;
- if(alg->name == nil)
- error("unknown algorithm");
- *p = alg;
- alg->init(c);
- }
- static void
- setsecret(OneWay *ow, char *secret)
- {
- char *p;
- int i, c;
-
- i = 0;
- memset(ow->secret, 0, sizeof(ow->secret));
- for(p=secret; *p; p++) {
- if(i >= sizeof(ow->secret)*2)
- break;
- c = *p;
- if(c >= '0' && c <= '9')
- c -= '0';
- else if(c >= 'a' && c <= 'f')
- c -= 'a'-10;
- else if(c >= 'A' && c <= 'F')
- c -= 'A'-10;
- else
- error("bad character in secret");
- if((i&1) == 0)
- c <<= 4;
- ow->secret[i>>1] |= c;
- i++;
- }
- }
- static void
- setkey(uchar *key, int n, OneWay *ow, char *prefix)
- {
- uchar ibuf[SHA1dlen], obuf[MD5dlen], salt[10];
- int i, round = 0;
- while(n > 0){
- for(i=0; i<round+1; i++)
- salt[i] = 'A'+round;
- sha1((uchar*)prefix, strlen(prefix), ibuf, sha1(salt, round+1, nil, nil));
- md5(ibuf, SHA1dlen, obuf, md5(ow->secret, sizeof(ow->secret), nil, nil));
- i = (n<MD5dlen) ? n : MD5dlen;
- memmove(key, obuf, i);
- key += i;
- n -= i;
- if(++round > sizeof salt)
- panic("setkey: you ask too much");
- }
- }
- static void
- cipherfree(Conv *c)
- {
- if(c->in.cipherstate) {
- free(c->in.cipherstate);
- c->in.cipherstate = nil;
- }
- if(c->out.cipherstate) {
- free(c->out.cipherstate);
- c->out.cipherstate = nil;
- }
- c->in.cipher = nil;
- c->in.cipherblklen = 0;
- c->out.cipherblklen = 0;
- c->in.cipherivlen = 0;
- c->out.cipherivlen = 0;
- }
- static void
- authfree(Conv *c)
- {
- if(c->in.authstate) {
- free(c->in.authstate);
- c->in.authstate = nil;
- }
- if(c->out.authstate) {
- free(c->out.authstate);
- c->out.authstate = nil;
- }
- c->in.auth = nil;
- c->in.authlen = 0;
- c->out.authlen = 0;
- }
- static void
- compfree(Conv *c)
- {
- if(c->in.compstate) {
- free(c->in.compstate);
- c->in.compstate = nil;
- }
- if(c->out.compstate) {
- free(c->out.compstate);
- c->out.compstate = nil;
- }
- c->in.comp = nil;
- }
- static void
- nullcipherinit(Conv *c)
- {
- cipherfree(c);
- }
- static int
- desencrypt(OneWay *ow, uchar *p, int n)
- {
- uchar *pp, *ip, *eip, *ep;
- DESstate *ds = ow->cipherstate;
- if(n < 8 || (n & 0x7 != 0))
- return 0;
- ep = p + n;
- memmove(p, ds->ivec, 8);
- for(p += 8; p < ep; p += 8){
- pp = p;
- ip = ds->ivec;
- for(eip = ip+8; ip < eip; )
- *pp++ ^= *ip++;
- block_cipher(ds->expanded, p, 0);
- memmove(ds->ivec, p, 8);
- }
- return 1;
- }
- static int
- desdecrypt(OneWay *ow, uchar *p, int n)
- {
- uchar tmp[8];
- uchar *tp, *ip, *eip, *ep;
- DESstate *ds = ow->cipherstate;
- if(n < 8 || (n & 0x7 != 0))
- return 0;
- ep = p + n;
- memmove(ds->ivec, p, 8);
- p += 8;
- while(p < ep){
- memmove(tmp, p, 8);
- block_cipher(ds->expanded, p, 1);
- tp = tmp;
- ip = ds->ivec;
- for(eip = ip+8; ip < eip; ){
- *p++ ^= *ip;
- *ip++ = *tp++;
- }
- }
- return 1;
- }
- static void
- descipherinit(Conv *c)
- {
- uchar key[8];
- uchar ivec[8];
- int i;
- int n = c->cipher->keylen;
- cipherfree(c);
-
- if(n > sizeof(key))
- n = sizeof(key);
- /* in */
- memset(key, 0, sizeof(key));
- setkey(key, n, &c->in, "cipher");
- memset(ivec, 0, sizeof(ivec));
- c->in.cipherblklen = 8;
- c->in.cipherivlen = 8;
- c->in.cipher = desdecrypt;
- c->in.cipherstate = smalloc(sizeof(DESstate));
- setupDESstate(c->in.cipherstate, key, ivec);
-
- /* out */
- memset(key, 0, sizeof(key));
- setkey(key, n, &c->out, "cipher");
- for(i=0; i<8; i++)
- ivec[i] = nrand(256);
- c->out.cipherblklen = 8;
- c->out.cipherivlen = 8;
- c->out.cipher = desencrypt;
- c->out.cipherstate = smalloc(sizeof(DESstate));
- setupDESstate(c->out.cipherstate, key, ivec);
- }
- static int
- rc4encrypt(OneWay *ow, uchar *p, int n)
- {
- CipherRc4 *cr = ow->cipherstate;
- if(n < 4)
- return 0;
- hnputl(p, cr->cseq);
- p += 4;
- n -= 4;
- rc4(&cr->current, p, n);
- cr->cseq += n;
- return 1;
- }
- static int
- rc4decrypt(OneWay *ow, uchar *p, int n)
- {
- CipherRc4 *cr = ow->cipherstate;
- RC4state tmpstate;
- ulong seq;
- long d, dd;
- if(n < 4)
- return 0;
- seq = nhgetl(p);
- p += 4;
- n -= 4;
- d = seq-cr->cseq;
- if(d == 0) {
- rc4(&cr->current, p, n);
- cr->cseq += n;
- if(cr->ovalid) {
- dd = cr->cseq - cr->lgseq;
- if(dd > RC4back)
- cr->ovalid = 0;
- }
- } else if(d > 0) {
- //print("missing packet: %uld %ld\n", seq, d);
- // this link is hosed
- if(d > RC4forward)
- return 0;
- cr->lgseq = seq;
- if(!cr->ovalid) {
- cr->ovalid = 1;
- cr->oseq = cr->cseq;
- memmove(&cr->old, &cr->current, sizeof(RC4state));
- }
- rc4skip(&cr->current, d);
- rc4(&cr->current, p, n);
- cr->cseq = seq+n;
- } else {
- //print("reordered packet: %uld %ld\n", seq, d);
- dd = seq - cr->oseq;
- if(!cr->ovalid || -d > RC4back || dd < 0)
- return 0;
- memmove(&tmpstate, &cr->old, sizeof(RC4state));
- rc4skip(&tmpstate, dd);
- rc4(&tmpstate, p, n);
- return 1;
- }
- // move old state up
- if(cr->ovalid) {
- dd = cr->cseq - RC4back - cr->oseq;
- if(dd > 0) {
- rc4skip(&cr->old, dd);
- cr->oseq += dd;
- }
- }
- return 1;
- }
- static void
- rc4cipherinit(Conv *c)
- {
- uchar key[32];
- CipherRc4 *cr;
- int n;
- cipherfree(c);
- n = c->cipher->keylen;
- if(n > sizeof(key))
- n = sizeof(key);
- /* in */
- memset(key, 0, sizeof(key));
- setkey(key, n, &c->in, "cipher");
- c->in.cipherblklen = 1;
- c->in.cipherivlen = 4;
- c->in.cipher = rc4decrypt;
- cr = smalloc(sizeof(CipherRc4));
- memset(cr, 0, sizeof(*cr));
- setupRC4state(&cr->current, key, n);
- c->in.cipherstate = cr;
- /* out */
- memset(key, 0, sizeof(key));
- setkey(key, n, &c->out, "cipher");
- c->out.cipherblklen = 1;
- c->out.cipherivlen = 4;
- c->out.cipher = rc4encrypt;
- cr = smalloc(sizeof(CipherRc4));
- memset(cr, 0, sizeof(*cr));
- setupRC4state(&cr->current, key, n);
- c->out.cipherstate = cr;
- }
- static void
- nullauthinit(Conv *c)
- {
- authfree(c);
- }
- static void
- shaauthinit(Conv *c)
- {
- authfree(c);
- }
- static void
- seanq_hmac_md5(uchar hash[MD5dlen], ulong wrap, uchar *t, long tlen, uchar *key, long klen)
- {
- uchar ipad[65], opad[65], wbuf[4];
- int i;
- DigestState *digest;
- uchar innerhash[MD5dlen];
- for(i=0; i<64; i++){
- ipad[i] = 0x36;
- opad[i] = 0x5c;
- }
- ipad[64] = opad[64] = 0;
- for(i=0; i<klen; i++){
- ipad[i] ^= key[i];
- opad[i] ^= key[i];
- }
- hnputl(wbuf, wrap);
- digest = md5(ipad, 64, nil, nil);
- digest = md5(wbuf, sizeof(wbuf), nil, digest);
- md5(t, tlen, innerhash, digest);
- digest = md5(opad, 64, nil, nil);
- md5(innerhash, MD5dlen, hash, digest);
- }
- static int
- md5auth(OneWay *ow, uchar *t, int tlen)
- {
- uchar hash[MD5dlen];
- int r;
- if(tlen < ow->authlen)
- return 0;
- tlen -= ow->authlen;
- memset(hash, 0, MD5dlen);
- seanq_hmac_md5(hash, ow->seqwrap, t, tlen, (uchar*)ow->authstate, 16);
- r = memcmp(t+tlen, hash, ow->authlen) == 0;
- memmove(t+tlen, hash, ow->authlen);
- return r;
- }
- static void
- md5authinit(Conv *c)
- {
- int keylen;
- authfree(c);
- keylen = c->auth->keylen;
- if(keylen > 16)
- keylen = 16;
- /* in */
- c->in.authstate = smalloc(16);
- memset(c->in.authstate, 0, 16);
- setkey(c->in.authstate, keylen, &c->in, "auth");
- c->in.authlen = 12;
- c->in.auth = md5auth;
-
- /* out */
- c->out.authstate = smalloc(16);
- memset(c->out.authstate, 0, 16);
- setkey(c->out.authstate, keylen, &c->out, "auth");
- c->out.authlen = 12;
- c->out.auth = md5auth;
- }
- static void
- nullcompinit(Conv *c)
- {
- compfree(c);
- }
- static int
- thwackcomp(Conv *c, int, ulong seq, Block **bp)
- {
- Block *b, *bb;
- int nn;
- ulong ackseq;
- uchar mask;
- // add ack info
- b = padblock(*bp, 4);
- ackseq = unthwackstate(c->in.compstate, &mask);
- b->rp[0] = mask;
- b->rp[1] = ackseq>>16;
- b->rp[2] = ackseq>>8;
- b->rp[3] = ackseq;
- bb = allocb(BLEN(b));
- nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq, c->lstats.outCompStats);
- if(nn < 0) {
- freeb(bb);
- *bp = b;
- return ThwackU;
- } else {
- bb->wp += nn;
- freeb(b);
- *bp = bb;
- return ThwackC;
- }
- }
- static int
- thwackuncomp(Conv *c, int subtype, ulong seq, Block **bp)
- {
- Block *b, *bb;
- ulong mask;
- ulong mseq;
- int n;
- switch(subtype) {
- default:
- return 0;
- case ThwackU:
- b = *bp;
- mask = b->rp[0];
- mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
- b->rp += 4;
- thwackack(c->out.compstate, mseq, mask);
- return 1;
- case ThwackC:
- bb = *bp;
- b = allocb(ThwMaxBlock);
- n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq);
- freeb(bb);
- *bp = nil;
- if(n < 0) {
- if(0)print("unthwack failed: %d\n", n);
- freeb(b);
- return 0;
- }
- b->wp += n;
- mask = b->rp[0];
- mseq = (b->rp[1]<<16) | (b->rp[2]<<8) | b->rp[3];
- thwackack(c->out.compstate, mseq, mask);
- b->rp += 4;
- *bp = b;
- return 1;
- }
- }
- static void
- thwackcompinit(Conv *c)
- {
- compfree(c);
- c->in.compstate = malloc(sizeof(Unthwack));
- if(c->in.compstate == nil)
- error(Enomem);
- unthwackinit(c->in.compstate);
- c->out.compstate = malloc(sizeof(Thwack));
- if(c->out.compstate == nil)
- error(Enomem);
- thwackinit(c->out.compstate);
- c->in.comp = thwackuncomp;
- c->out.comp = thwackcomp;
- }
|