]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - proxy.c
Updates to proxy support, both from me and from Justin Bradford.
[PuTTY.git] / proxy.c
1 /*
2  * Network proxy abstraction in PuTTY
3  *
4  * A proxy layer, if necessary, wedges itself between the network
5  * code and the higher level backend.
6  */
7
8 #include <windows.h>
9
10 #define DEFINE_PLUG_METHOD_MACROS
11 #include "putty.h"
12 #include "network.h"
13 #include "proxy.h"
14
15 /*
16  * Call this when proxy negotiation is complete, so that this
17  * socket can begin working normally.
18  */
19 void proxy_activate (Proxy_Socket p)
20 {
21     void *data;
22     int len;
23
24     p->state = PROXY_STATE_ACTIVE;
25
26     /* let's try to keep extra receive events from coming through */
27     sk_set_frozen(p->sub_socket, 1);
28
29     /* send buffered OOB writes */
30     while (bufchain_size(&p->pending_oob_output_data) > 0) {
31         bufchain_prefix(&p->pending_oob_output_data, &data, &len);
32         sk_write_oob(p->sub_socket, data, len);
33         bufchain_consume(&p->pending_oob_output_data, len);
34     }
35     bufchain_clear(&p->pending_oob_output_data);
36
37     /* send buffered normal writes */
38     while (bufchain_size(&p->pending_output_data) > 0) {
39         bufchain_prefix(&p->pending_output_data, &data, &len);
40         sk_write(p->sub_socket, data, len);
41         bufchain_consume(&p->pending_output_data, len);
42     }
43     bufchain_clear(&p->pending_output_data);
44
45     /* if we were asked to flush the output during
46      * the proxy negotiation process, do so now.
47      */
48     if (p->pending_flush) sk_flush(p->sub_socket);
49
50     /* forward buffered recv data to the backend */
51     while (bufchain_size(&p->pending_input_data) > 0) {
52         bufchain_prefix(&p->pending_input_data, &data, &len);
53         plug_receive(p->plug, 0, data, len);
54         bufchain_consume(&p->pending_input_data, len);
55     }
56     bufchain_clear(&p->pending_input_data);
57
58     /* now set the underlying socket to whatever freeze state they wanted */
59     sk_set_frozen(p->sub_socket, p->freeze);
60 }
61
62 /* basic proxy socket functions */
63
64 static Plug sk_proxy_plug (Socket s, Plug p)
65 {
66     Proxy_Socket ps = (Proxy_Socket) s;
67     Plug ret = ps->plug;
68     if (p)
69         ps->plug = p;
70     return ret;
71 }
72
73 static void sk_proxy_close (Socket s)
74 {
75     Proxy_Socket ps = (Proxy_Socket) s;
76
77     sk_close(ps->sub_socket);
78     sfree(ps);
79 }
80
81 static int sk_proxy_write (Socket s, char *data, int len)
82 {
83     Proxy_Socket ps = (Proxy_Socket) s;
84
85     if (ps->state != PROXY_STATE_ACTIVE) {
86         bufchain_add(&ps->pending_output_data, data, len);
87         return bufchain_size(&ps->pending_output_data);
88     }
89     return sk_write(ps->sub_socket, data, len);
90 }
91
92 static int sk_proxy_write_oob (Socket s, char *data, int len)
93 {
94     Proxy_Socket ps = (Proxy_Socket) s;
95
96     if (ps->state != PROXY_STATE_ACTIVE) {
97         bufchain_clear(&ps->pending_output_data);
98         bufchain_clear(&ps->pending_oob_output_data);
99         bufchain_add(&ps->pending_oob_output_data, data, len);
100         return len;
101     }
102     return sk_write_oob(ps->sub_socket, data, len);
103 }
104
105 static void sk_proxy_flush (Socket s)
106 {
107     Proxy_Socket ps = (Proxy_Socket) s;
108
109     if (ps->state != PROXY_STATE_ACTIVE) {
110         ps->pending_flush = 1;
111         return;
112     }
113     sk_flush(ps->sub_socket);
114 }
115
116 static void sk_proxy_set_private_ptr (Socket s, void *ptr)
117 {
118     Proxy_Socket ps = (Proxy_Socket) s;
119     sk_set_private_ptr(ps->sub_socket, ptr);
120 }
121
122 static void * sk_proxy_get_private_ptr (Socket s)
123 {
124     Proxy_Socket ps = (Proxy_Socket) s;
125     return sk_get_private_ptr(ps->sub_socket);
126 }
127
128 static void sk_proxy_set_frozen (Socket s, int is_frozen)
129 {
130     Proxy_Socket ps = (Proxy_Socket) s;
131
132     if (ps->state != PROXY_STATE_ACTIVE) {
133         ps->freeze = is_frozen;
134         return;
135     }
136     sk_set_frozen(ps->sub_socket, is_frozen);
137 }
138
139 static char * sk_proxy_socket_error (Socket s)
140 {
141     Proxy_Socket ps = (Proxy_Socket) s;
142     if (ps->error != NULL || ps->sub_socket == NULL) {
143         return ps->error;
144     }
145     return sk_socket_error(ps->sub_socket);
146 }
147
148 /* basic proxy plug functions */
149
150 static int plug_proxy_closing (Plug p, char *error_msg,
151                                int error_code, int calling_back)
152 {
153     Proxy_Plug pp = (Proxy_Plug) p;
154     Proxy_Socket ps = pp->proxy_socket;
155
156     if (ps->state != PROXY_STATE_ACTIVE) {
157         ps->closing_error_msg = error_msg;
158         ps->closing_error_code = error_code;
159         ps->closing_calling_back = calling_back;
160         return ps->negotiate(ps, PROXY_CHANGE_CLOSING);
161     }
162     return plug_closing(ps->plug, error_msg,
163                         error_code, calling_back);
164 }
165
166 static int plug_proxy_receive (Plug p, int urgent, char *data, int len)
167 {
168     Proxy_Plug pp = (Proxy_Plug) p;
169     Proxy_Socket ps = pp->proxy_socket;
170
171     if (ps->state != PROXY_STATE_ACTIVE) {
172         /* we will lose the urgentness of this data, but since most,
173          * if not all, of this data will be consumed by the negotiation
174          * process, hopefully it won't affect the protocol above us
175          */
176         bufchain_add(&ps->pending_input_data, data, len);
177         ps->receive_urgent = urgent;
178         ps->receive_data = data;
179         ps->receive_len = len;
180         return ps->negotiate(ps, PROXY_CHANGE_RECEIVE);
181     }
182     return plug_receive(ps->plug, urgent, data, len);
183 }
184
185 static void plug_proxy_sent (Plug p, int bufsize)
186 {
187     Proxy_Plug pp = (Proxy_Plug) p;
188     Proxy_Socket ps = pp->proxy_socket;
189
190     if (ps->state != PROXY_STATE_ACTIVE) {
191         ps->sent_bufsize = bufsize;
192         ps->negotiate(ps, PROXY_CHANGE_SENT);
193         return;
194     }
195     plug_sent(ps->plug, bufsize);
196 }
197
198 static int plug_proxy_accepting (Plug p, void *sock)
199 {
200     Proxy_Plug pp = (Proxy_Plug) p;
201     Proxy_Socket ps = pp->proxy_socket;
202
203     if (ps->state != PROXY_STATE_ACTIVE) {
204         ps->accepting_sock = sock;
205         return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING);
206     }
207     return plug_accepting(ps->plug, sock);
208 }
209
210 static int proxy_for_destination (SockAddr addr, char * hostname, int port)
211 {
212     int s = 0, e = 0;
213     char hostip[64];
214     int hostip_len, hostname_len;
215     char * exclude_list;
216
217     /* we want a string representation of the IP address for comparisons */
218     sk_getaddr(addr, hostip, 64);
219
220     hostip_len = strlen(hostip);
221     hostname_len = strlen(hostname);
222
223     exclude_list = cfg.proxy_exclude_list;
224
225     /* now parse the exclude list, and see if either our IP
226      * or hostname matches anything in it.
227      */
228
229     while (exclude_list[s]) {
230         while (exclude_list[s] &&
231                (isspace(exclude_list[s]) ||
232                 exclude_list[s] == ',')) s++;
233
234         if (!exclude_list[s]) break;
235
236         e = s;
237
238         while (exclude_list[e] &&
239                (isalnum(exclude_list[e]) ||
240                 exclude_list[e] == '-' ||
241                 exclude_list[e] == '.' ||
242                 exclude_list[e] == '*')) e++;
243
244         if (exclude_list[s] == '*') {
245             /* wildcard at beginning of entry */
246
247             if (strnicmp(hostip + hostip_len - (e - s - 1),
248                          exclude_list + s + 1, e - s - 1) == 0 ||
249                 strnicmp(hostname + hostname_len - (e - s - 1),
250                          exclude_list + s + 1, e - s - 1) == 0)
251                 return 0; /* IP/hostname range excluded. do not use proxy. */
252
253         } else if (exclude_list[e-1] == '*') {
254             /* wildcard at end of entry */
255
256             if (strnicmp(hostip, exclude_list + s, e - s - 1) == 0 ||
257                 strnicmp(hostname, exclude_list + s, e - s - 1) == 0)
258                 return 0; /* IP/hostname range excluded. do not use proxy. */
259
260         } else {
261             /* no wildcard at either end, so let's try an absolute
262              * match (ie. a specific IP)
263              */
264
265             if (stricmp(hostip, exclude_list + s) == 0)
266                 return 0; /* IP/hostname excluded. do not use proxy. */
267             if (stricmp(hostname, exclude_list + s) == 0)
268                 return 0; /* IP/hostname excluded. do not use proxy. */
269         }
270
271         s = e;
272     }
273
274     /* no matches in the exclude list, so use the proxy */
275     return 1;
276 }
277
278 Socket new_connection(SockAddr addr, char *hostname,
279                       int port, int privport,
280                       int oobinline, int nodelay, Plug plug)
281 {
282     static struct socket_function_table socket_fn_table = {
283         sk_proxy_plug,
284         sk_proxy_close,
285         sk_proxy_write,
286         sk_proxy_write_oob,
287         sk_proxy_flush,
288         sk_proxy_set_private_ptr,
289         sk_proxy_get_private_ptr,
290         sk_proxy_set_frozen,
291         sk_proxy_socket_error
292     };
293
294     static struct plug_function_table plug_fn_table = {
295         plug_proxy_closing,
296         plug_proxy_receive,
297         plug_proxy_sent,
298         plug_proxy_accepting
299     };
300
301     if (cfg.proxy_type != PROXY_NONE &&
302         proxy_for_destination(addr, hostname, port))
303     {
304         Proxy_Socket ret;
305         Proxy_Plug pplug;
306         SockAddr proxy_addr;
307         char * proxy_canonical_name;
308
309         ret = smalloc(sizeof(struct Socket_proxy_tag));
310         ret->fn = &socket_fn_table;
311         ret->plug = plug;
312         ret->remote_addr = addr;
313         ret->remote_port = port;
314
315         bufchain_init(&ret->pending_input_data);
316         bufchain_init(&ret->pending_output_data);
317         bufchain_init(&ret->pending_oob_output_data);
318
319         ret->sub_socket = NULL;
320         ret->state = PROXY_STATE_NEW;
321
322         if (cfg.proxy_type == PROXY_HTTP) {
323             ret->negotiate = proxy_http_negotiate;
324         } else if (cfg.proxy_type == PROXY_SOCKS) {
325             ret->negotiate = proxy_socks_negotiate;
326         } else if (cfg.proxy_type == PROXY_TELNET) {
327             ret->negotiate = proxy_telnet_negotiate;
328         } else {
329             ret->error = "Network error: Unknown proxy method";
330             return (Socket) ret;
331         }
332
333         /* create the proxy plug to map calls from the actual
334          * socket into our proxy socket layer */
335         pplug = smalloc(sizeof(struct Plug_proxy_tag));
336         pplug->fn = &plug_fn_table;
337         pplug->proxy_socket = ret;
338
339         /* look-up proxy */
340         proxy_addr = sk_namelookup(cfg.proxy_host,
341                                    &proxy_canonical_name);
342         sfree(proxy_canonical_name);
343
344         /* create the actual socket we will be using,
345          * connected to our proxy server and port.
346          */
347         ret->sub_socket = sk_new(proxy_addr, cfg.proxy_port,
348                                  privport, oobinline,
349                                  nodelay, (Plug) pplug);
350         if (sk_socket_error(ret->sub_socket) != NULL)
351             return (Socket) ret;
352
353         sk_addr_free(proxy_addr);
354
355         /* start the proxy negotiation process... */
356         sk_set_frozen(ret->sub_socket, 0);
357         ret->negotiate(ret, PROXY_CHANGE_NEW);
358
359         return (Socket) ret;
360     }
361
362     /* no proxy, so just return the direct socket */
363     return sk_new(addr, port, privport, oobinline, nodelay, plug);
364 }
365
366 Socket new_listener(int port, Plug plug, int local_host_only)
367 {
368     /* TODO: SOCKS (and potentially others) support inbound
369      * TODO: connections via the proxy. support them.
370      */
371
372     return sk_newlistener(port, plug, local_host_only);
373 }
374
375 /* ----------------------------------------------------------------------
376  * HTTP CONNECT proxy type.
377  */
378
379 static int get_line_end (char * data, int len)
380 {
381     int off = 0;
382
383     while (off < len)
384     {
385         if (data[off] == '\n') {
386             /* we have a newline */
387             off++;
388
389             /* is that the only thing on this line? */
390             if (off <= 2) return off;
391
392             /* if not, then there is the possibility that this header
393              * continues onto the next line, if it starts with a space
394              * or a tab.
395              */
396
397             if (off + 1 < len &&
398                 data[off+1] != ' ' &&
399                 data[off+1] != '\t') return off;
400
401             /* the line does continue, so we have to keep going
402              * until we see an the header's "real" end of line.
403              */
404             off++;
405         }
406
407         off++;
408     }
409
410     return -1;
411 }
412
413 int proxy_http_negotiate (Proxy_Socket p, int change)
414 {
415     if (p->state == PROXY_STATE_NEW) {
416         /* we are just beginning the proxy negotiate process,
417          * so we'll send off the initial bits of the request.
418          * for this proxy method, it's just a simple HTTP
419          * request
420          */
421         char buf[256], dest[64];
422
423         sk_getaddr(p->remote_addr, dest, 64);
424
425         sprintf(buf, "CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n\r\n",
426                 dest, p->remote_port, dest, p->remote_port);
427         sk_write(p->sub_socket, buf, strlen(buf));
428
429         p->state = 1;
430
431         return 0;
432     }
433
434     if (change == PROXY_CHANGE_CLOSING) {
435         /* if our proxy negotiation process involves closing and opening
436          * new sockets, then we would want to intercept this closing
437          * callback when we were expecting it. if we aren't anticipating
438          * a socket close, then some error must have occurred. we'll
439          * just pass those errors up to the backend.
440          */
441         return plug_closing(p->plug, p->closing_error_msg,
442                             p->closing_error_code,
443                             p->closing_calling_back);
444     }
445
446     if (change == PROXY_CHANGE_SENT) {
447         /* some (or all) of what we wrote to the proxy was sent.
448          * we don't do anything new, however, until we receive the
449          * proxy's response. we might want to set a timer so we can
450          * timeout the proxy negotiation after a while...
451          */
452         return 0;
453     }
454
455     if (change == PROXY_CHANGE_ACCEPTING) {
456         /* we should _never_ see this, as we are using our socket to
457          * connect to a proxy, not accepting inbound connections.
458          * what should we do? close the socket with an appropriate
459          * error message?
460          */
461         return plug_accepting(p->plug, p->accepting_sock);
462     }
463
464     if (change == PROXY_CHANGE_RECEIVE) {
465         /* we have received data from the underlying socket, which
466          * we'll need to parse, process, and respond to appropriately.
467          */
468
469         void *data;
470         int len;
471         int eol;
472
473         if (p->state == 1) {
474
475             int min_ver, maj_ver, status;
476
477             /* get the status line */
478             bufchain_prefix(&p->pending_input_data, &data, &len);
479             eol = get_line_end(data, len);
480             if (eol < 0) return 1;
481
482             sscanf((char *)data, "HTTP/%i.%i %i", &maj_ver, &min_ver, &status);
483
484             /* remove the status line from the input buffer. */
485             bufchain_consume(&p->pending_input_data, eol);
486
487             /* TODO: we need to support Proxy-Auth headers */
488
489             if (status < 200 || status > 299) {
490                 /* error */
491                 /* TODO: return a more specific error message,
492                  * TODO: based on the status code.
493                  */
494                 plug_closing(p->plug, "Network error: Error while communicating with proxy",
495                             PROXY_ERROR_GENERAL, 0);
496                 return 1;
497             }
498
499             p->state = 2;
500         }
501
502         if (p->state == 2) {
503
504             /* get headers. we're done when we get a
505              * header of length 2, (ie. just "\r\n")
506              */
507
508             bufchain_prefix(&p->pending_input_data, &data, &len);
509             eol = get_line_end(data, len);
510             while (eol > 2)
511             {
512                 /* TODO: Proxy-Auth stuff. in some cases, we will
513                  * TODO: need to extract information from headers.
514                  */
515                 bufchain_consume(&p->pending_input_data, eol);
516                 bufchain_prefix(&p->pending_input_data, &data, &len);
517                 eol = get_line_end(data, len);
518             }
519
520             if (eol == 2) {
521                 /* we're done */
522                 bufchain_consume(&p->pending_input_data, 2);
523                 proxy_activate(p);
524                 /* proxy activate will have dealt with
525                  * whatever is left of the buffer */
526                 return 1;
527             }
528
529             return 1;
530         }
531     }
532
533     plug_closing(p->plug, "Network error: Unexpected proxy error",
534                  PROXY_ERROR_UNEXPECTED, 0);
535     return 0;
536 }
537
538 /* ----------------------------------------------------------------------
539  * SOCKS proxy type (as yet unimplemented).
540  */
541
542 int proxy_socks_negotiate (Proxy_Socket p, int change)
543 {
544     p->error = "Network error: SOCKS proxy implementation is incomplete";
545     return 0;
546 }
547
548 /* ----------------------------------------------------------------------
549  * `Telnet' proxy type.
550  *
551  * (This is for ad-hoc proxies where you connect to the proxy's
552  * telnet port and send a command such as `connect host port'. The
553  * command is configurable, since this proxy type is typically not
554  * standardised or at all well-defined.)
555  */
556
557 int proxy_telnet_negotiate (Proxy_Socket p, int change)
558 {
559     if (p->state == PROXY_CHANGE_NEW) {
560
561         int so = 0, eo = 0;
562
563         /* we need to escape \\, \%, \r, \n, \t, \x??, \0???, 
564          * %%, %host, and %port 
565          */
566
567         while (cfg.proxy_telnet_command[eo] != 0) {
568
569             /* scan forward until we hit end-of-line, 
570              * or an escape character (\ or %) */
571             while (cfg.proxy_telnet_command[eo] != 0 &&
572                    cfg.proxy_telnet_command[eo] != '%' &&
573                    cfg.proxy_telnet_command[eo] != '\\') eo++;
574
575             /* if we hit eol, break out of our escaping loop */
576             if (cfg.proxy_telnet_command[eo] == 0) break;
577
578             /* if there was any unescaped text before the escape
579              * character, send that now */
580             if (eo != so) {
581                 sk_write(p->sub_socket, 
582                          cfg.proxy_telnet_command + so, eo - so);
583             }
584
585             so = eo++;
586
587             /* if the escape character was the last character of
588              * the line, we'll just stop and send it. */
589             if (cfg.proxy_telnet_command[eo] == 0) break;
590
591             if (cfg.proxy_telnet_command[so] == '\\') {
592
593                 /* we recognize \\, \%, \r, \n, \t, \x??. 
594                  * anything else, we just send unescaped (including the \). 
595                  */
596
597                 switch (cfg.proxy_telnet_command[eo]) {
598
599                   case '\\':
600                     sk_write(p->sub_socket, "\\", 1);
601                     eo++;
602                     break;
603
604                   case '%':
605                     sk_write(p->sub_socket, "%%", 1);
606                     eo++;
607                     break;
608
609                   case 'r':
610                     sk_write(p->sub_socket, "\r", 1);
611                     eo++;
612                     break;
613
614                   case 'n':
615                     sk_write(p->sub_socket, "\n", 1);
616                     eo++;
617                     break;
618
619                   case 't':
620                     sk_write(p->sub_socket, "\t", 1);
621                     eo++;
622                     break;
623
624                   case 'x':
625                   case 'X':
626                     {
627                     /* escaped hexadecimal value (ie. \xff) */
628                     unsigned char v = 0;
629                     int i = 0;
630
631                     for (;;) {
632                         eo++;
633                         if (cfg.proxy_telnet_command[eo] >= '0' &&
634                             cfg.proxy_telnet_command[eo] <= '9')
635                             v += cfg.proxy_telnet_command[eo] - '0';
636                         else if (cfg.proxy_telnet_command[eo] >= 'a' &&
637                                  cfg.proxy_telnet_command[eo] <= 'f')
638                             v += cfg.proxy_telnet_command[eo] - 'a' + 10;
639                         else if (cfg.proxy_telnet_command[eo] >= 'A' &&
640                                  cfg.proxy_telnet_command[eo] <= 'F')
641                             v += cfg.proxy_telnet_command[eo] - 'A' + 10;
642                         else {
643                             /* non hex character, so we abort and just
644                              * send the whole thing unescaped (including \x)
645                              */
646                             sk_write(p->sub_socket, "\\", 1);
647                             eo = so + 1;
648                             break;
649                         }
650
651                         /* we only extract two hex characters */
652                         if (i == 1) {
653                             sk_write(p->sub_socket, &v, 1);
654                             eo++;
655                             break;
656                         }
657
658                         i++;
659                         v <<= 4;
660                     }
661                     }
662                     break;
663
664                   default:
665                     sk_write(p->sub_socket, 
666                              cfg.proxy_telnet_command + so, 2);
667                     eo++;
668                     break;
669                 }
670             } else {
671
672                 /* % escape. we recognize %%, %host, %port. anything else,
673                  * we just send unescaped (including the %). */
674
675                 if (cfg.proxy_telnet_command[eo] == '%') {
676                     sk_write(p->sub_socket, "%", 1);
677                     eo++;
678                 } 
679                 else if (strnicmp(cfg.proxy_telnet_command + eo,
680                                   "host", 4) == 0) {
681                     char dest[64];
682                     sk_getaddr(p->remote_addr, dest, 64);
683                     sk_write(p->sub_socket, dest, strlen(dest));
684                     eo += 4;
685                 } 
686                 else if (strnicmp(cfg.proxy_telnet_command + eo,
687                                   "port", 4) == 0) {
688                     char port[8];
689                     sprintf(port, "%i", p->remote_port);
690                     sk_write(p->sub_socket, port, strlen(port));
691                     eo += 4;
692                 } 
693                 else {
694                     /* we don't escape this, so send the % now, and
695                      * don't advance eo, so that we'll consider the
696                      * text immediately following the % as unescaped.
697                      */
698                     sk_write(p->sub_socket, "%", 1);
699                 }
700             }
701
702             /* resume scanning for additional escapes after this one. */
703             so = eo;
704         }
705
706         /* if there is any unescaped text at the end of the line, send it */
707         if (eo != so) {
708             sk_write(p->sub_socket, cfg.proxy_telnet_command + so, eo - so);
709         }
710
711         p->state = 1;
712
713         return 0;
714     }
715
716     if (change == PROXY_CHANGE_CLOSING) {
717         /* if our proxy negotiation process involves closing and opening
718          * new sockets, then we would want to intercept this closing
719          * callback when we were expecting it. if we aren't anticipating
720          * a socket close, then some error must have occurred. we'll
721          * just pass those errors up to the backend.
722          */
723         return plug_closing(p->plug, p->closing_error_msg,
724                             p->closing_error_code,
725                             p->closing_calling_back);
726     }
727
728     if (change == PROXY_CHANGE_SENT) {
729         /* some (or all) of what we wrote to the proxy was sent.
730          * we don't do anything new, however, until we receive the
731          * proxy's response. we might want to set a timer so we can
732          * timeout the proxy negotiation after a while...
733          */
734         return 0;
735     }
736
737     if (change == PROXY_CHANGE_ACCEPTING) {
738         /* we should _never_ see this, as we are using our socket to
739          * connect to a proxy, not accepting inbound connections.
740          * what should we do? close the socket with an appropriate
741          * error message?
742          */
743         return plug_accepting(p->plug, p->accepting_sock);
744     }
745
746     if (change == PROXY_CHANGE_RECEIVE) {
747         /* we have received data from the underlying socket, which
748          * we'll need to parse, process, and respond to appropriately.
749          */
750
751         /* we're done */
752         proxy_activate(p);
753         /* proxy activate will have dealt with
754          * whatever is left of the buffer */
755         return 1;
756     }
757
758     plug_closing(p->plug, "Network error: Unexpected proxy error",
759                  PROXY_ERROR_UNEXPECTED, 0);
760     return 0;
761 }