]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - proxy.c
Justin Bradford's proxy support patch. Currently supports only HTTP
[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->lock_close =
25         p->lock_write =
26         p->lock_write_oob =
27         p->lock_receive =
28         p->lock_flush =
29         p->lock_closing =
30         p->lock_sent =
31         p->lock_accepting =
32         p->lock_freeze = 1;
33
34     p->state = PROXY_STATE_ACTIVE;
35
36     /* let's try to keep extra receive events from coming through */
37     sk_set_frozen(p->sub_socket, 1);
38
39     while (bufchain_size(&p->pending_oob_output_data) > 0) {
40         bufchain_prefix(&p->pending_oob_output_data, &data, &len);
41         sk_write_oob(p->sub_socket, data, len);
42         bufchain_consume(&p->pending_oob_output_data, len);
43     }
44     bufchain_clear(&p->pending_oob_output_data);
45
46     while (bufchain_size(&p->pending_output_data) > 0) {
47         bufchain_prefix(&p->pending_output_data, &data, &len);
48         sk_write(p->sub_socket, data, len);
49         bufchain_consume(&p->pending_output_data, len);
50     }
51     bufchain_clear(&p->pending_output_data);
52
53     p->lock_write_oob = 0;
54     p->lock_write = 0;
55
56     if (p->pending_flush) sk_flush(p->sub_socket);
57     p->lock_flush = 0;
58
59     while (bufchain_size(&p->pending_input_data) > 0) {
60         bufchain_prefix(&p->pending_input_data, &data, &len);
61         plug_receive(p->plug, 0, data, len);
62         bufchain_consume(&p->pending_input_data, len);
63     }
64     bufchain_clear(&p->pending_input_data);
65     p->lock_receive = 0;
66
67     /* now set the underlying socket to whatever freeze state they wanted */
68     sk_set_frozen(p->sub_socket, p->freeze);
69     p->lock_freeze = 0;
70
71     p->lock_sent = 0;
72     p->lock_accepting = 0;
73     p->lock_closing = 0;
74     p->lock_close = 0;
75 }
76
77 /* basic proxy socket functions */
78
79 static Plug sk_proxy_plug (Socket s, Plug p)
80 {
81     Proxy_Socket ps = (Proxy_Socket) s;
82     Plug ret = ps->plug;
83     if (p)
84         ps->plug = p;
85     return ret;
86 }
87
88 static void sk_proxy_close (Socket s)
89 {
90     Proxy_Socket ps = (Proxy_Socket) s;
91
92     while (ps->lock_close) ;
93     sk_close(ps->sub_socket);
94     sfree(ps);
95 }
96
97 static int sk_proxy_write (Socket s, char *data, int len)
98 {
99     Proxy_Socket ps = (Proxy_Socket) s;
100
101     while (ps->lock_write) ;
102     if (ps->state != PROXY_STATE_ACTIVE) {
103         bufchain_add(&ps->pending_output_data, data, len);
104         return bufchain_size(&ps->pending_output_data);
105     }
106     return sk_write(ps->sub_socket, data, len);
107 }
108
109 static int sk_proxy_write_oob (Socket s, char *data, int len)
110 {
111     Proxy_Socket ps = (Proxy_Socket) s;
112
113     while (ps->lock_write_oob) ;
114     if (ps->state != PROXY_STATE_ACTIVE) {
115         bufchain_clear(&ps->pending_output_data);
116         bufchain_clear(&ps->pending_oob_output_data);
117         bufchain_add(&ps->pending_oob_output_data, data, len);
118         return len;
119     }
120     return sk_write_oob(ps->sub_socket, data, len);
121 }
122
123 static void sk_proxy_flush (Socket s)
124 {
125     Proxy_Socket ps = (Proxy_Socket) s;
126
127     while (ps->lock_flush) ;
128     if (ps->state != PROXY_STATE_ACTIVE) {
129         ps->pending_flush = 1;
130         return;
131     }
132     sk_flush(ps->sub_socket);
133 }
134
135 static void sk_proxy_set_private_ptr (Socket s, void *ptr)
136 {
137     Proxy_Socket ps = (Proxy_Socket) s;
138     sk_set_private_ptr(ps->sub_socket, ptr);
139 }
140
141 static void * sk_proxy_get_private_ptr (Socket s)
142 {
143     Proxy_Socket ps = (Proxy_Socket) s;
144     return sk_get_private_ptr(ps->sub_socket);
145 }
146
147 static void sk_proxy_set_frozen (Socket s, int is_frozen)
148 {
149     Proxy_Socket ps = (Proxy_Socket) s;
150
151     while (ps->lock_freeze) ;
152     if (ps->state != PROXY_STATE_ACTIVE) {
153         ps->freeze = is_frozen;
154         return;
155     }
156     sk_set_frozen(ps->sub_socket, is_frozen);
157 }
158
159 static char * sk_proxy_socket_error (Socket s)
160 {
161     Proxy_Socket ps = (Proxy_Socket) s;
162     if (ps->error != NULL || ps->sub_socket == NULL) {
163         return ps->error;
164     }
165     return sk_socket_error(ps->sub_socket);
166 }
167
168 /* basic proxy plug functions */
169
170 static int plug_proxy_closing (Plug p, char *error_msg,
171                                int error_code, int calling_back)
172 {
173     Proxy_Plug pp = (Proxy_Plug) p;
174     Proxy_Socket ps = pp->proxy_socket;
175
176     while (ps->lock_closing) ;
177     if (ps->state != PROXY_STATE_ACTIVE) {
178         ps->closing_error_msg = error_msg;
179         ps->closing_error_code = error_code;
180         ps->closing_calling_back = calling_back;
181         return ps->negotiate(ps, PROXY_CHANGE_CLOSING);
182     }
183     return plug_closing(ps->plug, error_msg,
184                         error_code, calling_back);
185 }
186
187 static int plug_proxy_receive (Plug p, int urgent, char *data, int len)
188 {
189     Proxy_Plug pp = (Proxy_Plug) p;
190     Proxy_Socket ps = pp->proxy_socket;
191
192     while (ps->lock_receive) ;
193     if (ps->state != PROXY_STATE_ACTIVE) {
194         /* we will lose the urgentness of this data, but since most,
195          * if not all, of this data will be consumed by the negotiation
196          * process, hopefully it won't affect the protocol above us
197          */
198         bufchain_add(&ps->pending_input_data, data, len);
199         ps->receive_urgent = urgent;
200         ps->receive_data = data;
201         ps->receive_len = len;
202         return ps->negotiate(ps, PROXY_CHANGE_RECEIVE);
203     }
204     return plug_receive(ps->plug, urgent, data, len);
205 }
206
207 static void plug_proxy_sent (Plug p, int bufsize)
208 {
209     Proxy_Plug pp = (Proxy_Plug) p;
210     Proxy_Socket ps = pp->proxy_socket;
211
212     while (ps->lock_sent) ;
213     if (ps->state != PROXY_STATE_ACTIVE) {
214         ps->sent_bufsize = bufsize;
215         ps->negotiate(ps, PROXY_CHANGE_SENT);
216         return;
217     }
218     plug_sent(ps->plug, bufsize);
219 }
220
221 static int plug_proxy_accepting (Plug p, void *sock)
222 {
223     Proxy_Plug pp = (Proxy_Plug) p;
224     Proxy_Socket ps = pp->proxy_socket;
225
226     while (ps->lock_accepting) ;
227     if (ps->state != PROXY_STATE_ACTIVE) {
228         ps->accepting_sock = sock;
229         return ps->negotiate(ps, PROXY_CHANGE_ACCEPTING);
230     }
231     return plug_accepting(ps->plug, sock);
232 }
233
234 static int proxy_for_destination (SockAddr addr, char * hostname, int port)
235 {
236     int s = 0, e = 0;
237     char hostip[64];
238     int hostip_len, hostname_len;
239     char * exclude_list;
240
241     /* we want a string representation of the IP address for comparisons */
242     sk_getaddr(addr, hostip, 64);
243
244     hostip_len = strlen(hostip);
245     hostname_len = strlen(hostname);
246
247     exclude_list = cfg.proxy_exclude_list;
248
249     /* now parse the exclude list, and see if either our IP
250      * or hostname matches anything in it.
251      */
252
253     while (exclude_list[s]) {
254         while (exclude_list[s] &&
255                (isspace(exclude_list[s]) ||
256                 exclude_list[s] == ',')) s++;
257
258         if (!exclude_list[s]) break;
259
260         e = s;
261
262         while (exclude_list[e] &&
263                (isalnum(exclude_list[e]) ||
264                 exclude_list[e] == '-' ||
265                 exclude_list[e] == '.' ||
266                 exclude_list[e] == '*')) e++;
267
268         if (exclude_list[s] == '*') {
269             /* wildcard at beginning of entry */
270
271             if (strnicmp(hostip + hostip_len - (e - s - 1),
272                          exclude_list + s + 1, e - s - 1) == 0 ||
273                 strnicmp(hostname + hostname_len - (e - s - 1),
274                          exclude_list + s + 1, e - s - 1) == 0)
275                 return 0; /* IP/hostname range excluded. do not use proxy. */
276
277         } else if (exclude_list[e-1] == '*') {
278             /* wildcard at end of entry */
279
280             if (strnicmp(hostip, exclude_list + s, e - s - 1) == 0 ||
281                 strnicmp(hostname, exclude_list + s, e - s - 1) == 0)
282                 return 0; /* IP/hostname range excluded. do not use proxy. */
283
284         } else {
285             /* no wildcard at either end, so let's try an absolute
286              * match (ie. a specific IP)
287              */
288
289             if (stricmp(hostip, exclude_list + s) == 0)
290                 return 0; /* IP/hostname excluded. do not use proxy. */
291             if (stricmp(hostname, exclude_list + s) == 0)
292                 return 0; /* IP/hostname excluded. do not use proxy. */
293         }
294
295         s = e;
296     }
297
298     /* no matches in the exclude list, so use the proxy */
299     return 1;
300 }
301
302 Socket new_connection(SockAddr addr, char *hostname,
303                       int port, int privport,
304                       int oobinline, int nodelay, Plug plug)
305 {
306     static struct socket_function_table socket_fn_table = {
307         sk_proxy_plug,
308         sk_proxy_close,
309         sk_proxy_write,
310         sk_proxy_write_oob,
311         sk_proxy_flush,
312         sk_proxy_set_private_ptr,
313         sk_proxy_get_private_ptr,
314         sk_proxy_set_frozen,
315         sk_proxy_socket_error
316     };
317
318     static struct plug_function_table plug_fn_table = {
319         plug_proxy_closing,
320         plug_proxy_receive,
321         plug_proxy_sent,
322         plug_proxy_accepting
323     };
324
325     if (cfg.proxy_type != PROXY_NONE &&
326         proxy_for_destination(addr, hostname, port))
327     {
328         Proxy_Socket ret;
329         Proxy_Plug pplug;
330         SockAddr proxy_addr;
331         char * proxy_canonical_name;
332
333         ret = smalloc(sizeof(struct Socket_proxy_tag));
334         ret->fn = &socket_fn_table;
335         ret->plug = plug;
336         ret->remote_addr = addr;
337         ret->remote_port = port;
338
339         bufchain_init(&ret->pending_input_data);
340         bufchain_init(&ret->pending_output_data);
341         bufchain_init(&ret->pending_oob_output_data);
342
343         ret->lock_close =
344             ret->lock_write =
345             ret->lock_write_oob =
346             ret->lock_receive =
347             ret->lock_flush =
348             ret->lock_closing =
349             ret->lock_sent =
350             ret->lock_accepting = 0;
351
352         ret->sub_socket = NULL;
353         ret->state = PROXY_STATE_NEW;
354
355         if (cfg.proxy_type == PROXY_HTTP) {
356             ret->negotiate = proxy_http_negotiate;
357         } else if (cfg.proxy_type == PROXY_SOCKS) {
358             ret->negotiate = proxy_socks_negotiate;
359         } else if (cfg.proxy_type == PROXY_TELNET) {
360             ret->negotiate = proxy_telnet_negotiate;
361         } else {
362             ret->error = "Network error: Unknown proxy method";
363             return (Socket) ret;
364         }
365
366         /* create the proxy plug to map calls from the actual
367          * socket into our proxy socket layer */
368         pplug = smalloc(sizeof(struct Plug_proxy_tag));
369         pplug->fn = &plug_fn_table;
370         pplug->proxy_socket = ret;
371
372         /* look-up proxy */
373         proxy_addr = sk_namelookup(cfg.proxy_host,
374                                    &proxy_canonical_name);
375         sfree(proxy_canonical_name);
376
377         /* create the actual socket we will be using,
378          * connected to our proxy server and port.
379          */
380         ret->sub_socket = sk_new(proxy_addr, cfg.proxy_port,
381                                  privport, oobinline,
382                                  nodelay, (Plug) pplug);
383         if (sk_socket_error(ret->sub_socket) != NULL)
384             return (Socket) ret;
385
386         sk_addr_free(proxy_addr);
387
388         /* start the proxy negotiation process... */
389         sk_set_frozen(ret->sub_socket, 0);
390         ret->negotiate(ret, PROXY_CHANGE_NEW);
391
392         return (Socket) ret;
393     }
394
395     /* no proxy, so just return the direct socket */
396     return sk_new(addr, port, privport, oobinline, nodelay, plug);
397 }
398
399 Socket new_listener(int port, Plug plug, int local_host_only)
400 {
401     /* TODO: SOCKS (and potentially others) support inbound
402      * TODO: connections via the proxy. support them.
403      */
404
405     return sk_newlistener(port, plug, local_host_only);
406 }
407
408 /* ----------------------------------------------------------------------
409  * HTTP CONNECT proxy type.
410  */
411
412 static int get_line_end (char * data, int len)
413 {
414     int off = 0;
415
416     while (off < len)
417     {
418         if (data[off] == '\n') {
419             /* we have a newline */
420             off++;
421
422             /* is that the only thing on this line? */
423             if (off <= 2) return off;
424
425             /* if not, then there is the possibility that this header
426              * continues onto the next line, if it starts with a space
427              * or a tab.
428              */
429
430             if (off + 1 < len &&
431                 data[off+1] != ' ' &&
432                 data[off+1] != '\t') return off;
433
434             /* the line does continue, so we have to keep going
435              * until we see an the header's "real" end of line.
436              */
437             off++;
438         }
439
440         off++;
441     }
442
443     return -1;
444 }
445
446 int proxy_http_negotiate (Proxy_Socket p, int change)
447 {
448     if (p->state == PROXY_STATE_NEW) {
449         /* we are just beginning the proxy negotiate process,
450          * so we'll send off the initial bits of the request.
451          * for this proxy method, it's just a simple HTTP
452          * request
453          */
454         char buf[1024], dest[21];
455
456         sk_getaddr(p->remote_addr, dest, 20);
457
458         sprintf(buf, "CONNECT %s:%i HTTP/1.1\r\nHost: %s:%i\r\n\r\n",
459                 dest, p->remote_port, dest, p->remote_port);
460         sk_write(p->sub_socket, buf, strlen(buf));
461
462         p->state = 1;
463
464         return 0;
465     }
466
467     if (change == PROXY_CHANGE_CLOSING) {
468         /* if our proxy negotiation process involves closing and opening
469          * new sockets, then we would want to intercept this closing
470          * callback when we were expecting it. if we aren't anticipating
471          * a socket close, then some error must have occurred. we'll
472          * just pass those errors up to the backend.
473          */
474         return plug_closing(p->plug, p->closing_error_msg,
475                             p->closing_error_code,
476                             p->closing_calling_back);
477     }
478
479     if (change == PROXY_CHANGE_SENT) {
480         /* some (or all) of what we wrote to the proxy was sent.
481          * we don't do anything new, however, until we receive the
482          * proxy's response. we might want to set a timer so we can
483          * timeout the proxy negotiation after a while...
484          */
485         return 0;
486     }
487
488     if (change == PROXY_CHANGE_ACCEPTING) {
489         /* we should _never_ see this, as we are using our socket to
490          * connect to a proxy, not accepting inbound connections.
491          * what should we do? close the socket with an appropriate
492          * error message?
493          */
494         return plug_accepting(p->plug, p->accepting_sock);
495     }
496
497     if (change == PROXY_CHANGE_RECEIVE) {
498         /* we have received data from the underlying socket, which
499          * we'll need to parse, process, and respond to appropriately.
500          */
501
502         void *data;
503         int len;
504         int eol;
505
506         if (p->state == 1) {
507
508             int min_ver, maj_ver, status;
509
510             /* get the status line */
511             bufchain_prefix(&p->pending_input_data, &data, &len);
512             eol = get_line_end(data, len);
513             if (eol < 0) return 1;
514
515             sscanf((char *)data, "HTTP/%i.%i %i", &maj_ver, &min_ver, &status);
516
517             /* remove the status line from the input buffer. */
518             bufchain_consume(&p->pending_input_data, eol);
519
520             /* TODO: we need to support Proxy-Auth headers */
521
522             if (status < 200 || status > 299) {
523                 /* error */
524                 /* TODO: return a more specific error message,
525                  * TODO: based on the status code.
526                  */
527                 plug_closing(p->plug, "Network error: Error while communicating with proxy",
528                             PROXY_ERROR_GENERAL, 0);
529                 return 1;
530             }
531
532             p->state = 2;
533         }
534
535         if (p->state == 2) {
536
537             /* get headers. we're done when we get a
538              * header of length 2, (ie. just "\r\n")
539              */
540
541             bufchain_prefix(&p->pending_input_data, &data, &len);
542             eol = get_line_end(data, len);
543             while (eol > 2)
544             {
545                 /* TODO: Proxy-Auth stuff. in some cases, we will
546                  * TODO: need to extract information from headers.
547                  */
548                 bufchain_consume(&p->pending_input_data, eol);
549                 bufchain_prefix(&p->pending_input_data, &data, &len);
550                 eol = get_line_end(data, len);
551             }
552
553             if (eol == 2) {
554                 /* we're done */
555                 bufchain_consume(&p->pending_input_data, 2);
556                 proxy_activate(p);
557                 /* proxy activate will have dealt with
558                  * whatever is left of the buffer */
559                 return 1;
560             }
561
562             return 1;
563         }
564     }
565
566     plug_closing(p->plug, "Network error: Unexpected proxy error",
567                  PROXY_ERROR_UNEXPECTED, 0);
568     return 0;
569 }
570
571 /* ----------------------------------------------------------------------
572  * SOCKS proxy type (as yet unimplemented).
573  */
574
575 int proxy_socks_negotiate (Proxy_Socket p, int change)
576 {
577     p->error = "Network error: SOCKS proxy implementation is incomplete";
578     return 0;
579 }
580
581 /* ----------------------------------------------------------------------
582  * `Telnet' proxy type (as yet unimplemented).
583  *
584  * (This is for ad-hoc proxies where you connect to the proxy's
585  * telnet port and send a command such as `connect host port'. The
586  * command is configurable, since this proxy type is typically not
587  * standardised or at all well-defined.)
588  */
589
590 int proxy_telnet_negotiate (Proxy_Socket p, int change)
591 {
592     p->error = "Network error: Telnet proxy implementation is incomplete";
593     return 0;
594 }