CPatch: New memory management
[TortoiseGit.git] / src / TortoisePlink / PROXY.C
blobd9a0f78718034e249ebed0fa8e1021715711b8da
1 /*\r
2  * Network proxy abstraction in PuTTY\r
3  *\r
4  * A proxy layer, if necessary, wedges itself between the network\r
5  * code and the higher level backend.\r
6  */\r
7 \r
8 #include <assert.h>\r
9 #include <ctype.h>\r
10 #include <string.h>\r
12 #define DEFINE_PLUG_METHOD_MACROS\r
13 #include "putty.h"\r
14 #include "network.h"\r
15 #include "proxy.h"\r
17 #define do_proxy_dns(conf) \\r
18     (conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \\r
19          (conf_get_int(conf, CONF_proxy_dns) == AUTO && \\r
20               conf_get_int(conf, CONF_proxy_type) != PROXY_SOCKS4))\r
22 /*\r
23  * Call this when proxy negotiation is complete, so that this\r
24  * socket can begin working normally.\r
25  */\r
26 void proxy_activate (Proxy_Socket p)\r
27 {\r
28     void *data;\r
29     int len;\r
30     long output_before, output_after;\r
31     \r
32     p->state = PROXY_STATE_ACTIVE;\r
34     /* we want to ignore new receive events until we have sent\r
35      * all of our buffered receive data.\r
36      */\r
37     sk_set_frozen(p->sub_socket, 1);\r
39     /* how many bytes of output have we buffered? */\r
40     output_before = bufchain_size(&p->pending_oob_output_data) +\r
41         bufchain_size(&p->pending_output_data);\r
42     /* and keep track of how many bytes do not get sent. */\r
43     output_after = 0;\r
44     \r
45     /* send buffered OOB writes */\r
46     while (bufchain_size(&p->pending_oob_output_data) > 0) {\r
47         bufchain_prefix(&p->pending_oob_output_data, &data, &len);\r
48         output_after += sk_write_oob(p->sub_socket, data, len);\r
49         bufchain_consume(&p->pending_oob_output_data, len);\r
50     }\r
52     /* send buffered normal writes */\r
53     while (bufchain_size(&p->pending_output_data) > 0) {\r
54         bufchain_prefix(&p->pending_output_data, &data, &len);\r
55         output_after += sk_write(p->sub_socket, data, len);\r
56         bufchain_consume(&p->pending_output_data, len);\r
57     }\r
59     /* if we managed to send any data, let the higher levels know. */\r
60     if (output_after < output_before)\r
61         plug_sent(p->plug, output_after);\r
63     /* if we were asked to flush the output during\r
64      * the proxy negotiation process, do so now.\r
65      */\r
66     if (p->pending_flush) sk_flush(p->sub_socket);\r
68     /* if we have a pending EOF to send, send it */\r
69     if (p->pending_eof) sk_write_eof(p->sub_socket);\r
71     /* if the backend wanted the socket unfrozen, try to unfreeze.\r
72      * our set_frozen handler will flush buffered receive data before\r
73      * unfreezing the actual underlying socket.\r
74      */\r
75     if (!p->freeze)\r
76         sk_set_frozen((Socket)p, 0);\r
77 }\r
79 /* basic proxy socket functions */\r
81 static Plug sk_proxy_plug (Socket s, Plug p)\r
82 {\r
83     Proxy_Socket ps = (Proxy_Socket) s;\r
84     Plug ret = ps->plug;\r
85     if (p)\r
86         ps->plug = p;\r
87     return ret;\r
88 }\r
90 static void sk_proxy_close (Socket s)\r
91 {\r
92     Proxy_Socket ps = (Proxy_Socket) s;\r
94     sk_close(ps->sub_socket);\r
95     sk_addr_free(ps->remote_addr);\r
96     sfree(ps);\r
97 }\r
99 static int sk_proxy_write (Socket s, const char *data, int len)\r
101     Proxy_Socket ps = (Proxy_Socket) s;\r
103     if (ps->state != PROXY_STATE_ACTIVE) {\r
104         bufchain_add(&ps->pending_output_data, data, len);\r
105         return bufchain_size(&ps->pending_output_data);\r
106     }\r
107     return sk_write(ps->sub_socket, data, len);\r
110 static int sk_proxy_write_oob (Socket s, const char *data, int len)\r
112     Proxy_Socket ps = (Proxy_Socket) s;\r
114     if (ps->state != PROXY_STATE_ACTIVE) {\r
115         bufchain_clear(&ps->pending_output_data);\r
116         bufchain_clear(&ps->pending_oob_output_data);\r
117         bufchain_add(&ps->pending_oob_output_data, data, len);\r
118         return len;\r
119     }\r
120     return sk_write_oob(ps->sub_socket, data, len);\r
123 static void sk_proxy_write_eof (Socket s)\r
125     Proxy_Socket ps = (Proxy_Socket) s;\r
127     if (ps->state != PROXY_STATE_ACTIVE) {\r
128         ps->pending_eof = 1;\r
129         return;\r
130     }\r
131     sk_write_eof(ps->sub_socket);\r
134 static void sk_proxy_flush (Socket s)\r
136     Proxy_Socket ps = (Proxy_Socket) s;\r
138     if (ps->state != PROXY_STATE_ACTIVE) {\r
139         ps->pending_flush = 1;\r
140         return;\r
141     }\r
142     sk_flush(ps->sub_socket);\r
145 static void sk_proxy_set_frozen (Socket s, int is_frozen)\r
147     Proxy_Socket ps = (Proxy_Socket) s;\r
149     if (ps->state != PROXY_STATE_ACTIVE) {\r
150         ps->freeze = is_frozen;\r
151         return;\r
152     }\r
153     \r
154     /* handle any remaining buffered recv data first */\r
155     if (bufchain_size(&ps->pending_input_data) > 0) {\r
156         ps->freeze = is_frozen;\r
158         /* loop while we still have buffered data, and while we are\r
159          * unfrozen. the plug_receive call in the loop could result \r
160          * in a call back into this function refreezing the socket, \r
161          * so we have to check each time.\r
162          */\r
163         while (!ps->freeze && bufchain_size(&ps->pending_input_data) > 0) {\r
164             void *data;\r
165             char databuf[512];\r
166             int len;\r
167             bufchain_prefix(&ps->pending_input_data, &data, &len);\r
168             if (len > lenof(databuf))\r
169                 len = lenof(databuf);\r
170             memcpy(databuf, data, len);\r
171             bufchain_consume(&ps->pending_input_data, len);\r
172             plug_receive(ps->plug, 0, databuf, len);\r
173         }\r
175         /* if we're still frozen, we'll have to wait for another\r
176          * call from the backend to finish unbuffering the data.\r
177          */\r
178         if (ps->freeze) return;\r
179     }\r
180     \r
181     sk_set_frozen(ps->sub_socket, is_frozen);\r
184 static const char * sk_proxy_socket_error (Socket s)\r
186     Proxy_Socket ps = (Proxy_Socket) s;\r
187     if (ps->error != NULL || ps->sub_socket == NULL) {\r
188         return ps->error;\r
189     }\r
190     return sk_socket_error(ps->sub_socket);\r
193 /* basic proxy plug functions */\r
195 static void plug_proxy_log(Plug plug, int type, SockAddr addr, int port,\r
196                            const char *error_msg, int error_code)\r
198     Proxy_Plug pp = (Proxy_Plug) plug;\r
199     Proxy_Socket ps = pp->proxy_socket;\r
201     plug_log(ps->plug, type, addr, port, error_msg, error_code);\r
204 static void plug_proxy_closing (Plug p, const char *error_msg,\r
205                                 int error_code, int calling_back)\r
207     Proxy_Plug pp = (Proxy_Plug) p;\r
208     Proxy_Socket ps = pp->proxy_socket;\r
210     if (ps->state != PROXY_STATE_ACTIVE) {\r
211         ps->closing_error_msg = error_msg;\r
212         ps->closing_error_code = error_code;\r
213         ps->closing_calling_back = calling_back;\r
214         ps->negotiate(ps, PROXY_CHANGE_CLOSING);\r
215     } else {\r
216         plug_closing(ps->plug, error_msg, error_code, calling_back);\r
217     }\r
220 static void plug_proxy_receive (Plug p, int urgent, char *data, int len)\r
222     Proxy_Plug pp = (Proxy_Plug) p;\r
223     Proxy_Socket ps = pp->proxy_socket;\r
225     if (ps->state != PROXY_STATE_ACTIVE) {\r
226         /* we will lose the urgentness of this data, but since most,\r
227          * if not all, of this data will be consumed by the negotiation\r
228          * process, hopefully it won't affect the protocol above us\r
229          */\r
230         bufchain_add(&ps->pending_input_data, data, len);\r
231         ps->receive_urgent = urgent;\r
232         ps->receive_data = data;\r
233         ps->receive_len = len;\r
234         ps->negotiate(ps, PROXY_CHANGE_RECEIVE);\r
235     } else {\r
236         plug_receive(ps->plug, urgent, data, len);\r
237     }\r
240 static void plug_proxy_sent (Plug p, int bufsize)\r
242     Proxy_Plug pp = (Proxy_Plug) p;\r
243     Proxy_Socket ps = pp->proxy_socket;\r
245     if (ps->state != PROXY_STATE_ACTIVE) {\r
246         ps->sent_bufsize = bufsize;\r
247         ps->negotiate(ps, PROXY_CHANGE_SENT);\r
248         return;\r
249     }\r
250     plug_sent(ps->plug, bufsize);\r
253 static int plug_proxy_accepting(Plug p,\r
254                                 accept_fn_t constructor, accept_ctx_t ctx)\r
256     Proxy_Plug pp = (Proxy_Plug) p;\r
257     Proxy_Socket ps = pp->proxy_socket;\r
259     if (ps->state != PROXY_STATE_ACTIVE) {\r
260         ps->accepting_constructor = constructor;\r
261         ps->accepting_ctx = ctx;\r
262         return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING);\r
263     }\r
264     return plug_accepting(ps->plug, constructor, ctx);\r
267 /*\r
268  * This function can accept a NULL pointer as `addr', in which case\r
269  * it will only check the host name.\r
270  */\r
271 int proxy_for_destination (SockAddr addr, const char *hostname,\r
272                            int port, Conf *conf)\r
274     int s = 0, e = 0;\r
275     char hostip[64];\r
276     int hostip_len, hostname_len;\r
277     const char *exclude_list;\r
279     /*\r
280      * Special local connections such as Unix-domain sockets\r
281      * unconditionally cannot be proxied, even in proxy-localhost\r
282      * mode. There just isn't any way to ask any known proxy type for\r
283      * them.\r
284      */\r
285     if (addr && sk_address_is_special_local(addr))\r
286         return 0;                      /* do not proxy */\r
288     /*\r
289      * Check the host name and IP against the hard-coded\r
290      * representations of `localhost'.\r
291      */\r
292     if (!conf_get_int(conf, CONF_even_proxy_localhost) &&\r
293         (sk_hostname_is_local(hostname) ||\r
294          (addr && sk_address_is_local(addr))))\r
295         return 0;                      /* do not proxy */\r
297     /* we want a string representation of the IP address for comparisons */\r
298     if (addr) {\r
299         sk_getaddr(addr, hostip, 64);\r
300         hostip_len = strlen(hostip);\r
301     } else\r
302         hostip_len = 0;                /* placate gcc; shouldn't be required */\r
304     hostname_len = strlen(hostname);\r
306     exclude_list = conf_get_str(conf, CONF_proxy_exclude_list);\r
308     /* now parse the exclude list, and see if either our IP\r
309      * or hostname matches anything in it.\r
310      */\r
312     while (exclude_list[s]) {\r
313         while (exclude_list[s] &&\r
314                (isspace((unsigned char)exclude_list[s]) ||\r
315                 exclude_list[s] == ',')) s++;\r
317         if (!exclude_list[s]) break;\r
319         e = s;\r
321         while (exclude_list[e] &&\r
322                (isalnum((unsigned char)exclude_list[e]) ||\r
323                 exclude_list[e] == '-' ||\r
324                 exclude_list[e] == '.' ||\r
325                 exclude_list[e] == '*')) e++;\r
327         if (exclude_list[s] == '*') {\r
328             /* wildcard at beginning of entry */\r
330             if ((addr && strnicmp(hostip + hostip_len - (e - s - 1),\r
331                                   exclude_list + s + 1, e - s - 1) == 0) ||\r
332                 strnicmp(hostname + hostname_len - (e - s - 1),\r
333                          exclude_list + s + 1, e - s - 1) == 0)\r
334                 return 0; /* IP/hostname range excluded. do not use proxy. */\r
336         } else if (exclude_list[e-1] == '*') {\r
337             /* wildcard at end of entry */\r
339             if ((addr && strnicmp(hostip, exclude_list + s, e - s - 1) == 0) ||\r
340                 strnicmp(hostname, exclude_list + s, e - s - 1) == 0)\r
341                 return 0; /* IP/hostname range excluded. do not use proxy. */\r
343         } else {\r
344             /* no wildcard at either end, so let's try an absolute\r
345              * match (ie. a specific IP)\r
346              */\r
348             if (addr && strnicmp(hostip, exclude_list + s, e - s) == 0)\r
349                 return 0; /* IP/hostname excluded. do not use proxy. */\r
350             if (strnicmp(hostname, exclude_list + s, e - s) == 0)\r
351                 return 0; /* IP/hostname excluded. do not use proxy. */\r
352         }\r
354         s = e;\r
356         /* Make sure we really have reached the next comma or end-of-string */\r
357         while (exclude_list[s] &&\r
358                !isspace((unsigned char)exclude_list[s]) &&\r
359                exclude_list[s] != ',') s++;\r
360     }\r
362     /* no matches in the exclude list, so use the proxy */\r
363     return 1;\r
366 static char *dns_log_msg(const char *host, int addressfamily,\r
367                          const char *reason)\r
369     return dupprintf("Looking up host \"%s\"%s for %s", host,\r
370                      (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :\r
371                       addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :\r
372                       ""), reason);\r
375 SockAddr name_lookup(const char *host, int port, char **canonicalname,\r
376                      Conf *conf, int addressfamily, void *frontend,\r
377                      const char *reason)\r
379     char *logmsg;\r
380     if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&\r
381         do_proxy_dns(conf) &&\r
382         proxy_for_destination(NULL, host, port, conf)) {\r
384         if (frontend) {\r
385             logmsg = dupprintf("Leaving host lookup to proxy of \"%s\""\r
386                                " (for %s)", host, reason);\r
387             logevent(frontend, logmsg);\r
388             sfree(logmsg);\r
389         }\r
391         *canonicalname = dupstr(host);\r
392         return sk_nonamelookup(host);\r
393     } else {\r
394         if (frontend) {\r
395             logmsg = dns_log_msg(host, addressfamily, reason);\r
396             logevent(frontend, logmsg);\r
397             sfree(logmsg);\r
398         }\r
400         return sk_namelookup(host, canonicalname, addressfamily);\r
401     }\r
404 Socket new_connection(SockAddr addr, const char *hostname,\r
405                       int port, int privport,\r
406                       int oobinline, int nodelay, int keepalive,\r
407                       Plug plug, Conf *conf)\r
409     static const struct socket_function_table socket_fn_table = {\r
410         sk_proxy_plug,\r
411         sk_proxy_close,\r
412         sk_proxy_write,\r
413         sk_proxy_write_oob,\r
414         sk_proxy_write_eof,\r
415         sk_proxy_flush,\r
416         sk_proxy_set_frozen,\r
417         sk_proxy_socket_error,\r
418         NULL, /* peer_info */\r
419     };\r
421     static const struct plug_function_table plug_fn_table = {\r
422         plug_proxy_log,\r
423         plug_proxy_closing,\r
424         plug_proxy_receive,\r
425         plug_proxy_sent,\r
426         plug_proxy_accepting\r
427     };\r
429     if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&\r
430         proxy_for_destination(addr, hostname, port, conf))\r
431     {\r
432         Proxy_Socket ret;\r
433         Proxy_Plug pplug;\r
434         SockAddr proxy_addr;\r
435         char *proxy_canonical_name;\r
436         const char *proxy_type;\r
437         Socket sret;\r
438         int type;\r
440         if ((sret = platform_new_connection(addr, hostname, port, privport,\r
441                                             oobinline, nodelay, keepalive,\r
442                                             plug, conf)) !=\r
443             NULL)\r
444             return sret;\r
446         ret = snew(struct Socket_proxy_tag);\r
447         ret->fn = &socket_fn_table;\r
448         ret->conf = conf_copy(conf);\r
449         ret->plug = plug;\r
450         ret->remote_addr = addr;       /* will need to be freed on close */\r
451         ret->remote_port = port;\r
453         ret->error = NULL;\r
454         ret->pending_flush = 0;\r
455         ret->pending_eof = 0;\r
456         ret->freeze = 0;\r
458         bufchain_init(&ret->pending_input_data);\r
459         bufchain_init(&ret->pending_output_data);\r
460         bufchain_init(&ret->pending_oob_output_data);\r
462         ret->sub_socket = NULL;\r
463         ret->state = PROXY_STATE_NEW;\r
464         ret->negotiate = NULL;\r
466         type = conf_get_int(conf, CONF_proxy_type);\r
467         if (type == PROXY_HTTP) {\r
468             ret->negotiate = proxy_http_negotiate;\r
469             proxy_type = "HTTP";\r
470         } else if (type == PROXY_SOCKS4) {\r
471             ret->negotiate = proxy_socks4_negotiate;\r
472             proxy_type = "SOCKS 4";\r
473         } else if (type == PROXY_SOCKS5) {\r
474             ret->negotiate = proxy_socks5_negotiate;\r
475             proxy_type = "SOCKS 5";\r
476         } else if (type == PROXY_TELNET) {\r
477             ret->negotiate = proxy_telnet_negotiate;\r
478             proxy_type = "Telnet";\r
479         } else {\r
480             ret->error = "Proxy error: Unknown proxy method";\r
481             return (Socket) ret;\r
482         }\r
484         {\r
485             char *logmsg = dupprintf("Will use %s proxy at %s:%d to connect"\r
486                                       " to %s:%d", proxy_type,\r
487                                       conf_get_str(conf, CONF_proxy_host),\r
488                                       conf_get_int(conf, CONF_proxy_port),\r
489                                       hostname, port);\r
490             plug_log(plug, 2, NULL, 0, logmsg, 0);\r
491             sfree(logmsg);\r
492         }\r
494         /* create the proxy plug to map calls from the actual\r
495          * socket into our proxy socket layer */\r
496         pplug = snew(struct Plug_proxy_tag);\r
497         pplug->fn = &plug_fn_table;\r
498         pplug->proxy_socket = ret;\r
500         {\r
501             char *logmsg = dns_log_msg(conf_get_str(conf, CONF_proxy_host),\r
502                                        conf_get_int(conf, CONF_addressfamily),\r
503                                        "proxy");\r
504             plug_log(plug, 2, NULL, 0, logmsg, 0);\r
505             sfree(logmsg);\r
506         }\r
508         /* look-up proxy */\r
509         proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host),\r
510                                    &proxy_canonical_name,\r
511                                    conf_get_int(conf, CONF_addressfamily));\r
512         if (sk_addr_error(proxy_addr) != NULL) {\r
513             ret->error = "Proxy error: Unable to resolve proxy host name";\r
514             sfree(pplug);\r
515             sk_addr_free(proxy_addr);\r
516             return (Socket)ret;\r
517         }\r
518         sfree(proxy_canonical_name);\r
520         {\r
521             char addrbuf[256], *logmsg;\r
522             sk_getaddr(proxy_addr, addrbuf, lenof(addrbuf));\r
523             logmsg = dupprintf("Connecting to %s proxy at %s port %d",\r
524                                proxy_type, addrbuf,\r
525                                conf_get_int(conf, CONF_proxy_port));\r
526             plug_log(plug, 2, NULL, 0, logmsg, 0);\r
527             sfree(logmsg);\r
528         }\r
530         /* create the actual socket we will be using,\r
531          * connected to our proxy server and port.\r
532          */\r
533         ret->sub_socket = sk_new(proxy_addr,\r
534                                  conf_get_int(conf, CONF_proxy_port),\r
535                                  privport, oobinline,\r
536                                  nodelay, keepalive, (Plug) pplug);\r
537         if (sk_socket_error(ret->sub_socket) != NULL)\r
538             return (Socket) ret;\r
540         /* start the proxy negotiation process... */\r
541         sk_set_frozen(ret->sub_socket, 0);\r
542         ret->negotiate(ret, PROXY_CHANGE_NEW);\r
544         return (Socket) ret;\r
545     }\r
547     /* no proxy, so just return the direct socket */\r
548     return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);\r
551 Socket new_listener(const char *srcaddr, int port, Plug plug,\r
552                     int local_host_only, Conf *conf, int addressfamily)\r
554     /* TODO: SOCKS (and potentially others) support inbound\r
555      * TODO: connections via the proxy. support them.\r
556      */\r
558     return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily);\r
561 /* ----------------------------------------------------------------------\r
562  * HTTP CONNECT proxy type.\r
563  */\r
565 static int get_line_end (char * data, int len)\r
567     int off = 0;\r
569     while (off < len)\r
570     {\r
571         if (data[off] == '\n') {\r
572             /* we have a newline */\r
573             off++;\r
575             /* is that the only thing on this line? */\r
576             if (off <= 2) return off;\r
578             /* if not, then there is the possibility that this header\r
579              * continues onto the next line, if it starts with a space\r
580              * or a tab.\r
581              */\r
583             if (off + 1 < len &&\r
584                 data[off+1] != ' ' &&\r
585                 data[off+1] != '\t') return off;\r
587             /* the line does continue, so we have to keep going\r
588              * until we see an the header's "real" end of line.\r
589              */\r
590             off++;\r
591         }\r
593         off++;\r
594     }\r
596     return -1;\r
599 int proxy_http_negotiate (Proxy_Socket p, int change)\r
601     if (p->state == PROXY_STATE_NEW) {\r
602         /* we are just beginning the proxy negotiate process,\r
603          * so we'll send off the initial bits of the request.\r
604          * for this proxy method, it's just a simple HTTP\r
605          * request\r
606          */\r
607         char *buf, dest[512];\r
608         char *username, *password;\r
610         sk_getaddr(p->remote_addr, dest, lenof(dest));\r
612         buf = dupprintf("CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n",\r
613                         dest, p->remote_port, dest, p->remote_port);\r
614         sk_write(p->sub_socket, buf, strlen(buf));\r
615         sfree(buf);\r
617         username = conf_get_str(p->conf, CONF_proxy_username);\r
618         password = conf_get_str(p->conf, CONF_proxy_password);\r
619         if (username[0] || password[0]) {\r
620             char *buf, *buf2;\r
621             int i, j, len;\r
622             buf = dupprintf("%s:%s", username, password);\r
623             len = strlen(buf);\r
624             buf2 = snewn(len * 4 / 3 + 100, char);\r
625             sprintf(buf2, "Proxy-Authorization: Basic ");\r
626             for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4)\r
627                 base64_encode_atom((unsigned char *)(buf+i),\r
628                                    (len-i > 3 ? 3 : len-i), buf2+j);\r
629             strcpy(buf2+j, "\r\n");\r
630             sk_write(p->sub_socket, buf2, strlen(buf2));\r
631             sfree(buf);\r
632             sfree(buf2);\r
633         }\r
635         sk_write(p->sub_socket, "\r\n", 2);\r
637         p->state = 1;\r
638         return 0;\r
639     }\r
641     if (change == PROXY_CHANGE_CLOSING) {\r
642         /* if our proxy negotiation process involves closing and opening\r
643          * new sockets, then we would want to intercept this closing\r
644          * callback when we were expecting it. if we aren't anticipating\r
645          * a socket close, then some error must have occurred. we'll\r
646          * just pass those errors up to the backend.\r
647          */\r
648         plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,\r
649                      p->closing_calling_back);\r
650         return 0; /* ignored */\r
651     }\r
653     if (change == PROXY_CHANGE_SENT) {\r
654         /* some (or all) of what we wrote to the proxy was sent.\r
655          * we don't do anything new, however, until we receive the\r
656          * proxy's response. we might want to set a timer so we can\r
657          * timeout the proxy negotiation after a while...\r
658          */\r
659         return 0;\r
660     }\r
662     if (change == PROXY_CHANGE_ACCEPTING) {\r
663         /* we should _never_ see this, as we are using our socket to\r
664          * connect to a proxy, not accepting inbound connections.\r
665          * what should we do? close the socket with an appropriate\r
666          * error message?\r
667          */\r
668         return plug_accepting(p->plug,\r
669                               p->accepting_constructor, p->accepting_ctx);\r
670     }\r
672     if (change == PROXY_CHANGE_RECEIVE) {\r
673         /* we have received data from the underlying socket, which\r
674          * we'll need to parse, process, and respond to appropriately.\r
675          */\r
677         char *data, *datap;\r
678         int len;\r
679         int eol;\r
681         if (p->state == 1) {\r
683             int min_ver, maj_ver, status;\r
685             /* get the status line */\r
686             len = bufchain_size(&p->pending_input_data);\r
687             assert(len > 0);           /* or we wouldn't be here */\r
688             data = snewn(len+1, char);\r
689             bufchain_fetch(&p->pending_input_data, data, len);\r
690             /*\r
691              * We must NUL-terminate this data, because Windows\r
692              * sscanf appears to require a NUL at the end of the\r
693              * string because it strlens it _first_. Sigh.\r
694              */\r
695             data[len] = '\0';\r
697             eol = get_line_end(data, len);\r
698             if (eol < 0) {\r
699                 sfree(data);\r
700                 return 1;\r
701             }\r
703             status = -1;\r
704             /* We can't rely on whether the %n incremented the sscanf return */\r
705             if (sscanf((char *)data, "HTTP/%i.%i %n",\r
706                        &maj_ver, &min_ver, &status) < 2 || status == -1) {\r
707                 plug_closing(p->plug, "Proxy error: HTTP response was absent",\r
708                              PROXY_ERROR_GENERAL, 0);\r
709                 sfree(data);\r
710                 return 1;\r
711             }\r
713             /* remove the status line from the input buffer. */\r
714             bufchain_consume(&p->pending_input_data, eol);\r
715             if (data[status] != '2') {\r
716                 /* error */\r
717                 char *buf;\r
718                 data[eol] = '\0';\r
719                 while (eol > status &&\r
720                        (data[eol-1] == '\r' || data[eol-1] == '\n'))\r
721                     data[--eol] = '\0';\r
722                 buf = dupprintf("Proxy error: %s", data+status);\r
723                 plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);\r
724                 sfree(buf);\r
725                 sfree(data);\r
726                 return 1;\r
727             }\r
729             sfree(data);\r
731             p->state = 2;\r
732         }\r
734         if (p->state == 2) {\r
736             /* get headers. we're done when we get a\r
737              * header of length 2, (ie. just "\r\n")\r
738              */\r
740             len = bufchain_size(&p->pending_input_data);\r
741             assert(len > 0);           /* or we wouldn't be here */\r
742             data = snewn(len, char);\r
743             datap = data;\r
744             bufchain_fetch(&p->pending_input_data, data, len);\r
746             eol = get_line_end(datap, len);\r
747             if (eol < 0) {\r
748                 sfree(data);\r
749                 return 1;\r
750             }\r
751             while (eol > 2)\r
752             {\r
753                 bufchain_consume(&p->pending_input_data, eol);\r
754                 datap += eol;\r
755                 len   -= eol;\r
756                 eol = get_line_end(datap, len);\r
757             }\r
759             if (eol == 2) {\r
760                 /* we're done */\r
761                 bufchain_consume(&p->pending_input_data, 2);\r
762                 proxy_activate(p);\r
763                 /* proxy activate will have dealt with\r
764                  * whatever is left of the buffer */\r
765                 sfree(data);\r
766                 return 1;\r
767             }\r
769             sfree(data);\r
770             return 1;\r
771         }\r
772     }\r
774     plug_closing(p->plug, "Proxy error: unexpected proxy error",\r
775                  PROXY_ERROR_UNEXPECTED, 0);\r
776     return 1;\r
779 /* ----------------------------------------------------------------------\r
780  * SOCKS proxy type.\r
781  */\r
783 /* SOCKS version 4 */\r
784 int proxy_socks4_negotiate (Proxy_Socket p, int change)\r
786     if (p->state == PROXY_CHANGE_NEW) {\r
788         /* request format:\r
789          *  version number (1 byte) = 4\r
790          *  command code (1 byte)\r
791          *    1 = CONNECT\r
792          *    2 = BIND\r
793          *  dest. port (2 bytes) [network order]\r
794          *  dest. address (4 bytes)\r
795          *  user ID (variable length, null terminated string)\r
796          */\r
798         int length, type, namelen;\r
799         char *command, addr[4], hostname[512];\r
800         char *username;\r
802         type = sk_addrtype(p->remote_addr);\r
803         if (type == ADDRTYPE_IPV6) {\r
804             p->error = "Proxy error: SOCKS version 4 does not support IPv6";\r
805             return 1;\r
806         } else if (type == ADDRTYPE_IPV4) {\r
807             namelen = 0;\r
808             sk_addrcopy(p->remote_addr, addr);\r
809         } else {                       /* type == ADDRTYPE_NAME */\r
810             assert(type == ADDRTYPE_NAME);\r
811             sk_getaddr(p->remote_addr, hostname, lenof(hostname));\r
812             namelen = strlen(hostname) + 1;   /* include the NUL */\r
813             addr[0] = addr[1] = addr[2] = 0;\r
814             addr[3] = 1;\r
815         }\r
817         username = conf_get_str(p->conf, CONF_proxy_username);\r
818         length = strlen(username) + namelen + 9;\r
819         command = snewn(length, char);\r
820         strcpy(command + 8, username);\r
822         command[0] = 4; /* version 4 */\r
823         command[1] = 1; /* CONNECT command */\r
825         /* port */\r
826         command[2] = (char) (p->remote_port >> 8) & 0xff;\r
827         command[3] = (char) p->remote_port & 0xff;\r
829         /* address */\r
830         memcpy(command + 4, addr, 4);\r
832         /* hostname */\r
833         memcpy(command + 8 + strlen(username) + 1,\r
834                hostname, namelen);\r
836         sk_write(p->sub_socket, command, length);\r
837         sfree(username);\r
838         sfree(command);\r
840         p->state = 1;\r
841         return 0;\r
842     }\r
844     if (change == PROXY_CHANGE_CLOSING) {\r
845         /* if our proxy negotiation process involves closing and opening\r
846          * new sockets, then we would want to intercept this closing\r
847          * callback when we were expecting it. if we aren't anticipating\r
848          * a socket close, then some error must have occurred. we'll\r
849          * just pass those errors up to the backend.\r
850          */\r
851         plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,\r
852                      p->closing_calling_back);\r
853         return 0; /* ignored */\r
854     }\r
856     if (change == PROXY_CHANGE_SENT) {\r
857         /* some (or all) of what we wrote to the proxy was sent.\r
858          * we don't do anything new, however, until we receive the\r
859          * proxy's response. we might want to set a timer so we can\r
860          * timeout the proxy negotiation after a while...\r
861          */\r
862         return 0;\r
863     }\r
865     if (change == PROXY_CHANGE_ACCEPTING) {\r
866         /* we should _never_ see this, as we are using our socket to\r
867          * connect to a proxy, not accepting inbound connections.\r
868          * what should we do? close the socket with an appropriate\r
869          * error message?\r
870          */\r
871         return plug_accepting(p->plug,\r
872                               p->accepting_constructor, p->accepting_ctx);\r
873     }\r
875     if (change == PROXY_CHANGE_RECEIVE) {\r
876         /* we have received data from the underlying socket, which\r
877          * we'll need to parse, process, and respond to appropriately.\r
878          */\r
880         if (p->state == 1) {\r
881             /* response format:\r
882              *  version number (1 byte) = 4\r
883              *  reply code (1 byte)\r
884              *    90 = request granted\r
885              *    91 = request rejected or failed\r
886              *    92 = request rejected due to lack of IDENTD on client\r
887              *    93 = request rejected due to difference in user ID \r
888              *         (what we sent vs. what IDENTD said)\r
889              *  dest. port (2 bytes)\r
890              *  dest. address (4 bytes)\r
891              */\r
893             char data[8];\r
895             if (bufchain_size(&p->pending_input_data) < 8)\r
896                 return 1;              /* not got anything yet */\r
897             \r
898             /* get the response */\r
899             bufchain_fetch(&p->pending_input_data, data, 8);\r
901             if (data[0] != 0) {\r
902                 plug_closing(p->plug, "Proxy error: SOCKS proxy responded with "\r
903                                       "unexpected reply code version",\r
904                              PROXY_ERROR_GENERAL, 0);\r
905                 return 1;\r
906             }\r
908             if (data[1] != 90) {\r
910                 switch (data[1]) {\r
911                   case 92:\r
912                     plug_closing(p->plug, "Proxy error: SOCKS server wanted IDENTD on client",\r
913                                  PROXY_ERROR_GENERAL, 0);\r
914                     break;\r
915                   case 93:\r
916                     plug_closing(p->plug, "Proxy error: Username and IDENTD on client don't agree",\r
917                                  PROXY_ERROR_GENERAL, 0);\r
918                     break;\r
919                   case 91:\r
920                   default:\r
921                     plug_closing(p->plug, "Proxy error: Error while communicating with proxy",\r
922                                  PROXY_ERROR_GENERAL, 0);\r
923                     break;\r
924                 }\r
926                 return 1;\r
927             }\r
928             bufchain_consume(&p->pending_input_data, 8);\r
930             /* we're done */\r
931             proxy_activate(p);\r
932             /* proxy activate will have dealt with\r
933              * whatever is left of the buffer */\r
934             return 1;\r
935         }\r
936     }\r
938     plug_closing(p->plug, "Proxy error: unexpected proxy error",\r
939                  PROXY_ERROR_UNEXPECTED, 0);\r
940     return 1;\r
943 /* SOCKS version 5 */\r
944 int proxy_socks5_negotiate (Proxy_Socket p, int change)\r
946     if (p->state == PROXY_CHANGE_NEW) {\r
948         /* initial command:\r
949          *  version number (1 byte) = 5\r
950          *  number of available authentication methods (1 byte)\r
951          *  available authentication methods (1 byte * previous value)\r
952          *    authentication methods:\r
953          *     0x00 = no authentication\r
954          *     0x01 = GSSAPI\r
955          *     0x02 = username/password\r
956          *     0x03 = CHAP\r
957          */\r
959         char command[5];\r
960         char *username, *password;\r
961         int len;\r
963         command[0] = 5; /* version 5 */\r
964         username = conf_get_str(p->conf, CONF_proxy_username);\r
965         password = conf_get_str(p->conf, CONF_proxy_password);\r
966         if (username[0] || password[0]) {\r
967             command[2] = 0x00;         /* no authentication */\r
968             len = 3;\r
969             proxy_socks5_offerencryptedauth (command, &len);\r
970             command[len++] = 0x02;             /* username/password */\r
971             command[1] = len - 2;       /* Number of methods supported */\r
972         } else {\r
973             command[1] = 1;            /* one methods supported: */\r
974             command[2] = 0x00;         /* no authentication */\r
975             len = 3;\r
976         }\r
978         sk_write(p->sub_socket, command, len);\r
980         p->state = 1;\r
981         return 0;\r
982     }\r
984     if (change == PROXY_CHANGE_CLOSING) {\r
985         /* if our proxy negotiation process involves closing and opening\r
986          * new sockets, then we would want to intercept this closing\r
987          * callback when we were expecting it. if we aren't anticipating\r
988          * a socket close, then some error must have occurred. we'll\r
989          * just pass those errors up to the backend.\r
990          */\r
991         plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,\r
992                      p->closing_calling_back);\r
993         return 0; /* ignored */\r
994     }\r
996     if (change == PROXY_CHANGE_SENT) {\r
997         /* some (or all) of what we wrote to the proxy was sent.\r
998          * we don't do anything new, however, until we receive the\r
999          * proxy's response. we might want to set a timer so we can\r
1000          * timeout the proxy negotiation after a while...\r
1001          */\r
1002         return 0;\r
1003     }\r
1005     if (change == PROXY_CHANGE_ACCEPTING) {\r
1006         /* we should _never_ see this, as we are using our socket to\r
1007          * connect to a proxy, not accepting inbound connections.\r
1008          * what should we do? close the socket with an appropriate\r
1009          * error message?\r
1010          */\r
1011         return plug_accepting(p->plug,\r
1012                               p->accepting_constructor, p->accepting_ctx);\r
1013     }\r
1015     if (change == PROXY_CHANGE_RECEIVE) {\r
1016         /* we have received data from the underlying socket, which\r
1017          * we'll need to parse, process, and respond to appropriately.\r
1018          */\r
1020         if (p->state == 1) {\r
1022             /* initial response:\r
1023              *  version number (1 byte) = 5\r
1024              *  authentication method (1 byte)\r
1025              *    authentication methods:\r
1026              *     0x00 = no authentication\r
1027              *     0x01 = GSSAPI\r
1028              *     0x02 = username/password\r
1029              *     0x03 = CHAP\r
1030              *     0xff = no acceptable methods\r
1031              */\r
1032             char data[2];\r
1034             if (bufchain_size(&p->pending_input_data) < 2)\r
1035                 return 1;              /* not got anything yet */\r
1037             /* get the response */\r
1038             bufchain_fetch(&p->pending_input_data, data, 2);\r
1040             if (data[0] != 5) {\r
1041                 plug_closing(p->plug, "Proxy error: SOCKS proxy returned unexpected version",\r
1042                              PROXY_ERROR_GENERAL, 0);\r
1043                 return 1;\r
1044             }\r
1046             if (data[1] == 0x00) p->state = 2; /* no authentication needed */\r
1047             else if (data[1] == 0x01) p->state = 4; /* GSSAPI authentication */\r
1048             else if (data[1] == 0x02) p->state = 5; /* username/password authentication */\r
1049             else if (data[1] == 0x03) p->state = 6; /* CHAP authentication */\r
1050             else {\r
1051                 plug_closing(p->plug, "Proxy error: SOCKS proxy did not accept our authentication",\r
1052                              PROXY_ERROR_GENERAL, 0);\r
1053                 return 1;\r
1054             }\r
1055             bufchain_consume(&p->pending_input_data, 2);\r
1056         }\r
1058         if (p->state == 7) {\r
1060             /* password authentication reply format:\r
1061              *  version number (1 bytes) = 1\r
1062              *  reply code (1 byte)\r
1063              *    0 = succeeded\r
1064              *    >0 = failed\r
1065              */\r
1066             char data[2];\r
1068             if (bufchain_size(&p->pending_input_data) < 2)\r
1069                 return 1;              /* not got anything yet */\r
1071             /* get the response */\r
1072             bufchain_fetch(&p->pending_input_data, data, 2);\r
1074             if (data[0] != 1) {\r
1075                 plug_closing(p->plug, "Proxy error: SOCKS password "\r
1076                              "subnegotiation contained wrong version number",\r
1077                              PROXY_ERROR_GENERAL, 0);\r
1078                 return 1;\r
1079             }\r
1081             if (data[1] != 0) {\r
1083                 plug_closing(p->plug, "Proxy error: SOCKS proxy refused"\r
1084                              " password authentication",\r
1085                              PROXY_ERROR_GENERAL, 0);\r
1086                 return 1;\r
1087             }\r
1089             bufchain_consume(&p->pending_input_data, 2);\r
1090             p->state = 2;              /* now proceed as authenticated */\r
1091         }\r
1093         if (p->state == 8) {\r
1094             int ret;\r
1095             ret = proxy_socks5_handlechap(p);\r
1096             if (ret) return ret;\r
1097         }\r
1099         if (p->state == 2) {\r
1101             /* request format:\r
1102              *  version number (1 byte) = 5\r
1103              *  command code (1 byte)\r
1104              *    1 = CONNECT\r
1105              *    2 = BIND\r
1106              *    3 = UDP ASSOCIATE\r
1107              *  reserved (1 byte) = 0x00\r
1108              *  address type (1 byte)\r
1109              *    1 = IPv4\r
1110              *    3 = domainname (first byte has length, no terminating null)\r
1111              *    4 = IPv6\r
1112              *  dest. address (variable)\r
1113              *  dest. port (2 bytes) [network order]\r
1114              */\r
1116             char command[512];\r
1117             int len;\r
1118             int type;\r
1120             type = sk_addrtype(p->remote_addr);\r
1121             if (type == ADDRTYPE_IPV4) {\r
1122                 len = 10;              /* 4 hdr + 4 addr + 2 trailer */\r
1123                 command[3] = 1; /* IPv4 */\r
1124                 sk_addrcopy(p->remote_addr, command+4);\r
1125             } else if (type == ADDRTYPE_IPV6) {\r
1126                 len = 22;              /* 4 hdr + 16 addr + 2 trailer */\r
1127                 command[3] = 4; /* IPv6 */\r
1128                 sk_addrcopy(p->remote_addr, command+4);\r
1129             } else {\r
1130                 assert(type == ADDRTYPE_NAME);\r
1131                 command[3] = 3;\r
1132                 sk_getaddr(p->remote_addr, command+5, 256);\r
1133                 command[4] = strlen(command+5);\r
1134                 len = 7 + command[4];  /* 4 hdr, 1 len, N addr, 2 trailer */\r
1135             }\r
1137             command[0] = 5; /* version 5 */\r
1138             command[1] = 1; /* CONNECT command */\r
1139             command[2] = 0x00;\r
1141             /* port */\r
1142             command[len-2] = (char) (p->remote_port >> 8) & 0xff;\r
1143             command[len-1] = (char) p->remote_port & 0xff;\r
1145             sk_write(p->sub_socket, command, len);\r
1147             p->state = 3;\r
1148             return 1;\r
1149         }\r
1151         if (p->state == 3) {\r
1153             /* reply format:\r
1154              *  version number (1 bytes) = 5\r
1155              *  reply code (1 byte)\r
1156              *    0 = succeeded\r
1157              *    1 = general SOCKS server failure\r
1158              *    2 = connection not allowed by ruleset\r
1159              *    3 = network unreachable\r
1160              *    4 = host unreachable\r
1161              *    5 = connection refused\r
1162              *    6 = TTL expired\r
1163              *    7 = command not supported\r
1164              *    8 = address type not supported\r
1165              * reserved (1 byte) = x00\r
1166              * address type (1 byte)\r
1167              *    1 = IPv4\r
1168              *    3 = domainname (first byte has length, no terminating null)\r
1169              *    4 = IPv6\r
1170              * server bound address (variable)\r
1171              * server bound port (2 bytes) [network order]\r
1172              */\r
1173             char data[5];\r
1174             int len;\r
1176             /* First 5 bytes of packet are enough to tell its length. */ \r
1177             if (bufchain_size(&p->pending_input_data) < 5)\r
1178                 return 1;              /* not got anything yet */\r
1180             /* get the response */\r
1181             bufchain_fetch(&p->pending_input_data, data, 5);\r
1183             if (data[0] != 5) {\r
1184                 plug_closing(p->plug, "Proxy error: SOCKS proxy returned wrong version number",\r
1185                              PROXY_ERROR_GENERAL, 0);\r
1186                 return 1;\r
1187             }\r
1189             if (data[1] != 0) {\r
1190                 char buf[256];\r
1192                 strcpy(buf, "Proxy error: ");\r
1194                 switch (data[1]) {\r
1195                   case 1: strcat(buf, "General SOCKS server failure"); break;\r
1196                   case 2: strcat(buf, "Connection not allowed by ruleset"); break;\r
1197                   case 3: strcat(buf, "Network unreachable"); break;\r
1198                   case 4: strcat(buf, "Host unreachable"); break;\r
1199                   case 5: strcat(buf, "Connection refused"); break;\r
1200                   case 6: strcat(buf, "TTL expired"); break;\r
1201                   case 7: strcat(buf, "Command not supported"); break;\r
1202                   case 8: strcat(buf, "Address type not supported"); break;\r
1203                   default: sprintf(buf+strlen(buf),\r
1204                                    "Unrecognised SOCKS error code %d",\r
1205                                    data[1]);\r
1206                     break;\r
1207                 }\r
1208                 plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);\r
1210                 return 1;\r
1211             }\r
1213             /*\r
1214              * Eat the rest of the reply packet.\r
1215              */\r
1216             len = 6;                   /* first 4 bytes, last 2 */\r
1217             switch (data[3]) {\r
1218               case 1: len += 4; break; /* IPv4 address */\r
1219               case 4: len += 16; break;/* IPv6 address */\r
1220               case 3: len += (unsigned char)data[4]; break; /* domain name */\r
1221               default:\r
1222                 plug_closing(p->plug, "Proxy error: SOCKS proxy returned "\r
1223                              "unrecognised address format",\r
1224                              PROXY_ERROR_GENERAL, 0);\r
1225                 return 1;\r
1226             }\r
1227             if (bufchain_size(&p->pending_input_data) < len)\r
1228                 return 1;              /* not got whole reply yet */\r
1229             bufchain_consume(&p->pending_input_data, len);\r
1231             /* we're done */\r
1232             proxy_activate(p);\r
1233             return 1;\r
1234         }\r
1236         if (p->state == 4) {\r
1237             /* TODO: Handle GSSAPI authentication */\r
1238             plug_closing(p->plug, "Proxy error: We don't support GSSAPI authentication",\r
1239                          PROXY_ERROR_GENERAL, 0);\r
1240             return 1;\r
1241         }\r
1243         if (p->state == 5) {\r
1244             char *username = conf_get_str(p->conf, CONF_proxy_username);\r
1245             char *password = conf_get_str(p->conf, CONF_proxy_password);\r
1246             if (username[0] || password[0]) {\r
1247                 char userpwbuf[255 + 255 + 3];\r
1248                 int ulen, plen;\r
1249                 ulen = strlen(username);\r
1250                 if (ulen > 255) ulen = 255;\r
1251                 if (ulen < 1) ulen = 1;\r
1252                 plen = strlen(password);\r
1253                 if (plen > 255) plen = 255;\r
1254                 if (plen < 1) plen = 1;\r
1255                 userpwbuf[0] = 1;      /* version number of subnegotiation */\r
1256                 userpwbuf[1] = ulen;\r
1257                 memcpy(userpwbuf+2, username, ulen);\r
1258                 userpwbuf[ulen+2] = plen;\r
1259                 memcpy(userpwbuf+ulen+3, password, plen);\r
1260                 sk_write(p->sub_socket, userpwbuf, ulen + plen + 3);\r
1261                 p->state = 7;\r
1262             } else \r
1263                 plug_closing(p->plug, "Proxy error: Server chose "\r
1264                              "username/password authentication but we "\r
1265                              "didn't offer it!",\r
1266                          PROXY_ERROR_GENERAL, 0);\r
1267             return 1;\r
1268         }\r
1270         if (p->state == 6) {\r
1271             int ret;\r
1272             ret = proxy_socks5_selectchap(p);\r
1273             if (ret) return ret;\r
1274         }\r
1276     }\r
1278     plug_closing(p->plug, "Proxy error: Unexpected proxy error",\r
1279                  PROXY_ERROR_UNEXPECTED, 0);\r
1280     return 1;\r
1283 /* ----------------------------------------------------------------------\r
1284  * `Telnet' proxy type.\r
1285  *\r
1286  * (This is for ad-hoc proxies where you connect to the proxy's\r
1287  * telnet port and send a command such as `connect host port'. The\r
1288  * command is configurable, since this proxy type is typically not\r
1289  * standardised or at all well-defined.)\r
1290  */\r
1292 char *format_telnet_command(SockAddr addr, int port, Conf *conf)\r
1294     char *fmt = conf_get_str(conf, CONF_proxy_telnet_command);\r
1295     char *ret = NULL;\r
1296     int retlen = 0, retsize = 0;\r
1297     int so = 0, eo = 0;\r
1298 #define ENSURE(n) do { \\r
1299     if (retsize < retlen + n) { \\r
1300         retsize = retlen + n + 512; \\r
1301         ret = sresize(ret, retsize, char); \\r
1302     } \\r
1303 } while (0)\r
1305     /* we need to escape \\, \%, \r, \n, \t, \x??, \0???, \r
1306      * %%, %host, %port, %user, and %pass\r
1307      */\r
1309     while (fmt[eo] != 0) {\r
1311         /* scan forward until we hit end-of-line,\r
1312          * or an escape character (\ or %) */\r
1313         while (fmt[eo] != 0 && fmt[eo] != '%' && fmt[eo] != '\\')\r
1314             eo++;\r
1316         /* if we hit eol, break out of our escaping loop */\r
1317         if (fmt[eo] == 0) break;\r
1319         /* if there was any unescaped text before the escape\r
1320          * character, send that now */\r
1321         if (eo != so) {\r
1322             ENSURE(eo - so);\r
1323             memcpy(ret + retlen, fmt + so, eo - so);\r
1324             retlen += eo - so;\r
1325         }\r
1327         so = eo++;\r
1329         /* if the escape character was the last character of\r
1330          * the line, we'll just stop and send it. */\r
1331         if (fmt[eo] == 0) break;\r
1333         if (fmt[so] == '\\') {\r
1335             /* we recognize \\, \%, \r, \n, \t, \x??.\r
1336              * anything else, we just send unescaped (including the \).\r
1337              */\r
1339             switch (fmt[eo]) {\r
1341               case '\\':\r
1342                 ENSURE(1);\r
1343                 ret[retlen++] = '\\';\r
1344                 eo++;\r
1345                 break;\r
1347               case '%':\r
1348                 ENSURE(1);\r
1349                 ret[retlen++] = '%';\r
1350                 eo++;\r
1351                 break;\r
1353               case 'r':\r
1354                 ENSURE(1);\r
1355                 ret[retlen++] = '\r';\r
1356                 eo++;\r
1357                 break;\r
1359               case 'n':\r
1360                 ENSURE(1);\r
1361                 ret[retlen++] = '\n';\r
1362                 eo++;\r
1363                 break;\r
1365               case 't':\r
1366                 ENSURE(1);\r
1367                 ret[retlen++] = '\t';\r
1368                 eo++;\r
1369                 break;\r
1371               case 'x':\r
1372               case 'X':\r
1373                 {\r
1374                     /* escaped hexadecimal value (ie. \xff) */\r
1375                     unsigned char v = 0;\r
1376                     int i = 0;\r
1378                     for (;;) {\r
1379                         eo++;\r
1380                         if (fmt[eo] >= '0' && fmt[eo] <= '9')\r
1381                             v += fmt[eo] - '0';\r
1382                         else if (fmt[eo] >= 'a' && fmt[eo] <= 'f')\r
1383                             v += fmt[eo] - 'a' + 10;\r
1384                         else if (fmt[eo] >= 'A' && fmt[eo] <= 'F')\r
1385                             v += fmt[eo] - 'A' + 10;\r
1386                         else {\r
1387                             /* non hex character, so we abort and just\r
1388                              * send the whole thing unescaped (including \x)\r
1389                              */\r
1390                             ENSURE(1);\r
1391                             ret[retlen++] = '\\';\r
1392                             eo = so + 1;\r
1393                             break;\r
1394                         }\r
1396                         /* we only extract two hex characters */\r
1397                         if (i == 1) {\r
1398                             ENSURE(1);\r
1399                             ret[retlen++] = v;\r
1400                             eo++;\r
1401                             break;\r
1402                         }\r
1404                         i++;\r
1405                         v <<= 4;\r
1406                     }\r
1407                 }\r
1408                 break;\r
1410               default:\r
1411                 ENSURE(2);\r
1412                 memcpy(ret+retlen, fmt + so, 2);\r
1413                 retlen += 2;\r
1414                 eo++;\r
1415                 break;\r
1416             }\r
1417         } else {\r
1419             /* % escape. we recognize %%, %host, %port, %user, %pass.\r
1420              * %proxyhost, %proxyport. Anything else we just send\r
1421              * unescaped (including the %).\r
1422              */\r
1424             if (fmt[eo] == '%') {\r
1425                 ENSURE(1);\r
1426                 ret[retlen++] = '%';\r
1427                 eo++;\r
1428             }\r
1429             else if (strnicmp(fmt + eo, "host", 4) == 0) {\r
1430                 char dest[512];\r
1431                 int destlen;\r
1432                 sk_getaddr(addr, dest, lenof(dest));\r
1433                 destlen = strlen(dest);\r
1434                 ENSURE(destlen);\r
1435                 memcpy(ret+retlen, dest, destlen);\r
1436                 retlen += destlen;\r
1437                 eo += 4;\r
1438             }\r
1439             else if (strnicmp(fmt + eo, "port", 4) == 0) {\r
1440                 char portstr[8], portlen;\r
1441                 portlen = sprintf(portstr, "%i", port);\r
1442                 ENSURE(portlen);\r
1443                 memcpy(ret + retlen, portstr, portlen);\r
1444                 retlen += portlen;\r
1445                 eo += 4;\r
1446             }\r
1447             else if (strnicmp(fmt + eo, "user", 4) == 0) {\r
1448                 char *username = conf_get_str(conf, CONF_proxy_username);\r
1449                 int userlen = strlen(username);\r
1450                 ENSURE(userlen);\r
1451                 memcpy(ret+retlen, username, userlen);\r
1452                 retlen += userlen;\r
1453                 eo += 4;\r
1454             }\r
1455             else if (strnicmp(fmt + eo, "pass", 4) == 0) {\r
1456                 char *password = conf_get_str(conf, CONF_proxy_password);\r
1457                 int passlen = strlen(password);\r
1458                 ENSURE(passlen);\r
1459                 memcpy(ret+retlen, password, passlen);\r
1460                 retlen += passlen;\r
1461                 eo += 4;\r
1462             }\r
1463             else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) {\r
1464                 char *host = conf_get_str(conf, CONF_proxy_host);\r
1465                 int phlen = strlen(host);\r
1466                 ENSURE(phlen);\r
1467                 memcpy(ret+retlen, host, phlen);\r
1468                 retlen += phlen;\r
1469                 eo += 9;\r
1470             }\r
1471             else if (strnicmp(fmt + eo, "proxyport", 9) == 0) {\r
1472                 int port = conf_get_int(conf, CONF_proxy_port);\r
1473                 char pport[50];\r
1474                 int pplen;\r
1475                 sprintf(pport, "%d", port);\r
1476                 pplen = strlen(pport);\r
1477                 ENSURE(pplen);\r
1478                 memcpy(ret+retlen, pport, pplen);\r
1479                 retlen += pplen;\r
1480                 eo += 9;\r
1481             }\r
1482             else {\r
1483                 /* we don't escape this, so send the % now, and\r
1484                  * don't advance eo, so that we'll consider the\r
1485                  * text immediately following the % as unescaped.\r
1486                  */\r
1487                 ENSURE(1);\r
1488                 ret[retlen++] = '%';\r
1489             }\r
1490         }\r
1492         /* resume scanning for additional escapes after this one. */\r
1493         so = eo;\r
1494     }\r
1496     /* if there is any unescaped text at the end of the line, send it */\r
1497     if (eo != so) {\r
1498         ENSURE(eo - so);\r
1499         memcpy(ret + retlen, fmt + so, eo - so);\r
1500         retlen += eo - so;\r
1501     }\r
1503     ENSURE(1);\r
1504     ret[retlen] = '\0';\r
1505     return ret;\r
1507 #undef ENSURE\r
1510 int proxy_telnet_negotiate (Proxy_Socket p, int change)\r
1512     if (p->state == PROXY_CHANGE_NEW) {\r
1513         char *formatted_cmd;\r
1515         formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port,\r
1516                                               p->conf);\r
1518         {\r
1519             /*\r
1520              * Re-escape control chars in the command, for logging.\r
1521              */\r
1522             char *reescaped = snewn(4*strlen(formatted_cmd) + 1, char);\r
1523             const char *in;\r
1524             char *out;\r
1525             char *logmsg;\r
1527             for (in = formatted_cmd, out = reescaped; *in; in++) {\r
1528                 if (*in == '\n') {\r
1529                     *out++ = '\\'; *out++ = 'n';\r
1530                 } else if (*in == '\r') {\r
1531                     *out++ = '\\'; *out++ = 'r';\r
1532                 } else if (*in == '\t') {\r
1533                     *out++ = '\\'; *out++ = 't';\r
1534                 } else if (*in == '\\') {\r
1535                     *out++ = '\\'; *out++ = '\\';\r
1536                 } else if ((unsigned)(((unsigned char)*in) - 0x20) <\r
1537                            (0x7F-0x20)) {\r
1538                     *out++ = *in;\r
1539                 } else {\r
1540                     out += sprintf(out, "\\x%02X", (unsigned)*in & 0xFF);\r
1541                 }\r
1542             }\r
1543             *out = '\0';\r
1545             logmsg = dupprintf("Sending Telnet proxy command: %s", reescaped);\r
1546             plug_log(p->plug, 2, NULL, 0, logmsg, 0);\r
1547             sfree(logmsg);\r
1548             sfree(reescaped);\r
1549         }\r
1551         sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));\r
1552         sfree(formatted_cmd);\r
1554         p->state = 1;\r
1555         return 0;\r
1556     }\r
1558     if (change == PROXY_CHANGE_CLOSING) {\r
1559         /* if our proxy negotiation process involves closing and opening\r
1560          * new sockets, then we would want to intercept this closing\r
1561          * callback when we were expecting it. if we aren't anticipating\r
1562          * a socket close, then some error must have occurred. we'll\r
1563          * just pass those errors up to the backend.\r
1564          */\r
1565         plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,\r
1566                      p->closing_calling_back);\r
1567         return 0; /* ignored */\r
1568     }\r
1570     if (change == PROXY_CHANGE_SENT) {\r
1571         /* some (or all) of what we wrote to the proxy was sent.\r
1572          * we don't do anything new, however, until we receive the\r
1573          * proxy's response. we might want to set a timer so we can\r
1574          * timeout the proxy negotiation after a while...\r
1575          */\r
1576         return 0;\r
1577     }\r
1579     if (change == PROXY_CHANGE_ACCEPTING) {\r
1580         /* we should _never_ see this, as we are using our socket to\r
1581          * connect to a proxy, not accepting inbound connections.\r
1582          * what should we do? close the socket with an appropriate\r
1583          * error message?\r
1584          */\r
1585         return plug_accepting(p->plug,\r
1586                               p->accepting_constructor, p->accepting_ctx);\r
1587     }\r
1589     if (change == PROXY_CHANGE_RECEIVE) {\r
1590         /* we have received data from the underlying socket, which\r
1591          * we'll need to parse, process, and respond to appropriately.\r
1592          */\r
1594         /* we're done */\r
1595         proxy_activate(p);\r
1596         /* proxy activate will have dealt with\r
1597          * whatever is left of the buffer */\r
1598         return 1;\r
1599     }\r
1601     plug_closing(p->plug, "Proxy error: Unexpected proxy error",\r
1602                  PROXY_ERROR_UNEXPECTED, 0);\r
1603     return 1;\r