Fixed issue #2931: Make “ChangeList” grids in “Git synchronization” multi-selectable
[TortoiseGit.git] / src / TortoisePlink / PORTFWD.C
blobd28d143df0337fd808ef07641121703402b31c2d
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
11 #ifndef FALSE\r
12 #define FALSE 0\r
13 #endif\r
14 #ifndef TRUE\r
15 #define TRUE 1\r
16 #endif\r
18 struct PortForwarding {\r
19     const struct plug_function_table *fn;\r
20     /* the above variable absolutely *must* be the first in this structure */\r
21     struct ssh_channel *c;        /* channel structure held 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 the nonzero values are also a state machine\r
31      * tracking where the SOCKS exchange has got to.\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.\r
37      */\r
38     char *hostname;\r
39     int port;\r
40     /*\r
41      * `socksbuf' is the buffer we use to accumulate a SOCKS request.\r
42      */\r
43     char *socksbuf;\r
44     int sockslen, sockssize;\r
45     /*\r
46      * When doing dynamic port forwarding, we can receive\r
47      * connection data before we are actually able to send it; so\r
48      * we may have to temporarily hold some in a dynamically\r
49      * allocated buffer here.\r
50      */\r
51     void *buffer;\r
52     int buflen;\r
53 };\r
55 struct PortListener {\r
56     const struct plug_function_table *fn;\r
57     /* the above variable absolutely *must* be the first in this structure */\r
58     void *backhandle;                  /* instance of SSH backend itself */\r
59     Socket s;\r
60     /*\r
61      * `dynamic' is set to 0 for an ordinary forwarded port, and\r
62      * nonzero for SOCKS-style dynamic port forwarding.\r
63      */\r
64     int dynamic;\r
65     /*\r
66      * `hostname' and `port' are the real hostname and port, for\r
67      * ordinary forwardings.\r
68      */\r
69     char *hostname;\r
70     int port;\r
71 };\r
73 static struct PortForwarding *new_portfwd_state(void)\r
74 {\r
75     struct PortForwarding *pf = snew(struct PortForwarding);\r
76     pf->hostname = NULL;\r
77     pf->socksbuf = NULL;\r
78     pf->sockslen = pf->sockssize = 0;\r
79     pf->buffer = NULL;\r
80     return pf;\r
81 }\r
83 static void free_portfwd_state(struct PortForwarding *pf)\r
84 {\r
85     if (!pf)\r
86         return;\r
87     sfree(pf->hostname);\r
88     sfree(pf->socksbuf);\r
89     sfree(pf->buffer);\r
90     sfree(pf);\r
91 }\r
93 static struct PortListener *new_portlistener_state(void)\r
94 {\r
95     struct PortListener *pl = snew(struct PortListener);\r
96     pl->hostname = NULL;\r
97     return pl;\r
98 }\r
100 static void free_portlistener_state(struct PortListener *pl)\r
102     if (!pl)\r
103         return;\r
104     sfree(pl->hostname);\r
105     sfree(pl);\r
108 static void pfd_log(Plug plug, int type, SockAddr addr, int port,\r
109                     const char *error_msg, int error_code)\r
111     /* we have to dump these since we have no interface to logging.c */\r
114 static void pfl_log(Plug plug, int type, SockAddr addr, int port,\r
115                     const char *error_msg, int error_code)\r
117     /* we have to dump these since we have no interface to logging.c */\r
120 static int pfd_closing(Plug plug, const char *error_msg, int error_code,\r
121                        int calling_back)\r
123     struct PortForwarding *pf = (struct PortForwarding *) plug;\r
125     if (error_msg) {\r
126         /*\r
127          * Socket error. Slam the connection instantly shut.\r
128          */\r
129         if (pf->c) {\r
130             sshfwd_unclean_close(pf->c, error_msg);\r
131         } else {\r
132             /*\r
133              * We might not have an SSH channel, if a socket error\r
134              * occurred during SOCKS negotiation. If not, we must\r
135              * clean ourself up without sshfwd_unclean_close's call\r
136              * back to pfd_close.\r
137              */\r
138             pfd_close(pf);\r
139         }\r
140     } else {\r
141         /*\r
142          * Ordinary EOF received on socket. Send an EOF on the SSH\r
143          * channel.\r
144          */\r
145         if (pf->c)\r
146             sshfwd_write_eof(pf->c);\r
147     }\r
149     return 1;\r
152 static int pfl_closing(Plug plug, const char *error_msg, int error_code,\r
153                        int calling_back)\r
155     struct PortListener *pl = (struct PortListener *) plug;\r
156     pfl_terminate(pl);\r
157     return 1;\r
160 static void wrap_send_port_open(void *channel, char *hostname, int port,\r
161                                 Socket s)\r
163     char *peerinfo, *description;\r
164     peerinfo = sk_peer_info(s);\r
165     if (peerinfo) {\r
166         description = dupprintf("forwarding from %s", peerinfo);\r
167         sfree(peerinfo);\r
168     } else {\r
169         description = dupstr("forwarding");\r
170     }\r
171     ssh_send_port_open(channel, hostname, port, description);\r
172     sfree(description);\r
175 static int pfd_receive(Plug plug, int urgent, char *data, int len)\r
177     struct PortForwarding *pf = (struct PortForwarding *) plug;\r
178     if (pf->dynamic) {\r
179         while (len--) {\r
180             if (pf->sockslen >= pf->sockssize) {\r
181                 pf->sockssize = pf->sockslen * 5 / 4 + 256;\r
182                 pf->socksbuf = sresize(pf->socksbuf, pf->sockssize, char);\r
183             }\r
184             pf->socksbuf[pf->sockslen++] = *data++;\r
186             /*\r
187              * Now check what's in the buffer to see if it's a\r
188              * valid and complete message in the SOCKS exchange.\r
189              */\r
190             if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 4) &&\r
191                 pf->socksbuf[0] == 4) {\r
192                 /*\r
193                  * SOCKS 4.\r
194                  */\r
195                 if (pf->dynamic == 1)\r
196                     pf->dynamic = 0x4000;\r
197                 if (pf->sockslen < 2)\r
198                     continue;        /* don't have command code yet */\r
199                 if (pf->socksbuf[1] != 1) {\r
200                     /* Not CONNECT. */\r
201                     /* Send back a SOCKS 4 error before closing. */\r
202                     char data[8];\r
203                     memset(data, 0, sizeof(data));\r
204                     data[1] = 91;      /* generic `request rejected' */\r
205                     sk_write(pf->s, data, 8);\r
206                     pfd_close(pf);\r
207                     return 1;\r
208                 }\r
209                 if (pf->sockslen <= 8)\r
210                     continue;      /* haven't started user/hostname */\r
211                 if (pf->socksbuf[pf->sockslen-1] != 0)\r
212                     continue;          /* haven't _finished_ user/hostname */\r
213                 /*\r
214                  * Now we have a full SOCKS 4 request. Check it to\r
215                  * see if it's a SOCKS 4A request.\r
216                  */\r
217                 if (pf->socksbuf[4] == 0 && pf->socksbuf[5] == 0 &&\r
218                     pf->socksbuf[6] == 0 && pf->socksbuf[7] != 0) {\r
219                     /*\r
220                      * It's SOCKS 4A. So if we haven't yet\r
221                      * collected the host name, we should continue\r
222                      * waiting for data in order to do so; if we\r
223                      * have, we can go ahead.\r
224                      */\r
225                     int len;\r
226                     if (pf->dynamic == 0x4000) {\r
227                         pf->dynamic = 0x4001;\r
228                         pf->sockslen = 8; /* reset buffer to overwrite name */\r
229                         continue;\r
230                     }\r
231                     pf->socksbuf[0] = 0;   /* reply version code */\r
232                     pf->socksbuf[1] = 90;   /* request granted */\r
233                     sk_write(pf->s, pf->socksbuf, 8);\r
234                     len = pf->sockslen - 8;\r
235                     pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2);\r
236                     pf->hostname = snewn(len+1, char);\r
237                     pf->hostname[len] = '\0';\r
238                     memcpy(pf->hostname, pf->socksbuf + 8, len);\r
239                     goto connect;\r
240                 } else {\r
241                     /*\r
242                      * It's SOCKS 4, which means we should format\r
243                      * the IP address into the hostname string and\r
244                      * then just go.\r
245                      */\r
246                     pf->socksbuf[0] = 0;   /* reply version code */\r
247                     pf->socksbuf[1] = 90;   /* request granted */\r
248                     sk_write(pf->s, pf->socksbuf, 8);\r
249                     pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+2);\r
250                     pf->hostname = dupprintf("%d.%d.%d.%d",\r
251                                              (unsigned char)pf->socksbuf[4],\r
252                                              (unsigned char)pf->socksbuf[5],\r
253                                              (unsigned char)pf->socksbuf[6],\r
254                                              (unsigned char)pf->socksbuf[7]);\r
255                     goto connect;\r
256                 }\r
257             }\r
259             if ((pf->dynamic == 1 || (pf->dynamic >> 12) == 5) &&\r
260                 pf->socksbuf[0] == 5) {\r
261                 /*\r
262                  * SOCKS 5.\r
263                  */\r
264                 if (pf->dynamic == 1)\r
265                     pf->dynamic = 0x5000;\r
267                 if (pf->dynamic == 0x5000) {\r
268                     int i, method;\r
269                     char data[2];\r
270                     /*\r
271                      * We're receiving a set of method identifiers.\r
272                      */\r
273                     if (pf->sockslen < 2)\r
274                         continue;      /* no method count yet */\r
275                     if (pf->sockslen < 2 + (unsigned char)pf->socksbuf[1])\r
276                         continue;      /* no methods yet */\r
277                     method = 0xFF;     /* invalid */\r
278                     for (i = 0; i < (unsigned char)pf->socksbuf[1]; i++)\r
279                         if (pf->socksbuf[2+i] == 0) {\r
280                             method = 0;/* no auth */\r
281                             break;\r
282                         }\r
283                     data[0] = 5;\r
284                     data[1] = method;\r
285                     sk_write(pf->s, data, 2);\r
286                     pf->dynamic = 0x5001;\r
287                     pf->sockslen = 0;      /* re-empty the buffer */\r
288                     continue;\r
289                 }\r
291                 if (pf->dynamic == 0x5001) {\r
292                     /*\r
293                      * We're receiving a SOCKS request.\r
294                      */\r
295                     unsigned char reply[10]; /* SOCKS5 atyp=1 reply */\r
296                     int atype, alen = 0;\r
298                     /*\r
299                      * Pre-fill reply packet.\r
300                      * In all cases, we set BND.{HOST,ADDR} to 0.0.0.0:0\r
301                      * (atyp=1) in the reply; if we succeed, we don't know\r
302                      * the right answers, and if we fail, they should be\r
303                      * ignored.\r
304                      */\r
305                     memset(reply, 0, lenof(reply));\r
306                     reply[0] = 5; /* VER */\r
307                     reply[3] = 1; /* ATYP = 1 (IPv4, 0.0.0.0:0) */\r
309                     if (pf->sockslen < 6) continue;\r
310                     atype = (unsigned char)pf->socksbuf[3];\r
311                     if (atype == 1)    /* IPv4 address */\r
312                         alen = 4;\r
313                     if (atype == 4)    /* IPv6 address */\r
314                         alen = 16;\r
315                     if (atype == 3)    /* domain name has leading length */\r
316                         alen = 1 + (unsigned char)pf->socksbuf[4];\r
317                     if (pf->sockslen < 6 + alen) continue;\r
318                     if (pf->socksbuf[1] != 1 || pf->socksbuf[2] != 0) {\r
319                         /* Not CONNECT or reserved field nonzero - error */\r
320                         reply[1] = 1;   /* generic failure */\r
321                         sk_write(pf->s, (char *) reply, lenof(reply));\r
322                         pfd_close(pf);\r
323                         return 1;\r
324                     }\r
325                     /*\r
326                      * Now we have a viable connect request. Switch\r
327                      * on atype.\r
328                      */\r
329                     pf->port = GET_16BIT_MSB_FIRST(pf->socksbuf+4+alen);\r
330                     if (atype == 1) {\r
331                         /* REP=0 (success) already */\r
332                         sk_write(pf->s, (char *) reply, lenof(reply));\r
333                         pf->hostname = dupprintf("%d.%d.%d.%d",\r
334                                                  (unsigned char)pf->socksbuf[4],\r
335                                                  (unsigned char)pf->socksbuf[5],\r
336                                                  (unsigned char)pf->socksbuf[6],\r
337                                                  (unsigned char)pf->socksbuf[7]);\r
338                         goto connect;\r
339                     } else if (atype == 3) {\r
340                         /* REP=0 (success) already */\r
341                         sk_write(pf->s, (char *) reply, lenof(reply));\r
342                         pf->hostname = snewn(alen, char);\r
343                         pf->hostname[alen-1] = '\0';\r
344                         memcpy(pf->hostname, pf->socksbuf + 5, alen-1);\r
345                         goto connect;\r
346                     } else {\r
347                         /*\r
348                          * Unknown address type. (FIXME: support IPv6!)\r
349                          */\r
350                         reply[1] = 8;   /* atype not supported */\r
351                         sk_write(pf->s, (char *) reply, lenof(reply));\r
352                         pfd_close(pf);\r
353                         return 1;\r
354                     }\r
355                 }\r
356             }\r
358             /*\r
359              * If we get here without either having done `continue'\r
360              * or `goto connect', it must be because there is no\r
361              * sensible interpretation of what's in our buffer. So\r
362              * close the connection rudely.\r
363              */\r
364             pfd_close(pf);\r
365             return 1;\r
366         }\r
367         return 1;\r
369         /*\r
370          * We come here when we're ready to make an actual\r
371          * connection.\r
372          */\r
373         connect:\r
374         sfree(pf->socksbuf);\r
375         pf->socksbuf = NULL;\r
377         /*\r
378          * Freeze the socket until the SSH server confirms the\r
379          * connection.\r
380          */\r
381         sk_set_frozen(pf->s, 1);\r
383         pf->c = new_sock_channel(pf->backhandle, pf);\r
384         if (pf->c == NULL) {\r
385             pfd_close(pf);\r
386             return 1;\r
387         } else {\r
388             /* asks to forward to the specified host/port for this */\r
389             wrap_send_port_open(pf->c, pf->hostname, pf->port, pf->s);\r
390         }\r
391         pf->dynamic = 0;\r
393         /*\r
394          * If there's any data remaining in our current buffer,\r
395          * save it to be sent on pfd_confirm().\r
396          */\r
397         if (len > 0) {\r
398             pf->buffer = snewn(len, char);\r
399             memcpy(pf->buffer, data, len);\r
400             pf->buflen = len;\r
401         }\r
402     }\r
403     if (pf->ready) {\r
404         if (sshfwd_write(pf->c, data, len) > 0) {\r
405             pf->throttled = 1;\r
406             sk_set_frozen(pf->s, 1);\r
407         }\r
408     }\r
409     return 1;\r
412 static void pfd_sent(Plug plug, int bufsize)\r
414     struct PortForwarding *pf = (struct PortForwarding *) plug;\r
416     if (pf->c)\r
417         sshfwd_unthrottle(pf->c, bufsize);\r
420 /*\r
421  * Called when receiving a PORT OPEN from the server to make a\r
422  * connection to a destination host.\r
423  *\r
424  * On success, returns NULL and fills in *pf_ret. On error, returns a\r
425  * dynamically allocated error message string.\r
426  */\r
427 char *pfd_connect(struct PortForwarding **pf_ret, char *hostname,int port,\r
428                   void *c, Conf *conf, int addressfamily)\r
430     static const struct plug_function_table fn_table = {\r
431         pfd_log,\r
432         pfd_closing,\r
433         pfd_receive,\r
434         pfd_sent,\r
435         NULL\r
436     };\r
438     SockAddr addr;\r
439     const char *err;\r
440     char *dummy_realhost;\r
441     struct PortForwarding *pf;\r
443     /*\r
444      * Try to find host.\r
445      */\r
446     addr = name_lookup(hostname, port, &dummy_realhost, conf, addressfamily);\r
447     if ((err = sk_addr_error(addr)) != NULL) {\r
448         char *err_ret = dupstr(err);\r
449         sk_addr_free(addr);\r
450         sfree(dummy_realhost);\r
451         return err_ret;\r
452     }\r
454     /*\r
455      * Open socket.\r
456      */\r
457     pf = *pf_ret = new_portfwd_state();\r
458     pf->fn = &fn_table;\r
459     pf->throttled = pf->throttle_override = 0;\r
460     pf->ready = 1;\r
461     pf->c = c;\r
462     pf->backhandle = NULL;             /* we shouldn't need this */\r
463     pf->dynamic = 0;\r
465     pf->s = new_connection(addr, dummy_realhost, port,\r
466                            0, 1, 0, 0, (Plug) pf, conf);\r
467     sfree(dummy_realhost);\r
468     if ((err = sk_socket_error(pf->s)) != NULL) {\r
469         char *err_ret = dupstr(err);\r
470         sk_close(pf->s);\r
471         free_portfwd_state(pf);\r
472         *pf_ret = NULL;\r
473         return err_ret;\r
474     }\r
476     return NULL;\r
479 /*\r
480  called when someone connects to the local port\r
481  */\r
483 static int pfl_accepting(Plug p, accept_fn_t constructor, accept_ctx_t ctx)\r
485     static const struct plug_function_table fn_table = {\r
486         pfd_log,\r
487         pfd_closing,\r
488         pfd_receive,\r
489         pfd_sent,\r
490         NULL\r
491     };\r
492     struct PortForwarding *pf;\r
493     struct PortListener *pl;\r
494     Socket s;\r
495     const char *err;\r
497     pl = (struct PortListener *)p;\r
498     pf = new_portfwd_state();\r
499     pf->fn = &fn_table;\r
501     pf->c = NULL;\r
502     pf->backhandle = pl->backhandle;\r
504     pf->s = s = constructor(ctx, (Plug) pf);\r
505     if ((err = sk_socket_error(s)) != NULL) {\r
506         free_portfwd_state(pf);\r
507         return err != NULL;\r
508     }\r
510     pf->throttled = pf->throttle_override = 0;\r
511     pf->ready = 0;\r
513     if (pl->dynamic) {\r
514         pf->dynamic = 1;\r
515         pf->port = 0;                  /* "hostname" buffer is so far empty */\r
516         sk_set_frozen(s, 0);           /* we want to receive SOCKS _now_! */\r
517     } else {\r
518         pf->dynamic = 0;\r
519         pf->hostname = dupstr(pl->hostname);\r
520         pf->port = pl->port;    \r
521         pf->c = new_sock_channel(pl->backhandle, pf);\r
523         if (pf->c == NULL) {\r
524             free_portfwd_state(pf);\r
525             return 1;\r
526         } else {\r
527             /* asks to forward to the specified host/port for this */\r
528             wrap_send_port_open(pf->c, pf->hostname, pf->port, s);\r
529         }\r
530     }\r
532     return 0;\r
536 /*\r
537  * Add a new port-forwarding listener from srcaddr:port -> desthost:destport.\r
538  *\r
539  * On success, returns NULL and fills in *pl_ret. On error, returns a\r
540  * dynamically allocated error message string.\r
541  */\r
542 char *pfl_listen(char *desthost, int destport, char *srcaddr,\r
543                  int port, void *backhandle, Conf *conf,\r
544                  struct PortListener **pl_ret, int address_family)\r
546     static const struct plug_function_table fn_table = {\r
547         pfl_log,\r
548         pfl_closing,\r
549         NULL,                          /* recv */\r
550         NULL,                          /* send */\r
551         pfl_accepting\r
552     };\r
554     const char *err;\r
555     struct PortListener *pl;\r
557     /*\r
558      * Open socket.\r
559      */\r
560     pl = *pl_ret = new_portlistener_state();\r
561     pl->fn = &fn_table;\r
562     if (desthost) {\r
563         pl->hostname = dupstr(desthost);\r
564         pl->port = destport;\r
565         pl->dynamic = 0;\r
566     } else\r
567         pl->dynamic = 1;\r
568     pl->backhandle = backhandle;\r
570     pl->s = new_listener(srcaddr, port, (Plug) pl,\r
571                          !conf_get_int(conf, CONF_lport_acceptall),\r
572                          conf, address_family);\r
573     if ((err = sk_socket_error(pl->s)) != NULL) {\r
574         char *err_ret = dupstr(err);\r
575         sk_close(pl->s);\r
576         free_portlistener_state(pl);\r
577         *pl_ret = NULL;\r
578         return err_ret;\r
579     }\r
581     return NULL;\r
584 void pfd_close(struct PortForwarding *pf)\r
586     if (!pf)\r
587         return;\r
589     sk_close(pf->s);\r
590     free_portfwd_state(pf);\r
593 /*\r
594  * Terminate a listener.\r
595  */\r
596 void pfl_terminate(struct PortListener *pl)\r
598     if (!pl)\r
599         return;\r
601     sk_close(pl->s);\r
602     free_portlistener_state(pl);\r
605 void pfd_unthrottle(struct PortForwarding *pf)\r
607     if (!pf)\r
608         return;\r
610     pf->throttled = 0;\r
611     sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);\r
614 void pfd_override_throttle(struct PortForwarding *pf, int enable)\r
616     if (!pf)\r
617         return;\r
619     pf->throttle_override = enable;\r
620     sk_set_frozen(pf->s, pf->throttled || pf->throttle_override);\r
623 /*\r
624  * Called to send data down the raw connection.\r
625  */\r
626 int pfd_send(struct PortForwarding *pf, char *data, int len)\r
628     if (pf == NULL)\r
629         return 0;\r
630     return sk_write(pf->s, data, len);\r
633 void pfd_send_eof(struct PortForwarding *pf)\r
635     sk_write_eof(pf->s);\r
638 void pfd_confirm(struct PortForwarding *pf)\r
640     if (pf == NULL)\r
641         return;\r
643     pf->ready = 1;\r
644     sk_set_frozen(pf->s, 0);\r
645     sk_write(pf->s, NULL, 0);\r
646     if (pf->buffer) {\r
647         sshfwd_write(pf->c, pf->buffer, pf->buflen);\r
648         sfree(pf->buffer);\r
649         pf->buffer = NULL;\r
650     }\r