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