+ 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);
+ agent_cancel_query(conn);
+ return 0;
+}
+
+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;
+ agent_pending_query *conn;
+
+ name = getenv("SSH_AUTH_SOCK");
+ if (!name)
+ goto failure;
+
+ sock = socket(PF_UNIX, SOCK_STREAM, 0);
+ if (sock < 0) {
+ perror("socket(PF_UNIX)");
+ exit(1);
+ }
+
+ cloexec(sock);
+
+ addr.sun_family = AF_UNIX;
+ strncpy(addr.sun_path, name, sizeof(addr.sun_path));
+ if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ close(sock);
+ goto failure;
+ }
+
+ for (done = 0; done < inlen ;) {
+ int ret = write(sock, (char *)in + done, inlen - done);
+ if (ret <= 0) {
+ close(sock);
+ goto failure;
+ }
+ done += ret;
+ }
+
+ 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;
+
+ 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 conn;
+
+ failure:
+ *out = NULL;
+ *outlen = 0;
+ return NULL;