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";