/* * p9any - protocol negotiator. * * Protocol: * Server->Client: list of proto@domain, tokenize separated, nul terminated * Client->Server: proto domain, tokenize separated (not proto@domain), nul terminated * * Server protocol: * read list of protocols. * write null-terminated */ #include "dat.h" static Proto *negotiable[] = { &p9sk1, }; struct State { Fsstate subfss; State *substate; /* be very careful; this is not one of our States */ Proto *subproto; int keyasked; String *subdom; int version; }; enum { CNeedProtos, CHaveProto, CNeedOK, CRelay, SHaveProtos, SNeedProto, SHaveOK, SRelay, Maxphase, }; static char *phasenames[Maxphase] = { [CNeedProtos] "CNeedProtos", [CHaveProto] "CHaveProto", [CNeedOK] "CNeedOK", [CRelay] "CRelay", [SHaveProtos] "SHaveProtos", [SNeedProto] "SNeedProto", [SHaveOK] "SHaveOK", [SRelay] "SRelay", }; static int p9anyinit(Proto*, Fsstate *fss) { int iscli; State *s; if((iscli = isclient(_strfindattr(fss->attr, "role"))) < 0) return failure(fss, nil); s = emalloc(sizeof *s); fss = fss; fss->phasename = phasenames; fss->maxphase = Maxphase; if(iscli) fss->phase = CNeedProtos; else fss->phase = SHaveProtos; s->version = 1; fss->ps = s; return RpcOk; } static void p9anyclose(Fsstate *fss) { State *s; s = fss->ps; if(s->subproto && s->subfss.ps && s->subproto->close) (*s->subproto->close)(&s->subfss); s->subproto = nil; s->substate = nil; s_free(s->subdom); s->subdom = nil; s->keyasked = 0; memset(&s->subfss, 0, sizeof s->subfss); free(s); } static void setupfss(Fsstate *fss, State *s, Key *k) { fss->attr = setattr(fss->attr, "proto=%q", s->subproto->name); fss->attr = setattr(fss->attr, "dom=%q", _strfindattr(k->attr, "dom")); s->subfss.attr = fss->attr; s->subfss.phase = Notstarted; s->subfss.sysuser = fss->sysuser; s->subfss.seqnum = fss->seqnum; s->subfss.conf = fss->conf; s->subfss.nconf = fss->nconf; } static int passret(Fsstate *fss, State *s, int ret) { switch(ret){ default: return ret; case RpcFailure: if(s->subfss.phase == Broken) fss->phase = Broken; memmove(fss->err, s->subfss.err, sizeof fss->err); return ret; case RpcNeedkey: memmove(fss->keyinfo, s->subfss.keyinfo, sizeof fss->keyinfo); return ret; case RpcOk: if(s->subfss.haveai){ fss->haveai = 1; fss->ai = s->subfss.ai; s->subfss.haveai = 0; } if(s->subfss.phase == Established) fss->phase = Established; return ret; case RpcToosmall: fss->rpc.nwant = s->subfss.rpc.nwant; return ret; case RpcConfirm: fss->conf = s->subfss.conf; fss->nconf = s->subfss.nconf; return ret; } } static int p9anyread(Fsstate *fss, void *a, uint *n) { int i, m, ophase, ret; Attr *anew; Key *k; Keyinfo ki; String *negstr; State *s; s = fss->ps; switch(fss->phase){ default: return phaseerror(fss, "read"); case SHaveProtos: m = 0; negstr = s_new(); mkkeyinfo(&ki, fss, nil); ki.attr = nil; ki.noconf = 1; ki.user = nil; for(i=0; iattr), "proto=%q dom?", negotiable[i]->name); ki.attr = anew; for(ki.skip=0; findkey(&k, &ki, nil)==RpcOk; ki.skip++){ if(m++) s_append(negstr, " "); s_append(negstr, negotiable[i]->name); s_append(negstr, "@"); s_append(negstr, _strfindattr(k->attr, "dom")); closekey(k); } _freeattr(anew); } if(m == 0){ s_free(negstr); return failure(fss, Enegotiation); } i = s_len(negstr)+1; if(*n < i){ s_free(negstr); return toosmall(fss, i); } *n = i; memmove(a, s_to_c(negstr), i+1); fss->phase = SNeedProto; s_free(negstr); return RpcOk; case CHaveProto: i = strlen(s->subproto->name)+1+s_len(s->subdom)+1; if(*n < i) return toosmall(fss, i); *n = i; strcpy(a, s->subproto->name); strcat(a, " "); strcat(a, s_to_c(s->subdom)); if(s->version == 1) fss->phase = CRelay; else fss->phase = CNeedOK; return RpcOk; case SHaveOK: i = 3; if(*n < i) return toosmall(fss, i); *n = i; strcpy(a, "OK"); fss->phase = SRelay; return RpcOk; case CRelay: case SRelay: ophase = s->subfss.phase; ret = (*s->subproto->read)(&s->subfss, a, n); rpcrdwrlog(&s->subfss, "read", *n, ophase, ret); return passret(fss, s, ret); } } static char* getdom(char *p) { p = strchr(p, '@'); if(p == nil) return ""; return p+1; } static Proto* findneg(char *name) { int i, len; char *p; if(p = strchr(name, '@')) len = p-name; else len = strlen(name); for(i=0; iname, name, len) == 0 && negotiable[i]->name[len] == 0) return negotiable[i]; return nil; } static int p9anywrite(Fsstate *fss, void *va, uint n) { char *a, *dom, *user, *token[20]; int asking, i, m, ophase, ret; Attr *anew, *anewsf, *attr; Key *k; Keyinfo ki; Proto *p; State *s; s = fss->ps; a = va; switch(fss->phase){ default: return phaseerror(fss, "write"); case CNeedProtos: if(n==0 || a[n-1] != '\0') return toosmall(fss, 2048); a = estrdup(a); m = tokenize(a, token, nelem(token)); if(m > 0 && strncmp(token[0], "v.", 2) == 0){ s->version = atoi(token[0]+2); if(s->version != 2){ free(a); return failure(fss, "unknown version of p9any"); } } /* * look for a key */ anew = _delattr(_delattr(_copyattr(fss->attr), "proto"), "role"); anewsf = _delattr(_copyattr(anew), "user"); user = _strfindattr(anew, "user"); k = nil; p = nil; dom = nil; for(i=(s->version==1?0:1); isysuser)==0){ ki.attr = anewsf; ki.user = nil; ret = findkey(&k, &ki, "proto=%q dom=%q role=speakfor %s", p->name, dom, p->keyprompt); } if(ret == RpcFailure){ ki.attr = anew; ki.user = fss->sysuser; ret = findkey(&k, &ki, "proto=%q dom=%q role=client %s", p->name, dom, p->keyprompt); } if(ret == RpcConfirm){ free(a); return ret; } if(ret == RpcOk) break; } _freeattr(anewsf); /* * no acceptable key, go through the proto@domains one at a time. */ asking = 0; if(k == nil){ while(!asking && s->keyasked < m){ p = findneg(token[s->keyasked]); if(p == nil){ s->keyasked++; continue; } dom = getdom(token[s->keyasked]); mkkeyinfo(&ki, fss, nil); ki.attr = anew; ret = findkey(&k, &ki, "proto=%q dom=%q role=client %s", p->name, dom, p->keyprompt); s->keyasked++; if(ret == RpcNeedkey){ asking = 1; break; } } } if(k == nil){ free(a); _freeattr(anew); if(asking) return RpcNeedkey; else if(s->keyasked) return failure(fss, nil); else return failure(fss, Enegotiation); } s->subdom = s_copy(dom); s->subproto = p; free(a); _freeattr(anew); setupfss(fss, s, k); closekey(k); ret = (*s->subproto->init)(p, &s->subfss); rpcstartlog(s->subfss.attr, &s->subfss, ret); if(ret == RpcOk) fss->phase = CHaveProto; return passret(fss, s, ret); case SNeedProto: if(n==0 || a[n-1] != '\0') return toosmall(fss, n+1); a = estrdup(a); m = tokenize(a, token, nelem(token)); if(m != 2){ free(a); return failure(fss, Ebadarg); } p = findneg(token[0]); if(p == nil){ free(a); return failure(fss, Enegotiation); } attr = _delattr(_copyattr(fss->attr), "proto"); mkkeyinfo(&ki, fss, nil); ki.attr = attr; ki.user = nil; ret = findkey(&k, &ki, "proto=%q dom=%q role=server", token[0], token[1]); free(a); _freeattr(attr); if(ret == RpcConfirm) return ret; if(ret != RpcOk) return failure(fss, Enegotiation); s->subproto = p; setupfss(fss, s, k); closekey(k); ret = (*s->subproto->init)(p, &s->subfss); if(ret == RpcOk){ if(s->version == 1) fss->phase = SRelay; else fss->phase = SHaveOK; } return passret(fss, s, ret); case CNeedOK: if(n < 3) return toosmall(fss, 3); if(strcmp("OK", a) != 0) return failure(fss, "server gave up"); fss->phase = CRelay; return RpcOk; case CRelay: case SRelay: ophase = s->subfss.phase; ret = (*s->subproto->write)(&s->subfss, va, n); rpcrdwrlog(&s->subfss, "write", n, ophase, ret); return passret(fss, s, ret); } } Proto p9any = { .name= "p9any", .init= p9anyinit, .write= p9anywrite, .read= p9anyread, .close= p9anyclose, };