/*
* 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;
};
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;
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)
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 = _str_findattr(anew, "user");
k = nil;
p = nil;
dom = nil;
for(i=(s->version==1?0:1); i<m; i++){
p = findneg(token[i]);
if(p == nil)
continue;
dom = getdom(token[i]);
ret = RpcFailure;
if(user==nil || strcmp(user, fss->sysuser)==0)
ret = findkey(&k, fss, Kowner, 0, anewsf,
"proto=%q dom=%q role=speakfor %s",
p->name, dom, p->keyprompt);
if(ret == RpcFailure)
ret = findkey(&k, fss, Kuser, 0, anew,
"proto=%q dom=%q role=client %s",
p->name, dom, p->keyprompt);
if(ret == RpcConfirm)
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]);
ret = findkey(&k, fss, Kuser, 0, anew,
"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)
return failure(fss, Ebadarg);
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);
}
}