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
);
107 static int localproxy_try_send(Local_Proxy_Socket ps
)
111 while (bufchain_size(&ps
->pending_output_data
) > 0) {
115 bufchain_prefix(&ps
->pending_output_data
, &data
, &len
);
116 ret
= write(ps
->to_cmd
, data
, len
);
117 if (ret
< 0 && errno
!= EWOULDBLOCK
) {
118 /* We're inside the Unix frontend here, so we know
119 * that the frontend handle is unnecessary. */
120 logevent(NULL
, strerror(errno
));
121 fatalbox("%s", strerror(errno
));
122 } else if (ret
<= 0) {
125 bufchain_consume(&ps
->pending_output_data
, ret
);
130 if (bufchain_size(&ps
->pending_output_data
) == 0)
131 uxsel_del(ps
->to_cmd
);
133 uxsel_set(ps
->to_cmd
, 2, localproxy_select_result
);
138 static int sk_localproxy_write (Socket s
, const char *data
, int len
)
140 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
142 bufchain_add(&ps
->pending_output_data
, data
, len
);
144 localproxy_try_send(ps
);
146 return bufchain_size(&ps
->pending_output_data
);
149 static int sk_localproxy_write_oob (Socket s
, const char *data
, int len
)
152 * oob data is treated as inband; nasty, but nothing really
155 return sk_localproxy_write(s
, data
, len
);
158 static void sk_localproxy_flush (Socket s
)
160 /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
164 static void sk_localproxy_set_private_ptr (Socket s
, void *ptr
)
166 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
170 static void * sk_localproxy_get_private_ptr (Socket s
)
172 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
176 static void sk_localproxy_set_frozen (Socket s
, int is_frozen
)
178 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
181 uxsel_del(ps
->from_cmd
);
183 uxsel_set(ps
->from_cmd
, 1, localproxy_select_result
);
186 static const char * sk_localproxy_socket_error (Socket s
)
188 Local_Proxy_Socket ps
= (Local_Proxy_Socket
) s
;
192 static int localproxy_select_result(int fd
, int event
)
194 Local_Proxy_Socket s
;
198 if (!(s
= find234(localproxy_by_fromfd
, &fd
, localproxy_fromfd_find
)) &&
199 !(s
= find234(localproxy_by_tofd
, &fd
, localproxy_tofd_find
)) )
200 return 1; /* boggle */
203 assert(fd
== s
->from_cmd
);
204 ret
= read(fd
, buf
, sizeof(buf
));
206 return plug_closing(s
->plug
, strerror(errno
), errno
, 0);
207 } else if (ret
== 0) {
208 return plug_closing(s
->plug
, NULL
, 0, 0);
210 return plug_receive(s
->plug
, 1, buf
, ret
);
212 } else if (event
== 2) {
213 assert(fd
== s
->to_cmd
);
214 if (localproxy_try_send(s
))
215 plug_sent(s
->plug
, bufchain_size(&s
->pending_output_data
));
222 Socket
platform_new_connection(SockAddr addr
, char *hostname
,
223 int port
, int privport
,
224 int oobinline
, int nodelay
, Plug plug
,
229 static const struct socket_function_table socket_fn_table
= {
233 sk_localproxy_write_oob
,
235 sk_localproxy_set_private_ptr
,
236 sk_localproxy_get_private_ptr
,
237 sk_localproxy_set_frozen
,
238 sk_localproxy_socket_error
241 Local_Proxy_Socket ret
;
242 int to_cmd_pipe
[2], from_cmd_pipe
[2], pid
;
244 if (cfg
->proxy_type
!= PROXY_CMD
)
247 cmd
= format_telnet_command(addr
, port
, cfg
);
249 ret
= snew(struct Socket_localproxy_tag
);
250 ret
->fn
= &socket_fn_table
;
254 bufchain_init(&ret
->pending_input_data
);
255 bufchain_init(&ret
->pending_output_data
);
258 * Create the pipes to the proxy command, and spawn the proxy
261 if (pipe(to_cmd_pipe
) < 0 ||
262 pipe(from_cmd_pipe
) < 0) {
263 ret
->error
= dupprintf("pipe: %s", strerror(errno
));
270 ret
->error
= dupprintf("fork: %s", strerror(errno
));
272 } else if (pid
== 0) {
276 dup2(to_cmd_pipe
[0], 0);
277 dup2(from_cmd_pipe
[1], 1);
278 for (i
= 3; i
< 127; i
++)
280 fcntl(0, F_SETFD
, 0);
281 fcntl(1, F_SETFD
, 0);
282 execl("/bin/sh", "sh", "-c", cmd
, NULL
);
286 close(to_cmd_pipe
[0]);
287 close(from_cmd_pipe
[1]);
289 ret
->to_cmd
= to_cmd_pipe
[1];
290 ret
->from_cmd
= from_cmd_pipe
[0];
292 if (!localproxy_by_fromfd
)
293 localproxy_by_fromfd
= newtree234(localproxy_fromfd_cmp
);
294 if (!localproxy_by_tofd
)
295 localproxy_by_tofd
= newtree234(localproxy_tofd_cmp
);
297 add234(localproxy_by_fromfd
, ret
);
298 add234(localproxy_by_tofd
, ret
);
300 uxsel_set(ret
->from_cmd
, 1, localproxy_select_result
);
302 /* We are responsible for this and don't need it any more */