]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - unix/uxproxy.c
Post-release destabilisation! Completely remove the struct type
[PuTTY.git] / unix / uxproxy.c
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
18 typedef struct Socket_localproxy_tag * Local_Proxy_Socket;
19
20 struct 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;              /* fds */
25
26     char *error;
27
28     Plug plug;
29
30     bufchain pending_output_data;
31     bufchain pending_input_data;
32
33     void *privptr;
34 };
35
36 static int localproxy_select_result(int fd, int event);
37
38 /*
39  * Trees to look up the pipe fds in.
40  */
41 static tree234 *localproxy_by_fromfd, *localproxy_by_tofd;
42 static int localproxy_fromfd_cmp(void *av, void *bv)
43 {
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)
47         return -1;
48     if (a->from_cmd > b->from_cmd)
49         return +1;
50     return 0;
51 }
52 static int localproxy_fromfd_find(void *av, void *bv)
53 {
54     int a = *(int *)av;
55     Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
56     if (a < b->from_cmd)
57         return -1;
58     if (a > b->from_cmd)
59         return +1;
60     return 0;
61 }
62 static int localproxy_tofd_cmp(void *av, void *bv)
63 {
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)
67         return -1;
68     if (a->to_cmd > b->to_cmd)
69         return +1;
70     return 0;
71 }
72 static int localproxy_tofd_find(void *av, void *bv)
73 {
74     int a = *(int *)av;
75     Local_Proxy_Socket b = (Local_Proxy_Socket)bv;
76     if (a < b->to_cmd)
77         return -1;
78     if (a > b->to_cmd)
79         return +1;
80     return 0;
81 }
82
83 /* basic proxy socket functions */
84
85 static Plug sk_localproxy_plug (Socket s, Plug p)
86 {
87     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
88     Plug ret = ps->plug;
89     if (p)
90         ps->plug = p;
91     return ret;
92 }
93
94 static void sk_localproxy_close (Socket s)
95 {
96     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
97
98     del234(localproxy_by_fromfd, ps);
99     del234(localproxy_by_tofd, ps);
100
101     uxsel_del(ps->to_cmd);
102     uxsel_del(ps->from_cmd);
103     close(ps->to_cmd);
104     close(ps->from_cmd);
105
106     sfree(ps);
107 }
108
109 static int localproxy_try_send(Local_Proxy_Socket ps)
110 {
111     int sent = 0;
112
113     while (bufchain_size(&ps->pending_output_data) > 0) {
114         void *data;
115         int len, ret;
116
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) {
125             break;
126         } else {
127             bufchain_consume(&ps->pending_output_data, ret);
128             sent += ret;
129         }
130     }
131
132     if (bufchain_size(&ps->pending_output_data) == 0)
133         uxsel_del(ps->to_cmd);
134     else
135         uxsel_set(ps->to_cmd, 2, localproxy_select_result);
136
137     return sent;
138 }
139
140 static int sk_localproxy_write (Socket s, const char *data, int len)
141 {
142     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
143
144     bufchain_add(&ps->pending_output_data, data, len);
145
146     localproxy_try_send(ps);
147
148     return bufchain_size(&ps->pending_output_data);
149 }
150
151 static int sk_localproxy_write_oob (Socket s, const char *data, int len)
152 {
153     /*
154      * oob data is treated as inband; nasty, but nothing really
155      * better we can do
156      */
157     return sk_localproxy_write(s, data, len);
158 }
159
160 static void sk_localproxy_flush (Socket s)
161 {
162     /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */
163     /* do nothing */
164 }
165
166 static void sk_localproxy_set_private_ptr (Socket s, void *ptr)
167 {
168     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
169     ps->privptr = ptr;
170 }
171
172 static void * sk_localproxy_get_private_ptr (Socket s)
173 {
174     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
175     return ps->privptr;
176 }
177
178 static void sk_localproxy_set_frozen (Socket s, int is_frozen)
179 {
180     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
181
182     if (is_frozen)
183         uxsel_del(ps->from_cmd);
184     else
185         uxsel_set(ps->from_cmd, 1, localproxy_select_result);
186 }
187
188 static const char * sk_localproxy_socket_error (Socket s)
189 {
190     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;
191     return ps->error;
192 }
193
194 static int localproxy_select_result(int fd, int event)
195 {
196     Local_Proxy_Socket s;
197     char buf[20480];
198     int ret;
199
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 */
203
204     if (event == 1) {
205         assert(fd == s->from_cmd);
206         ret = read(fd, buf, sizeof(buf));
207         if (ret < 0) {
208             return plug_closing(s->plug, strerror(errno), errno, 0);
209         } else if (ret == 0) {
210             return plug_closing(s->plug, NULL, 0, 0);
211         } else {
212             return plug_receive(s->plug, 0, buf, ret);
213         }
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));
218         return 1;
219     }
220
221     return 1;
222 }
223
224 Socket platform_new_connection(SockAddr addr, char *hostname,
225                                int port, int privport,
226                                int oobinline, int nodelay, int keepalive,
227                                Plug plug, Conf *conf)
228 {
229     char *cmd;
230
231     static const struct socket_function_table socket_fn_table = {
232         sk_localproxy_plug,
233         sk_localproxy_close,
234         sk_localproxy_write,
235         sk_localproxy_write_oob,
236         sk_localproxy_flush,
237         sk_localproxy_set_private_ptr,
238         sk_localproxy_get_private_ptr,
239         sk_localproxy_set_frozen,
240         sk_localproxy_socket_error
241     };
242
243     Local_Proxy_Socket ret;
244     int to_cmd_pipe[2], from_cmd_pipe[2], pid;
245
246     if (conf_get_int(conf, CONF_proxy_type) != PROXY_CMD)
247         return NULL;
248
249     cmd = format_telnet_command(addr, port, conf);
250
251     ret = snew(struct Socket_localproxy_tag);
252     ret->fn = &socket_fn_table;
253     ret->plug = plug;
254     ret->error = NULL;
255
256     bufchain_init(&ret->pending_input_data);
257     bufchain_init(&ret->pending_output_data);
258
259     /*
260      * Create the pipes to the proxy command, and spawn the proxy
261      * command process.
262      */
263     if (pipe(to_cmd_pipe) < 0 ||
264         pipe(from_cmd_pipe) < 0) {
265         ret->error = dupprintf("pipe: %s", strerror(errno));
266         return (Socket)ret;
267     }
268     cloexec(to_cmd_pipe[1]);
269     cloexec(from_cmd_pipe[0]);
270
271     pid = fork();
272
273     if (pid < 0) {
274         ret->error = dupprintf("fork: %s", strerror(errno));
275         return (Socket)ret;
276     } else if (pid == 0) {
277         close(0);
278         close(1);
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);
286         _exit(255);
287     }
288
289     sfree(cmd);
290
291     close(to_cmd_pipe[0]);
292     close(from_cmd_pipe[1]);
293
294     ret->to_cmd = to_cmd_pipe[1];
295     ret->from_cmd = from_cmd_pipe[0];
296
297     if (!localproxy_by_fromfd)
298         localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);
299     if (!localproxy_by_tofd)
300         localproxy_by_tofd = newtree234(localproxy_tofd_cmp);
301
302     add234(localproxy_by_fromfd, ret);
303     add234(localproxy_by_tofd, ret);
304
305     uxsel_set(ret->from_cmd, 1, localproxy_select_result);
306
307     /* We are responsible for this and don't need it any more */
308     sk_addr_free(addr);
309
310     return (Socket) ret;
311 }