X-Git-Url: https://asedeno.scripts.mit.edu/gitweb/?a=blobdiff_plain;f=unix%2Fuxagentc.c;h=ffc5879cfbc0f5dba078f56ab72f2690908963cc;hb=bd65d477920b8d53833f6161e261fc6139a873ee;hp=826e4394b6d6f7e5b9bb23f547c8129ca0245ffc;hpb=6eec320f0b3606f17f06a290acdbb8f84afdff00;p=PuTTY.git diff --git a/unix/uxagentc.c b/unix/uxagentc.c index 826e4394..ffc5879c 100644 --- a/unix/uxagentc.c +++ b/unix/uxagentc.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "putty.h" #include "misc.h" @@ -16,13 +17,14 @@ int agent_exists(void) { - if (getenv("SSH_AUTH_SOCK") != NULL) + const char *p = getenv("SSH_AUTH_SOCK"); + if (p && *p) return TRUE; return FALSE; } -static tree234 *agent_connections; -struct agent_connection { +static tree234 *agent_pending_queries; +struct agent_pending_query { int fd; char *retbuf; char sizebuf[4]; @@ -32,8 +34,8 @@ struct agent_connection { }; static int agent_conncmp(void *av, void *bv) { - struct agent_connection *a = (struct agent_connection *) av; - struct agent_connection *b = (struct agent_connection *) bv; + agent_pending_query *a = (agent_pending_query *) av; + agent_pending_query *b = (agent_pending_query *) bv; if (a->fd < b->fd) return -1; if (a->fd > b->fd) @@ -43,7 +45,7 @@ static int agent_conncmp(void *av, void *bv) static int agent_connfind(void *av, void *bv) { int afd = *(int *) av; - struct agent_connection *b = (struct agent_connection *) bv; + agent_pending_query *b = (agent_pending_query *) bv; if (afd < b->fd) return -1; if (afd > b->fd) @@ -51,35 +53,32 @@ static int agent_connfind(void *av, void *bv) return 0; } -static int agent_select_result(int fd, int event) +/* + * Attempt to read from an agent socket fd. Returns 0 if the expected + * response is as yet incomplete; returns 1 if it's either complete + * (conn->retbuf non-NULL and filled with something useful) or has + * failed totally (conn->retbuf is NULL). + */ +static int agent_try_read(agent_pending_query *conn) { int ret; - struct agent_connection *conn; - - assert(event == 1); /* not selecting for anything but R */ - - conn = find234(agent_connections, &fd, agent_connfind); - if (!conn) { - uxsel_del(fd); - return 1; - } - ret = read(fd, conn->retbuf+conn->retlen, conn->retsize-conn->retlen); + ret = read(conn->fd, conn->retbuf+conn->retlen, + conn->retsize-conn->retlen); if (ret <= 0) { if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf); conn->retbuf = NULL; conn->retlen = 0; - goto done; + return 1; } conn->retlen += ret; if (conn->retsize == 4 && conn->retlen == 4) { - conn->retsize = GET_32BIT(conn->retbuf); + conn->retsize = toint(GET_32BIT(conn->retbuf) + 4); if (conn->retsize <= 0) { conn->retbuf = NULL; conn->retlen = 0; - goto done; + return -1; /* way too large */ } - conn->retsize += 4; assert(conn->retbuf == conn->sizebuf); conn->retbuf = snewn(conn->retsize, char); memcpy(conn->retbuf, conn->sizebuf, 4); @@ -88,31 +87,54 @@ static int agent_select_result(int fd, int event) if (conn->retlen < conn->retsize) return 0; /* more data to come */ - done: + return 1; +} + +void agent_cancel_query(agent_pending_query *conn) +{ + uxsel_del(conn->fd); + close(conn->fd); + del234(agent_pending_queries, conn); + sfree(conn); +} + +static int agent_select_result(int fd, int event) +{ + agent_pending_query *conn; + + assert(event == 1); /* not selecting for anything but R */ + + conn = find234(agent_pending_queries, &fd, agent_connfind); + if (!conn) { + uxsel_del(fd); + return 1; + } + + if (!agent_try_read(conn)) + return 0; /* more data to come */ + /* * We have now completed the agent query. Do the callback, and * clean up. (Of course we don't free retbuf, since ownership * of that passes to the callback.) */ conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen); - uxsel_del(fd); - close(fd); - del234(agent_connections, conn); - sfree(conn); + agent_cancel_query(conn); return 0; } -int agent_query(void *in, int inlen, void **out, int *outlen, - void (*callback)(void *, void *, int), void *callback_ctx) +agent_pending_query *agent_query( + void *in, int inlen, void **out, int *outlen, + void (*callback)(void *, void *, int), void *callback_ctx) { char *name; int sock; struct sockaddr_un addr; int done; - struct agent_connection *conn; + agent_pending_query *conn; name = getenv("SSH_AUTH_SOCK"); - if (!name) + if (!name || strlen(name) >= sizeof(addr.sun_path)) goto failure; sock = socket(PF_UNIX, SOCK_STREAM, 0); @@ -121,8 +143,10 @@ int agent_query(void *in, int inlen, void **out, int *outlen, exit(1); } + cloexec(sock); + addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, name, sizeof(addr.sun_path)); + strcpy(addr.sun_path, name); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(sock); goto failure; @@ -137,23 +161,48 @@ int agent_query(void *in, int inlen, void **out, int *outlen, done += ret; } - if (!agent_connections) - agent_connections = newtree234(agent_conncmp); - - conn = snew(struct agent_connection); + conn = snew(agent_pending_query); conn->fd = sock; conn->retbuf = conn->sizebuf; conn->retsize = 4; conn->retlen = 0; conn->callback = callback; conn->callback_ctx = callback_ctx; - add234(agent_connections, conn); + + if (!callback) { + /* + * Bodge to permit making deliberately synchronous agent + * requests. Used by Unix Pageant in command-line client mode, + * which is legit because it really is true that no other part + * of the program is trying to get anything useful done + * simultaneously. But this special case shouldn't be used in + * any more general program. + */ + no_nonblock(conn->fd); + while (!agent_try_read(conn)) + /* empty loop body */; + + *out = conn->retbuf; + *outlen = conn->retlen; + sfree(conn); + return NULL; + } + + /* + * Otherwise do it properly: add conn to the tree of agent + * connections currently in flight, return 0 to indicate that the + * response hasn't been received yet, and call the callback when + * select_result comes back to us. + */ + if (!agent_pending_queries) + agent_pending_queries = newtree234(agent_conncmp); + add234(agent_pending_queries, conn); uxsel_set(sock, 1, agent_select_result); - return 0; + return conn; failure: *out = NULL; *outlen = 0; - return 1; + return NULL; }