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
, 0, 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
));
268 cloexec(to_cmd_pipe
[1]);
269 cloexec(from_cmd_pipe
[0]);
274 ret
->error
= dupprintf("fork: %s", strerror(errno
));
276 } else if (pid
== 0) {
279 dup2(to_cmd_pipe
[0], 0);
280 dup2(from_cmd_pipe
[1], 1);
281 close(to_cmd_pipe
[0]);
282 close(from_cmd_pipe
[1]);
283 fcntl(0, F_SETFD
, 0);
284 fcntl(1, F_SETFD
, 0);
285 execl("/bin/sh", "sh", "-c", cmd
, (void *)NULL
);
289 close(to_cmd_pipe
[0]);
290 close(from_cmd_pipe
[1]);
292 ret
->to_cmd
= to_cmd_pipe
[1];
293 ret
->from_cmd
= from_cmd_pipe
[0];
295 if (!localproxy_by_fromfd
)
296 localproxy_by_fromfd
= newtree234(localproxy_fromfd_cmp
);
297 if (!localproxy_by_tofd
)
298 localproxy_by_tofd
= newtree234(localproxy_tofd_cmp
);
300 add234(localproxy_by_fromfd
, ret
);
301 add234(localproxy_by_tofd
, ret
);
303 uxsel_set(ret
->from_cmd
, 1, localproxy_select_result
);
305 /* We are responsible for this and don't need it any more */