OSDN Git Service

Add PuTTY 0.61 to contrib directory.
[ffftp/ffftp.git] / contrib / putty / UNIX / UXPROXY.C
1 /*\r
2  * uxproxy.c: Unix implementation of platform_new_connection(),\r
3  * supporting an OpenSSH-like proxy command.\r
4  */\r
5 \r
6 #include <stdio.h>\r
7 #include <assert.h>\r
8 #include <errno.h>\r
9 #include <unistd.h>\r
10 #include <fcntl.h>\r
11 \r
12 #define DEFINE_PLUG_METHOD_MACROS\r
13 #include "tree234.h"\r
14 #include "putty.h"\r
15 #include "network.h"\r
16 #include "proxy.h"\r
17 \r
18 typedef struct Socket_localproxy_tag * Local_Proxy_Socket;\r
19 \r
20 struct Socket_localproxy_tag {\r
21     const struct socket_function_table *fn;\r
22     /* the above variable absolutely *must* be the first in this structure */\r
23 \r
24     int to_cmd, from_cmd;              /* fds */\r
25 \r
26     char *error;\r
27 \r
28     Plug plug;\r
29 \r
30     bufchain pending_output_data;\r
31     bufchain pending_input_data;\r
32 \r
33     void *privptr;\r
34 };\r
35 \r
36 static int localproxy_select_result(int fd, int event);\r
37 \r
38 /*\r
39  * Trees to look up the pipe fds in.\r
40  */\r
41 static tree234 *localproxy_by_fromfd, *localproxy_by_tofd;\r
42 static int localproxy_fromfd_cmp(void *av, void *bv)\r
43 {\r
44     Local_Proxy_Socket a = (Local_Proxy_Socket)av;\r
45     Local_Proxy_Socket b = (Local_Proxy_Socket)bv;\r
46     if (a->from_cmd < b->from_cmd)\r
47         return -1;\r
48     if (a->from_cmd > b->from_cmd)\r
49         return +1;\r
50     return 0;\r
51 }\r
52 static int localproxy_fromfd_find(void *av, void *bv)\r
53 {\r
54     int a = *(int *)av;\r
55     Local_Proxy_Socket b = (Local_Proxy_Socket)bv;\r
56     if (a < b->from_cmd)\r
57         return -1;\r
58     if (a > b->from_cmd)\r
59         return +1;\r
60     return 0;\r
61 }\r
62 static int localproxy_tofd_cmp(void *av, void *bv)\r
63 {\r
64     Local_Proxy_Socket a = (Local_Proxy_Socket)av;\r
65     Local_Proxy_Socket b = (Local_Proxy_Socket)bv;\r
66     if (a->to_cmd < b->to_cmd)\r
67         return -1;\r
68     if (a->to_cmd > b->to_cmd)\r
69         return +1;\r
70     return 0;\r
71 }\r
72 static int localproxy_tofd_find(void *av, void *bv)\r
73 {\r
74     int a = *(int *)av;\r
75     Local_Proxy_Socket b = (Local_Proxy_Socket)bv;\r
76     if (a < b->to_cmd)\r
77         return -1;\r
78     if (a > b->to_cmd)\r
79         return +1;\r
80     return 0;\r
81 }\r
82 \r
83 /* basic proxy socket functions */\r
84 \r
85 static Plug sk_localproxy_plug (Socket s, Plug p)\r
86 {\r
87     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
88     Plug ret = ps->plug;\r
89     if (p)\r
90         ps->plug = p;\r
91     return ret;\r
92 }\r
93 \r
94 static void sk_localproxy_close (Socket s)\r
95 {\r
96     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
97 \r
98     del234(localproxy_by_fromfd, ps);\r
99     del234(localproxy_by_tofd, ps);\r
100 \r
101     uxsel_del(ps->to_cmd);\r
102     uxsel_del(ps->from_cmd);\r
103     close(ps->to_cmd);\r
104     close(ps->from_cmd);\r
105 \r
106     sfree(ps);\r
107 }\r
108 \r
109 static int localproxy_try_send(Local_Proxy_Socket ps)\r
110 {\r
111     int sent = 0;\r
112 \r
113     while (bufchain_size(&ps->pending_output_data) > 0) {\r
114         void *data;\r
115         int len, ret;\r
116 \r
117         bufchain_prefix(&ps->pending_output_data, &data, &len);\r
118         ret = write(ps->to_cmd, data, len);\r
119         if (ret < 0 && errno != EWOULDBLOCK) {\r
120             /* We're inside the Unix frontend here, so we know\r
121              * that the frontend handle is unnecessary. */\r
122             logevent(NULL, strerror(errno));\r
123             fatalbox("%s", strerror(errno));\r
124         } else if (ret <= 0) {\r
125             break;\r
126         } else {\r
127             bufchain_consume(&ps->pending_output_data, ret);\r
128             sent += ret;\r
129         }\r
130     }\r
131 \r
132     if (bufchain_size(&ps->pending_output_data) == 0)\r
133         uxsel_del(ps->to_cmd);\r
134     else\r
135         uxsel_set(ps->to_cmd, 2, localproxy_select_result);\r
136 \r
137     return sent;\r
138 }\r
139 \r
140 static int sk_localproxy_write (Socket s, const char *data, int len)\r
141 {\r
142     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
143 \r
144     bufchain_add(&ps->pending_output_data, data, len);\r
145 \r
146     localproxy_try_send(ps);\r
147 \r
148     return bufchain_size(&ps->pending_output_data);\r
149 }\r
150 \r
151 static int sk_localproxy_write_oob (Socket s, const char *data, int len)\r
152 {\r
153     /*\r
154      * oob data is treated as inband; nasty, but nothing really\r
155      * better we can do\r
156      */\r
157     return sk_localproxy_write(s, data, len);\r
158 }\r
159 \r
160 static void sk_localproxy_flush (Socket s)\r
161 {\r
162     /* Local_Proxy_Socket ps = (Local_Proxy_Socket) s; */\r
163     /* do nothing */\r
164 }\r
165 \r
166 static void sk_localproxy_set_private_ptr (Socket s, void *ptr)\r
167 {\r
168     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
169     ps->privptr = ptr;\r
170 }\r
171 \r
172 static void * sk_localproxy_get_private_ptr (Socket s)\r
173 {\r
174     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
175     return ps->privptr;\r
176 }\r
177 \r
178 static void sk_localproxy_set_frozen (Socket s, int is_frozen)\r
179 {\r
180     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
181 \r
182     if (is_frozen)\r
183         uxsel_del(ps->from_cmd);\r
184     else\r
185         uxsel_set(ps->from_cmd, 1, localproxy_select_result);\r
186 }\r
187 \r
188 static const char * sk_localproxy_socket_error (Socket s)\r
189 {\r
190     Local_Proxy_Socket ps = (Local_Proxy_Socket) s;\r
191     return ps->error;\r
192 }\r
193 \r
194 static int localproxy_select_result(int fd, int event)\r
195 {\r
196     Local_Proxy_Socket s;\r
197     char buf[20480];\r
198     int ret;\r
199 \r
200     if (!(s = find234(localproxy_by_fromfd, &fd, localproxy_fromfd_find)) &&\r
201         !(s = find234(localproxy_by_tofd, &fd, localproxy_tofd_find)) )\r
202         return 1;                      /* boggle */\r
203 \r
204     if (event == 1) {\r
205         assert(fd == s->from_cmd);\r
206         ret = read(fd, buf, sizeof(buf));\r
207         if (ret < 0) {\r
208             return plug_closing(s->plug, strerror(errno), errno, 0);\r
209         } else if (ret == 0) {\r
210             return plug_closing(s->plug, NULL, 0, 0);\r
211         } else {\r
212             return plug_receive(s->plug, 0, buf, ret);\r
213         }\r
214     } else if (event == 2) {\r
215         assert(fd == s->to_cmd);\r
216         if (localproxy_try_send(s))\r
217             plug_sent(s->plug, bufchain_size(&s->pending_output_data));\r
218         return 1;\r
219     }\r
220 \r
221     return 1;\r
222 }\r
223 \r
224 Socket platform_new_connection(SockAddr addr, char *hostname,\r
225                                int port, int privport,\r
226                                int oobinline, int nodelay, int keepalive,\r
227                                Plug plug, const Config *cfg)\r
228 {\r
229     char *cmd;\r
230 \r
231     static const struct socket_function_table socket_fn_table = {\r
232         sk_localproxy_plug,\r
233         sk_localproxy_close,\r
234         sk_localproxy_write,\r
235         sk_localproxy_write_oob,\r
236         sk_localproxy_flush,\r
237         sk_localproxy_set_private_ptr,\r
238         sk_localproxy_get_private_ptr,\r
239         sk_localproxy_set_frozen,\r
240         sk_localproxy_socket_error\r
241     };\r
242 \r
243     Local_Proxy_Socket ret;\r
244     int to_cmd_pipe[2], from_cmd_pipe[2], pid;\r
245 \r
246     if (cfg->proxy_type != PROXY_CMD)\r
247         return NULL;\r
248 \r
249     cmd = format_telnet_command(addr, port, cfg);\r
250 \r
251     ret = snew(struct Socket_localproxy_tag);\r
252     ret->fn = &socket_fn_table;\r
253     ret->plug = plug;\r
254     ret->error = NULL;\r
255 \r
256     bufchain_init(&ret->pending_input_data);\r
257     bufchain_init(&ret->pending_output_data);\r
258 \r
259     /*\r
260      * Create the pipes to the proxy command, and spawn the proxy\r
261      * command process.\r
262      */\r
263     if (pipe(to_cmd_pipe) < 0 ||\r
264         pipe(from_cmd_pipe) < 0) {\r
265         ret->error = dupprintf("pipe: %s", strerror(errno));\r
266         return (Socket)ret;\r
267     }\r
268     cloexec(to_cmd_pipe[1]);\r
269     cloexec(from_cmd_pipe[0]);\r
270 \r
271     pid = fork();\r
272 \r
273     if (pid < 0) {\r
274         ret->error = dupprintf("fork: %s", strerror(errno));\r
275         return (Socket)ret;\r
276     } else if (pid == 0) {\r
277         close(0);\r
278         close(1);\r
279         dup2(to_cmd_pipe[0], 0);\r
280         dup2(from_cmd_pipe[1], 1);\r
281         close(to_cmd_pipe[0]);\r
282         close(from_cmd_pipe[1]);\r
283         fcntl(0, F_SETFD, 0);\r
284         fcntl(1, F_SETFD, 0);\r
285         execl("/bin/sh", "sh", "-c", cmd, (void *)NULL);\r
286         _exit(255);\r
287     }\r
288 \r
289     sfree(cmd);\r
290 \r
291     close(to_cmd_pipe[0]);\r
292     close(from_cmd_pipe[1]);\r
293 \r
294     ret->to_cmd = to_cmd_pipe[1];\r
295     ret->from_cmd = from_cmd_pipe[0];\r
296 \r
297     if (!localproxy_by_fromfd)\r
298         localproxy_by_fromfd = newtree234(localproxy_fromfd_cmp);\r
299     if (!localproxy_by_tofd)\r
300         localproxy_by_tofd = newtree234(localproxy_tofd_cmp);\r
301 \r
302     add234(localproxy_by_fromfd, ret);\r
303     add234(localproxy_by_tofd, ret);\r
304 \r
305     uxsel_set(ret->from_cmd, 1, localproxy_select_result);\r
306 \r
307     /* We are responsible for this and don't need it any more */\r
308     sk_addr_free(addr);\r
309 \r
310     return (Socket) ret;\r
311 }\r