| plan 9 kernel history: overview | file list | diff list |
1999/1221/port/devsdp.c (diff list | history)
| port/devsdp.c on 1999/0824 | ||
| 1999/0824 | #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 <libcrypt.h> | |
| 1999/1001 | #include "../port/thwack.h" | |
| 1999/0824 | ||
| 1999/0907 | /* * sdp - secure datagram protocol */ | |
| 1999/0902 | typedef struct Sdp Sdp; typedef struct Conv Conv; | |
| 1999/0907 | typedef struct OneWay OneWay; | |
| 1999/0929 | typedef struct Stats Stats; typedef struct AckPkt AckPkt; | |
| 1999/1001 | typedef struct Algorithm Algorithm; | |
| 1999/1022 | typedef struct CipherRc4 CipherRc4; | |
| 1999/0824 | enum { Qtopdir= 1, /* top level directory */ Qsdpdir, /* sdp directory */ Qclone, Qlog, | |
| 1999/0901 | Qconvdir, /* directory per conversation */ | |
| 1999/0824 | Qctl, | |
| 1999/0902 | Qdata, /* unreliable packet channel */ Qcontrol, /* reliable control channel */ | |
| 1999/0824 | Qstatus, | |
| 1999/0929 | Qstats, Qrstats, | |
| 1999/0824 | MaxQ, | |
| 1999/0907 | Maxconv= 256, // power of 2 Nfs= 4, // number of file systems | |
| 1999/1221 | MaxRetries= 12, KeepAlive = 120, // keep alive in seconds | |
| 1999/1015 | SecretLength= 32, // a secret per direction | |
| 1999/1001 | SeqMax = (1<<24), SeqWindow = 32, | |
| 1999/1019 | NCompStats = 8, | |
| 1999/0824 | }; #define TYPE(x) ((x).path & 0xff) | |
| 1999/0901 | #define CONV(x) (((x).path >> 8)&(Maxconv-1)) | |
| 1999/0824 | #define QID(x, y) (((x)<<8) | (y)) | |
| 1999/0929 | struct Stats { ulong outPackets; ulong outDataPackets; ulong outDataBytes; ulong outCompDataBytes; ulong outCompBytes; | |
| 1999/1019 | ulong outCompStats[NCompStats]; | |
| 1999/0929 | ulong inPackets; ulong inDataPackets; ulong inDataBytes; ulong inCompDataBytes; ulong inMissing; ulong inDup; ulong inReorder; | |
| 1999/1016 | ulong inBadComp; | |
| 1999/0929 | ulong inBadAuth; ulong inBadSeq; | |
| 1999/1022 | ulong inBadOther; | |
| 1999/0929 | }; | |
| 1999/0907 | struct OneWay | |
| 1999/0901 | { | |
| 1999/0929 | Rendez statsready; | |
| 1999/0901 | ulong seqwrap; // number of wraps of the sequence number ulong seq; | |
| 1999/0907 | ulong window; | |
| 1999/0901 | ||
| 1999/1015 | uchar secret[SecretLength]; | |
| 1999/0915 | QLock controllk; | |
| 1999/0907 | Rendez controlready; | |
| 1999/0902 | Block *controlpkt; // control channel | |
| 1999/0907 | ulong controlseq; | |
| 1999/0901 | ||
| 1999/0902 | void *cipherstate; // state cipher | |
| 1999/0907 | int cipherivlen; // initial vector length int cipherblklen; // block length int (*cipher)(OneWay*, uchar *buf, int len); | |
| 1999/0902 | void *authstate; // auth state int authlen; // auth data length in bytes | |
| 1999/0907 | int (*auth)(OneWay*, uchar *buf, int len); | |
| 1999/0902 | void *compstate; | |
| 1999/1016 | int (*comp)(Conv*, int subtype, ulong seq, Block **); | |
| 1999/0901 | }; | |
| 1999/0907 | // conv states | |
| 1999/0902 | enum { | |
| 1999/1211 | CFree, | |
| 1999/0906 | CInit, | |
| 1999/0909 | CDial, CAccept, | |
| 1999/0902 | COpen, | |
| 1999/0910 | CLocalClose, CRemoteClose, | |
| 1999/0906 | CClosed, | |
| 1999/0902 | }; | |
| 1999/0901 | struct Conv { QLock; | |
| 1999/0824 | Sdp *sdp; | |
| 1999/1211 | int id; | |
| 1999/0901 | ||
| 1999/1211 | int ref; // holds conv up | |
| 1999/0902 | int state; | |
| 1999/1211 | int dataopen; // ref count of opens on Qdata int controlopen; // ref count of opens on Qcontrol | |
| 1999/0910 | int reader; // reader proc has been started | |
| 1999/0901 | ||
| 1999/0929 | Stats lstats; Stats rstats; | |
| 1999/1001 | ulong lastrecv; // time last packet was received | |
| 1999/0906 | ulong timeout; int retries; // the following pair uniquely define conversation on this port ulong dialid; ulong acceptid; | |
| 1999/0910 | QLock readlk; // protects readproc | |
| 1999/0907 | Proc *readproc; | |
| 1999/0910 | ||
| 1999/1211 | Chan *chan; // packet channel | |
| 1999/0910 | char *channame; | |
| 1999/0902 | ||
| 1999/0907 | char owner[NAMELEN]; /* protections */ | |
| 1999/0901 | int perm; | |
| 1999/1022 | Algorithm *auth; Algorithm *cipher; Algorithm *comp; | |
| 1999/0907 | ||
| 1999/0906 | int drop; | |
| 1999/0907 | OneWay in; OneWay out; | |
| 1999/0824 | }; struct Sdp { QLock; Log; | |
| 1999/0906 | Rendez vous; /* used by sdpackproc */ | |
| 1999/0901 | int nconv; | |
| 1999/0902 | Conv *conv[Maxconv]; | |
| 1999/0906 | int ackproc; | |
| 1999/0824 | }; | |
| 1999/0906 | enum { TConnect, TControl, TData, | |
| 1999/1015 | TCompData, | |
| 1999/0906 | }; enum { | |
| 1999/1015 | ControlMesg, ControlAck, }; enum { | |
| 1999/1016 | ThwackU, ThwackC, }; enum { | |
| 1999/0909 | ConOpenRequest, | |
| 1999/0906 | ConOpenAck, | |
| 1999/0910 | ConOpenAckAck, | |
| 1999/0906 | ConClose, | |
| 1999/0909 | ConReset, | |
| 1999/0906 | }; | |
| 1999/0929 | struct AckPkt { uchar cseq[4]; uchar outPackets[4]; uchar outDataPackets[4]; uchar outDataBytes[4]; uchar outCompDataBytes[4]; | |
| 1999/1019 | uchar outCompStats[4*NCompStats]; | |
| 1999/0929 | uchar inPackets[4]; uchar inDataPackets[4]; uchar inDataBytes[4]; uchar inCompDataBytes[4]; uchar inMissing[4]; uchar inDup[4]; uchar inReorder[4]; | |
| 1999/1016 | uchar inBadComp[4]; | |
| 1999/0929 | uchar inBadAuth[4]; uchar inBadSeq[4]; | |
| 1999/1022 | uchar inBadOther[4]; | |
| 1999/0929 | }; | |
| 1999/0907 | ||
| 1999/1001 | struct Algorithm { char *name; int keylen; // in bytes | |
| 1999/1022 | void (*init)(Conv*); | |
| 1999/1001 | }; | |
| 1999/0929 | ||
| 1999/1022 | 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; }; | |
| 1999/0824 | static Dirtab sdpdirtab[]={ "log", {Qlog}, 0, 0666, | |
| 1999/0901 | "clone", {Qclone}, 0, 0666, | |
| 1999/0824 | }; | |
| 1999/0901 | static Dirtab convdirtab[]={ | |
| 1999/0824 | "ctl", {Qctl}, 0, 0666, "data", {Qdata}, 0, 0666, | |
| 1999/0902 | "control", {Qcontrol}, 0, 0666, | |
| 1999/0824 | "status", {Qstatus}, 0, 0444, | |
| 1999/0929 | "stats", {Qstats}, 0, 0444, "rstats", {Qrstats}, 0, 0444, | |
| 1999/0824 | }; 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]; | |
| 1999/0908 | static char *convstatename[] = { | |
| 1999/1211 | [CFree] "Free", [CInit] "Init", [CDial] "Dial", [CAccept] "Accept", [COpen] "Open", | |
| 1999/0910 | [CLocalClose] "LocalClose", [CRemoteClose] "RemoteClose", | |
| 1999/1211 | [CClosed] "Closed", | |
| 1999/0908 | }; | |
| 1999/0824 | static int sdpgen(Chan *c, Dirtab*, int, int s, Dir *dp); | |
| 1999/0901 | static Conv *sdpclone(Sdp *sdp); | |
| 1999/0906 | static void sdpackproc(void *a); | |
| 1999/0907 | static void onewaycleanup(OneWay *ow); static int readready(void *a); static int controlread(); | |
| 1999/1211 | 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); | |
| 1999/0907 | static Block *conviput(Conv *c, Block *b, int control); | |
| 1999/1015 | static void conviconnect(Conv *c, int op, Block *b); static void convicontrol(Conv *c, int op, Block *b); | |
| 1999/1016 | static Block *convicomp(Conv *c, int op, ulong, Block *b); | |
| 1999/1015 | static void convoput(Conv *c, int type, int subtype, Block *b); static void convoconnect(Conv *c, int op, ulong dialid, ulong acceptid); | |
| 1999/0914 | static void convopenchan(Conv *c, char *path); | |
| 1999/0929 | static void convstats(Conv *c, int local, char *buf, int n); | |
| 1999/1211 | static void convreader(void *a); | |
| 1999/0824 | ||
| 1999/1022 | static void setalg(Conv *c, char *name, Algorithm *tab, Algorithm **); | |
| 1999/1015 | static void setsecret(OneWay *cc, char *secret); | |
| 1999/1022 | 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); | |
| 1999/1015 | static Algorithm cipheralg[] = { "null", 0, nullcipherinit, "des_56_cbc", 7, descipherinit, "rc4_128", 16, rc4cipherinit, | |
| 1999/1022 | "rc4_256", 32, rc4cipherinit, | |
| 1999/1015 | nil, 0, nil, }; static Algorithm authalg[] = { "null", 0, nullauthinit, "hmac_sha_96", 16, shaauthinit, "hmac_md5_96", 16, md5authinit, nil, 0, nil, }; static Algorithm compalg[] = { "null", 0, nullcompinit, | |
| 1999/1028 | "thwack", 0, thwackcompinit, | |
| 1999/1015 | nil, 0, nil, }; | |
| 1999/0824 | static void sdpinit(void) { int i; Dirtab *dt; | |
| 1999/1027 | ||
| 1999/0824 | // setup dirtab with non directory entries for(i=0; i<nelem(sdpdirtab); i++) { dt = sdpdirtab + i; dirtab[TYPE(dt->qid)] = dt; } | |
| 1999/0901 | for(i=0; i<nelem(convdirtab); i++) { dt = convdirtab + i; | |
| 1999/0824 | dirtab[TYPE(dt->qid)] = dt; } | |
| 1999/0906 | ||
| 1999/0824 | } static Chan* sdpattach(char* spec) { Chan *c; int dev; | |
| 1999/0906 | char buf[100]; Sdp *sdp; int start; | |
| 1999/0824 | dev = atoi(spec); if(dev<0 || dev >= Nfs) error("bad specification"); | |
| 1999/1027 | c = devattach('E', spec); | |
| 1999/0824 | c->qid = (Qid){QID(0, Qtopdir)|CHDIR, 0}; c->dev = dev; | |
| 1999/0906 | 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); } | |
| 1999/0824 | return c; } static int sdpwalk(Chan *c, char *name) { if(strcmp(name, "..") == 0){ switch(TYPE(c->qid)){ case Qtopdir: case Qsdpdir: c->qid = (Qid){CHDIR|Qtopdir, 0}; break; | |
| 1999/0901 | case Qconvdir: | |
| 1999/0824 | c->qid = (Qid){CHDIR|Qsdpdir, 0}; break; default: panic("sdpwalk %lux", c->qid.path); } return 1; } return devwalk(c, name, 0, 0, sdpgen); } static void sdpstat(Chan* c, char* db) { devstat(c, db, nil, 0, sdpgen); } | |
| 1999/1211 | ||
| 1999/0824 | static Chan* | |
| 1999/0901 | sdpopen(Chan* ch, int omode) | |
| 1999/0824 | { int perm; Sdp *sdp; | |
| 1999/0901 | Conv *c; | |
| 1999/0824 | omode &= 3; perm = m2p[omode]; USED(perm); | |
| 1999/0901 | sdp = sdptab + ch->dev; | |
| 1999/0824 | ||
| 1999/0901 | switch(TYPE(ch->qid)) { | |
| 1999/0824 | default: break; case Qtopdir: case Qsdpdir: | |
| 1999/0901 | case Qconvdir: | |
| 1999/0824 | if(omode != OREAD) error(Eperm); break; case Qlog: logopen(sdp); break; | |
| 1999/0901 | case Qclone: c = sdpclone(sdp); if(c == nil) error(Enodev); ch->qid.path = QID(c->id, Qctl); break; | |
| 1999/0902 | case Qdata: case Qctl: case Qstatus: case Qcontrol: | |
| 1999/0929 | case Qstats: case Qrstats: | |
| 1999/0902 | c = sdp->conv[CONV(ch->qid)]; | |
| 1999/1211 | print("open %d:%d: ref=%d\n", c->id, TYPE(ch->qid), c->ref); | |
| 1999/0902 | qlock(c); if(waserror()) { qunlock(c); nexterror(); } if((perm & (c->perm>>6)) != perm) | |
| 1999/0907 | if(strcmp(up->user, c->owner) != 0 || (perm & c->perm) != perm) | |
| 1999/0902 | error(Eperm); | |
| 1999/1211 | ||
| 1999/0902 | c->ref++; | |
| 1999/0910 | if(TYPE(ch->qid) == Qdata) { | |
| 1999/1211 | c->dataopen++; // kill reader if Qdata is opened for the first time if(c->dataopen == 1) | |
| 1999/0910 | if(c->readproc != nil) postnote(c->readproc, 1, "interrupt", 0); | |
| 1999/1211 | } else if(TYPE(ch->qid) == Qcontrol) { c->controlopen++; | |
| 1999/0910 | } | |
| 1999/0902 | qunlock(c); poperror(); break; | |
| 1999/0824 | } | |
| 1999/0901 | ch->mode = openmode(omode); ch->flag |= COPEN; ch->offset = 0; return ch; | |
| 1999/0824 | } static void | |
| 1999/0902 | sdpclose(Chan* ch) | |
| 1999/0824 | { | |
| 1999/0902 | Sdp *sdp = sdptab + ch->dev; | |
| 1999/0907 | Conv *c; | |
| 1999/0824 | ||
| 1999/1211 | if(!(ch->flag & COPEN)) return; | |
| 1999/0902 | switch(TYPE(ch->qid)) { | |
| 1999/0824 | case Qlog: | |
| 1999/1211 | logclose(sdp); | |
| 1999/0824 | break; | |
| 1999/0907 | case Qctl: case Qstatus: | |
| 1999/1211 | case Qstats: case Qrstats: | |
| 1999/0907 | c = sdp->conv[CONV(ch->qid)]; qlock(c); | |
| 1999/1211 | 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(); | |
| 1999/0907 | } | |
| 1999/1211 | qunlock(c); break; case Qcontrol: c = sdp->conv[CONV(ch->qid)]; qlock(c); c->controlopen--; convderef(c); if(c->controlopen == 0 && c->ref != 0) { | |
| 1999/0907 | switch(c->state) { default: convsetstate(c, CClosed); break; | |
| 1999/0909 | case CAccept: | |
| 1999/0907 | case COpen: | |
| 1999/0910 | convsetstate(c, CLocalClose); | |
| 1999/0907 | break; } } qunlock(c); poperror(); break; | |
| 1999/0824 | } } static long | |
| 1999/0901 | sdpread(Chan *ch, void *a, long n, vlong off) | |
| 1999/0824 | { char buf[256]; | |
| 1999/0929 | char *s; | |
| 1999/0901 | Sdp *sdp = sdptab + ch->dev; Conv *c; | |
| 1999/0907 | Block *b; | |
| 1999/0929 | int rv; | |
| 1999/0824 | USED(off); | |
| 1999/0901 | switch(TYPE(ch->qid)) { | |
| 1999/0824 | default: error(Eperm); case Qtopdir: case Qsdpdir: | |
| 1999/0901 | case Qconvdir: return devdirread(ch, a, n, 0, 0, sdpgen); | |
| 1999/0824 | case Qlog: return logread(sdp, a, off, n); case Qstatus: | |
| 1999/0901 | c = sdp->conv[CONV(ch->qid)]; | |
| 1999/0902 | qlock(c); | |
| 1999/0909 | n = readstr(off, a, n, convstatename[c->state]); | |
| 1999/0902 | qunlock(c); | |
| 1999/0824 | return n; | |
| 1999/0901 | case Qctl: sprint(buf, "%lud", CONV(ch->qid)); return readstr(off, a, n, buf); | |
| 1999/0907 | case Qcontrol: b = readcontrol(sdp->conv[CONV(ch->qid)], n); if(b == nil) return 0; | |
| 1999/0914 | print("readcontrol asked %ld got %ld\n", n, BLEN(b)); | |
| 1999/0907 | 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; | |
| 1999/0929 | 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; | |
| 1999/0824 | } } | |
| 1999/0907 | 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); } | |
| 1999/0914 | ||
| 1999/0824 | static long | |
| 1999/0901 | sdpwrite(Chan *ch, void *a, long n, vlong off) | |
| 1999/0824 | { | |
| 1999/0901 | Sdp *sdp = sdptab + ch->dev; | |
| 1999/0824 | Cmdbuf *cb; char *arg0; char *p; | |
| 1999/0906 | Conv *c; | |
| 1999/0915 | Block *b; | |
| 1999/0824 | USED(off); | |
| 1999/0901 | switch(TYPE(ch->qid)) { | |
| 1999/0824 | default: error(Eperm); case Qctl: | |
| 1999/0906 | c = sdp->conv[CONV(ch->qid)]; | |
| 1999/0824 | cb = parsecmd(a, n); | |
| 1999/0906 | qlock(c); | |
| 1999/0824 | if(waserror()) { | |
| 1999/0906 | qunlock(c); | |
| 1999/0824 | free(cb); nexterror(); } if(cb->nf == 0) error("short write"); arg0 = cb->f[0]; | |
| 1999/0910 | if(strcmp(arg0, "accept") == 0) { | |
| 1999/0906 | if(cb->nf != 2) | |
| 1999/0910 | error("usage: accect file"); | |
| 1999/0914 | convopenchan(c, cb->f[1]); | |
| 1999/0906 | } else if(strcmp(arg0, "dial") == 0) { | |
| 1999/0910 | if(cb->nf != 2) error("usage: accect file"); | |
| 1999/0914 | convopenchan(c, cb->f[1]); | |
| 1999/0909 | convsetstate(c, CDial); | |
| 1999/0906 | } else if(strcmp(arg0, "drop") == 0) { if(cb->nf != 2) error("usage: drop permil"); c->drop = atoi(cb->f[1]); | |
| 1999/1015 | } else if(strcmp(arg0, "cipher") == 0) { if(cb->nf != 2) error("usage: cipher alg"); | |
| 1999/1022 | setalg(c, cb->f[1], cipheralg, &c->cipher); | |
| 1999/1015 | } else if(strcmp(arg0, "auth") == 0) { if(cb->nf != 2) error("usage: auth alg"); | |
| 1999/1022 | setalg(c, cb->f[1], authalg, &c->auth); | |
| 1999/1015 | } else if(strcmp(arg0, "comp") == 0) { if(cb->nf != 2) error("usage: comp alg"); | |
| 1999/1022 | setalg(c, cb->f[1], compalg, &c->comp); | |
| 1999/1015 | } else if(strcmp(arg0, "insecret") == 0) { if(cb->nf != 2) error("usage: insecret secret"); setsecret(&c->in, cb->f[1]); | |
| 1999/1022 | if(c->cipher) c->cipher->init(c); if(c->auth) c->auth->init(c); | |
| 1999/1015 | } else if(strcmp(arg0, "outsecret") == 0) { if(cb->nf != 2) error("usage: outsecret secret"); setsecret(&c->out, cb->f[1]); | |
| 1999/1022 | if(c->cipher) c->cipher->init(c); if(c->auth) c->auth->init(c); | |
| 1999/0824 | } else error("unknown control request"); poperror(); | |
| 1999/0906 | qunlock(c); | |
| 1999/0824 | 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; | |
| 1999/0914 | case Qcontrol: | |
| 1999/0929 | writecontrol(sdp->conv[CONV(ch->qid)], a, n, 0); | |
| 1999/0914 | return n; | |
| 1999/0915 | case Qdata: b = allocb(n); memmove(b->wp, a, n); b->wp += n; return writedata(sdp->conv[CONV(ch->qid)], b); | |
| 1999/0824 | } } | |
| 1999/0915 | 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); } | |
| 1999/0824 | static int sdpgen(Chan *c, Dirtab*, int, int s, Dir *dp) { Sdp *sdp = sdptab + c->dev; int type = TYPE(c->qid); char buf[32]; Dirtab *dt; Qid qid; switch(type) { default: // non directory entries end up here if(c->qid.path & CHDIR) panic("sdpgen: unexpected directory"); if(s != 0) return -1; dt = dirtab[TYPE(c->qid)]; if(dt == nil) panic("sdpgen: unknown type: %d", TYPE(c->qid)); devdir(c, c->qid, dt->name, dt->length, eve, dt->perm, dp); return 1; case Qtopdir: if(s != 0) return -1; devdir(c, (Qid){QID(0,Qsdpdir)|CHDIR,0}, "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); | |
| 1999/0901 | if(s >= sdp->nconv) | |
| 1999/0824 | return -1; | |
| 1999/0901 | qid = (Qid){QID(s,Qconvdir)|CHDIR, 0}; | |
| 1999/0824 | snprint(buf, sizeof(buf), "%d", s); devdir(c, qid, buf, 0, eve, 0555, dp); return 1; | |
| 1999/0901 | case Qconvdir: if(s>=nelem(convdirtab)) | |
| 1999/0824 | return -1; | |
| 1999/0901 | dt = convdirtab+s; qid = (Qid){QID(CONV(c->qid),TYPE(dt->qid)),0}; | |
| 1999/0824 | devdir(c, qid, dt->name, dt->length, eve, dt->perm, dp); return 1; } | |
| 1999/0901 | } static Conv* sdpclone(Sdp *sdp) { Conv *c, **pp, **ep; c = nil; ep = sdp->conv + nelem(sdp->conv); | |
| 1999/0902 | qlock(sdp); if(waserror()) { qunlock(sdp); nexterror(); } | |
| 1999/0901 | for(pp = sdp->conv; pp < ep; pp++) { c = *pp; if(c == nil){ c = malloc(sizeof(Conv)); if(c == nil) error(Enomem); | |
| 1999/1001 | memset(c, 0, sizeof(Conv)); | |
| 1999/0901 | qlock(c); c->sdp = sdp; c->id = pp - sdp->conv; *pp = c; sdp->nconv++; break; } | |
| 1999/1211 | if(c->ref == 0 && canqlock(c)){ print("%d: state=%d reader=%d ref=%d\n", c->id, c->state, c->reader, c->ref); if(c->ref == 0) | |
| 1999/0901 | break; qunlock(c); } } | |
| 1999/0902 | poperror(); qunlock(sdp); | |
| 1999/0901 | ||
| 1999/0902 | if(pp >= ep) | |
| 1999/0901 | return nil; | |
| 1999/1211 | assert(c->state == CFree); // set ref to 2 - 1 ref for open - 1 ref for channel state c->ref = 2; | |
| 1999/0906 | c->state = CInit; | |
| 1999/1001 | c->in.window = ~0; | |
| 1999/0907 | strncpy(c->owner, up->user, sizeof(c->owner)); | |
| 1999/0901 | c->perm = 0660; qunlock(c); | |
| 1999/0902 | ||
| 1999/0901 | return c; | |
| 1999/0824 | } | |
| 1999/0906 | // assume c is locked | |
| 1999/0909 | static void convretryinit(Conv *c) { c->retries = 0; // +2 to avoid rounding effects. c->timeout = TK2SEC(m->ticks) + 2; | |
| 1999/1015 | } | |
| 1999/0909 | // assume c is locked | |
| 1999/0906 | static int | |
| 1999/0914 | convretry(Conv *c, int reset) | |
| 1999/0906 | { c->retries++; | |
| 1999/0914 | print("convretry: %s: %d\n", convstatename[c->state], c->retries); | |
| 1999/0909 | if(c->retries > MaxRetries) { | |
| 1999/0906 | print("convretry: giving up\n"); | |
| 1999/0914 | if(reset) | |
| 1999/1015 | convoconnect(c, ConReset, c->dialid, c->acceptid); | |
| 1999/0906 | convsetstate(c, CClosed); return 0; } | |
| 1999/0909 | c->timeout = TK2SEC(m->ticks) + (c->retries+1); | |
| 1999/0906 | return 1; } | |
| 1999/1211 | // assumes c is locked | |
| 1999/0902 | static void | |
| 1999/0906 | convtimer(Conv *c, ulong sec) | |
| 1999/0902 | { | |
| 1999/0914 | Block *b; | |
| 1999/1001 | if(c->timeout > sec) | |
| 1999/0906 | return; | |
| 1999/1211 | ||
| 1999/0906 | switch(c->state) { | |
| 1999/1211 | case CInit: break; | |
| 1999/0909 | case CDial: | |
| 1999/0914 | if(convretry(c, 1)) | |
| 1999/1015 | convoconnect(c, ConOpenRequest, c->dialid, 0); | |
| 1999/0906 | break; | |
| 1999/0909 | case CAccept: | |
| 1999/0914 | if(convretry(c, 1)) | |
| 1999/1015 | convoconnect(c, ConOpenAck, c->dialid, c->acceptid); | |
| 1999/0909 | break; | |
| 1999/0906 | case COpen: | |
| 1999/0914 | b = c->out.controlpkt; if(b != nil) { if(convretry(c, 1)) | |
| 1999/1015 | convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b))); | |
| 1999/1001 | break; | |
| 1999/0914 | } | |
| 1999/1001 | 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; print("sending keep alive: %ld\n", sec - c->lastrecv); // 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()) { | |
| 1999/1015 | convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b))); | |
| 1999/1001 | poperror(); } | |
| 1999/0906 | break; | |
| 1999/0910 | case CLocalClose: | |
| 1999/0914 | if(convretry(c, 0)) | |
| 1999/1015 | convoconnect(c, ConClose, c->dialid, c->acceptid); | |
| 1999/0906 | break; | |
| 1999/0914 | case CRemoteClose: case CClosed: break; | |
| 1999/0906 | } } | |
| 1999/0902 | ||
| 1999/0906 | static void sdpackproc(void *a) { Sdp *sdp = a; ulong sec; int i; Conv *c; for(;;) { tsleep(&sdp->vous, return0, 0, 1000); sec = TK2SEC(m->ticks); qlock(sdp); for(i=0; i<sdp->nconv; i++) { c = sdp->conv[i]; | |
| 1999/1211 | if(c->ref == 0) continue; qunlock(sdp); qlock(c); if(c->ref > 0 && !waserror()) { | |
| 1999/0906 | convtimer(c, sec); poperror(); } | |
| 1999/1211 | qunlock(c); qlock(sdp); | |
| 1999/0906 | } qunlock(sdp); | |
| 1999/0902 | } } | |
| 1999/0824 | Dev sdpdevtab = { | |
| 1999/1027 | 'E', | |
| 1999/0824 | "sdp", devreset, sdpinit, sdpattach, devclone, sdpwalk, sdpstat, sdpopen, devcreate, sdpclose, sdpread, devbread, sdpwrite, devbwrite, devremove, devwstat, }; | |
| 1999/0906 | // assume hold lock on c static void convsetstate(Conv *c, int state) { | |
| 1999/0907 | ||
| 1999/0908 | print("convsetstate %s -> %s\n", convstatename[c->state], convstatename[state]); | |
| 1999/0907 | ||
| 1999/0906 | switch(state) { default: panic("setstate: bad state: %d", state); | |
| 1999/0909 | case CDial: assert(c->state == CInit); | |
| 1999/0906 | c->dialid = (rand()<<16) + rand(); | |
| 1999/0909 | convretryinit(c); | |
| 1999/1015 | convoconnect(c, ConOpenRequest, c->dialid, 0); | |
| 1999/0906 | break; | |
| 1999/0909 | case CAccept: assert(c->state == CInit); c->acceptid = (rand()<<16) + rand(); convretryinit(c); | |
| 1999/1015 | convoconnect(c, ConOpenAck, c->dialid, c->acceptid); | |
| 1999/0909 | break; | |
| 1999/0906 | case COpen: | |
| 1999/0909 | assert(c->state == CDial || c->state == CAccept); | |
| 1999/1211 | c->lastrecv = TK2SEC(m->ticks); | |
| 1999/0909 | if(c->state == CDial) { convretryinit(c); | |
| 1999/1015 | 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); | |
| 1999/0906 | } | |
| 1999/1022 | setalg(c, "hmac_md5_96", authalg, &c->auth); | |
| 1999/0906 | break; | |
| 1999/0910 | case CLocalClose: assert(c->state == CAccept || c->state == COpen); | |
| 1999/0909 | convretryinit(c); | |
| 1999/1015 | convoconnect(c, ConClose, c->dialid, c->acceptid); | |
| 1999/0906 | break; | |
| 1999/0910 | case CRemoteClose: | |
| 1999/0914 | wakeup(&c->in.controlready); | |
| 1999/1211 | wakeup(&c->out.controlready); | |
| 1999/0910 | break; | |
| 1999/0906 | case CClosed: | |
| 1999/0914 | wakeup(&c->in.controlready); | |
| 1999/0915 | wakeup(&c->out.controlready); | |
| 1999/0907 | if(c->readproc) postnote(c->readproc, 1, "interrupt", 0); | |
| 1999/1211 | if(c->state != CClosed) convderef(c); | |
| 1999/0906 | break; } c->state = state; } | |
| 1999/1211 | //assumes c is locked | |
| 1999/0906 | static void | |
| 1999/1211 | convderef(Conv *c) { c->ref--; print("convderef: %d: ref == %d\n", c->id, c->ref); if(c->ref > 0) return; assert(c->ref == 0); assert(c->dataopen == 0); assert(c->controlopen == 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 | |
| 1999/0907 | onewaycleanup(OneWay *ow) | |
| 1999/0906 | { | |
| 1999/0907 | 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)); } | |
| 1999/0906 | ||
| 1999/0907 | ||
| 1999/0914 | // assumes conv is locked static void convopenchan(Conv *c, char *path) { | |
| 1999/0915 | if(c->state != CInit || c->chan != nil) | |
| 1999/0914 | error("already connected"); c->chan = namec(path, Aopen, ORDWR, 0); c->channame = malloc(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); | |
| 1999/1211 | assert(c->reader == 0 && c->ref > 0); // after kproc in case it fails | |
| 1999/0914 | c->reader = 1; | |
| 1999/1211 | c->ref++; | |
| 1999/0914 | poperror(); } | |
| 1999/0907 | ||
| 1999/0929 | static void convstats(Conv *c, int local, char *buf, int n) { Stats *stats; char *p, *ep; | |
| 1999/1019 | int i; | |
| 1999/0907 | ||
| 1999/0929 | if(local) { stats = &c->lstats; } else { if(!waserror()) { writecontrol(c, 0, 0, 1); poperror(); } stats = &c->rstats; } | |
| 1999/0930 | qlock(c); | |
| 1999/0929 | p = buf; ep = buf + n; | |
| 1999/1019 | 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); | |
| 1999/1021 | for(i=0; i<NCompStats; i++) { if(stats->outCompStats[i] == 0) | |
| 1999/1019 | 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); | |
| 1999/1022 | p += snprint(p, ep-p, "inBadOther: %lud\n", stats->inBadOther); | |
| 1999/0929 | USED(p); qunlock(c); } // c is locked static void convack(Conv *c) { Block *b; AckPkt *ack; Stats *s; | |
| 1999/1019 | int i; | |
| 1999/0929 | b = allocb(sizeof(AckPkt)); | |
| 1999/0930 | ack = (AckPkt*)b->wp; | |
| 1999/0929 | 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); | |
| 1999/1021 | for(i=0; i<NCompStats; i++) | |
| 1999/1019 | hnputl(ack->outCompStats+i*4, s->outCompStats[i]); | |
| 1999/0929 | 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); | |
| 1999/1016 | hnputl(ack->inBadComp, s->inBadComp); | |
| 1999/0929 | hnputl(ack->inBadAuth, s->inBadAuth); hnputl(ack->inBadSeq, s->inBadSeq); | |
| 1999/1022 | hnputl(ack->inBadOther, s->inBadOther); | |
| 1999/1015 | convoput(c, TControl, ControlAck, b); | |
| 1999/0929 | } | |
| 1999/0907 | // assume we hold lock for c static Block * conviput(Conv *c, Block *b, int control) { | |
| 1999/1015 | int type, subtype; ulong seq, seqwrap; | |
| 1999/1001 | long seqdiff; | |
| 1999/1022 | int pad; | |
| 1999/0907 | ||
| 1999/0930 | c->lstats.inPackets++; | |
| 1999/0929 | ||
| 1999/0907 | if(BLEN(b) < 4) { | |
| 1999/1022 | c->lstats.inBadOther++; | |
| 1999/0907 | freeb(b); return nil; } | |
| 1999/1015 | type = b->rp[0] >> 4; | |
| 1999/1016 | subtype = b->rp[0] & 0xf; | |
| 1999/1015 | b->rp += 1; | |
| 1999/0907 | if(type == TConnect) { | |
| 1999/1015 | conviconnect(c, subtype, b); | |
| 1999/0907 | return nil; } | |
| 1999/1015 | seq = (b->rp[0]<<16) + (b->rp[1]<<8) + b->rp[2]; b->rp += 3; | |
| 1999/0907 | ||
| 1999/1001 | 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) { 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)) { 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 | |
| 1999/0930 | if(0) print("coniput seq=%ulx\n", seq); | |
| 1999/1015 | if(c->in.auth != 0) { if(!(*c->in.auth)(&c->in, b->rp-4, BLEN(b)+4)) { | |
| 1999/1106 | print("bad auth %ld\n", BLEN(b)+4); | |
| 1999/1015 | c->lstats.inBadAuth++; freeb(b); return nil; } b->wp -= c->in.authlen; } | |
| 1999/1022 | if(c->in.cipher != 0) { if(!(*c->in.cipher)(&c->in, b->rp, BLEN(b))) { 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)) { print("pad too big\n"); c->lstats.inBadOther++; freeb(b); return nil; } b->wp -= pad; } } | |
| 1999/0907 | // ok the packet is good | |
| 1999/1001 | if(seqdiff > 0) { while(seqdiff > 0 && c->in.window != 0) { if((c->in.window & (1<<(SeqWindow-1))) == 0) { | |
| 1999/1211 | //print("missing packet: %ld\n", seq - seqdiff); | |
| 1999/1001 | c->lstats.inMissing++; } c->in.window <<= 1; seqdiff--; } if(seqdiff > 0) { | |
| 1999/1211 | //print("missing packets: %ld-%ld\n", seq - SeqWindow - seqdiff+1, seq-SeqWindow); | |
| 1999/1001 | c->lstats.inMissing += seqdiff; } c->in.seq = seq; c->in.seqwrap = seqwrap; c->in.window |= 1; } c->lastrecv = TK2SEC(m->ticks); | |
| 1999/0907 | switch(type) { case TControl: | |
| 1999/1015 | convicontrol(c, subtype, b); | |
| 1999/0907 | return nil; case TData: | |
| 1999/0929 | c->lstats.inDataPackets++; c->lstats.inDataBytes += BLEN(b); | |
| 1999/0907 | if(control) break; return b; | |
| 1999/1015 | case TCompData: | |
| 1999/1001 | c->lstats.inDataPackets++; c->lstats.inCompDataBytes += BLEN(b); | |
| 1999/1016 | b = convicomp(c, subtype, seq, b); if(b == nil) { c->lstats.inBadComp++; | |
| 1999/1015 | return nil; | |
| 1999/1016 | } | |
| 1999/1001 | c->lstats.inDataBytes += BLEN(b); if(control) break; return b; | |
| 1999/0907 | } print("droping packet %d n=%ld\n", type, BLEN(b)); | |
| 1999/1022 | c->lstats.inBadOther++; | |
| 1999/0907 | freeb(b); return nil; } // assume hold conv lock static void | |
| 1999/1015 | conviconnect(Conv *c, int subtype, Block *b) | |
| 1999/0907 | { ulong dialid; ulong acceptid; | |
| 1999/1015 | if(BLEN(b) != 8) { | |
| 1999/0907 | freeb(b); return; } | |
| 1999/1015 | dialid = nhgetl(b->rp); acceptid = nhgetl(b->rp + 4); freeb(b); | |
| 1999/0907 | ||
| 1999/1015 | print("conviconnect: %s: %d %uld %uld\n", convstatename[c->state], subtype, dialid, acceptid); | |
| 1999/0914 | ||
| 1999/0910 | 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: | |
| 1999/1015 | if(dialid != c->dialid || subtype != ConOpenRequest && acceptid != c->acceptid) | |
| 1999/0910 | goto Reset; break; case CClosed: goto Reset; } | |
| 1999/1015 | switch(subtype) { | |
| 1999/0909 | case ConOpenRequest: switch(c->state) { case CInit: c->dialid = dialid; convsetstate(c, CAccept); | |
| 1999/0910 | return; | |
| 1999/0909 | case CAccept: case COpen: | |
| 1999/0910 | // duplicate ConOpenRequest that we ignore return; | |
| 1999/0909 | } | |
| 1999/0907 | break; case ConOpenAck: | |
| 1999/0909 | switch(c->state) { case CDial: c->acceptid = acceptid; convsetstate(c, COpen); | |
| 1999/0910 | return; case COpen: // duplicate that we have to ack | |
| 1999/1015 | convoconnect(c, ConOpenAckAck, acceptid, dialid); | |
| 1999/0910 | return; } break; case ConOpenAckAck: switch(c->state) { | |
| 1999/0909 | case CAccept: convsetstate(c, COpen); | |
| 1999/0910 | return; case COpen: // duplicate that we ignore return; | |
| 1999/0909 | } | |
| 1999/0907 | break; case ConClose: | |
| 1999/1015 | convoconnect(c, ConReset, dialid, acceptid); | |
| 1999/0909 | switch(c->state) { | |
| 1999/0910 | case CInit: | |
| 1999/0909 | case CDial: case CAccept: | |
| 1999/0910 | case CLocalClose: convsetstate(c, CClosed); return; | |
| 1999/0909 | case COpen: | |
| 1999/0910 | convsetstate(c, CRemoteClose); return; case CRemoteClose: return; | |
| 1999/0909 | } | |
| 1999/1015 | break; | |
| 1999/0910 | case ConReset: switch(c->state) { case CInit: case CDial: case CAccept: case COpen: case CLocalClose: | |
| 1999/0909 | convsetstate(c, CClosed); | |
| 1999/0910 | return; case CRemoteClose: return; } | |
| 1999/1015 | break; | |
| 1999/0907 | } | |
| 1999/0910 | Reset: // invalid connection message - reset to sender | |
| 1999/1015 | print("invalid conviconnect - sending reset\n"); convoconnect(c, ConReset, dialid, acceptid); | |
| 1999/0907 | } | |
| 1999/1015 | static void convicontrol(Conv *c, int subtype, Block *b) { ulong cseq; AckPkt *ack; | |
| 1999/1022 | int i; | |
| 1999/1015 | if(BLEN(b) < 4) return; cseq = nhgetl(b->rp); switch(subtype){ case ControlMesg: if(cseq == c->in.controlseq) { 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) { print("ControlAck expected %ulx got %ulx\n", c->out.controlseq, cseq); 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); | |
| 1999/1022 | for(i=0; i<NCompStats; i++) c->rstats.outCompStats[i] = nhgetl(ack->outCompStats + 4*i); | |
| 1999/1015 | 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); | |
| 1999/1016 | c->rstats.inBadComp = nhgetl(ack->inBadComp); | |
| 1999/1015 | c->rstats.inBadAuth = nhgetl(ack->inBadAuth); c->rstats.inBadSeq = nhgetl(ack->inBadSeq); | |
| 1999/1022 | c->rstats.inBadOther = nhgetl(ack->inBadOther); | |
| 1999/1015 | freeb(b); freeb(c->out.controlpkt); c->out.controlpkt = nil; c->timeout = c->lastrecv + KeepAlive; wakeup(&c->out.controlready); return; } } static Block* | |
| 1999/1016 | convicomp(Conv *c, int subtype, ulong seq, Block *b) | |
| 1999/1015 | { if(c->in.comp == nil) { freeb(b); return nil; } | |
| 1999/1016 | if(!(*c->in.comp)(c, subtype, seq, &b)) | |
| 1999/1015 | return nil; return b; } | |
| 1999/0930 | // c is locked static void convwriteblock(Conv *c, Block *b) { // simulated errors | |
| 1999/1001 | if(c->drop && nrand(c->drop) == 0) | |
| 1999/0930 | return; if(waserror()) { convsetstate(c, CClosed); nexterror(); } devtab[c->chan->type]->bwrite(c->chan, b, 0); poperror(); } | |
| 1999/0907 | // assume hold conv lock static void | |
| 1999/1015 | convoput(Conv *c, int type, int subtype, Block *b) | |
| 1999/0907 | { | |
| 1999/1022 | int pad; | |
| 1999/0929 | c->lstats.outPackets++; | |
| 1999/1022 | /* 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; } | |
| 1999/0907 | /* Make space to fit sdp header */ b = padblock(b, 4 + c->out.cipherivlen); | |
| 1999/1015 | b->rp[0] = (type << 4) | subtype; | |
| 1999/0907 | 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; | |
| 1999/1022 | if(c->out.cipher) (*c->out.cipher)(&c->out, b->rp+4, BLEN(b)-4); | |
| 1999/0907 | // auth | |
| 1999/1015 | if(c->out.auth) { b->wp += c->out.authlen; (*c->out.auth)(&c->out, b->rp, BLEN(b)); } | |
| 1999/0930 | convwriteblock(c, b); | |
| 1999/0907 | } // assume hold conv lock static void | |
| 1999/1015 | convoconnect(Conv *c, int op, ulong dialid, ulong acceptid) | |
| 1999/0907 | { | |
| 1999/0930 | Block *b; | |
| 1999/0907 | ||
| 1999/0929 | c->lstats.outPackets++; | |
| 1999/1211 | assert(c->chan != nil); | |
| 1999/1015 | b = allocb(9); b->wp[0] = (TConnect << 4) | op; hnputl(b->wp+1, dialid); hnputl(b->wp+5, acceptid); b->wp += 9; | |
| 1999/0906 | ||
| 1999/1211 | if(!waserror()) { convwriteblock(c, b); poperror(); } | |
| 1999/0907 | } | |
| 1999/0910 | static Block * convreadblock(Conv *c, int n) { Block *b; | |
| 1999/1211 | Chan *ch; | |
| 1999/0910 | 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; | |
| 1999/1211 | assert(c->ref > 0); | |
| 1999/0910 | qunlock(c); b = devtab[ch->type]->bread(ch, n, 0); c->readproc = nil; poperror(); qunlock(&c->readlk); return b; } | |
| 1999/0907 | static int readready(void *a) { Conv *c = a; | |
| 1999/0914 | return c->in.controlpkt != nil || (c->state == CClosed) || (c->state == CRemoteClose); | |
| 1999/0907 | } static Block * readcontrol(Conv *c, int n) { Block *b; | |
| 1999/0910 | USED(n); | |
| 1999/0915 | qlock(&c->in.controllk); if(waserror()) { qunlock(&c->in.controllk); nexterror(); } qlock(c); // this lock is not held during the sleep below | |
| 1999/0907 | for(;;) { | |
| 1999/0910 | if(c->state == CInit || c->state == CClosed) { | |
| 1999/0907 | qunlock(c); | |
| 1999/0914 | print("readcontrol: return error - state = %s\n", convstatename[c->state]); error("conversation closed"); | |
| 1999/0907 | } | |
| 1999/0914 | if(c->in.controlpkt != nil) break; if(c->state == CRemoteClose) { | |
| 1999/0907 | qunlock(c); | |
| 1999/0914 | print("readcontrol: return nil - state = %s\n", convstatename[c->state]); | |
| 1999/0915 | poperror(); | |
| 1999/0914 | return nil; | |
| 1999/0907 | } qunlock(c); | |
| 1999/0910 | sleep(&c->in.controlready, readready, c); | |
| 1999/0914 | qlock(c); | |
| 1999/0907 | } | |
| 1999/0914 | ||
| 1999/0929 | convack(c); | |
| 1999/0914 | b = c->in.controlpkt; c->in.controlpkt = nil; qunlock(c); | |
| 1999/0915 | poperror(); qunlock(&c->in.controllk); | |
| 1999/0914 | return b; | |
| 1999/0907 | } | |
| 1999/0914 | static int writeready(void *a) { Conv *c = a; return c->out.controlpkt == nil || (c->state == CClosed) || (c->state == CRemoteClose); } | |
| 1999/0929 | // c is locked | |
| 1999/0914 | static void | |
| 1999/0929 | writewait(Conv *c) | |
| 1999/0914 | { for(;;) { if(c->state == CInit || c->state == CClosed || c->state == CRemoteClose) { print("writecontrol: return error - state = %s\n", convstatename[c->state]); error("conversation closed"); } if(c->state == COpen && c->out.controlpkt == nil) break; qunlock(c); | |
| 1999/0929 | if(waserror()) { qlock(c); nexterror(); } | |
| 1999/0914 | sleep(&c->out.controlready, writeready, c); | |
| 1999/0929 | poperror(); | |
| 1999/0914 | qlock(c); } | |
| 1999/0929 | } 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); | |
| 1999/0914 | 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); | |
| 1999/1015 | convoput(c, TControl, ControlMesg, copyblock(b, blocklen(b))); | |
| 1999/1001 | if(wait) | |
| 1999/0929 | writewait(c); | |
| 1999/0915 | poperror(); | |
| 1999/0929 | qunlock(c); | |
| 1999/0915 | qunlock(&c->out.controllk); | |
| 1999/0914 | } | |
| 1999/0907 | static Block * readdata(Conv *c, int n) { Block *b; | |
| 1999/1106 | int nn; | |
| 1999/0907 | for(;;) { | |
| 1999/1106 | // 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); | |
| 1999/0930 | if(b == nil) return nil; | |
| 1999/0907 | qlock(c); if(waserror()) { qunlock(c); return nil; } b = conviput(c, b, 0); poperror(); qunlock(c); | |
| 1999/1106 | if(b != nil) { if(BLEN(b) > n) b->wp = b->rp + n; | |
| 1999/0907 | return b; | |
| 1999/1106 | } | |
| 1999/0907 | } | |
| 1999/0910 | } | |
| 1999/0915 | static long writedata(Conv *c, Block *b) { | |
| 1999/1015 | int n; | |
| 1999/1016 | ulong seq; int subtype; | |
| 1999/0915 | qlock(c); if(waserror()) { qunlock(c); nexterror(); } if(c->state != COpen) { freeb(b); error("conversation not open"); } n = BLEN(b); | |
| 1999/0930 | c->lstats.outDataPackets++; c->lstats.outDataBytes += n; | |
| 1999/1001 | ||
| 1999/1111 | if(c->out.comp != nil) { | |
| 1999/1016 | // must generate same value as convoput seq = (c->out.seq + 1) & (SeqMax-1); subtype = (*c->out.comp)(c, 0, seq, &b); | |
| 1999/1001 | c->lstats.outCompDataBytes += BLEN(b); | |
| 1999/1015 | convoput(c, TCompData, subtype, b); } else convoput(c, TData, 0, b); | |
| 1999/1001 | ||
| 1999/0915 | poperror(); qunlock(c); return n; } | |
| 1999/0910 | static void convreader(void *a) { Conv *c = a; Block *b; | |
| 1999/0914 | print("convreader\n"); | |
| 1999/0910 | qlock(c); assert(c->reader == 1); | |
| 1999/0915 | while(c->dataopen == 0 && c->state != CClosed) { | |
| 1999/0910 | qunlock(c); b = nil; if(!waserror()) { b = convreadblock(c, 2000); poperror(); } qlock(c); if(b == nil) { | |
| 1999/0915 | print("up->error = %s\n", up->error); if(strcmp(up->error, Eintr) != 0) { | |
| 1999/1211 | convsetstate(c, CClosed); | |
| 1999/0915 | break; } } else if(!waserror()) { | |
| 1999/0910 | conviput(c, b, 1); poperror(); } } | |
| 1999/0914 | print("convreader exiting\n"); | |
| 1999/0910 | c->reader = 0; | |
| 1999/1211 | convderef(c); | |
| 1999/0910 | qunlock(c); pexit("hangup", 1); | |
| 1999/0906 | } | |
| 1999/1015 | /* ciphers, authenticators, and compressors */ static void | |
| 1999/1022 | setalg(Conv *c, char *name, Algorithm *alg, Algorithm **p) | |
| 1999/1015 | { for(; alg->name; alg++) if(strcmp(name, alg->name) == 0) break; if(alg->name == nil) error("unknown algorithm"); | |
| 1999/1022 | *p = alg; alg->init(c); | |
| 1999/1015 | } 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[SHAdlen], obuf[MD5dlen], salt[10]; int i, round = 0; while(n > 0){ for(i=0; i<round+1; i++) salt[i] = 'A'+round; sha((uchar*)prefix, strlen(prefix), ibuf, sha(salt, round+1, nil, nil)); md5(ibuf, SHAdlen, 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; | |
| 1999/1022 | c->in.cipherblklen = 0; c->out.cipherblklen = 0; c->in.cipherivlen = 0; c->out.cipherivlen = 0; | |
| 1999/1015 | } 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; | |
| 1999/1022 | c->in.authlen = 0; c->out.authlen = 0; | |
| 1999/1015 | } 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 | |
| 1999/1022 | nullcipherinit(Conv *c) | |
| 1999/1015 | { cipherfree(c); } static int desencrypt(OneWay *ow, uchar *p, int n) { uchar *pp, *ip, *eip, *ep; DESstate *ds = ow->cipherstate; | |
| 1999/1022 | if(n < 8 || (n & 0x7 != 0)) return 0; | |
| 1999/1015 | 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; | |
| 1999/1022 | if(n < 8 || (n & 0x7 != 0)) return 0; | |
| 1999/1015 | 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 | |
| 1999/1022 | descipherinit(Conv *c) | |
| 1999/1015 | { uchar key[8]; uchar ivec[8]; int i; | |
| 1999/1022 | int n = c->cipher->keylen; | |
| 1999/1015 | 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); } | |
| 1999/1022 | 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) { | |
| 1999/1211 | //print("missing packet: %uld %ld\n", seq, d); | |
| 1999/1022 | // 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 { | |
| 1999/1211 | //print("reordered packet: %uld %ld\n", seq, d); | |
| 1999/1022 | 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; } | |
| 1999/1015 | static void | |
| 1999/1022 | rc4cipherinit(Conv *c) | |
| 1999/1015 | { | |
| 1999/1022 | uchar key[32]; CipherRc4 *cr; int n; | |
| 1999/1016 | cipherfree(c); | |
| 1999/1022 | 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; | |
| 1999/1015 | } static void | |
| 1999/1022 | nullauthinit(Conv *c) | |
| 1999/1015 | { authfree(c); } static void | |
| 1999/1022 | shaauthinit(Conv *c) | |
| 1999/1015 | { authfree(c); } static void 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); 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 | |
| 1999/1022 | md5authinit(Conv *c) | |
| 1999/1015 | { | |
| 1999/1022 | int keylen; | |
| 1999/1015 | authfree(c); | |
| 1999/1022 | keylen = c->auth->keylen; | |
| 1999/1015 | 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 | |
| 1999/1022 | nullcompinit(Conv *c) | |
| 1999/1015 | { | |
| 1999/1016 | compfree(c); | |
| 1999/1015 | } | |
| 1999/1016 | static int thwackcomp(Conv *c, int, ulong seq, Block **bp) | |
| 1999/1015 | { | |
| 1999/1016 | Block *b, *bb; int nn; // add ack info b = padblock(*bp, 4); b->rp[0] = (c->in.window>>1) & 0xff; b->rp[1] = c->in.seq>>16; b->rp[2] = c->in.seq>>8; b->rp[3] = c->in.seq; bb = allocb(BLEN(b)); | |
| 1999/1022 | nn = thwack(c->out.compstate, bb->wp, b->rp, BLEN(b), seq, c->lstats.outCompStats); | |
| 1999/1016 | if(nn < 0) { freeb(bb); *bp = b; return ThwackU; } else { bb->wp += nn; freeb(b); *bp = bb; return ThwackC; } | |
| 1999/1015 | } | |
| 1999/1016 | static int thwackuncomp(Conv *c, int subtype, ulong seq, Block **bp) { Block *b, *bb; ulong mask; ulong mseq; int n; | |
| 1999/1015 | ||
| 1999/1016 | switch(subtype) { default: return 0; case ThwackU: b = *bp; | |
| 1999/1015 | 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); | |
| 1999/1016 | return 1; case ThwackC: bb = *bp; | |
| 1999/1015 | b = allocb(ThwMaxBlock); n = unthwack(c->in.compstate, b->wp, ThwMaxBlock, bb->rp, BLEN(bb), seq); freeb(bb); | |
| 1999/1016 | if(n < 0) { print("unthwack failed: %r!\n"); freeb(b); return 0; } | |
| 1999/1015 | 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; | |
| 1999/1016 | *bp = b; return 1; | |
| 1999/1015 | } | |
| 1999/1016 | } | |
| 1999/1015 | ||
| 1999/1016 | static void | |
| 1999/1022 | thwackcompinit(Conv *c) | |
| 1999/1016 | { compfree(c); | |
| 1999/1015 | ||
| 1999/1016 | c->in.compstate = malloc(sizeof(Unthwack)); unthwackinit(c->in.compstate); c->out.compstate = malloc(sizeof(Thwack)); thwackinit(c->out.compstate); c->in.comp = thwackuncomp; c->out.comp = thwackcomp; } | |