Unify GET_32BIT()/PUT_32BIT() et al from numerous source files into misc.h.
[u/mdw/putty] / unix / uxagentc.c
CommitLineData
c5e438ec 1/*
2 * SSH agent client code.
3 */
4
5#include <stdio.h>
6#include <stdlib.h>
fbbb86aa 7#include <assert.h>
8#include <unistd.h>
9#include <sys/socket.h>
10#include <sys/un.h>
c5e438ec 11
839f10db 12#include "putty.h"
c5e438ec 13#include "misc.h"
839f10db 14#include "tree234.h"
c5e438ec 15#include "puttymem.h"
16
c5e438ec 17int agent_exists(void)
18{
fbbb86aa 19 if (getenv("SSH_AUTH_SOCK") != NULL)
20 return TRUE;
21 return FALSE;
c5e438ec 22}
23
839f10db 24static tree234 *agent_connections;
25struct agent_connection {
26 int fd;
27 char *retbuf;
28 char sizebuf[4];
29 int retsize, retlen;
30 void (*callback)(void *, void *, int);
31 void *callback_ctx;
32};
33static int agent_conncmp(void *av, void *bv)
34{
35 struct agent_connection *a = (struct agent_connection *) av;
36 struct agent_connection *b = (struct agent_connection *) bv;
37 if (a->fd < b->fd)
38 return -1;
39 if (a->fd > b->fd)
40 return +1;
41 return 0;
42}
43static int agent_connfind(void *av, void *bv)
44{
45 int afd = *(int *) av;
46 struct agent_connection *b = (struct agent_connection *) bv;
47 if (afd < b->fd)
48 return -1;
49 if (afd > b->fd)
50 return +1;
51 return 0;
52}
53
54static int agent_select_result(int fd, int event)
55{
56 int ret;
57 struct agent_connection *conn;
58
59 assert(event == 1); /* not selecting for anything but R */
60
61 conn = find234(agent_connections, &fd, agent_connfind);
62 if (!conn) {
63 uxsel_del(fd);
64 return 1;
65 }
66
67 ret = read(fd, conn->retbuf+conn->retlen, conn->retsize-conn->retlen);
68 if (ret <= 0) {
69 if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf);
70 conn->retbuf = NULL;
71 conn->retlen = 0;
72 goto done;
73 }
74 conn->retlen += ret;
75 if (conn->retsize == 4 && conn->retlen == 4) {
76 conn->retsize = GET_32BIT(conn->retbuf);
77 if (conn->retsize <= 0) {
78 conn->retbuf = NULL;
79 conn->retlen = 0;
80 goto done;
81 }
82 conn->retsize += 4;
83 assert(conn->retbuf == conn->sizebuf);
84 conn->retbuf = snewn(conn->retsize, char);
85 memcpy(conn->retbuf, conn->sizebuf, 4);
86 }
87
88 if (conn->retlen < conn->retsize)
89 return 0; /* more data to come */
90
91 done:
92 /*
93 * We have now completed the agent query. Do the callback, and
94 * clean up. (Of course we don't free retbuf, since ownership
95 * of that passes to the callback.)
96 */
97 conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen);
98 uxsel_del(fd);
99 close(fd);
100 del234(agent_connections, conn);
101 sfree(conn);
102 return 0;
103}
104
105int agent_query(void *in, int inlen, void **out, int *outlen,
106 void (*callback)(void *, void *, int), void *callback_ctx)
c5e438ec 107{
fbbb86aa 108 char *name;
109 int sock;
110 struct sockaddr_un addr;
111 int done;
839f10db 112 struct agent_connection *conn;
fbbb86aa 113
114 name = getenv("SSH_AUTH_SOCK");
115 if (!name)
116 goto failure;
117
118 sock = socket(PF_UNIX, SOCK_STREAM, 0);
119 if (sock < 0) {
120 perror("socket(PF_UNIX)");
121 exit(1);
122 }
123
124 addr.sun_family = AF_UNIX;
125 strncpy(addr.sun_path, name, sizeof(addr.sun_path));
126 if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
127 close(sock);
128 goto failure;
129 }
130
131 for (done = 0; done < inlen ;) {
132 int ret = write(sock, (char *)in + done, inlen - done);
133 if (ret <= 0) {
134 close(sock);
135 goto failure;
136 }
137 done += ret;
138 }
139
839f10db 140 if (!agent_connections)
141 agent_connections = newtree234(agent_conncmp);
fbbb86aa 142
839f10db 143 conn = snew(struct agent_connection);
144 conn->fd = sock;
145 conn->retbuf = conn->sizebuf;
146 conn->retsize = 4;
147 conn->retlen = 0;
148 conn->callback = callback;
149 conn->callback_ctx = callback_ctx;
150 add234(agent_connections, conn);
fbbb86aa 151
839f10db 152 uxsel_set(sock, 1, agent_select_result);
153 return 0;
fbbb86aa 154
155 failure:
156 *out = NULL;
157 *outlen = 0;
839f10db 158 return 1;
c5e438ec 159}