2 * uxproxy.c: Unix implementation of platform_new_connection(),
3 * supporting an OpenSSH-like proxy command.
12 #define DEFINE_PLUG_METHOD_MACROS
18 typedef struct Socket_localproxy_tag * Local_Proxy_Socket;
20 struct Socket_localproxy_tag {
21 const struct socket_function_table *fn;
22 /* the above variable absolutely *must* be the first in this structure */
24 int to_cmd, from_cmd; /* fds */
30 bufchain pending_output_data;
31 bufchain pending_input_data;
36 static int localproxy_select_result(int fd, int event);
39 * Trees to look up the pipe fds in.
41 static tree234 *localproxy_by_fromfd, *localproxy_by_tofd;
42 static int localproxy_fromfd_cmp(void *av, void *bv)
44 Local_Proxy_Socket a = (Local_Proxy_Socket)av;
45 Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
46 if (a->from_cmd < b->from_cmd)
48 if (a->from_cmd > b->from_cmd)
52 static int localproxy_fromfd_find(void *av, void *bv)
55 Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
62 static int localproxy_tofd_cmp(void *av, void *bv)
64 Local_Proxy_Socket a = (Local_Proxy_Socket)av;
65 Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
66 if (a->to_cmd < b->to_cmd)
68 if (a->to_cmd > b->to_cmd)
72 static int localproxy_tofd_find(void *av, void *bv)
75 Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
83 /* basic proxy socket functions */
85 static Plug sk_localproxy_plug (Socket s, Plug p)
87 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
94 static void sk_localproxy_close (Socket s)
96 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
98 del234(localproxy_by_fromfd, ps);
99 del234(localproxy_by_tofd, ps);
101 uxsel_del(ps->to_cmd);
102 uxsel_del(ps->from_cmd);
109 static int localproxy_try_send(Local_Proxy_Socket ps)
113 while (bufchain_size(&ps->pending_output_data) > 0) {
117 bufchain_prefix(&ps->pending_output_data, &data, &len);
118 ret = write(ps->to_cmd, data, len);
119 if (ret < 0 && errno != EWOULDBLOCK) {
120 /* We're inside the Unix frontend here, so we know
121 * that the frontend handle is unnecessary. */
122 logevent(NULL, strerror(errno));
123 fatalbox("%s", strerror(errno));
124 } else if (ret <= 0) {
127 bufchain_consume(&ps->pending_output_data, ret);
132 if (bufchain_size(&ps->pending_output_data) == 0)
133 uxsel_del(ps->to_cmd);
135 uxsel_set(ps->to_cmd, 2, localproxy_select_result);
140 static int sk_localproxy_write (Socket s, const char *data, int len)
142 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
144 bufchain_add(&ps->pending_output_data, data, len);
146 localproxy_try_send(ps);
148 return bufchain_size(&ps->pending_output_data);
151 static int sk_localproxy_write_oob (Socket s, const char *data, int len)
154 * oob data is treated as inband; nasty, but nothing really
157 return sk_localproxy_write(s, data, len);
160 static void sk_localproxy_flush (Socket s)
162 /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
166 static void sk_localproxy_set_private_ptr (Socket s, void *ptr)
168 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
172 static void * sk_localproxy_get_private_ptr (Socket s)
174 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
178 static void sk_localproxy_set_frozen (Socket s, int is_frozen)
180 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
183 uxsel_del(ps->from_cmd);
185 uxsel_set(ps->from_cmd, 1, localproxy_select_result);
188 static const char * sk_localproxy_socket_error (Socket s)
190 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
194 static int localproxy_select_result(int fd, int event)
196 Local_Proxy_Socket s;
200 if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
201 !(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
202 return 1; /* boggle */
205 assert(fd == s->from_cmd);
206 ret = read(fd, buf, sizeof(buf));
208 return plug_closing(s->plug, strerror(errno), errno, 0);
209 } else if (ret == 0) {
210 return plug_closing(s->plug, NULL, 0, 0);
212 return plug_receive(s->plug, 1, buf, ret);
214 } else if (event == 2) {
215 assert(fd == s->to_cmd);
216 if (localproxy_try_send(s))
217 plug_sent(s->plug, bufchain_size(&s->pending_output_data));
224 Socket platform_new_connection(SockAddr addr, char *hostname,
225 int port, int privport,
226 int oobinline, int nodelay, int keepalive,
227 Plug plug, const Config *cfg)
231 static const struct socket_function_table socket_fn_table = {
235 sk_localproxy_write_oob,
237 sk_localproxy_set_private_ptr,
238 sk_localproxy_get_private_ptr,
239 sk_localproxy_set_frozen,
240 sk_localproxy_socket_error
243 Local_Proxy_Socket ret;
244 int to_cmd_pipe[2], from_cmd_pipe[2], pid;
246 if (cfg->proxy_type != PROXY_CMD)
249 cmd = format_telnet_command(addr, port, cfg);
251 ret = snew(struct Socket_localproxy_tag);
252 ret->fn = &socket_fn_table;
256 bufchain_init(&ret->pending_input_data);
257 bufchain_init(&ret->pending_output_data);
260 * Create the pipes to the proxy command, and spawn the proxy
263 if (pipe(to_cmd_pipe) < 0 ||
264 pipe(from_cmd_pipe) < 0) {
265 ret->error = dupprintf("pipe: %s", strerror(errno));
272 ret->error = dupprintf("fork: %s", strerror(errno));
274 } else if (pid == 0) {
278 dup2(to_cmd_pipe[0], 0);
279 dup2(from_cmd_pipe[1], 1);
280 for (i = 3; i < 127; i++)
282 fcntl(0, F_SETFD, 0);
283 fcntl(1, F_SETFD, 0);
284 execl("/bin/sh", "sh", "-c", cmd, NULL);
288 close(to_cmd_pipe[0]);
289 close(from_cmd_pipe[1]);
291 ret->to_cmd = to_cmd_pipe[1];
292 ret->from_cmd = from_cmd_pipe[0];
294 if (!localproxy_by_fromfd)
295 localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
296 if (!localproxy_by_tofd)
297 localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
299 add234(localproxy_by_fromfd, ret);
300 add234(localproxy_by_tofd, ret);
302 uxsel_set(ret->from_cmd, 1, localproxy_select_result);
304 /* We are responsible for this and don't need it any more */