2 * Network proxy abstraction in PuTTY
4 * A proxy layer, if necessary, wedges itself between the network
5 * code and the higher level backend.
10 #define DEFINE_PLUG_METHOD_MACROS
16 * Call this when proxy negotiation is complete, so that this
17 * socket can begin working normally.
19 void proxy_activate (Proxy_Socket p)
34 p->state = PROXY_STATE_ACTIVE;
36 /* let's try to keep extra receive events from coming through */
37 sk_set_frozen(p->sub_socket, 1);
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);
44 bufchain_clear(&p->pending_oob_output_data);
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);
51 bufchain_clear(&p->pending_output_data);
53 p->lock_write_oob = 0;
56 if (p->pending_flush) sk_flush(p->sub_socket);
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);
64 bufchain_clear(&p->pending_input_data);
67 /* now set the underlying socket to whatever freeze state they wanted */
68 sk_set_frozen(p->sub_socket, p->freeze);
72 p->lock_accepting = 0;
77 /* basic proxy socket functions */
79 static Plug sk_proxy_plug (Socket s, Plug p)
81 Proxy_Socket ps = (Proxy_Socket) s;
88 static void sk_proxy_close (Socket s)
90 Proxy_Socket ps = (Proxy_Socket) s;
92 while (ps->lock_close) ;
93 sk_close(ps->sub_socket);
97 static int sk_proxy_write (Socket s, char *data, int len)
99 Proxy_Socket ps = (Proxy_Socket) s;
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);
106 return sk_write(ps->sub_socket, data, len);
109 static int sk_proxy_write_oob (Socket s, char *data, int len)
111 Proxy_Socket ps = (Proxy_Socket) s;
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);
120 return sk_write_oob(ps->sub_socket, data, len);
123 static void sk_proxy_flush (Socket s)
125 Proxy_Socket ps = (Proxy_Socket) s;
127 while (ps->lock_flush) ;
128 if (ps->state != PROXY_STATE_ACTIVE) {
129 ps->pending_flush = 1;
132 sk_flush(ps->sub_socket);
135 static void sk_proxy_set_private_ptr (Socket s, void *ptr)
137 Proxy_Socket ps = (Proxy_Socket) s;
138 sk_set_private_ptr(ps->sub_socket, ptr);
141 static void * sk_proxy_get_private_ptr (Socket s)
143 Proxy_Socket ps = (Proxy_Socket) s;
144 return sk_get_private_ptr(ps->sub_socket);
147 static void sk_proxy_set_frozen (Socket s, int is_frozen)
149 Proxy_Socket ps = (Proxy_Socket) s;
151 while (ps->lock_freeze) ;
152 if (ps->state != PROXY_STATE_ACTIVE) {
153 ps->freeze = is_frozen;
156 sk_set_frozen(ps->sub_socket, is_frozen);
159 static char * sk_proxy_socket_error (Socket s)
161 Proxy_Socket ps = (Proxy_Socket) s;
162 if (ps->error != NULL || ps->sub_socket == NULL) {
165 return sk_socket_error(ps->sub_socket);
168 /* basic proxy plug functions */
170 static int plug_proxy_closing (Plug p, char *error_msg,
171 int error_code, int calling_back)
173 Proxy_Plug pp = (Proxy_Plug) p;
174 Proxy_Socket ps = pp->proxy_socket;
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);
183 return plug_closing(ps->plug, error_msg,
184 error_code, calling_back);
187 static int plug_proxy_receive (Plug p, int urgent, char *data, int len)
189 Proxy_Plug pp = (Proxy_Plug) p;
190 Proxy_Socket ps = pp->proxy_socket;
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
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);
204 return plug_receive(ps->plug, urgent, data, len);
207 static void plug_proxy_sent (Plug p, int bufsize)
209 Proxy_Plug pp = (Proxy_Plug) p;
210 Proxy_Socket ps = pp->proxy_socket;
212 while (ps->lock_sent) ;
213 if (ps->state != PROXY_STATE_ACTIVE) {
214 ps->sent_bufsize = bufsize;
215 ps->negotiate(ps, PROXY_CHANGE_SENT);
218 plug_sent(ps->plug, bufsize);
221 static int plug_proxy_accepting (Plug p, void *sock)
223 Proxy_Plug pp = (Proxy_Plug) p;
224 Proxy_Socket ps = pp->proxy_socket;
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);
231 return plug_accepting(ps->plug, sock);
234 static int proxy_for_destination (SockAddr addr, char * hostname, int port)
238 int hostip_len, hostname_len;
241 /* we want a string representation of the IP address for comparisons */
242 sk_getaddr(addr, hostip, 64);
244 hostip_len = strlen(hostip);
245 hostname_len = strlen(hostname);
247 exclude_list = cfg.proxy_exclude_list;
249 /* now parse the exclude list, and see if either our IP
250 * or hostname matches anything in it.
253 while (exclude_list[s]) {
254 while (exclude_list[s] &&
255 (isspace(exclude_list[s]) ||
256 exclude_list[s] == ',')) s++;
258 if (!exclude_list[s]) break;
262 while (exclude_list[e] &&
263 (isalnum(exclude_list[e]) ||
264 exclude_list[e] == '-' ||
265 exclude_list[e] == '.' ||
266 exclude_list[e] == '*')) e++;
268 if (exclude_list[s] == '*') {
269 /* wildcard at beginning of entry */
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. */
277 } else if (exclude_list[e-1] == '*') {
278 /* wildcard at end of entry */
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. */
285 /* no wildcard at either end, so let's try an absolute
286 * match (ie. a specific IP)
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. */
298 /* no matches in the exclude list, so use the proxy */
302 Socket new_connection(SockAddr addr, char *hostname,
303 int port, int privport,
304 int oobinline, int nodelay, Plug plug)
306 static struct socket_function_table socket_fn_table = {
312 sk_proxy_set_private_ptr,
313 sk_proxy_get_private_ptr,
315 sk_proxy_socket_error
318 static struct plug_function_table plug_fn_table = {
325 if (cfg.proxy_type != PROXY_NONE &&
326 proxy_for_destination(addr, hostname, port))
331 char * proxy_canonical_name;
333 ret = smalloc(sizeof(struct Socket_proxy_tag));
334 ret->fn = &socket_fn_table;
336 ret->remote_addr = addr;
337 ret->remote_port = port;
339 bufchain_init(&ret->pending_input_data);
340 bufchain_init(&ret->pending_output_data);
341 bufchain_init(&ret->pending_oob_output_data);
345 ret->lock_write_oob =
350 ret->lock_accepting = 0;
352 ret->sub_socket = NULL;
353 ret->state = PROXY_STATE_NEW;
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;
362 ret->error = "Network error: Unknown proxy method";
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;
373 proxy_addr = sk_namelookup(cfg.proxy_host,
374 &proxy_canonical_name);
375 sfree(proxy_canonical_name);
377 /* create the actual socket we will be using,
378 * connected to our proxy server and port.
380 ret->sub_socket = sk_new(proxy_addr, cfg.proxy_port,
382 nodelay, (Plug) pplug);
383 if (sk_socket_error(ret->sub_socket) != NULL)
386 sk_addr_free(proxy_addr);
388 /* start the proxy negotiation process... */
389 sk_set_frozen(ret->sub_socket, 0);
390 ret->negotiate(ret, PROXY_CHANGE_NEW);
395 /* no proxy, so just return the direct socket */
396 return sk_new(addr, port, privport, oobinline, nodelay, plug);
399 Socket new_listener(int port, Plug plug, int local_host_only)
401 /* TODO: SOCKS (and potentially others) support inbound
402 * TODO: connections via the proxy. support them.
405 return sk_newlistener(port, plug, local_host_only);
408 /* ----------------------------------------------------------------------
409 * HTTP CONNECT proxy type.
412 static int get_line_end (char * data, int len)
418 if (data[off] == '\n') {
419 /* we have a newline */
422 /* is that the only thing on this line? */
423 if (off <= 2) return off;
425 /* if not, then there is the possibility that this header
426 * continues onto the next line, if it starts with a space
431 data[off+1] != ' ' &&
432 data[off+1] != '\t') return off;
434 /* the line does continue, so we have to keep going
435 * until we see an the header's "real" end of line.
446 int proxy_http_negotiate (Proxy_Socket p, int change)
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
454 char buf[1024], dest[21];
456 sk_getaddr(p->remote_addr, dest, 20);
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));
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.
474 return plug_closing(p->plug, p->closing_error_msg,
475 p->closing_error_code,
476 p->closing_calling_back);
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...
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
494 return plug_accepting(p->plug, p->accepting_sock);
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.
508 int min_ver, maj_ver, status;
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;
515 sscanf((char *)data, "HTTP/%i.%i %i", &maj_ver, &min_ver, &status);
517 /* remove the status line from the input buffer. */
518 bufchain_consume(&p->pending_input_data, eol);
520 /* TODO: we need to support Proxy-Auth headers */
522 if (status < 200 || status > 299) {
524 /* TODO: return a more specific error message,
525 * TODO: based on the status code.
527 plug_closing(p->plug, "Network error: Error while communicating with proxy",
528 PROXY_ERROR_GENERAL, 0);
537 /* get headers. we're done when we get a
538 * header of length 2, (ie. just "\r\n")
541 bufchain_prefix(&p->pending_input_data, &data, &len);
542 eol = get_line_end(data, len);
545 /* TODO: Proxy-Auth stuff. in some cases, we will
546 * TODO: need to extract information from headers.
548 bufchain_consume(&p->pending_input_data, eol);
549 bufchain_prefix(&p->pending_input_data, &data, &len);
550 eol = get_line_end(data, len);
555 bufchain_consume(&p->pending_input_data, 2);
557 /* proxy activate will have dealt with
558 * whatever is left of the buffer */
566 plug_closing(p->plug, "Network error: Unexpected proxy error",
567 PROXY_ERROR_UNEXPECTED, 0);
571 /* ----------------------------------------------------------------------
572 * SOCKS proxy type (as yet unimplemented).
575 int proxy_socks_negotiate (Proxy_Socket p, int change)
577 p->error = "Network error: SOCKS proxy implementation is incomplete";
581 /* ----------------------------------------------------------------------
582 * `Telnet' proxy type (as yet unimplemented).
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.)
590 int proxy_telnet_negotiate (Proxy_Socket p, int change)
592 p->error = "Network error: Telnet proxy implementation is incomplete";