#include "std.h"
#include "dat.h"
Conv *conv;
ulong taggen = 1;
Conv*
convalloc(char *sysuser)
{
Conv *c;
c = mallocz(sizeof(Conv), 1);
if(c == nil)
return nil;
c->ref = 1;
c->tag = taggen++;
c->next = conv;
c->sysuser = estrdup(sysuser);
c->state = "nascent";
c->rpcwait = chancreate(sizeof(void*), 0);
c->keywait = chancreate(sizeof(void*), 0);
strcpy(c->err, "protocol has not started");
conv = c;
convreset(c);
return c;
}
void
convfree(Conv *c)
{
freeattr(c->attr);
free(c->sysuser);
chanfree(c->rpcwait);
chanfree(c->keywait);
free(c);
}
void
convreset(Conv *c)
{
if(c->ref != 1){
c->hangup = 1;
nbsendp(c->rpcwait, 0);
while(c->ref > 1)
yield();
c->hangup = 0;
}
c->state = "nascent";
c->err[0] = '\0';
freeattr(c->attr);
c->attr = nil;
c->proto = nil;
c->rpc.op = 0;
c->active = 0;
c->done = 0;
c->hangup = 0;
}
void
convhangup(Conv *c)
{
c->hangup = 1;
c->rpc.op = 0;
(*c->kickreply)(c);
nbsendp(c->rpcwait, 0);
}
void
convclose(Conv *c)
{
Conv *p;
if(c == nil)
return;
if(--c->ref > 0)
return;
if(c == conv){
conv = c->next;
goto free;
}
for(p=conv; p && p->next!=c; p=p->next)
;
if(p == nil){
print("cannot find conv in list\n");
return;
}
p->next = c->next;
free:
c->next = nil;
convfree(c);
}
static Rpc*
convgetrpc(Conv *c, int want)
{
for(;;){
if(c->hangup){
flog("convgetrpc: hangup");
werrstr("hangup");
return nil;
}
if(c->rpc.op == RpcUnknown){
recvp(c->rpcwait);
if(c->hangup){
flog("convgetrpc: hangup");
werrstr("hangup");
return nil;
}
if(c->rpc.op == RpcUnknown)
continue;
}
if(want < 0 || c->rpc.op == want)
return &c->rpc;
rpcrespond(c, "phase in state '%s' want '%s'", c->state, rpcname[want]);
}
/* not reached */
}
/* read until the done function tells us that's enough */
int
convreadfn(Conv *c, int (*done)(void*, int), char **ps)
{
int n;
Rpc *r;
char *s;
for(;;){
r = convgetrpc(c, RpcWrite);
if(r == nil)
return -1;
n = (*done)(r->data, r->count);
if(n == r->count)
break;
rpcrespond(c, "toosmall %d", n);
}
s = emalloc(r->count+1);
memmove(s, r->data, r->count);
s[r->count] = 0;
*ps = s;
rpcrespond(c, "ok");
return r->count;
}
/*
* read until we get a non-zero write. assumes remote side
* knows something about the protocol (is not auth_proxy).
* the remote side typically won't bother with the zero-length
* write to find out the length -- the loop is there only so the
* test program can call auth_proxy on both sides of a pipe
* to play a conversation.
*/
int
convreadm(Conv *c, char **ps)
{
char *s;
Rpc *r;
*ps = nil;
for(;;){
r = convgetrpc(c, RpcWrite);
if(r == nil)
return -1;
if(r->count > 0)
break;
rpcrespond(c, "toosmall %d", AuthRpcMax);
}
s = emalloc(r->count+1);
memmove(s, r->data, r->count);
s[r->count] = 0;
*ps = s;
rpcrespond(c, "ok");
return r->count;
}
/* read exactly count bytes */
int
convread(Conv *c, void *data, int count)
{
Rpc *r;
for(;;){
r = convgetrpc(c, RpcWrite);
if(r == nil)
return -1;
if(r->count == count)
break;
if(r->count < count)
rpcrespond(c, "toosmall %d", count);
else
rpcrespond(c, "error too much data; want %d got %d", count, r->count);
}
memmove(data, r->data, count);
rpcrespond(c, "ok");
return 0;
}
/* write exactly count bytes */
int
convwrite(Conv *c, void *data, int count)
{
Rpc *r;
r = convgetrpc(c, RpcRead);
if(r == nil)
return -1;
if(c->done)
rpcrespondn(c, "done", data, count);
else
rpcrespondn(c, "ok", data, count);
return 0;
}
/* print to the conversation */
int
convprint(Conv *c, char *fmt, ...)
{
char *s;
va_list arg;
int ret;
va_start(arg, fmt);
s = vsmprint(fmt, arg);
va_end(arg);
if(s == nil)
return -1;
ret = convwrite(c, s, strlen(s));
free(s);
return ret;
}
/* ask for a key */
int
convneedkey(Conv *c, Attr *a)
{
/*
* Piggyback key requests in the usual RPC channel.
* Wait for the next RPC and then send a key request
* in response. The keys get added out-of-band (via the
* ctl file), so assume the key has been added when the
* next request comes in.
*
* The convgetrpc seems dodgy, because we might be in
* the middle of an rpc, and what about the one that comes
* in later? It's all actually okay: convgetrpc is idempotent
* until rpcrespond is called, so if we're in the middle of an rpc,
* the first convgetrpc is a no-op, the rpcrespond sends back
* the needkey, and then the client repeats the rpc we're in
* the middle of. Otherwise, if we're not in the middle of an
* rpc, the first convgetrpc waits for one, we respond needkey,
* and then the second convgetrpc waits for another. Because
* there is no second response, eventually the caller will get
* around to asking for an rpc itself, at which point the already
* gotten rpc will be returned again.
*/
if(convgetrpc(c, -1) == nil)
return -1;
if(conv->proto!=nil && c->proto->keyprompt!=nil)
a = addattrs(parseattr(c->proto->keyprompt), a);
flog("convneedkey %A", a);
rpcrespond(c, "needkey %A", a);
if(convgetrpc(c, -1) == nil)
return -1;
flog("convneedkey returning");
return 0;
}
/* ask for a replacement for a bad key*/
int
convbadkey(Conv *c, Key *k, char *msg, Attr *a)
{
if(convgetrpc(c, -1) == nil)
return -1;
flog("convbadkey %A %N / %s / %A", k->attr, k->privattr, msg, a);
rpcrespond(c, "badkey %A %N\n%s\n%A",
k->attr, k->privattr, msg, a);
if(convgetrpc(c, -1) == nil)
return -1;
return 0;
}
|