1 /*
2  * Copyright (c) 2007-2009, 2013-2015, 2018-2020 Paul Mattes.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the names of Paul Mattes nor the names of his contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY PAUL MATTES "AS IS" AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL PAUL MATTES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  *	proxy.c
30  *		This module implements various kinds of proxies.
31  */
32 
33 #include "globals.h"
34 
35 #if !defined(_WIN32) /*[*/
36 #include <sys/ioctl.h>
37 #include <netinet/in.h>
38 #include <netdb.h>
39 #include <arpa/inet.h>
40 #endif /*]*/
41 
42 #include "popups.h"
43 #include "proxy.h"
44 #include "proxy_names.h"
45 #include "proxy_private.h"
46 #include "proxy_passthru.h"
47 #include "proxy_http.h"
48 #include "proxy_names.h"
49 #include "proxy_telnet.h"
50 #include "proxy_socks4.h"
51 #include "proxy_socks5.h"
52 #include "task.h"
53 #include "trace.h"
54 #include "utils.h"
55 #include "w3misc.h"
56 
57 #define PROXY_MSEC	(15 * 1000)
58 
59 /* proxy type names -- keep these in sync with proxytype_t! */
60 const char *type_name[PT_MAX] = {
61     "unknown",
62     PROXY_PASSTHRU,
63     PROXY_HTTP,
64     PROXY_TELNET,
65     PROXY_SOCKS4,
66     PROXY_SOCKS4A,
67     PROXY_SOCKS5,
68     PROXY_SOCKS5D
69 };
70 
71 int proxy_ports[PT_MAX] = {
72     0,
73     NPORT_PASSTHRU,
74     NPORT_HTTP,
75     0,
76     NPORT_SOCKS4,
77     NPORT_SOCKS4A,
78     NPORT_SOCKS5,
79     NPORT_SOCKS5D
80 };
81 
82 static bool parse_host_port(char *s, char **puser, char **phost, char **pport);
83 
84 /* Continue functions. */
85 static continue_t *continues[PT_MAX] = {
86     NULL,
87     NULL,
88     proxy_http_continue,
89     NULL,
90     proxy_socks4_continue,
91     proxy_socks4_continue,
92     proxy_socks5_continue,
93     proxy_socks5_continue
94 };
95 
96 /* Close functions. */
97 typedef void close_t(void);
98 static close_t *closes[PT_MAX] = {
99     NULL,
100     NULL,
101     proxy_http_close,
102     NULL,
103     proxy_socks4_close,
104     proxy_socks4_close,
105     proxy_socks5_close,
106     proxy_socks5_close
107 };
108 
109 static proxytype_t proxy_type = PT_NONE;
110 static bool proxy_pending = false;
111 static ioid_t proxy_timeout_id = NULL_IOID;
112 
113 /* Return the name for a given proxy type. */
114 const char *
proxy_type_name(proxytype_t type)115 proxy_type_name(proxytype_t type)
116 {
117     if (type <= PT_NONE || type >= PT_MAX) {
118 	return "unknown";
119     } else {
120 	return type_name[type];
121     }
122 }
123 
124 /* Return whether a proxy type accepts a username. */
125 bool
proxy_takes_username(proxytype_t type)126 proxy_takes_username(proxytype_t type)
127 {
128     switch (type) {
129     case PT_HTTP:
130     case PT_SOCKS4:
131     case PT_SOCKS4A:
132     case PT_SOCKS5:
133     case PT_SOCKS5D:
134 	return true;
135     default:
136 	return false;
137     }
138 }
139 
140 /* Return the default port for a proxy type. */
141 int
proxy_default_port(proxytype_t type)142 proxy_default_port(proxytype_t type)
143 {
144     if (type <= PT_NONE || type >= PT_MAX) {
145 	return 0;
146     } else {
147 	return proxy_ports[type];
148     }
149 }
150 
151 /*
152  * Resolve the type, hostname and port for a proxy.
153  * Returns -1 for failure, 0 for no proxy, >0 (the proxy type) for success.
154  */
155 int
proxy_setup(const char * proxy,char ** puser,char ** phost,char ** pport)156 proxy_setup(const char *proxy, char **puser, char **phost, char **pport)
157 {
158     char *colon;
159     size_t sl;
160 
161     if (proxy == NULL) {
162 	return PT_NONE;
163     }
164 
165     if ((colon = strchr(proxy, ':')) == NULL || (colon == proxy)) {
166 	popup_an_error("Invalid proxy syntax");
167 	return -1;
168     }
169 
170     sl = colon - proxy;
171     if (sl == strlen(PROXY_PASSTHRU) &&
172 	    !strncasecmp(proxy, PROXY_PASSTHRU, sl)) {
173 
174 	if (!parse_host_port(colon + 1, NULL, phost, pport)) {
175 	    return -1;
176 	}
177 	if (*pport == NULL) {
178 	    *pport = NewString(PORT_PASSTHRU);
179 	}
180 	return proxy_type = PT_PASSTHRU;
181     }
182     if (sl == strlen(PROXY_HTTP) && !strncasecmp(proxy, PROXY_HTTP, sl)) {
183 
184 	if (!parse_host_port(colon + 1, puser, phost, pport)) {
185 	    return -1;
186 	}
187 	if (*pport == NULL) {
188 	    *pport = NewString(PORT_HTTP);
189 	}
190 	return proxy_type = PT_HTTP;
191     }
192     if (sl == strlen(PROXY_TELNET) && !strncasecmp(proxy, PROXY_TELNET, sl)) {
193 
194 	if (!parse_host_port(colon + 1, NULL, phost, pport)) {
195 	    return -1;
196 	}
197 	if (*pport == NULL) {
198 	    popup_an_error("Must specify port for telnet proxy");
199 	    return -1;
200 	}
201 	return proxy_type = PT_TELNET;
202     }
203     if (sl == strlen(PROXY_SOCKS4) && !strncasecmp(proxy, PROXY_SOCKS4, sl)) {
204 
205 	if (!parse_host_port(colon + 1, puser, phost, pport)) {
206 	    return -1;
207 	}
208 	if (*pport == NULL) {
209 	    *pport = NewString(PORT_SOCKS4);
210 	}
211 	return proxy_type = PT_SOCKS4;
212     }
213     if (sl == strlen(PROXY_SOCKS4A) &&
214 	    !strncasecmp(proxy, PROXY_SOCKS4A, sl)) {
215 
216 	if (!parse_host_port(colon + 1, puser, phost, pport)) {
217 	    return -1;
218 	}
219 	if (*pport == NULL) {
220 	    *pport = NewString(PORT_SOCKS4A);
221 	}
222 	return proxy_type = PT_SOCKS4A;
223     }
224     if (sl == strlen(PROXY_SOCKS5) && !strncasecmp(proxy, PROXY_SOCKS5, sl)) {
225 
226 	if (!parse_host_port(colon + 1, puser, phost, pport)) {
227 	    return -1;
228 	}
229 	if (*pport == NULL) {
230 	    *pport = NewString(PORT_SOCKS5);
231 	}
232 	return proxy_type = PT_SOCKS5;
233     }
234     if (sl == strlen(PROXY_SOCKS5D) &&
235 	    !strncasecmp(proxy, PROXY_SOCKS5D, sl)) {
236 
237 	if (!parse_host_port(colon + 1, puser, phost, pport)) {
238 	    return -1;
239 	}
240 	if (*pport == NULL) {
241 	    *pport = NewString(PORT_SOCKS5D);
242 	}
243 	return proxy_type = PT_SOCKS5D;
244     }
245     popup_an_error("Invalid proxy type '%.*s'", (int)sl, proxy);
246     return -1;
247 }
248 
249 /*
250  * Parse [user:password@]host[:port] from a string.
251  * 'host' can be in square brackets to allow numeric IPv6 addresses.
252  * Returns the host name and port name in heap memory.
253  * Returns false for failure, true for success.
254  */
255 static bool
parse_host_port(char * s,char ** puser,char ** phost,char ** pport)256 parse_host_port(char *s, char **puser, char **phost, char **pport)
257 {
258     char *at;
259     char *h;
260     char *colon;
261     char *hstart;
262     size_t hlen;
263 
264     /* Check for 'username:password@' first. */
265     if ((at = strchr(s, '@')) != NULL) {
266 	if (puser == NULL) {
267 	    popup_an_error("Proxy type does not support username");
268 	    return false;
269 	}
270 	if (at == s) {
271 	    popup_an_error("Invalid proxy username syntax");
272 	    return false;
273 	}
274 	h = at + 1;
275     } else {
276 	h = s;
277     }
278 
279     if (*h == '[') {
280 	char *rbrack;
281 
282 	/* Hostname in square brackets. */
283 	hstart = h + 1;
284 	rbrack = strchr(h, ']');
285 	if (rbrack == NULL || rbrack == h + 1 ||
286 		(*(rbrack + 1) != '\0' && *(rbrack + 1) != ':')) {
287 	    popup_an_error("Invalid proxy hostname syntax");
288 	    return false;
289 	}
290 	if (*(rbrack + 1) == ':') {
291 	    colon = rbrack + 1;
292 	} else {
293 	    colon = NULL;
294 	}
295 	hlen = rbrack - (h + 1);
296     } else {
297 	hstart = h;
298 	colon = strchr(h, ':');
299 	if (colon == h) {
300 	    popup_an_error("Invalid proxy hostname syntax");
301 	    return false;
302 	}
303 	if (colon == NULL) {
304 	    hlen = strlen(h);
305 	} else {
306 	    hlen = colon - h;
307 	}
308     }
309 
310     /* Parse the port. */
311     if (colon == NULL || !*(colon + 1)) {
312 	*pport = NULL;
313     } else {
314 	*pport = NewString(colon + 1);
315     }
316 
317     /* Copy out the hostname. */
318     *phost = Malloc(hlen + 1);
319     strncpy(*phost, hstart, hlen);
320     (*phost)[hlen] = '\0';
321 
322     /* Copy out the username. */
323     if (puser != NULL) {
324 	if (at != NULL) {
325 	    *puser = Malloc((at - s) + 1);
326 	    strncpy(*puser, s, at - s);
327 	    (*puser)[at - s] = '\0';
328 	} else {
329 	    *puser = NULL;
330 	}
331     } else if (at != NULL) {
332 	popup_an_error("Invalid proxy hostname syntax (user@ not supported for this type");
333 	return false;
334     }
335 
336     return true;
337 }
338 
339 /*
340  * Proxy negotiation timed out.
341  */
342 static void
proxy_timeout(ioid_t id _is_unused)343 proxy_timeout(ioid_t id _is_unused)
344 {
345     proxy_timeout_id = NULL_IOID;
346     connect_error("%s proxy timed out", type_name[proxy_type]);
347 }
348 
349 /*
350  * Negotiate with the proxy server.
351  */
352 proxy_negotiate_ret_t
proxy_negotiate(socket_t fd,const char * user,const char * host,unsigned short port,bool blocking)353 proxy_negotiate(socket_t fd, const char *user, const char *host,
354 	unsigned short port, bool blocking)
355 {
356     proxy_negotiate_ret_t ret;
357 
358     if (proxy_timeout_id != NULL_IOID) {
359 	RemoveTimeOut(proxy_timeout_id);
360 	proxy_timeout_id = NULL_IOID;
361     }
362 
363     switch (proxy_type) {
364     case PT_NONE:
365 	ret = PX_SUCCESS;
366 	break;
367     case PT_PASSTHRU:
368 	ret = proxy_passthru(fd, host, port);
369 	break;
370     case PT_HTTP:
371 	ret = proxy_http(fd, user, host, port);
372 	break;
373     case PT_TELNET:
374 	ret = proxy_telnet(fd, host, port);
375 	break;
376     case PT_SOCKS4:
377 	ret = proxy_socks4(fd, user, host, port, false);
378 	break;
379     case PT_SOCKS4A:
380 	ret = proxy_socks4(fd, user, host, port, true);
381 	break;
382     case PT_SOCKS5:
383 	ret = proxy_socks5(fd, user, host, port, false);
384 	break;
385     case PT_SOCKS5D:
386 	ret = proxy_socks5(fd, user, host, port, true);
387 	break;
388     default:
389 	ret = PX_FAILURE;
390 	break;
391     }
392 
393     proxy_pending = (ret == PX_WANTMORE);
394     if (proxy_pending) {
395 	if (blocking) {
396 	    do {
397 		fd_set rfds;
398 		struct timeval tv;
399 
400 		/* Wait for more input. */
401 		FD_ZERO(&rfds);
402 		FD_SET(fd, &rfds);
403 		tv.tv_sec = PROXY_MSEC / 1000;
404 		tv.tv_usec = (PROXY_MSEC % 1000) * 10;
405 		if (select((int)(fd + 1), &rfds, NULL, NULL, &tv) <= 0) {
406 		    popup_an_error("%s proxy timeout", type_name[proxy_type]);
407 		    return PX_FAILURE;
408 		}
409 
410 		ret = proxy_continue();
411 	    } while (ret == PX_WANTMORE);
412 	} else {
413 	    /* Set a timeout in case the input never arrives. */
414 	    proxy_timeout_id = AddTimeOut(PROXY_MSEC, proxy_timeout);
415 	}
416     }
417 
418     if (ret == PX_SUCCESS) {
419 	proxy_close();
420     }
421     return ret;
422 }
423 
424 /*
425  * Continue proxy negotiation.
426  */
427 proxy_negotiate_ret_t
proxy_continue(void)428 proxy_continue(void)
429 {
430     proxy_negotiate_ret_t ret;
431 
432     if (proxy_type <= 0 ||
433 	    proxy_type >= PT_MAX ||
434 	    continues[proxy_type] == NULL ||
435 	    !proxy_pending) {
436 	popup_an_error("proxy_continue: wrong state");
437 	return PX_FAILURE;
438     }
439 
440     ret = (*continues[proxy_type])();
441     if (ret == PX_SUCCESS) {
442 	proxy_close();
443     }
444     return ret;
445 }
446 
447 /*
448  * Clean up pending proxy state.
449  */
450 void
proxy_close(void)451 proxy_close(void)
452 {
453     if (proxy_type > 0 &&
454 	    proxy_type < PT_MAX &&
455 	    closes[proxy_type] != NULL) {
456 	(*closes[proxy_type])();
457     }
458     proxy_type = PT_NONE;
459     proxy_pending = false;
460     if (proxy_timeout_id != NULL_IOID) {
461 	RemoveTimeOut(proxy_timeout_id);
462 	proxy_timeout_id = NULL_IOID;
463     }
464 }
465