source: filezilla/trunk/fuentes/src/putty/unix/uxproxy.c @ 3185

Last change on this file since 3185 was 3185, checked in by jrpelegrina, 2 years ago

Update new version: 3.15.02

File size: 10.5 KB
Line 
1/*
2 * uxproxy.c: Unix implementation of platform_new_connection(),
3 * supporting an OpenSSH-like proxy command.
4 */
5
6#include <stdio.h>
7#include <assert.h>
8#include <errno.h>
9#include <unistd.h>
10#include <fcntl.h>
11
12#define DEFINE_PLUG_METHOD_MACROS
13#include "tree234.h"
14#include "putty.h"
15#include "network.h"
16#include "proxy.h"
17
18typedef struct Socket_localproxy_tag * Local_Proxy_Socket;
19
20struct Socket_localproxy_tag {
21    const struct socket_function_table *fn;
22    /* the above variable absolutely *must* be the first in this structure */
23
24    int to_cmd, from_cmd, cmd_err;     /* fds */
25
26    char *error;
27
28    Plug plug;
29
30    bufchain pending_output_data;
31    bufchain pending_input_data;
32    bufchain pending_error_data;
33    enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof;
34};
35
36static int localproxy_select_result(int fd, int event);
37
38/*
39 * Trees to look up the pipe fds in.
40 */
41static tree234 *localproxy_by_fromfd;
42static tree234 *localproxy_by_tofd;
43static tree234 *localproxy_by_errfd;
44static int localproxy_fromfd_cmp(void *av, void *bv)
45{
46    Local_Proxy_Socket a = (Local_Proxy_Socket)av;
47    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
48    if (a->from_cmd < b->from_cmd)
49        return -1;
50    if (a->from_cmd > b->from_cmd)
51        return +1;
52    return 0;
53}
54static int localproxy_fromfd_find(void *av, void *bv)
55{
56    int a = *(int *)av;
57    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
58    if (a < b->from_cmd)
59        return -1;
60    if (a > b->from_cmd)
61        return +1;
62    return 0;
63}
64static int localproxy_tofd_cmp(void *av, void *bv)
65{
66    Local_Proxy_Socket a = (Local_Proxy_Socket)av;
67    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
68    if (a->to_cmd < b->to_cmd)
69        return -1;
70    if (a->to_cmd > b->to_cmd)
71        return +1;
72    return 0;
73}
74static int localproxy_tofd_find(void *av, void *bv)
75{
76    int a = *(int *)av;
77    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
78    if (a < b->to_cmd)
79        return -1;
80    if (a > b->to_cmd)
81        return +1;
82    return 0;
83}
84static int localproxy_errfd_cmp(void *av, void *bv)
85{
86    Local_Proxy_Socket a = (Local_Proxy_Socket)av;
87    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
88    if (a->cmd_err < b->cmd_err)
89        return -1;
90    if (a->cmd_err > b->cmd_err)
91        return +1;
92    return 0;
93}
94static int localproxy_errfd_find(void *av, void *bv)
95{
96    int a = *(int *)av;
97    Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
98    if (a < b->cmd_err)
99        return -1;
100    if (a > b->cmd_err)
101        return +1;
102    return 0;
103}
104
105/* basic proxy socket functions */
106
107static Plug sk_localproxy_plug (Socket s, Plug p)
108{
109    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
110    Plug ret = ps->plug;
111    if (p)
112        ps->plug = p;
113    return ret;
114}
115
116static void sk_localproxy_close (Socket s)
117{
118    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
119
120    if (ps->to_cmd >= 0) {
121        del234(localproxy_by_tofd, ps);
122        uxsel_del(ps->to_cmd);
123        close(ps->to_cmd);
124    }
125
126    del234(localproxy_by_fromfd, ps);
127    uxsel_del(ps->from_cmd);
128    close(ps->from_cmd);
129
130    del234(localproxy_by_errfd, ps);
131    uxsel_del(ps->cmd_err);
132    close(ps->cmd_err);
133
134    bufchain_clear(&ps->pending_input_data);
135    bufchain_clear(&ps->pending_output_data);
136    bufchain_clear(&ps->pending_error_data);
137
138    sfree(ps);
139}
140
141static int localproxy_try_send(Local_Proxy_Socket ps)
142{
143    int sent = 0;
144
145    while (bufchain_size(&ps->pending_output_data) > 0) {
146        void *data;
147        int len, ret;
148
149        bufchain_prefix(&ps->pending_output_data, &data, &len);
150        ret = write(ps->to_cmd, data, len);
151        if (ret < 0 && errno != EWOULDBLOCK) {
152            /* We're inside the Unix frontend here, so we know
153             * that the frontend handle is unnecessary. */
154            logevent(NULL, strerror(errno));
155            fatalbox("%s", strerror(errno));
156        } else if (ret <= 0) {
157            break;
158        } else {
159            bufchain_consume(&ps->pending_output_data, ret);
160            sent += ret;
161        }
162    }
163
164    if (ps->outgoingeof == EOF_PENDING) {
165        del234(localproxy_by_tofd, ps);
166        close(ps->to_cmd);
167        uxsel_del(ps->to_cmd);
168        ps->to_cmd = -1;
169        ps->outgoingeof = EOF_SENT;
170    }
171
172    if (bufchain_size(&ps->pending_output_data) == 0)
173        uxsel_del(ps->to_cmd);
174    else
175        uxsel_set(ps->to_cmd, 2, localproxy_select_result);
176
177    return sent;
178}
179
180static int sk_localproxy_write (Socket s, const char *data, int len)
181{
182    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
183
184    assert(ps->outgoingeof == EOF_NO);
185
186    bufchain_add(&ps->pending_output_data, data, len);
187
188    localproxy_try_send(ps);
189
190    return bufchain_size(&ps->pending_output_data);
191}
192
193static int sk_localproxy_write_oob (Socket s, const char *data, int len)
194{
195    /*
196     * oob data is treated as inband; nasty, but nothing really
197     * better we can do
198     */
199    return sk_localproxy_write(s, data, len);
200}
201
202static void sk_localproxy_write_eof (Socket s)
203{
204    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
205
206    assert(ps->outgoingeof == EOF_NO);
207    ps->outgoingeof = EOF_PENDING;
208
209    localproxy_try_send(ps);
210}
211
212static void sk_localproxy_flush (Socket s)
213{
214    /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
215    /* do nothing */
216}
217
218static void sk_localproxy_set_frozen (Socket s, int is_frozen)
219{
220    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
221
222    if (is_frozen)
223        uxsel_del(ps->from_cmd);
224    else
225        uxsel_set(ps->from_cmd, 1, localproxy_select_result);
226}
227
228static const char * sk_localproxy_socket_error (Socket s)
229{
230    Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
231    return ps->error;
232}
233
234static int localproxy_select_result(int fd, int event)
235{
236    Local_Proxy_Socket s;
237    char buf[20480];
238    int ret;
239
240    if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&
241        !(s = find234(localproxy_by_fromfd, &fd, localproxy_errfd_find)) &&
242        !(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )
243        return 1;                      /* boggle */
244
245    if (event == 1) {
246        if (fd == s->cmd_err) {
247            ret = read(fd, buf, sizeof(buf));
248            if (ret > 0)
249                log_proxy_stderr(s->plug, &s->pending_error_data, buf, ret);
250        } else {
251            assert(fd == s->from_cmd);
252            ret = read(fd, buf, sizeof(buf));
253            if (ret < 0) {
254                return plug_closing(s->plug, strerror(errno), errno, 0);
255            } else if (ret == 0) {
256                return plug_closing(s->plug, NULL, 0, 0);
257            } else {
258                return plug_receive(s->plug, 0, buf, ret);
259            }
260        }
261    } else if (event == 2) {
262        assert(fd == s->to_cmd);
263        if (localproxy_try_send(s))
264            plug_sent(s->plug, bufchain_size(&s->pending_output_data));
265        return 1;
266    }
267
268    return 1;
269}
270
271Socket platform_new_connection(SockAddr addr, const char *hostname,
272                               int port, int privport,
273                               int oobinline, int nodelay, int keepalive,
274                               Plug plug, Conf *conf)
275{
276    char *cmd;
277
278    static const struct socket_function_table socket_fn_table = {
279        sk_localproxy_plug,
280        sk_localproxy_close,
281        sk_localproxy_write,
282        sk_localproxy_write_oob,
283        sk_localproxy_write_eof,
284        sk_localproxy_flush,
285        sk_localproxy_set_frozen,
286        sk_localproxy_socket_error,
287        NULL, /* peer_info */
288    };
289
290    Local_Proxy_Socket ret;
291    int to_cmd_pipe[2], from_cmd_pipe[2], cmd_err_pipe[2], pid, proxytype;
292
293    proxytype = conf_get_int(conf, CONF_proxy_type);
294    if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ)
295        return NULL;
296
297    ret = snew(struct Socket_localproxy_tag);
298    ret->fn = &socket_fn_table;
299    ret->plug = plug;
300    ret->error = NULL;
301    ret->outgoingeof = EOF_NO;
302
303    bufchain_init(&ret->pending_input_data);
304    bufchain_init(&ret->pending_output_data);
305    bufchain_init(&ret->pending_error_data);
306
307    if (proxytype == PROXY_CMD) {
308        cmd = format_telnet_command(addr, port, conf);
309
310        if (flags & FLAG_STDERR) {
311            /* If we have a sensible stderr, the proxy command can
312             * send its own standard error there, so we won't
313             * interfere. */
314            cmd_err_pipe[0] = cmd_err_pipe[1] = -1;
315        } else {
316            /* If we don't have a sensible stderr, we should catch the
317             * proxy command's standard error to put in our event
318             * log. */
319            cmd_err_pipe[0] = cmd_err_pipe[1] = 0;
320        }
321
322        {
323            char *logmsg = dupprintf("Starting local proxy command: %s", cmd);
324            plug_log(plug, 2, NULL, 0, logmsg, 0);
325            sfree(logmsg);
326        }
327
328        /*
329         * Create the pipes to the proxy command, and spawn the proxy
330         * command process.
331         */
332        if (pipe(to_cmd_pipe) < 0 ||
333            pipe(from_cmd_pipe) < 0 ||
334            (cmd_err_pipe[0] == 0 && pipe(cmd_err_pipe) < 0)) {
335            ret->error = dupprintf("pipe: %s", strerror(errno));
336            sfree(cmd);
337            return (Socket)ret;
338        }
339        cloexec(to_cmd_pipe[1]);
340        cloexec(from_cmd_pipe[0]);
341        if (cmd_err_pipe[0] >= 0)
342            cloexec(cmd_err_pipe[0]);
343
344        pid = fork();
345
346        if (pid < 0) {
347            ret->error = dupprintf("fork: %s", strerror(errno));
348            sfree(cmd);
349            return (Socket)ret;
350        } else if (pid == 0) {
351            close(0);
352            close(1);
353            dup2(to_cmd_pipe[0], 0);
354            dup2(from_cmd_pipe[1], 1);
355            close(to_cmd_pipe[0]);
356            close(from_cmd_pipe[1]);
357            if (cmd_err_pipe[0] >= 0) {
358                dup2(cmd_err_pipe[1], 2);
359                close(cmd_err_pipe[1]);
360            }
361            noncloexec(0);
362            noncloexec(1);
363            execl("/bin/sh", "sh", "-c", cmd, (void *)NULL);
364            _exit(255);
365        }
366
367        sfree(cmd);
368
369        close(to_cmd_pipe[0]);
370        close(from_cmd_pipe[1]);
371        if (cmd_err_pipe[0] >= 0)
372            close(cmd_err_pipe[1]);
373
374        ret->to_cmd = to_cmd_pipe[1];
375        ret->from_cmd = from_cmd_pipe[0];
376        ret->cmd_err = cmd_err_pipe[0];
377    } else {
378        cmd = format_telnet_command(addr, port, conf);
379        ret->to_cmd = open("/dev/null", O_WRONLY);
380        if (ret->to_cmd == -1) {
381            ret->error = dupprintf("/dev/null: %s", strerror(errno));
382            sfree(cmd);
383            return (Socket)ret;
384        }
385        ret->from_cmd = open(cmd, O_RDONLY);
386        if (ret->from_cmd == -1) {
387            ret->error = dupprintf("%s: %s", cmd, strerror(errno));
388            sfree(cmd);
389            return (Socket)ret;
390        }
391        sfree(cmd);
392        ret->cmd_err = -1;
393    }
394
395    if (!localproxy_by_fromfd)
396        localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
397    if (!localproxy_by_tofd)
398        localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
399    if (!localproxy_by_errfd)
400        localproxy_by_errfd = newtree234(localproxy_errfd_cmp);
401
402    add234(localproxy_by_fromfd, ret);
403    add234(localproxy_by_tofd, ret);
404    if (ret->cmd_err >= 0)
405        add234(localproxy_by_errfd, ret);
406
407    uxsel_set(ret->from_cmd, 1, localproxy_select_result);
408    if (ret->cmd_err >= 0)
409        uxsel_set(ret->cmd_err, 1, localproxy_select_result);
410
411    /* We are responsible for this and don't need it any more */
412    sk_addr_free(addr);
413
414    return (Socket) ret;
415}
Note: See TracBrowser for help on using the repository browser.