OSDN Git Service

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