Fixed issue #3815: TortoiseGitMerge crashes on Win7 on startup when winrt libraries...
[TortoiseGit.git] / src / TortoisePlink / PORTFWD.C
blobd76367a8b6dee923a2c86a5b42487eb16d92399c
1 /*\r
2  * SSH port forwarding.\r
3  */\r
4 \r
5 #include <assert.h>\r
6 #include <stdio.h>\r
7 #include <stdlib.h>\r
8 \r
9 #include "putty.h"\r
10 #include "ssh.h"\r
11 #include "sshchan.h"\r
13 /*\r
14  * Enumeration of values that live in the 'socks_state' field of\r
15  * struct PortForwarding.\r
16  */\r
17 typedef enum {\r
18     SOCKS_NONE, /* direct connection (no SOCKS, or SOCKS already done) */\r
19     SOCKS_INITIAL,       /* don't know if we're SOCKS 4 or 5 yet */\r
20     SOCKS_4,             /* expect a SOCKS 4 (or 4A) connection message */\r
21     SOCKS_5_INITIAL,     /* expect a SOCKS 5 preliminary message */\r
22     SOCKS_5_CONNECT      /* expect a SOCKS 5 connection message */\r
23 } SocksState;\r
25 typedef struct PortForwarding {\r
26     SshChannel *c;         /* channel structure held by SSH connection layer */\r
27     ConnectionLayer *cl;   /* the connection layer itself */\r
28     /* Note that ssh need not be filled in if c is non-NULL */\r
29     Socket *s;\r
30     bool input_wanted;\r
31     bool ready;\r
32     SocksState socks_state;\r
33     /*\r
34      * `hostname' and `port' are the real hostname and port, once\r
35      * we know what we're connecting to.\r
36      */\r
37     char *hostname;\r
38     int port;\r
39     /*\r
40      * `socksbuf' is the buffer we use to accumulate the initial SOCKS\r
41      * segment of the incoming data, plus anything after that that we\r
42      * receive before we're ready to send data to the SSH server.\r
43      */\r
44     strbuf *socksbuf;\r
45     size_t socksbuf_consumed;\r
47     Plug plug;\r
48     Channel chan;\r
49 } PortForwarding;\r
51 struct PortListener {\r
52     ConnectionLayer *cl;\r
53     Socket *s;\r
54     bool is_dynamic;\r
55     /*\r
56      * `hostname' and `port' are the real hostname and port, for\r
57      * ordinary forwardings.\r
58      */\r
59     char *hostname;\r
60     int port;\r
62     Plug plug;\r
63 };\r
65 static struct PortForwarding *new_portfwd_state(void)\r
66 {\r
67     struct PortForwarding *pf = snew(struct PortForwarding);\r
68     pf->hostname = NULL;\r
69     pf->socksbuf = NULL;\r
70     return pf;\r
71 }\r
73 static void free_portfwd_state(struct PortForwarding *pf)\r
74 {\r
75     if (!pf)\r
76         return;\r
77     sfree(pf->hostname);\r
78     if (pf->socksbuf)\r
79         strbuf_free(pf->socksbuf);\r
80     sfree(pf);\r
81 }\r
83 static struct PortListener *new_portlistener_state(void)\r
84 {\r
85     struct PortListener *pl = snew(struct PortListener);\r
86     pl->hostname = NULL;\r
87     return pl;\r
88 }\r
90 static void free_portlistener_state(struct PortListener *pl)\r
91 {\r
92     if (!pl)\r
93         return;\r
94     sfree(pl->hostname);\r
95     sfree(pl);\r
96 }\r
98 static void pfd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,\r
99                     const char *error_msg, int error_code)\r
101     /* we have to dump these since we have no interface to logging.c */\r
104 static void pfl_log(Plug *plug, PlugLogType type, SockAddr *addr, int port,\r
105                     const char *error_msg, int error_code)\r
107     /* we have to dump these since we have no interface to logging.c */\r
110 static void pfd_close(struct PortForwarding *pf);\r
112 static void pfd_closing(Plug *plug, const char *error_msg, int error_code,\r
113                         bool calling_back)\r
115     struct PortForwarding *pf =\r
116         container_of(plug, struct PortForwarding, plug);\r
118     if (error_msg) {\r
119         /*\r
120          * Socket error. Slam the connection instantly shut.\r
121          */\r
122         if (pf->c) {\r
123             sshfwd_initiate_close(pf->c, error_msg);\r
124         } else {\r
125             /*\r
126              * We might not have an SSH channel, if a socket error\r
127              * occurred during SOCKS negotiation. If not, we must\r
128              * clean ourself up without sshfwd_initiate_close's call\r
129              * back to pfd_close.\r
130              */\r
131             pfd_close(pf);\r
132         }\r
133     } else {\r
134         /*\r
135          * Ordinary EOF received on socket. Send an EOF on the SSH\r
136          * channel.\r
137          */\r
138         if (pf->c)\r
139             sshfwd_write_eof(pf->c);\r
140     }\r
143 static void pfl_terminate(struct PortListener *pl);\r
145 static void pfl_closing(Plug *plug, const char *error_msg, int error_code,\r
146                         bool calling_back)\r
148     struct PortListener *pl = (struct PortListener *) plug;\r
149     pfl_terminate(pl);\r
152 static SshChannel *wrap_lportfwd_open(\r
153     ConnectionLayer *cl, const char *hostname, int port,\r
154     Socket *s, Channel *chan)\r
156     SocketPeerInfo *pi;\r
157     char *description;\r
158     SshChannel *toret;\r
160     pi = sk_peer_info(s);\r
161     if (pi && pi->log_text) {\r
162         description = dupprintf("forwarding from %s", pi->log_text);\r
163     } else {\r
164         description = dupstr("forwarding");\r
165     }\r
166     toret = ssh_lportfwd_open(cl, hostname, port, description, pi, chan);\r
167     sk_free_peer_info(pi);\r
169     sfree(description);\r
170     return toret;\r
173 static char *ipv4_to_string(unsigned ipv4)\r
175     return dupprintf("%u.%u.%u.%u",\r
176                      (ipv4 >> 24) & 0xFF, (ipv4 >> 16) & 0xFF,\r
177                      (ipv4 >>  8) & 0xFF, (ipv4      ) & 0xFF);\r
180 static char *ipv6_to_string(ptrlen ipv6)\r
182     const unsigned char *addr = ipv6.ptr;\r
183     assert(ipv6.len == 16);\r
184     return dupprintf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",\r
185                      (unsigned)GET_16BIT_MSB_FIRST(addr + 0),\r
186                      (unsigned)GET_16BIT_MSB_FIRST(addr + 2),\r
187                      (unsigned)GET_16BIT_MSB_FIRST(addr + 4),\r
188                      (unsigned)GET_16BIT_MSB_FIRST(addr + 6),\r
189                      (unsigned)GET_16BIT_MSB_FIRST(addr + 8),\r
190                      (unsigned)GET_16BIT_MSB_FIRST(addr + 10),\r
191                      (unsigned)GET_16BIT_MSB_FIRST(addr + 12),\r
192                      (unsigned)GET_16BIT_MSB_FIRST(addr + 14));\r
195 static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len)\r
197     struct PortForwarding *pf =\r
198         container_of(plug, struct PortForwarding, plug);\r
200     if (len == 0)\r
201         return;\r
203     if (pf->socks_state != SOCKS_NONE) {\r
204         BinarySource src[1];\r
206         /*\r
207          * Store all the data we've got in socksbuf.\r
208          */\r
209         put_data(pf->socksbuf, data, len);\r
211         /*\r
212          * Check the start of socksbuf to see if it's a valid and\r
213          * complete message in the SOCKS exchange.\r
214          */\r
216         if (pf->socks_state == SOCKS_INITIAL) {\r
217             /* Preliminary: check the first byte of the data (which we\r
218              * _must_ have by now) to find out which SOCKS major\r
219              * version we're speaking. */\r
220             switch (pf->socksbuf->u[0]) {\r
221               case 4:\r
222                 pf->socks_state = SOCKS_4;\r
223                 break;\r
224               case 5:\r
225                 pf->socks_state = SOCKS_5_INITIAL;\r
226                 break;\r
227               default:\r
228                 pfd_close(pf);         /* unrecognised version */\r
229                 return;\r
230             }\r
231         }\r
233         BinarySource_BARE_INIT(src, pf->socksbuf->u, pf->socksbuf->len);\r
234         get_data(src, pf->socksbuf_consumed);\r
236         while (pf->socks_state != SOCKS_NONE) {\r
237             unsigned socks_version, message_type, reserved_byte;\r
238             unsigned reply_code, port, ipv4, method;\r
239             ptrlen methods;\r
240             const char *socks4_hostname;\r
241             strbuf *output;\r
243             switch (pf->socks_state) {\r
244               case SOCKS_INITIAL:\r
245               case SOCKS_NONE:\r
246                 unreachable("These case values cannot appear");\r
248               case SOCKS_4:\r
249                 /* SOCKS 4/4A connect message */\r
250                 socks_version = get_byte(src);\r
251                 message_type = get_byte(src);\r
253                 if (get_err(src) == BSE_OUT_OF_DATA)\r
254                     return;\r
255                 if (socks_version == 4 && message_type == 1) {\r
256                     /* CONNECT message */\r
257                     bool name_based = false;\r
259                     port = get_uint16(src);\r
260                     ipv4 = get_uint32(src);\r
261                     if (ipv4 > 0x00000000 && ipv4 < 0x00000100) {\r
262                         /*\r
263                          * Addresses in this range indicate the SOCKS 4A\r
264                          * extension to specify a hostname, which comes\r
265                          * after the username.\r
266                          */\r
267                         name_based = true;\r
268                     }\r
269                     get_asciz(src);        /* skip username */\r
270                     socks4_hostname = name_based ? get_asciz(src) : NULL;\r
272                     if (get_err(src) == BSE_OUT_OF_DATA)\r
273                         return;\r
274                     if (get_err(src))\r
275                         goto socks4_reject;\r
277                     pf->port = port;\r
278                     if (name_based) {\r
279                         pf->hostname = dupstr(socks4_hostname);\r
280                     } else {\r
281                         pf->hostname = ipv4_to_string(ipv4);\r
282                     }\r
284                     output = strbuf_new();\r
285                     put_byte(output, 0);       /* reply version */\r
286                     put_byte(output, 90);      /* SOCKS 4 'request granted' */\r
287                     put_uint16(output, 0);     /* null port field */\r
288                     put_uint32(output, 0);     /* null address field */\r
289                     sk_write(pf->s, output->u, output->len);\r
290                     strbuf_free(output);\r
292                     pf->socks_state = SOCKS_NONE;\r
293                     pf->socksbuf_consumed = src->pos;\r
294                     break;\r
295                 }\r
297               socks4_reject:\r
298                 output = strbuf_new();\r
299                 put_byte(output, 0);       /* reply version */\r
300                 put_byte(output, 91);      /* SOCKS 4 'request rejected' */\r
301                 put_uint16(output, 0);     /* null port field */\r
302                 put_uint32(output, 0);     /* null address field */\r
303                 sk_write(pf->s, output->u, output->len);\r
304                 strbuf_free(output);\r
305                 pfd_close(pf);\r
306                 return;\r
308               case SOCKS_5_INITIAL:\r
309                 /* SOCKS 5 initial method list */\r
310                 socks_version = get_byte(src);\r
311                 methods = get_pstring(src);\r
313                 method = 0xFF;         /* means 'no usable method found' */\r
314                 {\r
315                     int i;\r
316                     for (i = 0; i < methods.len; i++) {\r
317                         if (((const unsigned char *)methods.ptr)[i] == 0 ) {\r
318                             method = 0;        /* no auth */\r
319                             break;\r
320                         }\r
321                     }\r
322                 }\r
324                 if (get_err(src) == BSE_OUT_OF_DATA)\r
325                     return;\r
326                 if (get_err(src))\r
327                     method = 0xFF;\r
329                 output = strbuf_new();\r
330                 put_byte(output, 5);       /* SOCKS version */\r
331                 put_byte(output, method);  /* selected auth method */\r
332                 sk_write(pf->s, output->u, output->len);\r
333                 strbuf_free(output);\r
335                 if (method == 0xFF) {\r
336                     pfd_close(pf);\r
337                     return;\r
338                 }\r
340                 pf->socks_state = SOCKS_5_CONNECT;\r
341                 pf->socksbuf_consumed = src->pos;\r
342                 break;\r
344               case SOCKS_5_CONNECT:\r
345                 /* SOCKS 5 connect message */\r
346                 socks_version = get_byte(src);\r
347                 message_type = get_byte(src);\r
348                 reserved_byte = get_byte(src);\r
350                 if (socks_version == 5 && message_type == 1 &&\r
351                     reserved_byte == 0) {\r
353                     reply_code = 0;        /* success */\r
355                     switch (get_byte(src)) {\r
356                       case 1:              /* IPv4 */\r
357                         pf->hostname = ipv4_to_string(get_uint32(src));\r
358                         break;\r
359                       case 4:              /* IPv6 */\r
360                         pf->hostname = ipv6_to_string(get_data(src, 16));\r
361                         break;\r
362                       case 3:              /* unresolved domain name */\r
363                         pf->hostname = mkstr(get_pstring(src));\r
364                         break;\r
365                       default:\r
366                         pf->hostname = NULL;\r
367                         reply_code = 8;    /* address type not supported */\r
368                         break;\r
369                     }\r
371                     pf->port = get_uint16(src);\r
372                 } else {\r
373                     reply_code = 7;        /* command not supported */\r
374                 }\r
376                 if (get_err(src) == BSE_OUT_OF_DATA)\r
377                     return;\r
378                 if (get_err(src))\r
379                     reply_code = 1;        /* general server failure */\r
381                 output = strbuf_new();\r
382                 put_byte(output, 5);       /* SOCKS version */\r
383                 put_byte(output, reply_code);\r
384                 put_byte(output, 0);       /* reserved */\r
385                 put_byte(output, 1);       /* IPv4 address follows */\r
386                 put_uint32(output, 0);     /* bound IPv4 address (unused) */\r
387                 put_uint16(output, 0);     /* bound port number (unused) */\r
388                 sk_write(pf->s, output->u, output->len);\r
389                 strbuf_free(output);\r
391                 if (reply_code != 0) {\r
392                     pfd_close(pf);\r
393                     return;\r
394                 }\r
396                 pf->socks_state = SOCKS_NONE;\r
397                 pf->socksbuf_consumed = src->pos;\r
398                 break;\r
399             }\r
400         }\r
402         /*\r
403          * We come here when we're ready to make an actual\r
404          * connection.\r
405          */\r
407         /*\r
408          * Freeze the socket until the SSH server confirms the\r
409          * connection.\r
410          */\r
411         sk_set_frozen(pf->s, true);\r
413         pf->c = wrap_lportfwd_open(pf->cl, pf->hostname, pf->port, pf->s,\r
414                                    &pf->chan);\r
415     }\r
416     if (pf->ready)\r
417         sshfwd_write(pf->c, data, len);\r
420 static void pfd_sent(Plug *plug, size_t bufsize)\r
422     struct PortForwarding *pf =\r
423         container_of(plug, struct PortForwarding, plug);\r
425     if (pf->c)\r
426         sshfwd_unthrottle(pf->c, bufsize);\r
429 static const PlugVtable PortForwarding_plugvt = {\r
430     .log = pfd_log,\r
431     .closing = pfd_closing,\r
432     .receive = pfd_receive,\r
433     .sent = pfd_sent,\r
434 };\r
436 static void pfd_chan_free(Channel *chan);\r
437 static void pfd_open_confirmation(Channel *chan);\r
438 static void pfd_open_failure(Channel *chan, const char *errtext);\r
439 static size_t pfd_send(\r
440     Channel *chan, bool is_stderr, const void *data, size_t len);\r
441 static void pfd_send_eof(Channel *chan);\r
442 static void pfd_set_input_wanted(Channel *chan, bool wanted);\r
443 static char *pfd_log_close_msg(Channel *chan);\r
445 static const ChannelVtable PortForwarding_channelvt = {\r
446     .free = pfd_chan_free,\r
447     .open_confirmation = pfd_open_confirmation,\r
448     .open_failed = pfd_open_failure,\r
449     .send = pfd_send,\r
450     .send_eof = pfd_send_eof,\r
451     .set_input_wanted = pfd_set_input_wanted,\r
452     .log_close_msg = pfd_log_close_msg,\r
453     .want_close = chan_default_want_close,\r
454     .rcvd_exit_status = chan_no_exit_status,\r
455     .rcvd_exit_signal = chan_no_exit_signal,\r
456     .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,\r
457     .run_shell = chan_no_run_shell,\r
458     .run_command = chan_no_run_command,\r
459     .run_subsystem = chan_no_run_subsystem,\r
460     .enable_x11_forwarding = chan_no_enable_x11_forwarding,\r
461     .enable_agent_forwarding = chan_no_enable_agent_forwarding,\r
462     .allocate_pty = chan_no_allocate_pty,\r
463     .set_env = chan_no_set_env,\r
464     .send_break = chan_no_send_break,\r
465     .send_signal = chan_no_send_signal,\r
466     .change_window_size = chan_no_change_window_size,\r
467     .request_response = chan_no_request_response,\r
468 };\r
470 Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug, bool start_ready)\r
472     struct PortForwarding *pf;\r
474     pf = new_portfwd_state();\r
475     pf->plug.vt = &PortForwarding_plugvt;\r
476     pf->chan.initial_fixed_window_size = 0;\r
477     pf->chan.vt = &PortForwarding_channelvt;\r
478     pf->input_wanted = true;\r
480     pf->c = NULL;\r
482     pf->cl = cl;\r
483     pf->input_wanted = true;\r
484     pf->ready = start_ready;\r
486     pf->socks_state = SOCKS_NONE;\r
487     pf->hostname = NULL;\r
488     pf->port = 0;\r
490     *plug = &pf->plug;\r
491     return &pf->chan;\r
494 void portfwd_raw_free(Channel *pfchan)\r
496     struct PortForwarding *pf;\r
497     assert(pfchan->vt == &PortForwarding_channelvt);\r
498     pf = container_of(pfchan, struct PortForwarding, chan);\r
499     free_portfwd_state(pf);\r
502 void portfwd_raw_setup(Channel *pfchan, Socket *s, SshChannel *sc)\r
504     struct PortForwarding *pf;\r
505     assert(pfchan->vt == &PortForwarding_channelvt);\r
506     pf = container_of(pfchan, struct PortForwarding, chan);\r
508     pf->s = s;\r
509     pf->c = sc;\r
512 /*\r
513  called when someone connects to the local port\r
514  */\r
516 static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx)\r
518     struct PortListener *pl = container_of(p, struct PortListener, plug);\r
519     struct PortForwarding *pf;\r
520     Channel *chan;\r
521     Plug *plug;\r
522     Socket *s;\r
523     const char *err;\r
525     chan = portfwd_raw_new(pl->cl, &plug, false);\r
526     s = constructor(ctx, plug);\r
527     if ((err = sk_socket_error(s)) != NULL) {\r
528         portfwd_raw_free(chan);\r
529         return 1;\r
530     }\r
532     pf = container_of(chan, struct PortForwarding, chan);\r
534     if (pl->is_dynamic) {\r
535         pf->s = s;\r
536         pf->socks_state = SOCKS_INITIAL;\r
537         pf->socksbuf = strbuf_new();\r
538         pf->socksbuf_consumed = 0;\r
539         pf->port = 0;                  /* "hostname" buffer is so far empty */\r
540         sk_set_frozen(s, false);       /* we want to receive SOCKS _now_! */\r
541     } else {\r
542         pf->hostname = dupstr(pl->hostname);\r
543         pf->port = pl->port;\r
544         portfwd_raw_setup(\r
545             chan, s,\r
546             wrap_lportfwd_open(pl->cl, pf->hostname, pf->port, s, &pf->chan));\r
547     }\r
549     return 0;\r
552 static const PlugVtable PortListener_plugvt = {\r
553     .log = pfl_log,\r
554     .closing = pfl_closing,\r
555     .accepting = pfl_accepting,\r
556 };\r
558 /*\r
559  * Add a new port-forwarding listener from srcaddr:port -> desthost:destport.\r
560  *\r
561  * desthost == NULL indicates dynamic SOCKS port forwarding.\r
562  *\r
563  * On success, returns NULL and fills in *pl_ret. On error, returns a\r
564  * dynamically allocated error message string.\r
565  */\r
566 static char *pfl_listen(const char *desthost, int destport,\r
567                         const char *srcaddr, int port,\r
568                         ConnectionLayer *cl, Conf *conf,\r
569                         struct PortListener **pl_ret, int address_family)\r
571     const char *err;\r
572     struct PortListener *pl;\r
574     /*\r
575      * Open socket.\r
576      */\r
577     pl = *pl_ret = new_portlistener_state();\r
578     pl->plug.vt = &PortListener_plugvt;\r
579     if (desthost) {\r
580         pl->hostname = dupstr(desthost);\r
581         pl->port = destport;\r
582         pl->is_dynamic = false;\r
583     } else\r
584         pl->is_dynamic = true;\r
585     pl->cl = cl;\r
587     pl->s = new_listener(srcaddr, port, &pl->plug,\r
588                          !conf_get_bool(conf, CONF_lport_acceptall),\r
589                          conf, address_family);\r
590     if ((err = sk_socket_error(pl->s)) != NULL) {\r
591         char *err_ret = dupstr(err);\r
592         sk_close(pl->s);\r
593         free_portlistener_state(pl);\r
594         *pl_ret = NULL;\r
595         return err_ret;\r
596     }\r
598     return NULL;\r
601 static char *pfd_log_close_msg(Channel *chan)\r
603     return dupstr("Forwarded port closed");\r
606 static void pfd_close(struct PortForwarding *pf)\r
608     if (!pf)\r
609         return;\r
611     sk_close(pf->s);\r
612     free_portfwd_state(pf);\r
615 /*\r
616  * Terminate a listener.\r
617  */\r
618 static void pfl_terminate(struct PortListener *pl)\r
620     if (!pl)\r
621         return;\r
623     sk_close(pl->s);\r
624     free_portlistener_state(pl);\r
627 static void pfd_set_input_wanted(Channel *chan, bool wanted)\r
629     assert(chan->vt == &PortForwarding_channelvt);\r
630     PortForwarding *pf = container_of(chan, PortForwarding, chan);\r
631     pf->input_wanted = wanted;\r
632     sk_set_frozen(pf->s, !pf->input_wanted);\r
635 static void pfd_chan_free(Channel *chan)\r
637     assert(chan->vt == &PortForwarding_channelvt);\r
638     PortForwarding *pf = container_of(chan, PortForwarding, chan);\r
639     pfd_close(pf);\r
642 /*\r
643  * Called to send data down the raw connection.\r
644  */\r
645 static size_t pfd_send(\r
646     Channel *chan, bool is_stderr, const void *data, size_t len)\r
648     assert(chan->vt == &PortForwarding_channelvt);\r
649     PortForwarding *pf = container_of(chan, PortForwarding, chan);\r
650     return sk_write(pf->s, data, len);\r
653 static void pfd_send_eof(Channel *chan)\r
655     assert(chan->vt == &PortForwarding_channelvt);\r
656     PortForwarding *pf = container_of(chan, PortForwarding, chan);\r
657     sk_write_eof(pf->s);\r
660 static void pfd_open_confirmation(Channel *chan)\r
662     assert(chan->vt == &PortForwarding_channelvt);\r
663     PortForwarding *pf = container_of(chan, PortForwarding, chan);\r
665     pf->ready = true;\r
666     sk_set_frozen(pf->s, false);\r
667     sk_write(pf->s, NULL, 0);\r
668     if (pf->socksbuf) {\r
669         sshfwd_write(pf->c, pf->socksbuf->u + pf->socksbuf_consumed,\r
670                      pf->socksbuf->len - pf->socksbuf_consumed);\r
671         strbuf_free(pf->socksbuf);\r
672         pf->socksbuf = NULL;\r
673     }\r
676 static void pfd_open_failure(Channel *chan, const char *errtext)\r
678     assert(chan->vt == &PortForwarding_channelvt);\r
679     PortForwarding *pf = container_of(chan, PortForwarding, chan);\r
681     logeventf(pf->cl->logctx,\r
682               "Forwarded connection refused by remote%s%s",\r
683               errtext ? ": " : "", errtext ? errtext : "");\r
686 /* ----------------------------------------------------------------------\r
687  * Code to manage the complete set of currently active port\r
688  * forwardings, and update it from Conf.\r
689  */\r
691 struct PortFwdRecord {\r
692     enum { DESTROY, KEEP, CREATE } status;\r
693     int type;\r
694     unsigned sport, dport;\r
695     char *saddr, *daddr;\r
696     char *sserv, *dserv;\r
697     struct ssh_rportfwd *remote;\r
698     int addressfamily;\r
699     struct PortListener *local;\r
700 };\r
702 static int pfr_cmp(void *av, void *bv)\r
704     PortFwdRecord *a = (PortFwdRecord *) av;\r
705     PortFwdRecord *b = (PortFwdRecord *) bv;\r
706     int i;\r
707     if (a->type > b->type)\r
708         return +1;\r
709     if (a->type < b->type)\r
710         return -1;\r
711     if (a->addressfamily > b->addressfamily)\r
712         return +1;\r
713     if (a->addressfamily < b->addressfamily)\r
714         return -1;\r
715     if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0)\r
716         return i < 0 ? -1 : +1;\r
717     if (a->sport > b->sport)\r
718         return +1;\r
719     if (a->sport < b->sport)\r
720         return -1;\r
721     if (a->type != 'D') {\r
722         if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0)\r
723             return i < 0 ? -1 : +1;\r
724         if (a->dport > b->dport)\r
725             return +1;\r
726         if (a->dport < b->dport)\r
727             return -1;\r
728     }\r
729     return 0;\r
732 static void pfr_free(PortFwdRecord *pfr)\r
734     /* Dispose of any listening socket. */\r
735     if (pfr->local)\r
736         pfl_terminate(pfr->local);\r
738     sfree(pfr->saddr);\r
739     sfree(pfr->daddr);\r
740     sfree(pfr->sserv);\r
741     sfree(pfr->dserv);\r
742     sfree(pfr);\r
745 struct PortFwdManager {\r
746     ConnectionLayer *cl;\r
747     Conf *conf;\r
748     tree234 *forwardings;\r
749 };\r
751 PortFwdManager *portfwdmgr_new(ConnectionLayer *cl)\r
753     PortFwdManager *mgr = snew(PortFwdManager);\r
755     mgr->cl = cl;\r
756     mgr->conf = NULL;\r
757     mgr->forwardings = newtree234(pfr_cmp);\r
759     return mgr;\r
762 void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr)\r
764     PortFwdRecord *realpfr = del234(mgr->forwardings, pfr);\r
765     if (realpfr == pfr)\r
766         pfr_free(pfr);\r
769 void portfwdmgr_close_all(PortFwdManager *mgr)\r
771     PortFwdRecord *pfr;\r
773     while ((pfr = delpos234(mgr->forwardings, 0)) != NULL)\r
774         pfr_free(pfr);\r
777 void portfwdmgr_free(PortFwdManager *mgr)\r
779     portfwdmgr_close_all(mgr);\r
780     freetree234(mgr->forwardings);\r
781     if (mgr->conf)\r
782         conf_free(mgr->conf);\r
783     sfree(mgr);\r
786 void portfwdmgr_config(PortFwdManager *mgr, Conf *conf)\r
788     PortFwdRecord *pfr;\r
789     int i;\r
790     char *key, *val;\r
792     if (mgr->conf)\r
793         conf_free(mgr->conf);\r
794     mgr->conf = conf_copy(conf);\r
796     /*\r
797      * Go through the existing port forwardings and tag them\r
798      * with status==DESTROY. Any that we want to keep will be\r
799      * re-enabled (status==KEEP) as we go through the\r
800      * configuration and find out which bits are the same as\r
801      * they were before.\r
802      */\r
803     for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++)\r
804         pfr->status = DESTROY;\r
806     for (val = conf_get_str_strs(conf, CONF_portfwd, NULL, &key);\r
807          val != NULL;\r
808          val = conf_get_str_strs(conf, CONF_portfwd, key, &key)) {\r
809         char *kp, *kp2, *vp, *vp2;\r
810         char address_family, type;\r
811         int sport, dport, sserv, dserv;\r
812         char *sports, *dports, *saddr, *host;\r
814         kp = key;\r
816         address_family = 'A';\r
817         type = 'L';\r
818         if (*kp == 'A' || *kp == '4' || *kp == '6')\r
819             address_family = *kp++;\r
820         if (*kp == 'L' || *kp == 'R')\r
821             type = *kp++;\r
823         if ((kp2 = host_strchr(kp, ':')) != NULL) {\r
824             /*\r
825              * There's a colon in the middle of the source port\r
826              * string, which means that the part before it is\r
827              * actually a source address.\r
828              */\r
829             char *saddr_tmp = dupprintf("%.*s", (int)(kp2 - kp), kp);\r
830             saddr = host_strduptrim(saddr_tmp);\r
831             sfree(saddr_tmp);\r
832             sports = kp2+1;\r
833         } else {\r
834             saddr = NULL;\r
835             sports = kp;\r
836         }\r
837         sport = atoi(sports);\r
838         sserv = 0;\r
839         if (sport == 0) {\r
840             sserv = 1;\r
841             sport = net_service_lookup(sports);\r
842             if (!sport) {\r
843                 logeventf(mgr->cl->logctx, "Service lookup failed for source"\r
844                           " port \"%s\"", sports);\r
845             }\r
846         }\r
848         if (type == 'L' && !strcmp(val, "D")) {\r
849             /* dynamic forwarding */\r
850             host = NULL;\r
851             dports = NULL;\r
852             dport = -1;\r
853             dserv = 0;\r
854             type = 'D';\r
855         } else {\r
856             /* ordinary forwarding */\r
857             vp = val;\r
858             vp2 = vp + host_strcspn(vp, ":");\r
859             host = dupprintf("%.*s", (int)(vp2 - vp), vp);\r
860             if (*vp2)\r
861                 vp2++;\r
862             dports = vp2;\r
863             dport = atoi(dports);\r
864             dserv = 0;\r
865             if (dport == 0) {\r
866                 dserv = 1;\r
867                 dport = net_service_lookup(dports);\r
868                 if (!dport) {\r
869                     logeventf(mgr->cl->logctx,\r
870                               "Service lookup failed for destination"\r
871                               " port \"%s\"", dports);\r
872                 }\r
873             }\r
874         }\r
876         if (sport && dport) {\r
877             /* Set up a description of the source port. */\r
878             pfr = snew(PortFwdRecord);\r
879             pfr->type = type;\r
880             pfr->saddr = saddr;\r
881             pfr->sserv = sserv ? dupstr(sports) : NULL;\r
882             pfr->sport = sport;\r
883             pfr->daddr = host;\r
884             pfr->dserv = dserv ? dupstr(dports) : NULL;\r
885             pfr->dport = dport;\r
886             pfr->local = NULL;\r
887             pfr->remote = NULL;\r
888             pfr->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 :\r
889                                   address_family == '6' ? ADDRTYPE_IPV6 :\r
890                                   ADDRTYPE_UNSPEC);\r
892             PortFwdRecord *existing = add234(mgr->forwardings, pfr);\r
893             if (existing != pfr) {\r
894                 if (existing->status == DESTROY) {\r
895                     /*\r
896                      * We already have a port forwarding up and running\r
897                      * with precisely these parameters. Hence, no need\r
898                      * to do anything; simply re-tag the existing one\r
899                      * as KEEP.\r
900                      */\r
901                     existing->status = KEEP;\r
902                 }\r
903                 /*\r
904                  * Anything else indicates that there was a duplicate\r
905                  * in our input, which we'll silently ignore.\r
906                  */\r
907                 pfr_free(pfr);\r
908             } else {\r
909                 pfr->status = CREATE;\r
910             }\r
911         } else {\r
912             sfree(saddr);\r
913             sfree(host);\r
914         }\r
915     }\r
917     /*\r
918      * Now go through and destroy any port forwardings which were\r
919      * not re-enabled.\r
920      */\r
921     for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) {\r
922         if (pfr->status == DESTROY) {\r
923             char *message;\r
925             message = dupprintf("%s port forwarding from %s%s%d",\r
926                                 pfr->type == 'L' ? "local" :\r
927                                 pfr->type == 'R' ? "remote" : "dynamic",\r
928                                 pfr->saddr ? pfr->saddr : "",\r
929                                 pfr->saddr ? ":" : "",\r
930                                 pfr->sport);\r
932             if (pfr->type != 'D') {\r
933                 char *msg2 = dupprintf("%s to %s:%d", message,\r
934                                        pfr->daddr, pfr->dport);\r
935                 sfree(message);\r
936                 message = msg2;\r
937             }\r
939             logeventf(mgr->cl->logctx, "Cancelling %s", message);\r
940             sfree(message);\r
942             /* pfr->remote or pfr->local may be NULL if setting up a\r
943              * forwarding failed. */\r
944             if (pfr->remote) {\r
945                 /*\r
946                  * Cancel the port forwarding at the server\r
947                  * end.\r
948                  *\r
949                  * Actually closing the listening port on the server\r
950                  * side may fail - because in SSH-1 there's no message\r
951                  * in the protocol to request it!\r
952                  *\r
953                  * Instead, we simply remove the record of the\r
954                  * forwarding from our local end, so that any\r
955                  * connections the server tries to make on it are\r
956                  * rejected.\r
957                  */\r
958                 ssh_rportfwd_remove(mgr->cl, pfr->remote);\r
959                 pfr->remote = NULL;\r
960             } else if (pfr->local) {\r
961                 pfl_terminate(pfr->local);\r
962                 pfr->local = NULL;\r
963             }\r
965             delpos234(mgr->forwardings, i);\r
966             pfr_free(pfr);\r
967             i--;                       /* so we don't skip one in the list */\r
968         }\r
969     }\r
971     /*\r
972      * And finally, set up any new port forwardings (status==CREATE).\r
973      */\r
974     for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) {\r
975         if (pfr->status == CREATE) {\r
976             char *sportdesc, *dportdesc;\r
977             sportdesc = dupprintf("%s%s%s%s%d%s",\r
978                                   pfr->saddr ? pfr->saddr : "",\r
979                                   pfr->saddr ? ":" : "",\r
980                                   pfr->sserv ? pfr->sserv : "",\r
981                                   pfr->sserv ? "(" : "",\r
982                                   pfr->sport,\r
983                                   pfr->sserv ? ")" : "");\r
984             if (pfr->type == 'D') {\r
985                 dportdesc = NULL;\r
986             } else {\r
987                 dportdesc = dupprintf("%s:%s%s%d%s",\r
988                                       pfr->daddr,\r
989                                       pfr->dserv ? pfr->dserv : "",\r
990                                       pfr->dserv ? "(" : "",\r
991                                       pfr->dport,\r
992                                       pfr->dserv ? ")" : "");\r
993             }\r
995             if (pfr->type == 'L') {\r
996                 char *err = pfl_listen(pfr->daddr, pfr->dport,\r
997                                        pfr->saddr, pfr->sport,\r
998                                        mgr->cl, conf, &pfr->local,\r
999                                        pfr->addressfamily);\r
1001                 logeventf(mgr->cl->logctx,\r
1002                           "Local %sport %s forwarding to %s%s%s",\r
1003                           pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :\r
1004                           pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",\r
1005                           sportdesc, dportdesc,\r
1006                           err ? " failed: " : "", err ? err : "");\r
1007                 if (err)\r
1008                     sfree(err);\r
1009             } else if (pfr->type == 'D') {\r
1010                 char *err = pfl_listen(NULL, -1, pfr->saddr, pfr->sport,\r
1011                                        mgr->cl, conf, &pfr->local,\r
1012                                        pfr->addressfamily);\r
1014                 logeventf(mgr->cl->logctx,\r
1015                           "Local %sport %s SOCKS dynamic forwarding%s%s",\r
1016                           pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " :\r
1017                           pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "",\r
1018                           sportdesc,\r
1019                           err ? " failed: " : "", err ? err : "");\r
1021                 if (err)\r
1022                     sfree(err);\r
1023             } else {\r
1024                 const char *shost;\r
1026                 if (pfr->saddr) {\r
1027                     shost = pfr->saddr;\r
1028                 } else if (conf_get_bool(conf, CONF_rport_acceptall)) {\r
1029                     shost = "";\r
1030                 } else {\r
1031                     shost = "localhost";\r
1032                 }\r
1034                 pfr->remote = ssh_rportfwd_alloc(\r
1035                     mgr->cl, shost, pfr->sport, pfr->daddr, pfr->dport,\r
1036                     pfr->addressfamily, sportdesc, pfr, NULL);\r
1038                 if (!pfr->remote) {\r
1039                     logeventf(mgr->cl->logctx,\r
1040                               "Duplicate remote port forwarding to %s:%d",\r
1041                               pfr->daddr, pfr->dport);\r
1042                     pfr_free(pfr);\r
1043                 } else {\r
1044                     logeventf(mgr->cl->logctx, "Requesting remote port %s"\r
1045                               " forward to %s", sportdesc, dportdesc);\r
1046                 }\r
1047             }\r
1048             sfree(sportdesc);\r
1049             sfree(dportdesc);\r
1050         }\r
1051     }\r
1054 bool portfwdmgr_listen(PortFwdManager *mgr, const char *host, int port,\r
1055                        const char *keyhost, int keyport, Conf *conf)\r
1057     PortFwdRecord *pfr;\r
1059     pfr = snew(PortFwdRecord);\r
1060     pfr->type = 'L';\r
1061     pfr->saddr = host ? dupstr(host) : NULL;\r
1062     pfr->daddr = keyhost ? dupstr(keyhost) : NULL;\r
1063     pfr->sserv = pfr->dserv = NULL;\r
1064     pfr->sport = port;\r
1065     pfr->dport = keyport;\r
1066     pfr->local = NULL;\r
1067     pfr->remote = NULL;\r
1068     pfr->addressfamily = ADDRTYPE_UNSPEC;\r
1070     PortFwdRecord *existing = add234(mgr->forwardings, pfr);\r
1071     if (existing != pfr) {\r
1072         /*\r
1073          * We had this record already. Return failure.\r
1074          */\r
1075         pfr_free(pfr);\r
1076         return false;\r
1077     }\r
1079     char *err = pfl_listen(keyhost, keyport, host, port,\r
1080                            mgr->cl, conf, &pfr->local, pfr->addressfamily);\r
1081     logeventf(mgr->cl->logctx,\r
1082               "%s on port %s:%d to forward to client%s%s",\r
1083               err ? "Failed to listen" : "Listening", host, port,\r
1084               err ? ": " : "", err ? err : "");\r
1085     if (err) {\r
1086         sfree(err);\r
1087         del234(mgr->forwardings, pfr);\r
1088         pfr_free(pfr);\r
1089         return false;\r
1090     }\r
1092     return true;\r
1095 bool portfwdmgr_unlisten(PortFwdManager *mgr, const char *host, int port)\r
1097     PortFwdRecord pfr_key;\r
1099     pfr_key.type = 'L';\r
1100     /* Safe to cast the const away here, because it will only be used\r
1101      * by pfr_cmp, which won't write to the string */\r
1102     pfr_key.saddr = pfr_key.daddr = (char *)host;\r
1103     pfr_key.sserv = pfr_key.dserv = NULL;\r
1104     pfr_key.sport = pfr_key.dport = port;\r
1105     pfr_key.local = NULL;\r
1106     pfr_key.remote = NULL;\r
1107     pfr_key.addressfamily = ADDRTYPE_UNSPEC;\r
1109     PortFwdRecord *pfr = del234(mgr->forwardings, &pfr_key);\r
1111     if (!pfr)\r
1112         return false;\r
1114     logeventf(mgr->cl->logctx, "Closing listening port %s:%d", host, port);\r
1116     pfr_free(pfr);\r
1117     return true;\r
1120 /*\r
1121  * Called when receiving a PORT OPEN from the server to make a\r
1122  * connection to a destination host.\r
1123  *\r
1124  * On success, returns NULL and fills in *pf_ret. On error, returns a\r
1125  * dynamically allocated error message string.\r
1126  */\r
1127 char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret,\r
1128                          char *hostname, int port, SshChannel *c,\r
1129                          int addressfamily)\r
1131     SockAddr *addr;\r
1132     const char *err;\r
1133     char *dummy_realhost = NULL;\r
1134     struct PortForwarding *pf;\r
1136     /*\r
1137      * Try to find host.\r
1138      */\r
1139     addr = name_lookup(hostname, port, &dummy_realhost, mgr->conf,\r
1140                        addressfamily, NULL, NULL);\r
1141     if ((err = sk_addr_error(addr)) != NULL) {\r
1142         char *err_ret = dupstr(err);\r
1143         sk_addr_free(addr);\r
1144         sfree(dummy_realhost);\r
1145         return err_ret;\r
1146     }\r
1148     /*\r
1149      * Open socket.\r
1150      */\r
1151     pf = new_portfwd_state();\r
1152     *chan_ret = &pf->chan;\r
1153     pf->plug.vt = &PortForwarding_plugvt;\r
1154     pf->chan.initial_fixed_window_size = 0;\r
1155     pf->chan.vt = &PortForwarding_channelvt;\r
1156     pf->input_wanted = true;\r
1157     pf->ready = true;\r
1158     pf->c = c;\r
1159     pf->cl = mgr->cl;\r
1160     pf->socks_state = SOCKS_NONE;\r
1162     pf->s = new_connection(addr, dummy_realhost, port,\r
1163                            false, true, false, false, &pf->plug, mgr->conf);\r
1164     sfree(dummy_realhost);\r
1165     if ((err = sk_socket_error(pf->s)) != NULL) {\r
1166         char *err_ret = dupstr(err);\r
1167         sk_close(pf->s);\r
1168         free_portfwd_state(pf);\r
1169         *chan_ret = NULL;\r
1170         return err_ret;\r
1171     }\r
1173     return NULL;\r