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;
32 enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
35 static int localproxy_select_result(int fd, int event);
38 * Trees to look up the pipe fds in.
40 static tree234 *localproxy_by_fromfd, *localproxy_by_tofd;
41 static int localproxy_fromfd_cmp(void *av, void *bv)
43 Local_Proxy_Socket a = (Local_Proxy_Socket)av;
44 Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
45 if (a->from_cmd < b->from_cmd)
47 if (a->from_cmd > b->from_cmd)
51 static int localproxy_fromfd_find(void *av, void *bv)
54 Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
61 static int localproxy_tofd_cmp(void *av, void *bv)
63 Local_Proxy_Socket a = (Local_Proxy_Socket)av;
64 Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
65 if (a->to_cmd < b->to_cmd)
67 if (a->to_cmd > b->to_cmd)
71 static int localproxy_tofd_find(void *av, void *bv)
74 Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
82 /* basic proxy socket functions */
84 static Plug sk_localproxy_plug (Socket s, Plug p)
86 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
93 static void sk_localproxy_close (Socket s)
95 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
97 if (ps->to_cmd >= 0) {
98 del234(localproxy_by_tofd, ps);
99 uxsel_del(ps->to_cmd);
103 del234(localproxy_by_fromfd, ps);
104 uxsel_del(ps->from_cmd);
107 bufchain_clear(&ps->pending_input_data);
108 bufchain_clear(&ps->pending_output_data);
112 static int localproxy_try_send(Local_Proxy_Socket ps)
116 while (bufchain_size(&ps->pending_output_data) > 0) {
120 bufchain_prefix(&ps->pending_output_data, &data, &len);
121 ret = write(ps->to_cmd, data, len);
122 if (ret < 0 && errno != EWOULDBLOCK) {
123 /* We're inside the Unix frontend here, so we know
124 * that the frontend handle is unnecessary. */
125 logevent(NULL, strerror(errno));
126 fatalbox("%s", strerror(errno));
127 } else if (ret <= 0) {
130 bufchain_consume(&ps->pending_output_data, ret);
135 if (ps->outgoingeof == EOF_PENDING) {
136 del234(localproxy_by_tofd, ps);
138 uxsel_del(ps->to_cmd);
140 ps->outgoingeof = EOF_SENT;
143 if (bufchain_size(&ps->pending_output_data) == 0)
144 uxsel_del(ps->to_cmd);
146 uxsel_set(ps->to_cmd, 2, localproxy_select_result);
151 static int sk_localproxy_write (Socket s, const char *data, int len)
153 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
155 assert(ps->outgoingeof == EOF_NO);
157 bufchain_add(&ps->pending_output_data, data, len);
159 localproxy_try_send(ps);
161 return bufchain_size(&ps->pending_output_data);
164 static int sk_localproxy_write_oob (Socket s, const char *data, int len)
167 * oob data is treated as inband; nasty, but nothing really
170 return sk_localproxy_write(s, data, len);
173 static void sk_localproxy_write_eof (Socket s)
175 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
177 assert(ps->outgoingeof == EOF_NO);
178 ps->outgoingeof = EOF_PENDING;
180 localproxy_try_send(ps);
183 static void sk_localproxy_flush (Socket s)
185 /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
189 static void sk_localproxy_set_frozen (Socket s, int is_frozen)
191 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
194 uxsel_del(ps->from_cmd);
196 uxsel_set(ps->from_cmd, 1, localproxy_select_result);
199 static const char * sk_localproxy_socket_error (Socket s)
201 Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
205 static int localproxy_select_result(int fd, int event)
207 Local_Proxy_Socket s;
211 if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
212 !(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
213 return 1; /* boggle */
216 assert(fd == s->from_cmd);
217 ret = read(fd, buf, sizeof(buf));
219 return plug_closing(s->plug, strerror(errno), errno, 0);
220 } else if (ret == 0) {
221 return plug_closing(s->plug, NULL, 0, 0);
223 return plug_receive(s->plug, 0, buf, ret);
225 } else if (event == 2) {
226 assert(fd == s->to_cmd);
227 if (localproxy_try_send(s))
228 plug_sent(s->plug, bufchain_size(&s->pending_output_data));
235 Socket platform_new_connection(SockAddr addr, const char *hostname,
236 int port, int privport,
237 int oobinline, int nodelay, int keepalive,
238 Plug plug, Conf *conf)
242 static const struct socket_function_table socket_fn_table = {
246 sk_localproxy_write_oob,
247 sk_localproxy_write_eof,
249 sk_localproxy_set_frozen,
250 sk_localproxy_socket_error,
251 NULL, /* peer_info */
254 Local_Proxy_Socket ret;
255 int to_cmd_pipe[2], from_cmd_pipe[2], pid, proxytype;
257 proxytype = conf_get_int(conf, CONF_proxy_type);
258 if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ)
261 ret = snew(struct Socket_localproxy_tag);
262 ret->fn = &socket_fn_table;
265 ret->outgoingeof = EOF_NO;
267 bufchain_init(&ret->pending_input_data);
268 bufchain_init(&ret->pending_output_data);
270 if (proxytype == PROXY_CMD) {
271 cmd = format_telnet_command(addr, port, conf);
274 * Create the pipes to the proxy command, and spawn the proxy
277 if (pipe(to_cmd_pipe) < 0 ||
278 pipe(from_cmd_pipe) < 0) {
279 ret->error = dupprintf("pipe: %s", strerror(errno));
283 cloexec(to_cmd_pipe[1]);
284 cloexec(from_cmd_pipe[0]);
289 ret->error = dupprintf("fork: %s", strerror(errno));
292 } else if (pid == 0) {
295 dup2(to_cmd_pipe[0], 0);
296 dup2(from_cmd_pipe[1], 1);
297 close(to_cmd_pipe[0]);
298 close(from_cmd_pipe[1]);
301 execl("/bin/sh", "sh", "-c", cmd, (void *)NULL);
307 close(to_cmd_pipe[0]);
308 close(from_cmd_pipe[1]);
310 ret->to_cmd = to_cmd_pipe[1];
311 ret->from_cmd = from_cmd_pipe[0];
313 cmd = format_telnet_command(addr, port, conf);
314 ret->to_cmd = open("/dev/null", O_WRONLY);
315 if (ret->to_cmd == -1) {
316 ret->error = dupprintf("/dev/null: %s", strerror(errno));
320 ret->from_cmd = open(cmd, O_RDONLY);
321 if (ret->from_cmd == -1) {
322 ret->error = dupprintf("%s: %s", cmd, strerror(errno));
329 if (!localproxy_by_fromfd)
330 localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
331 if (!localproxy_by_tofd)
332 localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
334 add234(localproxy_by_fromfd, ret);
335 add234(localproxy_by_tofd, ret);
337 uxsel_set(ret->from_cmd, 1, localproxy_select_result);
339 /* We are responsible for this and don't need it any more */