Prepare release and bump version numbers to 2.13.0
[TortoiseGit.git] / src / TortoisePlink / PROXY.C
blobc45875472983f8a6172117e10f537208896d9b11
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 #include "putty.h"\r
13 #include "network.h"\r
14 #include "proxy.h"\r
16 #define do_proxy_dns(conf) \\r
17     (conf_get_int(conf, CONF_proxy_dns) == FORCE_ON || \\r
18          (conf_get_int(conf, CONF_proxy_dns) == AUTO && \\r
19               conf_get_int(conf, CONF_proxy_type) != PROXY_SOCKS4))\r
21 /*\r
22  * Call this when proxy negotiation is complete, so that this\r
23  * socket can begin working normally.\r
24  */\r
25 void proxy_activate (ProxySocket *p)\r
26 {\r
27     size_t output_before, output_after;\r
29     p->state = PROXY_STATE_ACTIVE;\r
31     /* we want to ignore new receive events until we have sent\r
32      * all of our buffered receive data.\r
33      */\r
34     sk_set_frozen(p->sub_socket, true);\r
36     /* how many bytes of output have we buffered? */\r
37     output_before = bufchain_size(&p->pending_oob_output_data) +\r
38         bufchain_size(&p->pending_output_data);\r
39     /* and keep track of how many bytes do not get sent. */\r
40     output_after = 0;\r
42     /* send buffered OOB writes */\r
43     while (bufchain_size(&p->pending_oob_output_data) > 0) {\r
44         ptrlen data = bufchain_prefix(&p->pending_oob_output_data);\r
45         output_after += sk_write_oob(p->sub_socket, data.ptr, data.len);\r
46         bufchain_consume(&p->pending_oob_output_data, data.len);\r
47     }\r
49     /* send buffered normal writes */\r
50     while (bufchain_size(&p->pending_output_data) > 0) {\r
51         ptrlen data = bufchain_prefix(&p->pending_output_data);\r
52         output_after += sk_write(p->sub_socket, data.ptr, data.len);\r
53         bufchain_consume(&p->pending_output_data, data.len);\r
54     }\r
56     /* if we managed to send any data, let the higher levels know. */\r
57     if (output_after < output_before)\r
58         plug_sent(p->plug, output_after);\r
60     /* if we have a pending EOF to send, send it */\r
61     if (p->pending_eof) sk_write_eof(p->sub_socket);\r
63     /* if the backend wanted the socket unfrozen, try to unfreeze.\r
64      * our set_frozen handler will flush buffered receive data before\r
65      * unfreezing the actual underlying socket.\r
66      */\r
67     if (!p->freeze)\r
68         sk_set_frozen(&p->sock, false);\r
69 }\r
71 /* basic proxy socket functions */\r
73 static Plug *sk_proxy_plug (Socket *s, Plug *p)\r
74 {\r
75     ProxySocket *ps = container_of(s, ProxySocket, sock);\r
76     Plug *ret = ps->plug;\r
77     if (p)\r
78         ps->plug = p;\r
79     return ret;\r
80 }\r
82 static void sk_proxy_close (Socket *s)\r
83 {\r
84     ProxySocket *ps = container_of(s, ProxySocket, sock);\r
86     sk_close(ps->sub_socket);\r
87     sk_addr_free(ps->remote_addr);\r
88     sfree(ps);\r
89 }\r
91 static size_t sk_proxy_write (Socket *s, const void *data, size_t len)\r
92 {\r
93     ProxySocket *ps = container_of(s, ProxySocket, sock);\r
95     if (ps->state != PROXY_STATE_ACTIVE) {\r
96         bufchain_add(&ps->pending_output_data, data, len);\r
97         return bufchain_size(&ps->pending_output_data);\r
98     }\r
99     return sk_write(ps->sub_socket, data, len);\r
102 static size_t sk_proxy_write_oob (Socket *s, const void *data, size_t len)\r
104     ProxySocket *ps = container_of(s, ProxySocket, sock);\r
106     if (ps->state != PROXY_STATE_ACTIVE) {\r
107         bufchain_clear(&ps->pending_output_data);\r
108         bufchain_clear(&ps->pending_oob_output_data);\r
109         bufchain_add(&ps->pending_oob_output_data, data, len);\r
110         return len;\r
111     }\r
112     return sk_write_oob(ps->sub_socket, data, len);\r
115 static void sk_proxy_write_eof (Socket *s)\r
117     ProxySocket *ps = container_of(s, ProxySocket, sock);\r
119     if (ps->state != PROXY_STATE_ACTIVE) {\r
120         ps->pending_eof = true;\r
121         return;\r
122     }\r
123     sk_write_eof(ps->sub_socket);\r
126 static void sk_proxy_set_frozen (Socket *s, bool is_frozen)\r
128     ProxySocket *ps = container_of(s, ProxySocket, sock);\r
130     if (ps->state != PROXY_STATE_ACTIVE) {\r
131         ps->freeze = is_frozen;\r
132         return;\r
133     }\r
135     /* handle any remaining buffered recv data first */\r
136     if (bufchain_size(&ps->pending_input_data) > 0) {\r
137         ps->freeze = is_frozen;\r
139         /* loop while we still have buffered data, and while we are\r
140          * unfrozen. the plug_receive call in the loop could result\r
141          * in a call back into this function refreezing the socket,\r
142          * so we have to check each time.\r
143          */\r
144         while (!ps->freeze && bufchain_size(&ps->pending_input_data) > 0) {\r
145             char databuf[512];\r
146             ptrlen data = bufchain_prefix(&ps->pending_input_data);\r
147             if (data.len > lenof(databuf))\r
148                 data.len = lenof(databuf);\r
149             memcpy(databuf, data.ptr, data.len);\r
150             bufchain_consume(&ps->pending_input_data, data.len);\r
151             plug_receive(ps->plug, 0, databuf, data.len);\r
152         }\r
154         /* if we're still frozen, we'll have to wait for another\r
155          * call from the backend to finish unbuffering the data.\r
156          */\r
157         if (ps->freeze) return;\r
158     }\r
160     sk_set_frozen(ps->sub_socket, is_frozen);\r
163 static const char * sk_proxy_socket_error (Socket *s)\r
165     ProxySocket *ps = container_of(s, ProxySocket, sock);\r
166     if (ps->error != NULL || ps->sub_socket == NULL) {\r
167         return ps->error;\r
168     }\r
169     return sk_socket_error(ps->sub_socket);\r
172 /* basic proxy plug functions */\r
174 static void plug_proxy_log(Plug *plug, PlugLogType type, SockAddr *addr,\r
175                            int port, const char *error_msg, int error_code)\r
177     ProxySocket *ps = container_of(plug, ProxySocket, plugimpl);\r
179     plug_log(ps->plug, type, addr, port, error_msg, error_code);\r
182 static void plug_proxy_closing (Plug *p, const char *error_msg,\r
183                                 int error_code, bool calling_back)\r
185     ProxySocket *ps = container_of(p, ProxySocket, plugimpl);\r
187     if (ps->state != PROXY_STATE_ACTIVE) {\r
188         ps->closing_error_msg = error_msg;\r
189         ps->closing_error_code = error_code;\r
190         ps->closing_calling_back = calling_back;\r
191         ps->negotiate(ps, PROXY_CHANGE_CLOSING);\r
192     } else {\r
193         plug_closing(ps->plug, error_msg, error_code, calling_back);\r
194     }\r
197 static void plug_proxy_receive(\r
198     Plug *p, int urgent, const char *data, size_t len)\r
200     ProxySocket *ps = container_of(p, ProxySocket, plugimpl);\r
202     if (ps->state != PROXY_STATE_ACTIVE) {\r
203         /* we will lose the urgentness of this data, but since most,\r
204          * if not all, of this data will be consumed by the negotiation\r
205          * process, hopefully it won't affect the protocol above us\r
206          */\r
207         bufchain_add(&ps->pending_input_data, data, len);\r
208         ps->receive_urgent = (urgent != 0);\r
209         ps->receive_data = data;\r
210         ps->receive_len = len;\r
211         ps->negotiate(ps, PROXY_CHANGE_RECEIVE);\r
212     } else {\r
213         plug_receive(ps->plug, urgent, data, len);\r
214     }\r
217 static void plug_proxy_sent (Plug *p, size_t bufsize)\r
219     ProxySocket *ps = container_of(p, ProxySocket, plugimpl);\r
221     if (ps->state != PROXY_STATE_ACTIVE) {\r
222         ps->negotiate(ps, PROXY_CHANGE_SENT);\r
223         return;\r
224     }\r
225     plug_sent(ps->plug, bufsize);\r
228 static int plug_proxy_accepting(Plug *p,\r
229                                 accept_fn_t constructor, accept_ctx_t ctx)\r
231     ProxySocket *ps = container_of(p, ProxySocket, plugimpl);\r
233     if (ps->state != PROXY_STATE_ACTIVE) {\r
234         ps->accepting_constructor = constructor;\r
235         ps->accepting_ctx = ctx;\r
236         return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING);\r
237     }\r
238     return plug_accepting(ps->plug, constructor, ctx);\r
241 /*\r
242  * This function can accept a NULL pointer as `addr', in which case\r
243  * it will only check the host name.\r
244  */\r
245 static bool proxy_for_destination(SockAddr *addr, const char *hostname,\r
246                                   int port, Conf *conf)\r
248     int s = 0, e = 0;\r
249     char hostip[64];\r
250     int hostip_len, hostname_len;\r
251     const char *exclude_list;\r
253     /*\r
254      * Special local connections such as Unix-domain sockets\r
255      * unconditionally cannot be proxied, even in proxy-localhost\r
256      * mode. There just isn't any way to ask any known proxy type for\r
257      * them.\r
258      */\r
259     if (addr && sk_address_is_special_local(addr))\r
260         return false;                  /* do not proxy */\r
262     /*\r
263      * Check the host name and IP against the hard-coded\r
264      * representations of `localhost'.\r
265      */\r
266     if (!conf_get_bool(conf, CONF_even_proxy_localhost) &&\r
267         (sk_hostname_is_local(hostname) ||\r
268          (addr && sk_address_is_local(addr))))\r
269         return false;                  /* do not proxy */\r
271     /* we want a string representation of the IP address for comparisons */\r
272     if (addr) {\r
273         sk_getaddr(addr, hostip, 64);\r
274         hostip_len = strlen(hostip);\r
275     } else\r
276         hostip_len = 0;                /* placate gcc; shouldn't be required */\r
278     hostname_len = strlen(hostname);\r
280     exclude_list = conf_get_str(conf, CONF_proxy_exclude_list);\r
282     /* now parse the exclude list, and see if either our IP\r
283      * or hostname matches anything in it.\r
284      */\r
286     while (exclude_list[s]) {\r
287         while (exclude_list[s] &&\r
288                (isspace((unsigned char)exclude_list[s]) ||\r
289                 exclude_list[s] == ',')) s++;\r
291         if (!exclude_list[s]) break;\r
293         e = s;\r
295         while (exclude_list[e] &&\r
296                (isalnum((unsigned char)exclude_list[e]) ||\r
297                 exclude_list[e] == '-' ||\r
298                 exclude_list[e] == '.' ||\r
299                 exclude_list[e] == '*')) e++;\r
301         if (exclude_list[s] == '*') {\r
302             /* wildcard at beginning of entry */\r
304             if ((addr && strnicmp(hostip + hostip_len - (e - s - 1),\r
305                                   exclude_list + s + 1, e - s - 1) == 0) ||\r
306                 strnicmp(hostname + hostname_len - (e - s - 1),\r
307                          exclude_list + s + 1, e - s - 1) == 0) {\r
308                 /* IP/hostname range excluded. do not use proxy. */\r
309                 return false;\r
310             }\r
311         } else if (exclude_list[e-1] == '*') {\r
312             /* wildcard at end of entry */\r
314             if ((addr && strnicmp(hostip, exclude_list + s, e - s - 1) == 0) ||\r
315                 strnicmp(hostname, exclude_list + s, e - s - 1) == 0) {\r
316                 /* IP/hostname range excluded. do not use proxy. */\r
317                 return false;\r
318             }\r
319         } else {\r
320             /* no wildcard at either end, so let's try an absolute\r
321              * match (ie. a specific IP)\r
322              */\r
324             if (addr && strnicmp(hostip, exclude_list + s, e - s) == 0)\r
325                 return false; /* IP/hostname excluded. do not use proxy. */\r
326             if (strnicmp(hostname, exclude_list + s, e - s) == 0)\r
327                 return false; /* IP/hostname excluded. do not use proxy. */\r
328         }\r
330         s = e;\r
332         /* Make sure we really have reached the next comma or end-of-string */\r
333         while (exclude_list[s] &&\r
334                !isspace((unsigned char)exclude_list[s]) &&\r
335                exclude_list[s] != ',') s++;\r
336     }\r
338     /* no matches in the exclude list, so use the proxy */\r
339     return true;\r
342 static char *dns_log_msg(const char *host, int addressfamily,\r
343                          const char *reason)\r
345     return dupprintf("Looking up host \"%s\"%s for %s", host,\r
346                      (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" :\r
347                       addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" :\r
348                       ""), reason);\r
351 SockAddr *name_lookup(const char *host, int port, char **canonicalname,\r
352                      Conf *conf, int addressfamily, LogContext *logctx,\r
353                      const char *reason)\r
355     if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&\r
356         do_proxy_dns(conf) &&\r
357         proxy_for_destination(NULL, host, port, conf)) {\r
359         if (logctx)\r
360             logeventf(logctx, "Leaving host lookup to proxy of \"%s\""\r
361                       " (for %s)", host, reason);\r
363         *canonicalname = dupstr(host);\r
364         return sk_nonamelookup(host);\r
365     } else {\r
366         if (logctx)\r
367             logevent_and_free(\r
368                 logctx, dns_log_msg(host, addressfamily, reason));\r
370         return sk_namelookup(host, canonicalname, addressfamily);\r
371     }\r
374 static const SocketVtable ProxySocket_sockvt = {\r
375     .plug = sk_proxy_plug,\r
376     .close = sk_proxy_close,\r
377     .write = sk_proxy_write,\r
378     .write_oob = sk_proxy_write_oob,\r
379     .write_eof = sk_proxy_write_eof,\r
380     .set_frozen = sk_proxy_set_frozen,\r
381     .socket_error = sk_proxy_socket_error,\r
382     .peer_info = NULL,\r
383 };\r
385 static const PlugVtable ProxySocket_plugvt = {\r
386     .log = plug_proxy_log,\r
387     .closing = plug_proxy_closing,\r
388     .receive = plug_proxy_receive,\r
389     .sent = plug_proxy_sent,\r
390     .accepting = plug_proxy_accepting\r
391 };\r
393 Socket *new_connection(SockAddr *addr, const char *hostname,\r
394                        int port, bool privport,\r
395                        bool oobinline, bool nodelay, bool keepalive,\r
396                        Plug *plug, Conf *conf)\r
398     if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE &&\r
399         proxy_for_destination(addr, hostname, port, conf))\r
400     {\r
401         ProxySocket *ret;\r
402         SockAddr *proxy_addr;\r
403         char *proxy_canonical_name;\r
404         const char *proxy_type;\r
405         Socket *sret;\r
406         int type;\r
408         if ((sret = platform_new_connection(addr, hostname, port, privport,\r
409                                             oobinline, nodelay, keepalive,\r
410                                             plug, conf)) != NULL)\r
411             return sret;\r
413         ret = snew(ProxySocket);\r
414         ret->sock.vt = &ProxySocket_sockvt;\r
415         ret->plugimpl.vt = &ProxySocket_plugvt;\r
416         ret->conf = conf_copy(conf);\r
417         ret->plug = plug;\r
418         ret->remote_addr = addr;       /* will need to be freed on close */\r
419         ret->remote_port = port;\r
421         ret->error = NULL;\r
422         ret->pending_eof = false;\r
423         ret->freeze = false;\r
425         bufchain_init(&ret->pending_input_data);\r
426         bufchain_init(&ret->pending_output_data);\r
427         bufchain_init(&ret->pending_oob_output_data);\r
429         ret->sub_socket = NULL;\r
430         ret->state = PROXY_STATE_NEW;\r
431         ret->negotiate = NULL;\r
433         type = conf_get_int(conf, CONF_proxy_type);\r
434         if (type == PROXY_HTTP) {\r
435             ret->negotiate = proxy_http_negotiate;\r
436             proxy_type = "HTTP";\r
437         } else if (type == PROXY_SOCKS4) {\r
438             ret->negotiate = proxy_socks4_negotiate;\r
439             proxy_type = "SOCKS 4";\r
440         } else if (type == PROXY_SOCKS5) {\r
441             ret->negotiate = proxy_socks5_negotiate;\r
442             proxy_type = "SOCKS 5";\r
443         } else if (type == PROXY_TELNET) {\r
444             ret->negotiate = proxy_telnet_negotiate;\r
445             proxy_type = "Telnet";\r
446         } else {\r
447             ret->error = "Proxy error: Unknown proxy method";\r
448             return &ret->sock;\r
449         }\r
451         {\r
452             char *logmsg = dupprintf("Will use %s proxy at %s:%d to connect"\r
453                                       " to %s:%d", proxy_type,\r
454                                       conf_get_str(conf, CONF_proxy_host),\r
455                                       conf_get_int(conf, CONF_proxy_port),\r
456                                       hostname, port);\r
457             plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0);\r
458             sfree(logmsg);\r
459         }\r
461         {\r
462             char *logmsg = dns_log_msg(conf_get_str(conf, CONF_proxy_host),\r
463                                        conf_get_int(conf, CONF_addressfamily),\r
464                                        "proxy");\r
465             plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0);\r
466             sfree(logmsg);\r
467         }\r
469         /* look-up proxy */\r
470         proxy_addr = sk_namelookup(conf_get_str(conf, CONF_proxy_host),\r
471                                    &proxy_canonical_name,\r
472                                    conf_get_int(conf, CONF_addressfamily));\r
473         if (sk_addr_error(proxy_addr) != NULL) {\r
474             ret->error = "Proxy error: Unable to resolve proxy host name";\r
475             sk_addr_free(proxy_addr);\r
476             return &ret->sock;\r
477         }\r
478         sfree(proxy_canonical_name);\r
480         {\r
481             char addrbuf[256], *logmsg;\r
482             sk_getaddr(proxy_addr, addrbuf, lenof(addrbuf));\r
483             logmsg = dupprintf("Connecting to %s proxy at %s port %d",\r
484                                proxy_type, addrbuf,\r
485                                conf_get_int(conf, CONF_proxy_port));\r
486             plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0);\r
487             sfree(logmsg);\r
488         }\r
490         /* create the actual socket we will be using,\r
491          * connected to our proxy server and port.\r
492          */\r
493         ret->sub_socket = sk_new(proxy_addr,\r
494                                  conf_get_int(conf, CONF_proxy_port),\r
495                                  privport, oobinline,\r
496                                  nodelay, keepalive, &ret->plugimpl);\r
497         if (sk_socket_error(ret->sub_socket) != NULL)\r
498             return &ret->sock;\r
500         /* start the proxy negotiation process... */\r
501         sk_set_frozen(ret->sub_socket, false);\r
502         ret->negotiate(ret, PROXY_CHANGE_NEW);\r
504         return &ret->sock;\r
505     }\r
507     /* no proxy, so just return the direct socket */\r
508     return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug);\r
511 Socket *new_listener(const char *srcaddr, int port, Plug *plug,\r
512                      bool local_host_only, Conf *conf, int addressfamily)\r
514     /* TODO: SOCKS (and potentially others) support inbound\r
515      * TODO: connections via the proxy. support them.\r
516      */\r
518     return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily);\r
521 /* ----------------------------------------------------------------------\r
522  * HTTP CONNECT proxy type.\r
523  */\r
525 static bool get_line_end(char *data, size_t len, size_t *out)\r
527     size_t off = 0;\r
529     while (off < len)\r
530     {\r
531         if (data[off] == '\n') {\r
532             /* we have a newline */\r
533             off++;\r
535             /* is that the only thing on this line? */\r
536             if (off <= 2) {\r
537                 *out = off;\r
538                 return true;\r
539             }\r
541             /* if not, then there is the possibility that this header\r
542              * continues onto the next line, if it starts with a space\r
543              * or a tab.\r
544              */\r
546             if (off + 1 < len && data[off+1] != ' ' && data[off+1] != '\t') {\r
547                 *out = off;\r
548                 return true;\r
549             }\r
551             /* the line does continue, so we have to keep going\r
552              * until we see an the header's "real" end of line.\r
553              */\r
554             off++;\r
555         }\r
557         off++;\r
558     }\r
560     return false;\r
563 int proxy_http_negotiate (ProxySocket *p, int change)\r
565     if (p->state == PROXY_STATE_NEW) {\r
566         /* we are just beginning the proxy negotiate process,\r
567          * so we'll send off the initial bits of the request.\r
568          * for this proxy method, it's just a simple HTTP\r
569          * request\r
570          */\r
571         char *buf, dest[512];\r
572         char *username, *password;\r
574         sk_getaddr(p->remote_addr, dest, lenof(dest));\r
576         buf = dupprintf("CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n",\r
577                         dest, p->remote_port, dest, p->remote_port);\r
578         sk_write(p->sub_socket, buf, strlen(buf));\r
579         sfree(buf);\r
581         username = conf_get_str(p->conf, CONF_proxy_username);\r
582         password = conf_get_str(p->conf, CONF_proxy_password);\r
583         if (username[0] || password[0]) {\r
584             char *buf, *buf2;\r
585             int i, j, len;\r
586             buf = dupprintf("%s:%s", username, password);\r
587             len = strlen(buf);\r
588             buf2 = snewn(len * 4 / 3 + 100, char);\r
589             sprintf(buf2, "Proxy-Authorization: Basic ");\r
590             for (i = 0, j = strlen(buf2); i < len; i += 3, j += 4)\r
591                 base64_encode_atom((unsigned char *)(buf+i),\r
592                                    (len-i > 3 ? 3 : len-i), buf2+j);\r
593             strcpy(buf2+j, "\r\n");\r
594             sk_write(p->sub_socket, buf2, strlen(buf2));\r
595             sfree(buf);\r
596             sfree(buf2);\r
597         }\r
599         sk_write(p->sub_socket, "\r\n", 2);\r
601         p->state = 1;\r
602         return 0;\r
603     }\r
605     if (change == PROXY_CHANGE_CLOSING) {\r
606         /* if our proxy negotiation process involves closing and opening\r
607          * new sockets, then we would want to intercept this closing\r
608          * callback when we were expecting it. if we aren't anticipating\r
609          * a socket close, then some error must have occurred. we'll\r
610          * just pass those errors up to the backend.\r
611          */\r
612         plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,\r
613                      p->closing_calling_back);\r
614         return 0; /* ignored */\r
615     }\r
617     if (change == PROXY_CHANGE_SENT) {\r
618         /* some (or all) of what we wrote to the proxy was sent.\r
619          * we don't do anything new, however, until we receive the\r
620          * proxy's response. we might want to set a timer so we can\r
621          * timeout the proxy negotiation after a while...\r
622          */\r
623         return 0;\r
624     }\r
626     if (change == PROXY_CHANGE_ACCEPTING) {\r
627         /* we should _never_ see this, as we are using our socket to\r
628          * connect to a proxy, not accepting inbound connections.\r
629          * what should we do? close the socket with an appropriate\r
630          * error message?\r
631          */\r
632         return plug_accepting(p->plug,\r
633                               p->accepting_constructor, p->accepting_ctx);\r
634     }\r
636     if (change == PROXY_CHANGE_RECEIVE) {\r
637         /* we have received data from the underlying socket, which\r
638          * we'll need to parse, process, and respond to appropriately.\r
639          */\r
641         char *data, *datap;\r
642         size_t len, eol;\r
644         if (p->state == 1) {\r
646             int min_ver, maj_ver, status;\r
648             /* get the status line */\r
649             len = bufchain_size(&p->pending_input_data);\r
650             assert(len > 0);           /* or we wouldn't be here */\r
651             data = snewn(len+1, char);\r
652             bufchain_fetch(&p->pending_input_data, data, len);\r
653             /*\r
654              * We must NUL-terminate this data, because Windows\r
655              * sscanf appears to require a NUL at the end of the\r
656              * string because it strlens it _first_. Sigh.\r
657              */\r
658             data[len] = '\0';\r
660             if (!get_line_end(data, len, &eol)) {\r
661                 sfree(data);\r
662                 return 1;\r
663             }\r
665             status = -1;\r
666             /* We can't rely on whether the %n incremented the sscanf return */\r
667             if (sscanf((char *)data, "HTTP/%i.%i %n",\r
668                        &maj_ver, &min_ver, &status) < 2 || status == -1) {\r
669                 plug_closing(p->plug, "Proxy error: HTTP response was absent",\r
670                              PROXY_ERROR_GENERAL, 0);\r
671                 sfree(data);\r
672                 return 1;\r
673             }\r
675             /* remove the status line from the input buffer. */\r
676             bufchain_consume(&p->pending_input_data, eol);\r
677             if (data[status] != '2') {\r
678                 /* error */\r
679                 char *buf;\r
680                 data[eol] = '\0';\r
681                 while (eol > status &&\r
682                        (data[eol-1] == '\r' || data[eol-1] == '\n'))\r
683                     data[--eol] = '\0';\r
684                 buf = dupprintf("Proxy error: %s", data+status);\r
685                 plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);\r
686                 sfree(buf);\r
687                 sfree(data);\r
688                 return 1;\r
689             }\r
691             sfree(data);\r
693             p->state = 2;\r
694         }\r
696         if (p->state == 2) {\r
698             /* get headers. we're done when we get a\r
699              * header of length 2, (ie. just "\r\n")\r
700              */\r
702             len = bufchain_size(&p->pending_input_data);\r
703             assert(len > 0);           /* or we wouldn't be here */\r
704             data = snewn(len, char);\r
705             datap = data;\r
706             bufchain_fetch(&p->pending_input_data, data, len);\r
708             if (!get_line_end(datap, len, &eol)) {\r
709                 sfree(data);\r
710                 return 1;\r
711             }\r
712             while (eol > 2) {\r
713                 bufchain_consume(&p->pending_input_data, eol);\r
714                 datap += eol;\r
715                 len   -= eol;\r
716                 if (!get_line_end(datap, len, &eol))\r
717                     eol = 0;           /* terminate the loop */\r
718             }\r
720             if (eol == 2) {\r
721                 /* we're done */\r
722                 bufchain_consume(&p->pending_input_data, 2);\r
723                 proxy_activate(p);\r
724                 /* proxy activate will have dealt with\r
725                  * whatever is left of the buffer */\r
726                 sfree(data);\r
727                 return 1;\r
728             }\r
730             sfree(data);\r
731             return 1;\r
732         }\r
733     }\r
735     plug_closing(p->plug, "Proxy error: unexpected proxy error",\r
736                  PROXY_ERROR_UNEXPECTED, 0);\r
737     return 1;\r
740 /* ----------------------------------------------------------------------\r
741  * SOCKS proxy type.\r
742  */\r
744 /* SOCKS version 4 */\r
745 int proxy_socks4_negotiate (ProxySocket *p, int change)\r
747     if (p->state == PROXY_CHANGE_NEW) {\r
749         /* request format:\r
750          *  version number (1 byte) = 4\r
751          *  command code (1 byte)\r
752          *    1 = CONNECT\r
753          *    2 = BIND\r
754          *  dest. port (2 bytes) [network order]\r
755          *  dest. address (4 bytes)\r
756          *  user ID (variable length, null terminated string)\r
757          */\r
759         strbuf *command = strbuf_new();\r
760         char hostname[512];\r
761         bool write_hostname = false;\r
763         put_byte(command, 4);          /* SOCKS version 4 */\r
764         put_byte(command, 1);          /* CONNECT command */\r
765         put_uint16(command, p->remote_port);\r
767         switch (sk_addrtype(p->remote_addr)) {\r
768           case ADDRTYPE_IPV4: {\r
769             char addr[4];\r
770             sk_addrcopy(p->remote_addr, addr);\r
771             put_data(command, addr, 4);\r
772             break;\r
773           }\r
774           case ADDRTYPE_NAME:\r
775             sk_getaddr(p->remote_addr, hostname, lenof(hostname));\r
776             put_uint32(command, 1);\r
777             write_hostname = true;\r
778             break;\r
779           case ADDRTYPE_IPV6:\r
780             p->error = "Proxy error: SOCKS version 4 does not support IPv6";\r
781             strbuf_free(command);\r
782             return 1;\r
783         }\r
785         put_asciz(command, conf_get_str(p->conf, CONF_proxy_username));\r
786         if (write_hostname)\r
787             put_asciz(command, hostname);\r
788         sk_write(p->sub_socket, command->s, command->len);\r
789         strbuf_free(command);\r
791         p->state = 1;\r
792         return 0;\r
793     }\r
795     if (change == PROXY_CHANGE_CLOSING) {\r
796         /* if our proxy negotiation process involves closing and opening\r
797          * new sockets, then we would want to intercept this closing\r
798          * callback when we were expecting it. if we aren't anticipating\r
799          * a socket close, then some error must have occurred. we'll\r
800          * just pass those errors up to the backend.\r
801          */\r
802         plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,\r
803                      p->closing_calling_back);\r
804         return 0; /* ignored */\r
805     }\r
807     if (change == PROXY_CHANGE_SENT) {\r
808         /* some (or all) of what we wrote to the proxy was sent.\r
809          * we don't do anything new, however, until we receive the\r
810          * proxy's response. we might want to set a timer so we can\r
811          * timeout the proxy negotiation after a while...\r
812          */\r
813         return 0;\r
814     }\r
816     if (change == PROXY_CHANGE_ACCEPTING) {\r
817         /* we should _never_ see this, as we are using our socket to\r
818          * connect to a proxy, not accepting inbound connections.\r
819          * what should we do? close the socket with an appropriate\r
820          * error message?\r
821          */\r
822         return plug_accepting(p->plug,\r
823                               p->accepting_constructor, p->accepting_ctx);\r
824     }\r
826     if (change == PROXY_CHANGE_RECEIVE) {\r
827         /* we have received data from the underlying socket, which\r
828          * we'll need to parse, process, and respond to appropriately.\r
829          */\r
831         if (p->state == 1) {\r
832             /* response format:\r
833              *  version number (1 byte) = 4\r
834              *  reply code (1 byte)\r
835              *    90 = request granted\r
836              *    91 = request rejected or failed\r
837              *    92 = request rejected due to lack of IDENTD on client\r
838              *    93 = request rejected due to difference in user ID\r
839              *         (what we sent vs. what IDENTD said)\r
840              *  dest. port (2 bytes)\r
841              *  dest. address (4 bytes)\r
842              */\r
844             char data[8];\r
846             if (bufchain_size(&p->pending_input_data) < 8)\r
847                 return 1;              /* not got anything yet */\r
849             /* get the response */\r
850             bufchain_fetch(&p->pending_input_data, data, 8);\r
852             if (data[0] != 0) {\r
853                 plug_closing(p->plug, "Proxy error: SOCKS proxy responded with "\r
854                                       "unexpected reply code version",\r
855                              PROXY_ERROR_GENERAL, 0);\r
856                 return 1;\r
857             }\r
859             if (data[1] != 90) {\r
861                 switch (data[1]) {\r
862                   case 92:\r
863                     plug_closing(p->plug, "Proxy error: SOCKS server wanted IDENTD on client",\r
864                                  PROXY_ERROR_GENERAL, 0);\r
865                     break;\r
866                   case 93:\r
867                     plug_closing(p->plug, "Proxy error: Username and IDENTD on client don't agree",\r
868                                  PROXY_ERROR_GENERAL, 0);\r
869                     break;\r
870                   case 91:\r
871                   default:\r
872                     plug_closing(p->plug, "Proxy error: Error while communicating with proxy",\r
873                                  PROXY_ERROR_GENERAL, 0);\r
874                     break;\r
875                 }\r
877                 return 1;\r
878             }\r
879             bufchain_consume(&p->pending_input_data, 8);\r
881             /* we're done */\r
882             proxy_activate(p);\r
883             /* proxy activate will have dealt with\r
884              * whatever is left of the buffer */\r
885             return 1;\r
886         }\r
887     }\r
889     plug_closing(p->plug, "Proxy error: unexpected proxy error",\r
890                  PROXY_ERROR_UNEXPECTED, 0);\r
891     return 1;\r
894 /* SOCKS version 5 */\r
895 int proxy_socks5_negotiate (ProxySocket *p, int change)\r
897     if (p->state == PROXY_CHANGE_NEW) {\r
899         /* initial command:\r
900          *  version number (1 byte) = 5\r
901          *  number of available authentication methods (1 byte)\r
902          *  available authentication methods (1 byte * previous value)\r
903          *    authentication methods:\r
904          *     0x00 = no authentication\r
905          *     0x01 = GSSAPI\r
906          *     0x02 = username/password\r
907          *     0x03 = CHAP\r
908          */\r
910         strbuf *command;\r
911         char *username, *password;\r
912         int method_count_offset, methods_start;\r
914         command = strbuf_new();\r
915         put_byte(command, 5);          /* SOCKS version 5 */\r
916         username = conf_get_str(p->conf, CONF_proxy_username);\r
917         password = conf_get_str(p->conf, CONF_proxy_password);\r
919         method_count_offset = command->len;\r
920         put_byte(command, 0);\r
921         methods_start = command->len;\r
923         put_byte(command, 0x00);       /* no authentication */\r
925         if (username[0] || password[0]) {\r
926             proxy_socks5_offerencryptedauth(BinarySink_UPCAST(command));\r
927             put_byte(command, 0x02);    /* username/password */\r
928         }\r
930         command->u[method_count_offset] = command->len - methods_start;\r
932         sk_write(p->sub_socket, command->s, command->len);\r
933         strbuf_free(command);\r
935         p->state = 1;\r
936         return 0;\r
937     }\r
939     if (change == PROXY_CHANGE_CLOSING) {\r
940         /* if our proxy negotiation process involves closing and opening\r
941          * new sockets, then we would want to intercept this closing\r
942          * callback when we were expecting it. if we aren't anticipating\r
943          * a socket close, then some error must have occurred. we'll\r
944          * just pass those errors up to the backend.\r
945          */\r
946         plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,\r
947                      p->closing_calling_back);\r
948         return 0; /* ignored */\r
949     }\r
951     if (change == PROXY_CHANGE_SENT) {\r
952         /* some (or all) of what we wrote to the proxy was sent.\r
953          * we don't do anything new, however, until we receive the\r
954          * proxy's response. we might want to set a timer so we can\r
955          * timeout the proxy negotiation after a while...\r
956          */\r
957         return 0;\r
958     }\r
960     if (change == PROXY_CHANGE_ACCEPTING) {\r
961         /* we should _never_ see this, as we are using our socket to\r
962          * connect to a proxy, not accepting inbound connections.\r
963          * what should we do? close the socket with an appropriate\r
964          * error message?\r
965          */\r
966         return plug_accepting(p->plug,\r
967                               p->accepting_constructor, p->accepting_ctx);\r
968     }\r
970     if (change == PROXY_CHANGE_RECEIVE) {\r
971         /* we have received data from the underlying socket, which\r
972          * we'll need to parse, process, and respond to appropriately.\r
973          */\r
975         if (p->state == 1) {\r
977             /* initial response:\r
978              *  version number (1 byte) = 5\r
979              *  authentication method (1 byte)\r
980              *    authentication methods:\r
981              *     0x00 = no authentication\r
982              *     0x01 = GSSAPI\r
983              *     0x02 = username/password\r
984              *     0x03 = CHAP\r
985              *     0xff = no acceptable methods\r
986              */\r
987             char data[2];\r
989             if (bufchain_size(&p->pending_input_data) < 2)\r
990                 return 1;              /* not got anything yet */\r
992             /* get the response */\r
993             bufchain_fetch(&p->pending_input_data, data, 2);\r
995             if (data[0] != 5) {\r
996                 plug_closing(p->plug, "Proxy error: SOCKS proxy returned unexpected version",\r
997                              PROXY_ERROR_GENERAL, 0);\r
998                 return 1;\r
999             }\r
1001             if (data[1] == 0x00) p->state = 2; /* no authentication needed */\r
1002             else if (data[1] == 0x01) p->state = 4; /* GSSAPI authentication */\r
1003             else if (data[1] == 0x02) p->state = 5; /* username/password authentication */\r
1004             else if (data[1] == 0x03) p->state = 6; /* CHAP authentication */\r
1005             else {\r
1006                 plug_closing(p->plug, "Proxy error: SOCKS proxy did not accept our authentication",\r
1007                              PROXY_ERROR_GENERAL, 0);\r
1008                 return 1;\r
1009             }\r
1010             bufchain_consume(&p->pending_input_data, 2);\r
1011         }\r
1013         if (p->state == 7) {\r
1015             /* password authentication reply format:\r
1016              *  version number (1 bytes) = 1\r
1017              *  reply code (1 byte)\r
1018              *    0 = succeeded\r
1019              *    >0 = failed\r
1020              */\r
1021             char data[2];\r
1023             if (bufchain_size(&p->pending_input_data) < 2)\r
1024                 return 1;              /* not got anything yet */\r
1026             /* get the response */\r
1027             bufchain_fetch(&p->pending_input_data, data, 2);\r
1029             if (data[0] != 1) {\r
1030                 plug_closing(p->plug, "Proxy error: SOCKS password "\r
1031                              "subnegotiation contained wrong version number",\r
1032                              PROXY_ERROR_GENERAL, 0);\r
1033                 return 1;\r
1034             }\r
1036             if (data[1] != 0) {\r
1038                 plug_closing(p->plug, "Proxy error: SOCKS proxy refused"\r
1039                              " password authentication",\r
1040                              PROXY_ERROR_GENERAL, 0);\r
1041                 return 1;\r
1042             }\r
1044             bufchain_consume(&p->pending_input_data, 2);\r
1045             p->state = 2;              /* now proceed as authenticated */\r
1046         }\r
1048         if (p->state == 8) {\r
1049             int ret;\r
1050             ret = proxy_socks5_handlechap(p);\r
1051             if (ret) return ret;\r
1052         }\r
1054         if (p->state == 2) {\r
1056             /* request format:\r
1057              *  version number (1 byte) = 5\r
1058              *  command code (1 byte)\r
1059              *    1 = CONNECT\r
1060              *    2 = BIND\r
1061              *    3 = UDP ASSOCIATE\r
1062              *  reserved (1 byte) = 0x00\r
1063              *  address type (1 byte)\r
1064              *    1 = IPv4\r
1065              *    3 = domainname (first byte has length, no terminating null)\r
1066              *    4 = IPv6\r
1067              *  dest. address (variable)\r
1068              *  dest. port (2 bytes) [network order]\r
1069              */\r
1071             strbuf *command = strbuf_new();\r
1072             put_byte(command, 5);      /* SOCKS version 5 */\r
1073             put_byte(command, 1);      /* CONNECT command */\r
1074             put_byte(command, 0x00);   /* reserved byte */\r
1076             switch (sk_addrtype(p->remote_addr)) {\r
1077               case ADDRTYPE_IPV4:\r
1078                 put_byte(command, 1);  /* IPv4 */\r
1079                 sk_addrcopy(p->remote_addr, strbuf_append(command, 4));\r
1080                 break;\r
1081               case ADDRTYPE_IPV6:\r
1082                 put_byte(command, 4);  /* IPv6 */\r
1083                 sk_addrcopy(p->remote_addr, strbuf_append(command, 16));\r
1084                 break;\r
1085               case ADDRTYPE_NAME: {\r
1086                 char hostname[512];\r
1087                 put_byte(command, 3);  /* domain name */\r
1088                 sk_getaddr(p->remote_addr, hostname, lenof(hostname));\r
1089                 if (!put_pstring(command, hostname)) {\r
1090                   p->error = "Proxy error: SOCKS 5 cannot "\r
1091                       "support host names longer than 255 chars";\r
1092                   strbuf_free(command);\r
1093                   return 1;\r
1094                 }\r
1095                 break;\r
1096               }\r
1097             }\r
1099             put_uint16(command, p->remote_port);\r
1101             sk_write(p->sub_socket, command->s, command->len);\r
1103             strbuf_free(command);\r
1105             p->state = 3;\r
1106             return 1;\r
1107         }\r
1109         if (p->state == 3) {\r
1111             /* reply format:\r
1112              *  version number (1 bytes) = 5\r
1113              *  reply code (1 byte)\r
1114              *    0 = succeeded\r
1115              *    1 = general SOCKS server failure\r
1116              *    2 = connection not allowed by ruleset\r
1117              *    3 = network unreachable\r
1118              *    4 = host unreachable\r
1119              *    5 = connection refused\r
1120              *    6 = TTL expired\r
1121              *    7 = command not supported\r
1122              *    8 = address type not supported\r
1123              * reserved (1 byte) = x00\r
1124              * address type (1 byte)\r
1125              *    1 = IPv4\r
1126              *    3 = domainname (first byte has length, no terminating null)\r
1127              *    4 = IPv6\r
1128              * server bound address (variable)\r
1129              * server bound port (2 bytes) [network order]\r
1130              */\r
1131             char data[5];\r
1132             int len;\r
1134             /* First 5 bytes of packet are enough to tell its length. */\r
1135             if (bufchain_size(&p->pending_input_data) < 5)\r
1136                 return 1;              /* not got anything yet */\r
1138             /* get the response */\r
1139             bufchain_fetch(&p->pending_input_data, data, 5);\r
1141             if (data[0] != 5) {\r
1142                 plug_closing(p->plug, "Proxy error: SOCKS proxy returned wrong version number",\r
1143                              PROXY_ERROR_GENERAL, 0);\r
1144                 return 1;\r
1145             }\r
1147             if (data[1] != 0) {\r
1148                 char buf[256];\r
1150                 strcpy(buf, "Proxy error: ");\r
1152                 switch (data[1]) {\r
1153                   case 1: strcat(buf, "General SOCKS server failure"); break;\r
1154                   case 2: strcat(buf, "Connection not allowed by ruleset"); break;\r
1155                   case 3: strcat(buf, "Network unreachable"); break;\r
1156                   case 4: strcat(buf, "Host unreachable"); break;\r
1157                   case 5: strcat(buf, "Connection refused"); break;\r
1158                   case 6: strcat(buf, "TTL expired"); break;\r
1159                   case 7: strcat(buf, "Command not supported"); break;\r
1160                   case 8: strcat(buf, "Address type not supported"); break;\r
1161                   default: sprintf(buf+strlen(buf),\r
1162                                    "Unrecognised SOCKS error code %d",\r
1163                                    data[1]);\r
1164                     break;\r
1165                 }\r
1166                 plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0);\r
1168                 return 1;\r
1169             }\r
1171             /*\r
1172              * Eat the rest of the reply packet.\r
1173              */\r
1174             len = 6;                   /* first 4 bytes, last 2 */\r
1175             switch (data[3]) {\r
1176               case 1: len += 4; break; /* IPv4 address */\r
1177               case 4: len += 16; break;/* IPv6 address */\r
1178               case 3: len += 1+(unsigned char)data[4]; break; /* domain name */\r
1179               default:\r
1180                 plug_closing(p->plug, "Proxy error: SOCKS proxy returned "\r
1181                              "unrecognised address format",\r
1182                              PROXY_ERROR_GENERAL, 0);\r
1183                 return 1;\r
1184             }\r
1185             if (bufchain_size(&p->pending_input_data) < len)\r
1186                 return 1;              /* not got whole reply yet */\r
1187             bufchain_consume(&p->pending_input_data, len);\r
1189             /* we're done */\r
1190             proxy_activate(p);\r
1191             return 1;\r
1192         }\r
1194         if (p->state == 4) {\r
1195             /* TODO: Handle GSSAPI authentication */\r
1196             plug_closing(p->plug, "Proxy error: We don't support GSSAPI authentication",\r
1197                          PROXY_ERROR_GENERAL, 0);\r
1198             return 1;\r
1199         }\r
1201         if (p->state == 5) {\r
1202             const char *username = conf_get_str(p->conf, CONF_proxy_username);\r
1203             const char *password = conf_get_str(p->conf, CONF_proxy_password);\r
1204             if (username[0] || password[0]) {\r
1205                 strbuf *auth = strbuf_new_nm();\r
1206                 put_byte(auth, 1); /* version number of subnegotiation */\r
1207                 if (!put_pstring(auth, username)) {\r
1208                     p->error = "Proxy error: SOCKS 5 authentication cannot "\r
1209                         "support usernames longer than 255 chars";\r
1210                     strbuf_free(auth);\r
1211                     return 1;\r
1212                 }\r
1213                 if (!put_pstring(auth, password)) {\r
1214                     p->error = "Proxy error: SOCKS 5 authentication cannot "\r
1215                         "support passwords longer than 255 chars";\r
1216                     strbuf_free(auth);\r
1217                     return 1;\r
1218                 }\r
1219                 sk_write(p->sub_socket, auth->s, auth->len);\r
1220                 strbuf_free(auth);\r
1221                 p->state = 7;\r
1222             } else\r
1223                 plug_closing(p->plug, "Proxy error: Server chose "\r
1224                              "username/password authentication but we "\r
1225                              "didn't offer it!",\r
1226                          PROXY_ERROR_GENERAL, 0);\r
1227             return 1;\r
1228         }\r
1230         if (p->state == 6) {\r
1231             int ret;\r
1232             ret = proxy_socks5_selectchap(p);\r
1233             if (ret) return ret;\r
1234         }\r
1236     }\r
1238     plug_closing(p->plug, "Proxy error: Unexpected proxy error",\r
1239                  PROXY_ERROR_UNEXPECTED, 0);\r
1240     return 1;\r
1243 /* ----------------------------------------------------------------------\r
1244  * `Telnet' proxy type.\r
1245  *\r
1246  * (This is for ad-hoc proxies where you connect to the proxy's\r
1247  * telnet port and send a command such as `connect host port'. The\r
1248  * command is configurable, since this proxy type is typically not\r
1249  * standardised or at all well-defined.)\r
1250  */\r
1252 char *format_telnet_command(SockAddr *addr, int port, Conf *conf)\r
1254     char *fmt = conf_get_str(conf, CONF_proxy_telnet_command);\r
1255     int so = 0, eo = 0;\r
1256     strbuf *buf = strbuf_new();\r
1258     /* we need to escape \\, \%, \r, \n, \t, \x??, \0???,\r
1259      * %%, %host, %port, %user, and %pass\r
1260      */\r
1262     while (fmt[eo] != 0) {\r
1264         /* scan forward until we hit end-of-line,\r
1265          * or an escape character (\ or %) */\r
1266         while (fmt[eo] != 0 && fmt[eo] != '%' && fmt[eo] != '\\')\r
1267             eo++;\r
1269         /* if we hit eol, break out of our escaping loop */\r
1270         if (fmt[eo] == 0) break;\r
1272         /* if there was any unescaped text before the escape\r
1273          * character, send that now */\r
1274         if (eo != so)\r
1275             put_data(buf, fmt + so, eo - so);\r
1277         so = eo++;\r
1279         /* if the escape character was the last character of\r
1280          * the line, we'll just stop and send it. */\r
1281         if (fmt[eo] == 0) break;\r
1283         if (fmt[so] == '\\') {\r
1285             /* we recognize \\, \%, \r, \n, \t, \x??.\r
1286              * anything else, we just send unescaped (including the \).\r
1287              */\r
1289             switch (fmt[eo]) {\r
1291               case '\\':\r
1292                 put_byte(buf, '\\');\r
1293                 eo++;\r
1294                 break;\r
1296               case '%':\r
1297                 put_byte(buf, '%');\r
1298                 eo++;\r
1299                 break;\r
1301               case 'r':\r
1302                 put_byte(buf, '\r');\r
1303                 eo++;\r
1304                 break;\r
1306               case 'n':\r
1307                 put_byte(buf, '\n');\r
1308                 eo++;\r
1309                 break;\r
1311               case 't':\r
1312                 put_byte(buf, '\t');\r
1313                 eo++;\r
1314                 break;\r
1316               case 'x':\r
1317               case 'X': {\r
1318                 /* escaped hexadecimal value (ie. \xff) */\r
1319                 unsigned char v = 0;\r
1320                 int i = 0;\r
1322                 for (;;) {\r
1323                   eo++;\r
1324                   if (fmt[eo] >= '0' && fmt[eo] <= '9')\r
1325                       v += fmt[eo] - '0';\r
1326                   else if (fmt[eo] >= 'a' && fmt[eo] <= 'f')\r
1327                       v += fmt[eo] - 'a' + 10;\r
1328                   else if (fmt[eo] >= 'A' && fmt[eo] <= 'F')\r
1329                       v += fmt[eo] - 'A' + 10;\r
1330                   else {\r
1331                     /* non hex character, so we abort and just\r
1332                      * send the whole thing unescaped (including \x)\r
1333                      */\r
1334                     put_byte(buf, '\\');\r
1335                     eo = so + 1;\r
1336                     break;\r
1337                   }\r
1339                   /* we only extract two hex characters */\r
1340                   if (i == 1) {\r
1341                     put_byte(buf, v);\r
1342                     eo++;\r
1343                     break;\r
1344                   }\r
1346                   i++;\r
1347                   v <<= 4;\r
1348                 }\r
1349                 break;\r
1350               }\r
1352               default:\r
1353                 put_data(buf, fmt + so, 2);\r
1354                 eo++;\r
1355                 break;\r
1356             }\r
1357         } else {\r
1359             /* % escape. we recognize %%, %host, %port, %user, %pass.\r
1360              * %proxyhost, %proxyport. Anything else we just send\r
1361              * unescaped (including the %).\r
1362              */\r
1364             if (fmt[eo] == '%') {\r
1365                 put_byte(buf, '%');\r
1366                 eo++;\r
1367             }\r
1368             else if (strnicmp(fmt + eo, "host", 4) == 0) {\r
1369                 char dest[512];\r
1370                 sk_getaddr(addr, dest, lenof(dest));\r
1371                 put_data(buf, dest, strlen(dest));\r
1372                 eo += 4;\r
1373             }\r
1374             else if (strnicmp(fmt + eo, "port", 4) == 0) {\r
1375                 strbuf_catf(buf, "%d", port);\r
1376                 eo += 4;\r
1377             }\r
1378             else if (strnicmp(fmt + eo, "user", 4) == 0) {\r
1379                 const char *username = conf_get_str(conf, CONF_proxy_username);\r
1380                 put_data(buf, username, strlen(username));\r
1381                 eo += 4;\r
1382             }\r
1383             else if (strnicmp(fmt + eo, "pass", 4) == 0) {\r
1384                 const char *password = conf_get_str(conf, CONF_proxy_password);\r
1385                 put_data(buf, password, strlen(password));\r
1386                 eo += 4;\r
1387             }\r
1388             else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) {\r
1389                 const char *host = conf_get_str(conf, CONF_proxy_host);\r
1390                 put_data(buf, host, strlen(host));\r
1391                 eo += 9;\r
1392             }\r
1393             else if (strnicmp(fmt + eo, "proxyport", 9) == 0) {\r
1394                 int port = conf_get_int(conf, CONF_proxy_port);\r
1395                 strbuf_catf(buf, "%d", port);\r
1396                 eo += 9;\r
1397             }\r
1398             else {\r
1399                 /* we don't escape this, so send the % now, and\r
1400                  * don't advance eo, so that we'll consider the\r
1401                  * text immediately following the % as unescaped.\r
1402                  */\r
1403                 put_byte(buf, '%');\r
1404             }\r
1405         }\r
1407         /* resume scanning for additional escapes after this one. */\r
1408         so = eo;\r
1409     }\r
1411     /* if there is any unescaped text at the end of the line, send it */\r
1412     if (eo != so) {\r
1413         put_data(buf, fmt + so, eo - so);\r
1414     }\r
1416     return strbuf_to_str(buf);\r
1419 int proxy_telnet_negotiate (ProxySocket *p, int change)\r
1421     if (p->state == PROXY_CHANGE_NEW) {\r
1422         char *formatted_cmd;\r
1424         formatted_cmd = format_telnet_command(p->remote_addr, p->remote_port,\r
1425                                               p->conf);\r
1427         {\r
1428             /*\r
1429              * Re-escape control chars in the command, for logging.\r
1430              */\r
1431             char *reescaped = snewn(4*strlen(formatted_cmd) + 1, char);\r
1432             const char *in;\r
1433             char *out;\r
1434             char *logmsg;\r
1436             for (in = formatted_cmd, out = reescaped; *in; in++) {\r
1437                 if (*in == '\n') {\r
1438                     *out++ = '\\'; *out++ = 'n';\r
1439                 } else if (*in == '\r') {\r
1440                     *out++ = '\\'; *out++ = 'r';\r
1441                 } else if (*in == '\t') {\r
1442                     *out++ = '\\'; *out++ = 't';\r
1443                 } else if (*in == '\\') {\r
1444                     *out++ = '\\'; *out++ = '\\';\r
1445                 } else if ((unsigned)(((unsigned char)*in) - 0x20) <\r
1446                            (0x7F-0x20)) {\r
1447                     *out++ = *in;\r
1448                 } else {\r
1449                     out += sprintf(out, "\\x%02X", (unsigned)*in & 0xFF);\r
1450                 }\r
1451             }\r
1452             *out = '\0';\r
1454             logmsg = dupprintf("Sending Telnet proxy command: %s", reescaped);\r
1455             plug_log(p->plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0);\r
1456             sfree(logmsg);\r
1457             sfree(reescaped);\r
1458         }\r
1460         sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd));\r
1461         sfree(formatted_cmd);\r
1463         p->state = 1;\r
1464         return 0;\r
1465     }\r
1467     if (change == PROXY_CHANGE_CLOSING) {\r
1468         /* if our proxy negotiation process involves closing and opening\r
1469          * new sockets, then we would want to intercept this closing\r
1470          * callback when we were expecting it. if we aren't anticipating\r
1471          * a socket close, then some error must have occurred. we'll\r
1472          * just pass those errors up to the backend.\r
1473          */\r
1474         plug_closing(p->plug, p->closing_error_msg, p->closing_error_code,\r
1475                      p->closing_calling_back);\r
1476         return 0; /* ignored */\r
1477     }\r
1479     if (change == PROXY_CHANGE_SENT) {\r
1480         /* some (or all) of what we wrote to the proxy was sent.\r
1481          * we don't do anything new, however, until we receive the\r
1482          * proxy's response. we might want to set a timer so we can\r
1483          * timeout the proxy negotiation after a while...\r
1484          */\r
1485         return 0;\r
1486     }\r
1488     if (change == PROXY_CHANGE_ACCEPTING) {\r
1489         /* we should _never_ see this, as we are using our socket to\r
1490          * connect to a proxy, not accepting inbound connections.\r
1491          * what should we do? close the socket with an appropriate\r
1492          * error message?\r
1493          */\r
1494         return plug_accepting(p->plug,\r
1495                               p->accepting_constructor, p->accepting_ctx);\r
1496     }\r
1498     if (change == PROXY_CHANGE_RECEIVE) {\r
1499         /* we have received data from the underlying socket, which\r
1500          * we'll need to parse, process, and respond to appropriately.\r
1501          */\r
1503         /* we're done */\r
1504         proxy_activate(p);\r
1505         /* proxy activate will have dealt with\r
1506          * whatever is left of the buffer */\r
1507         return 1;\r
1508     }\r
1510     plug_closing(p->plug, "Proxy error: Unexpected proxy error",\r
1511                  PROXY_ERROR_UNEXPECTED, 0);\r
1512     return 1;\r