OSDN Git Service

Fix project files.
[ffftp/ffftp.git] / contrib / putty / PORTFWD.C
1 /*\r
2  * SSH port forwarding.\r
3  */\r
4 \r
5 #include <stdio.h>\r
6 #include <stdlib.h>\r
7 \r
8 #include "putty.h"\r
9 #include "ssh.h"\r
10 \r
11 #ifndef FALSE\r
12 #define FALSE 0\r
13 #endif\r
14 #ifndef TRUE\r
15 #define TRUE 1\r
16 #endif\r
17 \r
18 struct PFwdPrivate {\r
19     const struct plug_function_table *fn;\r
20     /* the above variable absolutely *must* be the first in this structure */\r
21     void *c;                           /* (channel) data used by ssh.c */\r
22     void *backhandle;                  /* instance of SSH backend itself */\r
23     /* Note that backhandle need not be filled in if c is non-NULL */\r
24     Socket s;\r
25     int throttled, throttle_override;\r
26     int ready;\r
27     /*\r
28      * `dynamic' does double duty. It's set to 0 for an ordinary\r
29      * forwarded port, and nonzero for SOCKS-style dynamic port\r
30      * forwarding; but it also represents the state of the SOCKS\r
31      * exchange.\r
32      */\r
33     int dynamic;\r
34     /*\r
35      * `hostname' and `port' are the real hostname and port, once\r
36      * we know what we're connecting to; they're unused for this\r
37      * purpose while conducting a local SOCKS exchange, which means\r
38      * we can also use them as a buffer and pointer for reading\r
39      * data from the SOCKS client.\r
40      */\r
41     char hostname[256+8];\r
42     int port;\r
43     /*\r
44      * When doing dynamic port forwarding, we can receive\r
45      * connection data before we are actually able to send it; so\r
46      * we may have to temporarily hold some in a dynamically\r
47      * allocated buffer here.\r
48      */\r
49     void *buffer;\r
50     int buflen;\r
51 };\r
52 \r
53 static void pfd_log(Plug plug, int type, SockAddr addr, int port,\r
54                     const char *error_msg, int error_code)\r
55 {\r
56     /* we have to dump these since we have no interface to logging.c */\r
57 }\r
58 \r
59 static int pfd_closing(Plug plug, const char *error_msg, int error_code,\r
60                        int calling_back)\r
61 {\r
62     struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
63 \r
64     /*\r
65      * We have no way to communicate down the forwarded connection,\r
66      * so if an error occurred on the socket, we just ignore it\r
67      * and treat it like a proper close.\r
68      */\r
69     if (pr->c)\r
70         sshfwd_close(pr->c);\r
71     pfd_close(pr->s);\r
72     return 1;\r
73 }\r
74 \r
75 static int pfd_receive(Plug plug, int urgent, char *data, int len)\r
76 {\r
77     struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
78     if (pr->dynamic) {\r
79         while (len--) {\r
80             /*\r
81              * Throughout SOCKS negotiation, "hostname" is re-used as a\r
82              * random protocol buffer with "port" storing the length.\r
83              */ \r
84             if (pr->port >= lenof(pr->hostname)) {\r
85                 /* Request too long. */\r
86                 if ((pr->dynamic >> 12) == 4) {\r
87                     /* Send back a SOCKS 4 error before closing. */\r
88                     char data[8];\r
89                     memset(data, 0, sizeof(data));\r
90                     data[1] = 91;      /* generic `request rejected' */\r
91                     sk_write(pr->s, data, 8);\r
92                 }\r
93                 pfd_close(pr->s);\r
94                 return 1;\r
95             }\r
96             pr->hostname[pr->port++] = *data++;\r
97 \r
98             /*\r
99              * Now check what's in the buffer to see if it's a\r
100              * valid and complete message in the SOCKS exchange.\r
101              */\r
102             if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 4) &&\r
103                 pr->hostname[0] == 4) {\r
104                 /*\r
105                  * SOCKS 4.\r
106                  */\r
107                 if (pr->dynamic == 1)\r
108                     pr->dynamic = 0x4000;\r
109                 if (pr->port < 2) continue;/* don't have command code yet */\r
110                 if (pr->hostname[1] != 1) {\r
111                     /* Not CONNECT. */\r
112                     /* Send back a SOCKS 4 error before closing. */\r
113                     char data[8];\r
114                     memset(data, 0, sizeof(data));\r
115                     data[1] = 91;      /* generic `request rejected' */\r
116                     sk_write(pr->s, data, 8);\r
117                     pfd_close(pr->s);\r
118                     return 1;\r
119                 }\r
120                 if (pr->port <= 8) continue; /* haven't started user/hostname */\r
121                 if (pr->hostname[pr->port-1] != 0)\r
122                     continue;          /* haven't _finished_ user/hostname */\r
123                 /*\r
124                  * Now we have a full SOCKS 4 request. Check it to\r
125                  * see if it's a SOCKS 4A request.\r
126                  */\r
127                 if (pr->hostname[4] == 0 && pr->hostname[5] == 0 &&\r
128                     pr->hostname[6] == 0 && pr->hostname[7] != 0) {\r
129                     /*\r
130                      * It's SOCKS 4A. So if we haven't yet\r
131                      * collected the host name, we should continue\r
132                      * waiting for data in order to do so; if we\r
133                      * have, we can go ahead.\r
134                      */\r
135                     int len;\r
136                     if (pr->dynamic == 0x4000) {\r
137                         pr->dynamic = 0x4001;\r
138                         pr->port = 8;      /* reset buffer to overwrite name */\r
139                         continue;\r
140                     }\r
141                     pr->hostname[0] = 0;   /* reply version code */\r
142                     pr->hostname[1] = 90;   /* request granted */\r
143                     sk_write(pr->s, pr->hostname, 8);\r
144                     len= pr->port - 8;\r
145                     pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2);\r
146                     memmove(pr->hostname, pr->hostname + 8, len);\r
147                     goto connect;\r
148                 } else {\r
149                     /*\r
150                      * It's SOCKS 4, which means we should format\r
151                      * the IP address into the hostname string and\r
152                      * then just go.\r
153                      */\r
154                     pr->hostname[0] = 0;   /* reply version code */\r
155                     pr->hostname[1] = 90;   /* request granted */\r
156                     sk_write(pr->s, pr->hostname, 8);\r
157                     pr->port = GET_16BIT_MSB_FIRST(pr->hostname+2);\r
158                     sprintf(pr->hostname, "%d.%d.%d.%d",\r
159                             (unsigned char)pr->hostname[4],\r
160                             (unsigned char)pr->hostname[5],\r
161                             (unsigned char)pr->hostname[6],\r
162                             (unsigned char)pr->hostname[7]);\r
163                     goto connect;\r
164                 }\r
165             }\r
166 \r
167             if ((pr->dynamic == 1 || (pr->dynamic >> 12) == 5) &&\r
168                 pr->hostname[0] == 5) {\r
169                 /*\r
170                  * SOCKS 5.\r
171                  */\r
172                 if (pr->dynamic == 1)\r
173                     pr->dynamic = 0x5000;\r
174 \r
175                 if (pr->dynamic == 0x5000) {\r
176                     int i, method;\r
177                     char data[2];\r
178                     /*\r
179                      * We're receiving a set of method identifiers.\r
180                      */\r
181                     if (pr->port < 2) continue;/* no method count yet */\r
182                     if (pr->port < 2 + (unsigned char)pr->hostname[1])\r
183                         continue;      /* no methods yet */\r
184                     method = 0xFF;     /* invalid */\r
185                     for (i = 0; i < (unsigned char)pr->hostname[1]; i++)\r
186                         if (pr->hostname[2+i] == 0) {\r
187                             method = 0;/* no auth */\r
188                             break;\r
189                         }\r
190                     data[0] = 5;\r
191                     data[1] = method;\r
192                     sk_write(pr->s, data, 2);\r
193                     pr->dynamic = 0x5001;\r
194                     pr->port = 0;      /* re-empty the buffer */\r
195                     continue;\r
196                 }\r
197 \r
198                 if (pr->dynamic == 0x5001) {\r
199                     /*\r
200                      * We're receiving a SOCKS request.\r
201                      */\r
202                     unsigned char reply[10]; /* SOCKS5 atyp=1 reply */\r
203                     int atype, alen = 0;\r
204 \r
205                     /*\r
206                      * Pre-fill reply packet.\r
207                      * In all cases, we set BND.{HOST,ADDR} to 0.0.0.0:0\r
208                      * (atyp=1) in the reply; if we succeed, we don't know\r
209                      * the right answers, and if we fail, they should be\r
210                      * ignored.\r
211                      */\r
212                     memset(reply, 0, lenof(reply));\r
213                     reply[0] = 5; /* VER */\r
214                     reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */\r
215 \r
216                     if (pr->port < 6) continue;\r
217                     atype = (unsigned char)pr->hostname[3];\r
218                     if (atype == 1)    /* IPv4 address */\r
219                         alen = 4;\r
220                     if (atype == 4)    /* IPv6 address */\r
221                         alen = 16;\r
222                     if (atype == 3)    /* domain name has leading length */\r
223                         alen = 1 + (unsigned char)pr->hostname[4];\r
224                     if (pr->port < 6 + alen) continue;\r
225                     if (pr->hostname[1] != 1 || pr->hostname[2] != 0) {\r
226                         /* Not CONNECT or reserved field nonzero - error */\r
227                         reply[1] = 1;   /* generic failure */\r
228                         sk_write(pr->s, (char *) reply, lenof(reply));\r
229                         pfd_close(pr->s);\r
230                         return 1;\r
231                     }\r
232                     /*\r
233                      * Now we have a viable connect request. Switch\r
234                      * on atype.\r
235                      */\r
236                     pr->port = GET_16BIT_MSB_FIRST(pr->hostname+4+alen);\r
237                     if (atype == 1) {\r
238                         /* REP=0 (success) already */\r
239                         sk_write(pr->s, (char *) reply, lenof(reply));\r
240                         sprintf(pr->hostname, "%d.%d.%d.%d",\r
241                                 (unsigned char)pr->hostname[4],\r
242                                 (unsigned char)pr->hostname[5],\r
243                                 (unsigned char)pr->hostname[6],\r
244                                 (unsigned char)pr->hostname[7]);\r
245                         goto connect;\r
246                     } else if (atype == 3) {\r
247                         /* REP=0 (success) already */\r
248                         sk_write(pr->s, (char *) reply, lenof(reply));\r
249                         memmove(pr->hostname, pr->hostname + 5, alen-1);\r
250                         pr->hostname[alen-1] = '\0';\r
251                         goto connect;\r
252                     } else {\r
253                         /*\r
254                          * Unknown address type. (FIXME: support IPv6!)\r
255                          */\r
256                         reply[1] = 8;   /* atype not supported */\r
257                         sk_write(pr->s, (char *) reply, lenof(reply));\r
258                         pfd_close(pr->s);\r
259                         return 1;\r
260                     }\r
261                 }\r
262             }\r
263 \r
264             /*\r
265              * If we get here without either having done `continue'\r
266              * or `goto connect', it must be because there is no\r
267              * sensible interpretation of what's in our buffer. So\r
268              * close the connection rudely.\r
269              */\r
270             pfd_close(pr->s);\r
271             return 1;\r
272         }\r
273         return 1;\r
274 \r
275         /*\r
276          * We come here when we're ready to make an actual\r
277          * connection.\r
278          */\r
279         connect:\r
280 \r
281         /*\r
282          * Freeze the socket until the SSH server confirms the\r
283          * connection.\r
284          */\r
285         sk_set_frozen(pr->s, 1);\r
286 \r
287         pr->c = new_sock_channel(pr->backhandle, pr->s);\r
288         if (pr->c == NULL) {\r
289             pfd_close(pr->s);\r
290             return 1;\r
291         } else {\r
292             /* asks to forward to the specified host/port for this */\r
293             ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");\r
294         }\r
295         pr->dynamic = 0;\r
296 \r
297         /*\r
298          * If there's any data remaining in our current buffer,\r
299          * save it to be sent on pfd_confirm().\r
300          */\r
301         if (len > 0) {\r
302             pr->buffer = snewn(len, char);\r
303             memcpy(pr->buffer, data, len);\r
304             pr->buflen = len;\r
305         }\r
306     }\r
307     if (pr->ready) {\r
308         if (sshfwd_write(pr->c, data, len) > 0) {\r
309             pr->throttled = 1;\r
310             sk_set_frozen(pr->s, 1);\r
311         }\r
312     }\r
313     return 1;\r
314 }\r
315 \r
316 static void pfd_sent(Plug plug, int bufsize)\r
317 {\r
318     struct PFwdPrivate *pr = (struct PFwdPrivate *) plug;\r
319 \r
320     if (pr->c)\r
321         sshfwd_unthrottle(pr->c, bufsize);\r
322 }\r
323 \r
324 /*\r
325  * Called when receiving a PORT OPEN from the server\r
326  */\r
327 const char *pfd_newconnect(Socket *s, char *hostname, int port,\r
328                            void *c, const Config *cfg, int addressfamily)\r
329 {\r
330     static const struct plug_function_table fn_table = {\r
331         pfd_log,\r
332         pfd_closing,\r
333         pfd_receive,\r
334         pfd_sent,\r
335         NULL\r
336     };\r
337 \r
338     SockAddr addr;\r
339     const char *err;\r
340     char *dummy_realhost;\r
341     struct PFwdPrivate *pr;\r
342 \r
343     /*\r
344      * Try to find host.\r
345      */\r
346     addr = name_lookup(hostname, port, &dummy_realhost, cfg, addressfamily);\r
347     if ((err = sk_addr_error(addr)) != NULL) {\r
348         sk_addr_free(addr);\r
349         return err;\r
350     }\r
351 \r
352     /*\r
353      * Open socket.\r
354      */\r
355     pr = snew(struct PFwdPrivate);\r
356     pr->buffer = NULL;\r
357     pr->fn = &fn_table;\r
358     pr->throttled = pr->throttle_override = 0;\r
359     pr->ready = 1;\r
360     pr->c = c;\r
361     pr->backhandle = NULL;             /* we shouldn't need this */\r
362     pr->dynamic = 0;\r
363 \r
364     pr->s = *s = new_connection(addr, dummy_realhost, port,\r
365                                 0, 1, 0, 0, (Plug) pr, cfg);\r
366     if ((err = sk_socket_error(*s)) != NULL) {\r
367         sfree(pr);\r
368         return err;\r
369     }\r
370 \r
371     sk_set_private_ptr(*s, pr);\r
372     return NULL;\r
373 }\r
374 \r
375 /*\r
376  called when someone connects to the local port\r
377  */\r
378 \r
379 static int pfd_accepting(Plug p, OSSocket sock)\r
380 {\r
381     static const struct plug_function_table fn_table = {\r
382         pfd_log,\r
383         pfd_closing,\r
384         pfd_receive,\r
385         pfd_sent,\r
386         NULL\r
387     };\r
388     struct PFwdPrivate *pr, *org;\r
389     Socket s;\r
390     const char *err;\r
391 \r
392     org = (struct PFwdPrivate *)p;\r
393     pr = snew(struct PFwdPrivate);\r
394     pr->buffer = NULL;\r
395     pr->fn = &fn_table;\r
396 \r
397     pr->c = NULL;\r
398     pr->backhandle = org->backhandle;\r
399 \r
400     pr->s = s = sk_register(sock, (Plug) pr);\r
401     if ((err = sk_socket_error(s)) != NULL) {\r
402         sfree(pr);\r
403         return err != NULL;\r
404     }\r
405 \r
406     sk_set_private_ptr(s, pr);\r
407 \r
408     pr->throttled = pr->throttle_override = 0;\r
409     pr->ready = 0;\r
410 \r
411     if (org->dynamic) {\r
412         pr->dynamic = 1;\r
413         pr->port = 0;                  /* "hostname" buffer is so far empty */\r
414         sk_set_frozen(s, 0);           /* we want to receive SOCKS _now_! */\r
415     } else {\r
416         pr->dynamic = 0;\r
417         strcpy(pr->hostname, org->hostname);\r
418         pr->port = org->port;   \r
419         pr->c = new_sock_channel(org->backhandle, s);\r
420 \r
421         if (pr->c == NULL) {\r
422             sfree(pr);\r
423             return 1;\r
424         } else {\r
425             /* asks to forward to the specified host/port for this */\r
426             ssh_send_port_open(pr->c, pr->hostname, pr->port, "forwarding");\r
427         }\r
428     }\r
429 \r
430     return 0;\r
431 }\r
432 \r
433 \r
434 /* Add a new forwarding from port -> desthost:destport\r
435  sets up a listener on the local machine on (srcaddr:)port\r
436  */\r
437 const char *pfd_addforward(char *desthost, int destport, char *srcaddr,\r
438                            int port, void *backhandle, const Config *cfg,\r
439                            void **sockdata, int address_family)\r
440 {\r
441     static const struct plug_function_table fn_table = {\r
442         pfd_log,\r
443         pfd_closing,\r
444         pfd_receive,                   /* should not happen... */\r
445         pfd_sent,                      /* also should not happen */\r
446         pfd_accepting\r
447     };\r
448 \r
449     const char *err;\r
450     struct PFwdPrivate *pr;\r
451     Socket s;\r
452 \r
453     /*\r
454      * Open socket.\r
455      */\r
456     pr = snew(struct PFwdPrivate);\r
457     pr->buffer = NULL;\r
458     pr->fn = &fn_table;\r
459     pr->c = NULL;\r
460     if (desthost) {\r
461         strcpy(pr->hostname, desthost);\r
462         pr->port = destport;\r
463         pr->dynamic = 0;\r
464     } else\r
465         pr->dynamic = 1;\r
466     pr->throttled = pr->throttle_override = 0;\r
467     pr->ready = 0;\r
468     pr->backhandle = backhandle;\r
469 \r
470     pr->s = s = new_listener(srcaddr, port, (Plug) pr,\r
471                              !cfg->lport_acceptall, cfg, address_family);\r
472     if ((err = sk_socket_error(s)) != NULL) {\r
473         sfree(pr);\r
474         return err;\r
475     }\r
476 \r
477     sk_set_private_ptr(s, pr);\r
478 \r
479     *sockdata = (void *)s;\r
480 \r
481     return NULL;\r
482 }\r
483 \r
484 void pfd_close(Socket s)\r
485 {\r
486     struct PFwdPrivate *pr;\r
487 \r
488     if (!s)\r
489         return;\r
490 \r
491     pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
492 \r
493     sfree(pr->buffer);\r
494     sfree(pr);\r
495 \r
496     sk_close(s);\r
497 }\r
498 \r
499 /*\r
500  * Terminate a listener.\r
501  */\r
502 void pfd_terminate(void *sv)\r
503 {\r
504     pfd_close((Socket)sv);\r
505 }\r
506 \r
507 void pfd_unthrottle(Socket s)\r
508 {\r
509     struct PFwdPrivate *pr;\r
510     if (!s)\r
511         return;\r
512     pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
513 \r
514     pr->throttled = 0;\r
515     sk_set_frozen(s, pr->throttled || pr->throttle_override);\r
516 }\r
517 \r
518 void pfd_override_throttle(Socket s, int enable)\r
519 {\r
520     struct PFwdPrivate *pr;\r
521     if (!s)\r
522         return;\r
523     pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
524 \r
525     pr->throttle_override = enable;\r
526     sk_set_frozen(s, pr->throttled || pr->throttle_override);\r
527 }\r
528 \r
529 /*\r
530  * Called to send data down the raw connection.\r
531  */\r
532 int pfd_send(Socket s, char *data, int len)\r
533 {\r
534     if (s == NULL)\r
535         return 0;\r
536     return sk_write(s, data, len);\r
537 }\r
538 \r
539 \r
540 void pfd_confirm(Socket s)\r
541 {\r
542     struct PFwdPrivate *pr;\r
543 \r
544     if (s == NULL)\r
545         return;\r
546 \r
547     pr = (struct PFwdPrivate *) sk_get_private_ptr(s);\r
548     pr->ready = 1;\r
549     sk_set_frozen(s, 0);\r
550     sk_write(s, NULL, 0);\r
551     if (pr->buffer) {\r
552         sshfwd_write(pr->c, pr->buffer, pr->buflen);\r
553         sfree(pr->buffer);\r
554         pr->buffer = NULL;\r
555     }\r
556 }\r