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
;
37 static int localproxy_select_result(int fd
, int event
);
40 * Trees to look up the pipe fds in.
42 static tree234
*localproxy_by_fromfd
, *localproxy_by_tofd
;
43 static int localproxy_fromfd_cmp(void *av
, void *bv
)
45 Local_Proxy_Socket a
= (Local_Proxy_Socket
)av
;
46 Local_Proxy_Socket b
= (Local_Proxy_Socket
)bv
;
47 if (a
->from_cmd
< b
->from_cmd
)
49 if (a
->from_cmd
> b
->from_cmd
)
53 static int localproxy_fromfd_find(void *av
, void *bv
)
56 Local_Proxy_Socket b
= (Local_Proxy_Socket
)bv
;
63 static int localproxy_tofd_cmp(void *av
, void *bv
)
65 Local_Proxy_Socket a
= (Local_Proxy_Socket
)av
;
66 Local_Proxy_Socket b
= (Local_Proxy_Socket
)bv
;
67 if (a
->to_cmd
< b
->to_cmd
)
69 if (a
->to_cmd
> b
->to_cmd
)
73 static int localproxy_tofd_find(void *av
, void *bv
)
76 Local_Proxy_Socket b
= (Local_Proxy_Socket
)bv
;
84 /* basic proxy socket functions */
86 static Plug
sk_localproxy_plug (Socket s
, Plug p
)
88 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
95 static void sk_localproxy_close (Socket s
)
97 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
99 if (ps
->to_cmd
>= 0) {
100 del234(localproxy_by_tofd
, ps
);
101 uxsel_del(ps
->to_cmd
);
105 del234(localproxy_by_fromfd
, ps
);
106 uxsel_del(ps
->from_cmd
);
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_private_ptr (Socket s
, void *ptr
)
191 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
195 static void * sk_localproxy_get_private_ptr (Socket s
)
197 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
201 static void sk_localproxy_set_frozen (Socket s
, int is_frozen
)
203 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
206 uxsel_del(ps
->from_cmd
);
208 uxsel_set(ps
->from_cmd
, 1, localproxy_select_result
);
211 static const char * sk_localproxy_socket_error (Socket s
)
213 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
217 static int localproxy_select_result(int fd
, int event
)
219 Local_Proxy_Socket s
;
223 if (!(s
= find234(localproxy_by_fromfd
, &fd
, localproxy_fromfd_find
)) &&
224 !(s
= find234(localproxy_by_tofd
, &fd
, localproxy_tofd_find
)) )
225 return 1; /* boggle */
228 assert(fd
== s
->from_cmd
);
229 ret
= read(fd
, buf
, sizeof(buf
));
231 return plug_closing(s
->plug
, strerror(errno
), errno
, 0);
232 } else if (ret
== 0) {
233 return plug_closing(s
->plug
, NULL
, 0, 0);
235 return plug_receive(s
->plug
, 0, buf
, ret
);
237 } else if (event
== 2) {
238 assert(fd
== s
->to_cmd
);
239 if (localproxy_try_send(s
))
240 plug_sent(s
->plug
, bufchain_size(&s
->pending_output_data
));
247 Socket
platform_new_connection(SockAddr addr
, char *hostname
,
248 int port
, int privport
,
249 int oobinline
, int nodelay
, int keepalive
,
250 Plug plug
, Conf
*conf
)
254 static const struct socket_function_table socket_fn_table
= {
258 sk_localproxy_write_oob
,
259 sk_localproxy_write_eof
,
261 sk_localproxy_set_private_ptr
,
262 sk_localproxy_get_private_ptr
,
263 sk_localproxy_set_frozen
,
264 sk_localproxy_socket_error
267 Local_Proxy_Socket ret
;
268 int to_cmd_pipe
[2], from_cmd_pipe
[2], pid
;
270 if (conf_get_int(conf
, CONF_proxy_type
) != PROXY_CMD
)
273 cmd
= format_telnet_command(addr
, port
, conf
);
275 ret
= snew(struct Socket_localproxy_tag
);
276 ret
->fn
= &socket_fn_table
;
279 ret
->outgoingeof
= EOF_NO
;
281 bufchain_init(&ret
->pending_input_data
);
282 bufchain_init(&ret
->pending_output_data
);
285 * Create the pipes to the proxy command, and spawn the proxy
288 if (pipe(to_cmd_pipe
) < 0 ||
289 pipe(from_cmd_pipe
) < 0) {
290 ret
->error
= dupprintf("pipe: %s", strerror(errno
));
293 cloexec(to_cmd_pipe
[1]);
294 cloexec(from_cmd_pipe
[0]);
299 ret
->error
= dupprintf("fork: %s", strerror(errno
));
301 } else if (pid
== 0) {
304 dup2(to_cmd_pipe
[0], 0);
305 dup2(from_cmd_pipe
[1], 1);
306 close(to_cmd_pipe
[0]);
307 close(from_cmd_pipe
[1]);
308 fcntl(0, F_SETFD
, 0);
309 fcntl(1, F_SETFD
, 0);
310 execl("/bin/sh", "sh", "-c", cmd
, (void *)NULL
);
316 close(to_cmd_pipe
[0]);
317 close(from_cmd_pipe
[1]);
319 ret
->to_cmd
= to_cmd_pipe
[1];
320 ret
->from_cmd
= from_cmd_pipe
[0];
322 if (!localproxy_by_fromfd
)
323 localproxy_by_fromfd
= newtree234(localproxy_fromfd_cmp
);
324 if (!localproxy_by_tofd
)
325 localproxy_by_tofd
= newtree234(localproxy_tofd_cmp
);
327 add234(localproxy_by_fromfd
, ret
);
328 add234(localproxy_by_tofd
, ret
);
330 uxsel_set(ret
->from_cmd
, 1, localproxy_select_result
);
332 /* We are responsible for this and don't need it any more */