diff options
author | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2005-12-12 19:46:10 +0000 |
---|---|---|
committer | james <james@e7ae566f-a301-0410-adde-c780ea21d3b5> | 2005-12-12 19:46:10 +0000 |
commit | f214bb2115994cc6b4123f3d06db0452f17f2e99 (patch) | |
tree | 7a09094e169f4097ceb033204c171eba2a51ae59 | |
parent | First attempt at automatic proxy detection, (diff) | |
download | openvpn-f214bb2115994cc6b4123f3d06db0452f17f2e99.tar.xz |
Added --auto-proxy directive to auto-detect HTTP or SOCKS
proxy settings (currently Windows only).
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@850 e7ae566f-a301-0410-adde-c780ea21d3b5
-rw-r--r-- | ChangeLog | 7 | ||||
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | base64.c | 26 | ||||
-rw-r--r-- | base64.h | 4 | ||||
-rw-r--r-- | buffer.c | 12 | ||||
-rw-r--r-- | buffer.h | 3 | ||||
-rw-r--r-- | init.c | 14 | ||||
-rwxr-xr-x | makefile.w32 | 2 | ||||
-rw-r--r-- | openvpn.8 | 30 | ||||
-rw-r--r-- | options.c | 146 | ||||
-rw-r--r-- | options.h | 4 | ||||
-rw-r--r-- | proxy.c | 388 | ||||
-rw-r--r-- | proxy.h | 37 | ||||
-rw-r--r-- | socket.c | 81 | ||||
-rw-r--r-- | socks.c | 15 | ||||
-rw-r--r-- | socks.h | 1 | ||||
-rw-r--r-- | syshead.h | 8 |
17 files changed, 539 insertions, 242 deletions
@@ -3,7 +3,7 @@ Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@openvpn.net> $Id$ -2005.11.xx -- Version 2.1-beta8 +2005.12.xx -- Version 2.1-beta8 * --remap-usr1 will now also remap signals thrown during initialization. @@ -17,6 +17,8 @@ $Id$ * Fixed typo in manage.c where inline function declaration was declared without the "static" keyword (David Stipp). * Patch to support --topology subnet on Mac OS X (Mathias Sundman). +* Added --auto-proxy directive to auto-detect HTTP or SOCKS + proxy settings (currently Windows only). 2005.11.12 -- Version 2.1-beta7 @@ -29,7 +31,8 @@ $Id$ but actually would only accept /29 or less. * Extend byte counters to 64 bits (M. van Cuijk). * PKCS#11 fixes (Alon Bar-Lev). - +* Removed redundant base64 code. + 2005.11.02 -- Version 2.0.5 * Fixed bug in Linux get_default_gateway function diff --git a/Makefile.am b/Makefile.am index ebfa22d..424b167 100644 --- a/Makefile.am +++ b/Makefile.am @@ -127,8 +127,7 @@ EXTRA_DIST = \ plugin \ management \ pkcs11-headers \ - cryptoki-win32.h \ - ieproxy.c ieproxy.h + cryptoki-win32.h dist-hook: cd $(distdir) && for i in $(EXTRA_DIST) ; do find $$i -name .svn -type d -prune -exec rm -rf '{}' ';' ; rm -f `find $$i -type f | grep -E '(^|\/)\.?\#|\~$$|\.s?o$$'` ; done @@ -39,7 +39,7 @@ #include "syshead.h" -#if NTLM +#ifdef ENABLE_HTTP_PROXY #include "base64.h" @@ -48,16 +48,6 @@ static char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -static int -pos(char c) -{ - char *p; - for (p = base64_chars; *p; p++) - if (*p == c) - return p - base64_chars; - return -1; -} - int base64_encode(const void *data, int size, char **str) { @@ -96,6 +86,18 @@ base64_encode(const void *data, int size, char **str) return strlen(s); } +#if NTLM + +static int +pos(char c) +{ + char *p; + for (p = base64_chars; *p; p++) + if (*p == c) + return p - base64_chars; + return -1; +} + #define DECODE_ERROR 0xffffffff static unsigned int @@ -141,6 +143,8 @@ base64_decode(const char *str, void *data) return q - (unsigned char *) data; } +#endif /* NTLM */ + #else static void dummy(void) {} #endif @@ -31,12 +31,10 @@ * SUCH DAMAGE. */ -/* $KTH: base64.h,v 1.2 1999/12/02 16:58:45 joda Exp $ */ - #ifndef _BASE64_H_ #define _BASE64_H_ -#if NTLM +#ifdef ENABLE_HTTP_PROXY int base64_encode(const void *data, int size, char **str); int base64_decode(const char *str, void *data); @@ -621,6 +621,18 @@ buf_parse (struct buffer *buf, const int delim, char *line, const int size) } /* + * Print a string which might be NULL + */ +const char * +np (const char *str) +{ + if (str) + return str; + else + return "[NULL]"; +} + +/* * Classify and mutate strings based on character types. */ @@ -553,8 +553,9 @@ xor (uint8_t *dest, const uint8_t *src, int len) } /* - * Classify and mutate strings based on character types. + * Print a string which might be NULL */ +const char *np (const char *str); /*#define CHARACTER_CLASS_DEBUG*/ @@ -102,6 +102,12 @@ init_remote_list (struct context *c) void context_init_1 (struct context *c) { +#ifdef ENABLE_HTTP_PROXY + bool did_http = false; +#else + const bool did_http = false; +#endif + context_clear_1 (c); packet_id_persist_init (&c->c1.pid_persist); @@ -145,20 +151,24 @@ context_init_1 (struct context *c) #endif #ifdef ENABLE_HTTP_PROXY - if (c->options.http_proxy_options) + if (c->options.http_proxy_options || c->options.auto_proxy_info) { /* Possible HTTP proxy user/pass input */ c->c1.http_proxy = new_http_proxy (c->options.http_proxy_options, + c->options.auto_proxy_info, &c->gc); + if (c->c1.http_proxy) + did_http = true; } #endif #ifdef ENABLE_SOCKS - if (c->options.socks_proxy_server) + if (!did_http && (c->options.socks_proxy_server || c->options.auto_proxy_info)) { c->c1.socks_proxy = new_socks_proxy (c->options.socks_proxy_server, c->options.socks_proxy_port, c->options.socks_proxy_retry, + c->options.auto_proxy_info, &c->gc); } #endif diff --git a/makefile.w32 b/makefile.w32 index c612512..22fbc28 100755 --- a/makefile.w32 +++ b/makefile.w32 @@ -78,7 +78,6 @@ HEADERS = \ fragment.h \ gremlin.h \ helper.h \ - ieproxy.h \ init.h \ integer.h \ interval.h \ @@ -137,7 +136,6 @@ OBJS = base64.o \ fragment.o \ gremlin.o \ helper.o \ - ieproxy.o \ init.o \ interval.o \ list.o \ @@ -101,6 +101,7 @@ openvpn \- secure IP tunnel daemon. [\ \fB\-\-auth\-user\-pass\-verify\fR\ \fIscript\fR\ ] [\ \fB\-\-auth\-user\-pass\fR\ \fIup\fR\ ] [\ \fB\-\-auth\fR\ \fIalg\fR\ ] +[\ \fB\-\-auto\-proxy\fR\ ] [\ \fB\-\-bcast\-buffers\fR\ \fIn\fR\ ] [\ \fB\-\-ca\fR\ \fIfile\fR\ ] [\ \fB\-\-ccd\-exclusive\fR\ ] @@ -597,7 +598,19 @@ as the number of retries of connection attempt (default=infinite). .\"********************************************************* .TP -.B --http-proxy server port [authfile] [auth-method] +.B --auto-proxy +Try to sense HTTP or SOCKS proxy settings automatically. +If no settings are present, a direct connection will be attempted. +If both HTTP and SOCKS settings are present, HTTP will be preferred. +If the HTTP proxy server requires a password, it will be queried from +stdin or the management interface. If the underlying OS doesn't support an API for +returning proxy settings, a direct connection will be attempted. +Currently, only Windows clients support this option via the +InternetQueryOption API. +This option exists in OpenVPN 2.1 or higher. +.\"********************************************************* +.TP +.B --http-proxy server port [authfile|'auto'] [auth-method] Connect to remote host through an HTTP proxy at address .B server and port @@ -608,7 +621,15 @@ is a file containing a username and password on 2 lines, or "stdin" to prompt from console. .B auth-method -should be one of "none", "basic", or "ntlm". +should be one of "none", "basic", or "ntlm". + +The +.B auto +flag causes OpenVPN to automatically determine the +.B auth-method +and query stdin or the management interface for +username/password credentials, if required. This flag +exists on OpenVPN 2.1 or higher. .\"********************************************************* .TP .B --http-proxy-retry @@ -857,6 +878,8 @@ of the TAP-Win32 driver. When used on *nix, requires that the tun driver supports an .BR ifconfig (8) command which sets a subnet instead of a remote endpoint IP address. + +This option exists in OpenVPN 2.1 or higher. .\"********************************************************* .TP .B --tun-ipv6 @@ -1175,8 +1198,7 @@ bypasses the tunnel (Available on Windows clients, may not be available on non-Windows clients). -Using the def1 flag is highly recommended, and is currently -planned to become the default by OpenVPN 2.1. +Using the def1 flag is highly recommended. .\"********************************************************* .TP .B --link-mtu n @@ -97,12 +97,18 @@ static const char usage_message[] = " between connection retries (default=%d).\n" "--connect-timeout n : For --proto tcp-client, connection timeout (in seconds).\n" "--connect-retry-max n : Maximum connection attempt retries, default infinite.\n" +#ifdef GENERAL_PROXY_SUPPORT + "--auto-proxy : Try to sense proxy settings (or lack thereof) automatically.\n" +#endif #ifdef ENABLE_HTTP_PROXY - "--http-proxy s p [up] [auth] : Connect to remote host through an HTTP proxy at\n" - " address s and port p. If proxy authentication is required,\n" + "--http-proxy s p [up] [auth] : Connect to remote host\n" + " through an HTTP proxy at address s and port p.\n" + " If proxy authentication is required,\n" " up is a file containing username/password on 2 lines, or\n" " 'stdin' to prompt from console. Add auth='ntlm' if\n" " the proxy requires NTLM authentication.\n" + "--http-proxy s p 'auto': Like the above directive, but automatically determine\n" + " auth method and query for username/password if needed.\n" "--http-proxy-retry : Retry indefinitely on HTTP proxy errors.\n" "--http-proxy-timeout n : Proxy timeout in seconds, default=5.\n" "--http-proxy-option type [parm] : Set extended HTTP proxy options.\n" @@ -1537,8 +1543,8 @@ options_postprocess (struct options *options, bool first_time) msg (M_USAGE, "--remote MUST be used in TCP Client mode"); #ifdef ENABLE_HTTP_PROXY - if (options->http_proxy_options && options->proto != PROTO_TCPv4_CLIENT) - msg (M_USAGE, "--http-proxy MUST be used in TCP Client mode (i.e. --proto tcp-client)"); + if ((options->http_proxy_options || options->auto_proxy_info) && options->proto != PROTO_TCPv4_CLIENT) + msg (M_USAGE, "--http-proxy or --auto-proxy MUST be used in TCP Client mode (i.e. --proto tcp-client)"); #endif #if defined(ENABLE_HTTP_PROXY) && defined(ENABLE_SOCKS) @@ -3675,67 +3681,78 @@ add_option (struct options *options, } options->proto = proto; } -#ifdef ENABLE_HTTP_PROXY - else if (streq (p[0], "http-proxy") && p[1]) +#ifdef GENERAL_PROXY_SUPPORT + else if (streq (p[0], "auto-proxy")) { - struct http_proxy_options *ho; + char *error = NULL; VERIFY_PERMISSION (OPT_P_GENERAL); + options->auto_proxy_info = get_proxy_settings (&error, &options->gc); + if (error) + msg (M_WARN, "PROXY: %s", error); + } + else if (streq (p[0], "show-proxy-settings")) + { + struct auto_proxy_info *pi; + char *error = NULL; - if (streq (p[1], "auto")) + VERIFY_PERMISSION (OPT_P_GENERAL); + pi = get_proxy_settings (&error, &options->gc); + if (pi) { - struct http_proxy_options hpo; - bool status; - char *error = NULL; - - p[4] = p[3]; - p[3] = p[2]; - p[1] = p[2] = NULL; - CLEAR (hpo); - - status = get_http_proxy_settings (&hpo, &error, &options->gc); - if (status) - { - ho = init_http_options_if_undefined (options); - ho->server = hpo.server; - ho->port = hpo.port; - } - else - { - if (error) - msg (M_WARN, "http-proxy auto error: %s", error); - goto err; - } + msg (M_INFO|M_NOPREFIX, "HTTP Server: %s", np(pi->http.server)); + msg (M_INFO|M_NOPREFIX, "HTTP Port: %d", pi->http.port); + msg (M_INFO|M_NOPREFIX, "SOCKS Server: %s", np(pi->socks.server)); + msg (M_INFO|M_NOPREFIX, "SOCKS Port: %d", pi->socks.port); } - else - { - int port; - if (!p[2]) - { - msg (msglevel, "http-proxy port number not defined"); - goto err; - } - port = atoi (p[2]); - if (!legal_ipv4_port (port)) - { - msg (msglevel, "Bad http-proxy port number: %s", p[2]); - goto err; - } + if (error) + msg (msglevel, "Proxy error: %s", error); +#ifdef WIN32 + show_win_proxy_settings (M_INFO|M_NOPREFIX); +#endif + openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ + } +#endif /* GENERAL_PROXY_SUPPORT */ +#ifdef ENABLE_HTTP_PROXY + else if (streq (p[0], "http-proxy") && p[1]) + { + struct http_proxy_options *ho; - ho = init_http_options_if_undefined (options); + VERIFY_PERMISSION (OPT_P_GENERAL); - ho->server = p[1]; - ho->port = port; - } + { + int port; + if (!p[2]) + { + msg (msglevel, "http-proxy port number not defined"); + goto err; + } + port = atoi (p[2]); + if (!legal_ipv4_port (port)) + { + msg (msglevel, "Bad http-proxy port number: %s", p[2]); + goto err; + } + + ho = init_http_options_if_undefined (options); + + ho->server = p[1]; + ho->port = port; + } if (p[3]) { - ho->auth_method_string = "basic"; - ho->auth_file = p[3]; - - if (p[4]) + if (streq (p[3], "auto")) + ho->auth_retry = true; + else { - ho->auth_method_string = p[4]; + ho->auth_method_string = "basic"; + ho->auth_file = p[3]; + + if (p[4]) + { + ho->auth_method_string = p[4]; + } } } else @@ -3778,29 +3795,6 @@ add_option (struct options *options, msg (msglevel, "Bad http-proxy-option or missing parameter: '%s'", p[1]); } } - else if (streq (p[0], "show-http-proxy-settings")) - { - struct http_proxy_options po; - bool status; - char *error = NULL; - - VERIFY_PERMISSION (OPT_P_GENERAL); - CLEAR (po); - status = get_http_proxy_settings (&po, &error, &options->gc); - if (status) - { - msg (M_INFO|M_NOPREFIX, "Server: %s", po.server); - msg (M_INFO|M_NOPREFIX, "Port: %d", po.port); - } - else - { - if (error) - msg (msglevel, "Proxy error: %s", error); - else - msg (msglevel, "Proxy settings are undefined"); - } - openvpn_exit (OPENVPN_EXIT_STATUS_GOOD); /* exit point */ - } #endif #ifdef ENABLE_SOCKS else if (streq (p[0], "socks-proxy") && p[1]) @@ -250,6 +250,10 @@ struct options struct route_option_list *routes; bool route_nopull; +#ifdef GENERAL_PROXY_SUPPORT + struct auto_proxy_info *auto_proxy_info; +#endif + #ifdef ENABLE_HTTP_PROXY struct http_proxy_options *http_proxy_options; #endif @@ -28,8 +28,6 @@ #include "config.h" #endif -#ifdef ENABLE_HTTP_PROXY - #include "syshead.h" #include "common.h" @@ -38,6 +36,7 @@ #include "socket.h" #include "fdmisc.h" #include "proxy.h" +#include "base64.h" #include "ntlm.h" #ifdef WIN32 @@ -46,55 +45,7 @@ #include "memdbg.h" -#ifdef WIN32 - -bool -get_http_proxy_settings (struct http_proxy_options *p, char **err, struct gc_arena *gc) -{ - bool ret = false; - const char *result; - - p->server = NULL; - p->port = 0; - getIeHttpProxyError = NULL; - if (err) - *err = NULL; - - result = getIeHttpProxy (); - if (result) - { - char addr_str[128]; - char port_str[16]; - struct buffer in; - buf_set_read (&in, (const uint8_t *)result, strlen (result)); - if (buf_parse (&in, ':', addr_str, sizeof (addr_str)) - && buf_parse (&in, ':', port_str, sizeof (port_str))) - { - p->server = string_alloc (addr_str, gc); - p->port = atoi (port_str); - ret = true; - } - free ((void *)result); - } - else if (getIeHttpProxyError) - { - if (err) - *err = string_alloc (getIeHttpProxyError, gc); - } - return ret; -} - -#else - -bool -get_http_proxy_settings (struct http_proxy_options *p, char **err, struct gc_arena *gc) -{ - if (err) - *err = string_alloc ("HTTP proxy detection not supported on this OS", gc); - return false; -} - -#endif +#ifdef ENABLE_HTTP_PROXY /* cached proxy username/password */ static struct user_pass static_proxy_user_pass; @@ -246,42 +197,12 @@ send_crlf (socket_descriptor_t sd) uint8_t * make_base64_string2 (const uint8_t *str, int src_len, struct gc_arena *gc) { - static const char base64_table[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - uint8_t *buf; - const uint8_t *src; - uint8_t *dst; - int bits, data, dst_len; - - /* make base64 string */ - dst_len = (src_len + 2) / 3 * 4; - buf = gc_malloc (dst_len + 1, false, gc); - bits = data = 0; - src = str; - dst = buf; - while (dst_len--) - { - if (bits < 6) - { - data = (data << 8) | *src; - bits += 8; - src++; - } - *dst++ = base64_table[0x3F & (data >> (bits - 6))]; - bits -= 6; - } - *dst = '\0'; - - /* fix-up tail padding */ - switch (src_len % 3) - { - case 1: - *--dst = '='; - case 2: - *--dst = '='; - } - return buf; + uint8_t *ret = NULL; + char *b64out = NULL; + ASSERT (base64_encode ((const void *)str, src_len, &b64out) >= 0); + ret = (uint8_t *) string_alloc (b64out, gc); + free (b64out); + return ret; } uint8_t * @@ -300,18 +221,67 @@ username_password_as_base64 (const struct http_proxy_info *p, return (const char *)make_base64_string ((const uint8_t*)BSTR (&out), gc); } +static void +get_user_pass_http (struct http_proxy_info *p, const bool force) +{ + if (!static_proxy_user_pass.defined || force) + { + get_user_pass (&static_proxy_user_pass, + p->options.auth_file, + "HTTP Proxy", + GET_USER_PASS_MANAGEMENT); + p->up = static_proxy_user_pass; + } +} + struct http_proxy_info * new_http_proxy (const struct http_proxy_options *o, + struct auto_proxy_info *auto_proxy_info, struct gc_arena *gc) { struct http_proxy_info *p; - ALLOC_OBJ_CLEAR_GC (p, struct http_proxy_info, gc); + struct http_proxy_options opt; + + if (auto_proxy_info) + { + if (o && o->server) + { + /* if --http-proxy explicitly given, disable auto-proxy */ + auto_proxy_info = NULL; + } + else + { + /* if no --http-proxy explicitly given and no auto settings, fail */ + if (!auto_proxy_info->http.server) + return NULL; - if (!o->server) + if (o) + { + opt = *o; + } + else + { + CLEAR (opt); + + /* These settings are only used for --auto-proxy */ + opt.timeout = 5; + opt.http_version = "1.0"; + } + + opt.server = auto_proxy_info->http.server; + opt.port = auto_proxy_info->http.port; + opt.auth_retry = true; + + o = &opt; + } + } + + if (!o || !o->server) msg (M_FATAL, "HTTP_PROXY: server not specified"); ASSERT (legal_ipv4_port (o->port)); + ALLOC_OBJ_CLEAR_GC (p, struct http_proxy_info, gc); p->options = *o; /* parse authentication method */ @@ -332,11 +302,7 @@ new_http_proxy (const struct http_proxy_options *o, /* only basic and NTLM authentication supported so far */ if (p->auth_method == HTTP_AUTH_BASIC || p->auth_method == HTTP_AUTH_NTLM) { - get_user_pass (&static_proxy_user_pass, - o->auth_file, - "HTTP Proxy", - GET_USER_PASS_MANAGEMENT); - p->up = static_proxy_user_pass; + get_user_pass_http (p, true); } #if !NTLM @@ -348,7 +314,7 @@ new_http_proxy (const struct http_proxy_options *o, return p; } -void +bool establish_http_proxy_passthru (struct http_proxy_info *p, socket_descriptor_t sd, /* already open to proxy */ const char *host, /* openvpn server remote */ @@ -362,6 +328,12 @@ establish_http_proxy_passthru (struct http_proxy_info *p, char get[80]; int status; int nparms; + bool ret = false; + + /* get user/pass if not previously given or if --auto-proxy is being used */ + if (p->auth_method == HTTP_AUTH_BASIC + || p->auth_method == HTTP_AUTH_NTLM) + get_user_pass_http (p, false); /* format HTTP CONNECT message */ openvpn_snprintf (buf, sizeof(buf), "CONNECT %s:%d HTTP/%s", @@ -519,7 +491,21 @@ establish_http_proxy_passthru (struct http_proxy_info *p, ASSERT (0); /* No NTLM support */ #endif } - else goto error; + else if (p->auth_method == HTTP_AUTH_NONE && p->options.auth_retry) + { + /* + * Proxy needs authentication, but we don't have a user/pass. + * Now we will change p->auth_method and return true so that + * our caller knows to call us again on a newly opened socket. + * JYFIXME: This code needs to check proxy error output and set + * JYFIXME: p->auth_method = HTTP_AUTH_NTLM if necessary. + */ + p->auth_method = HTTP_AUTH_BASIC; + ret = true; + goto done; + } + else + goto error; } @@ -527,7 +513,7 @@ establish_http_proxy_passthru (struct http_proxy_info *p, if (nparms < 1 || status != 200) { msg (D_LINK_ERRORS, "HTTP proxy returned bad status"); -#if 0 +#if 0 /* DEBUGGING -- show a multi-line HTTP error response */ while (true) { @@ -556,17 +542,221 @@ establish_http_proxy_passthru (struct http_proxy_info *p, msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0)); #endif + done: gc_free (&gc); - return; + return ret; error: /* on error, should we exit or restart? */ if (!*signal_received) *signal_received = (p->options.retry ? SIGUSR1 : SIGTERM); /* SOFT-SIGUSR1 -- HTTP proxy error */ gc_free (&gc); - return; + return ret; } #else static void dummy(void) {} #endif /* ENABLE_HTTP_PROXY */ + +#ifdef GENERAL_PROXY_SUPPORT + +#ifdef WIN32 + +#if 0 +char * +get_windows_internet_string (const DWORD dwOption, struct gc_arena *gc) +{ + DWORD size = 0; + char *ret = NULL; + + /* Initially, get size of return buffer */ + InternetQueryOption (NULL, dwOption, NULL, &size); + if (size) + { + /* Now get actual info */ + ret = (INTERNET_PROXY_INFO *) gc_malloc (size, false, gc); + if (!InternetQueryOption (NULL, dwOption, (LPVOID) ret, &size)) + ret = NULL; + } + return ret; +} +#endif + +static INTERNET_PROXY_INFO * +get_windows_proxy_settings (struct gc_arena *gc) +{ + DWORD size = 0; + INTERNET_PROXY_INFO *ret = NULL; + + /* Initially, get size of return buffer */ + InternetQueryOption (NULL, INTERNET_OPTION_PROXY, NULL, &size); + if (size) + { + /* Now get actual info */ + ret = (INTERNET_PROXY_INFO *) gc_malloc (size, false, gc); + if (!InternetQueryOption (NULL, INTERNET_OPTION_PROXY, (LPVOID) ret, &size)) + ret = NULL; + } + return ret; +} + +static const char * +parse_windows_proxy_setting (const char *str, struct auto_proxy_info_entry *e, struct gc_arena *gc) +{ + char buf[128]; + const char *ret = NULL; + struct buffer in; + + CLEAR (*e); + + buf_set_read (&in, (const uint8_t *)str, strlen (str)); + + if (strchr (str, '=') != NULL) + { + if (buf_parse (&in, '=', buf, sizeof (buf))) + ret = string_alloc (buf, gc); + } + + if (buf_parse (&in, ':', buf, sizeof (buf))) + e->server = string_alloc (buf, gc); + + if (e->server && buf_parse (&in, '\0', buf, sizeof (buf))) + e->port = atoi (buf); + + return ret; +} + +static void +parse_windows_proxy_setting_list (const char *str, const char *type, struct auto_proxy_info_entry *e, struct gc_arena *gc) +{ + struct gc_arena gc_local = gc_new (); + struct auto_proxy_info_entry el; + + CLEAR (*e); + if (type) + { + char buf[128]; + struct buffer in; + + buf_set_read (&in, (const uint8_t *)str, strlen (str)); + if (strchr (str, '=') != NULL) + { + while (buf_parse (&in, ' ', buf, sizeof (buf))) + { + const char *t = parse_windows_proxy_setting (buf, &el, &gc_local); + if (t && !strcmp (t, type)) + goto found; + } + } + } + else + { + if (!parse_windows_proxy_setting (str, &el, &gc_local)) + goto found; + } + goto done; + + found: + if (el.server && el.port > 0) + { + e->server = string_alloc (el.server, gc); + e->port = el.port; + } + + done: + gc_free (&gc_local); +} + +static const char * +win_proxy_access_type (const DWORD dwAccessType) +{ + switch (dwAccessType) + { + case INTERNET_OPEN_TYPE_DIRECT: + return "INTERNET_OPEN_TYPE_DIRECT"; + case INTERNET_OPEN_TYPE_PROXY: + return "INTERNET_OPEN_TYPE_PROXY"; + default: + return "[UNKNOWN]"; + } +} + +void +show_win_proxy_settings (const int msglevel) +{ + INTERNET_PROXY_INFO *info; + struct gc_arena gc = gc_new (); + + info = get_windows_proxy_settings (&gc); + msg (msglevel, "PROXY INFO: %s %s", + win_proxy_access_type (info->dwAccessType), + info->lpszProxy ? info->lpszProxy : "[NULL]"); + + gc_free (&gc); +} + +struct auto_proxy_info * +get_proxy_settings (char **err, struct gc_arena *gc) +{ + struct gc_arena gc_local = gc_new (); + INTERNET_PROXY_INFO *info; + struct auto_proxy_info *pi; + + ALLOC_OBJ_CLEAR_GC (pi, struct auto_proxy_info, gc); + + if (err) + *err = NULL; + + info = get_windows_proxy_settings (&gc_local); + + if (!info) + { + if (err) + *err = "PROXY: failed to obtain windows proxy info"; + goto done; + } + + switch (info->dwAccessType) + { + case INTERNET_OPEN_TYPE_DIRECT: + break; + case INTERNET_OPEN_TYPE_PROXY: + if (!info->lpszProxy) + break; + parse_windows_proxy_setting_list (info->lpszProxy, NULL, &pi->http, gc); + if (!pi->http.server) + parse_windows_proxy_setting_list (info->lpszProxy, "http", &pi->http, gc); + parse_windows_proxy_setting_list (info->lpszProxy, "socks", &pi->socks, gc); + break; + default: + if (err) + *err = "PROXY: unknown proxy type"; + break; + } + + done: + gc_free (&gc_local); + return pi; +} + +#else + +struct auto_proxy_info * +get_proxy_settings (char **err, struct gc_arena *gc) +{ +#if 1 + if (err) + *err = string_alloc ("PROXY: automatic detection not supported on this OS", gc); + return NULL; +#else /* JYFIXME, test --auto-proxy feature */ + struct auto_proxy_info *pi; + ALLOC_OBJ_CLEAR_GC (pi, struct auto_proxy_info, gc); + pi->http.server = "10.10.0.2"; + pi->http.port = 4000; + return pi; +#endif +} + +#endif + +#endif /* GENERAL_PROXY_SUPPORT */ @@ -25,11 +25,35 @@ #ifndef PROXY_H #define PROXY_H -#ifdef ENABLE_HTTP_PROXY - #include "buffer.h" #include "misc.h" +#ifdef GENERAL_PROXY_SUPPORT + +/* + * Return value for get_proxy_settings to automatically + * determine proxy information. + */ +struct auto_proxy_info_entry { + char *server; + int port; +}; + +struct auto_proxy_info { + struct auto_proxy_info_entry http; + struct auto_proxy_info_entry socks; +}; + +struct auto_proxy_info *get_proxy_settings (char **err, struct gc_arena *gc); + +#ifdef WIN32 +void show_win_proxy_settings (const int msglevel); +#endif /* WIN32 */ + +#endif /* GENERAL_PROXY_SUPPORT */ + +#ifdef ENABLE_HTTP_PROXY + /* HTTP CONNECT authentication methods */ #define HTTP_AUTH_NONE 0 #define HTTP_AUTH_BASIC 1 @@ -41,6 +65,7 @@ struct http_proxy_options { int port; bool retry; int timeout; + bool auth_retry; const char *auth_method_string; const char *auth_file; const char *http_version; @@ -55,9 +80,10 @@ struct http_proxy_info { }; struct http_proxy_info *new_http_proxy (const struct http_proxy_options *o, + struct auto_proxy_info *auto_proxy_info, struct gc_arena *gc); -void establish_http_proxy_passthru (struct http_proxy_info *p, +bool establish_http_proxy_passthru (struct http_proxy_info *p, socket_descriptor_t sd, /* already open to proxy */ const char *host, /* openvpn server remote */ const int port, /* openvpn server port */ @@ -67,7 +93,6 @@ void establish_http_proxy_passthru (struct http_proxy_info *p, uint8_t *make_base64_string2 (const uint8_t *str, int str_len, struct gc_arena *gc); uint8_t *make_base64_string (const uint8_t *str, struct gc_arena *gc); -bool get_http_proxy_settings (struct http_proxy_options *p, char **err, struct gc_arena *gc); +#endif /* ENABLE_HTTP_PROXY */ -#endif -#endif +#endif /* PROXY_H */ @@ -1307,44 +1307,57 @@ link_socket_init_phase2 (struct link_socket *sock, } else if (sock->info.proto == PROTO_TCPv4_CLIENT) { - socket_connect (&sock->sd, - &sock->info.lsa->local, - sock->bind_local, - &sock->info.lsa->actual.dest, - sock->remote_list, - remote_dynamic, - &remote_changed, - sock->connect_retry_seconds, - sock->connect_timeout, - sock->connect_retry_max, - signal_received); - - if (*signal_received) - goto done; - if (false) - ; +#ifdef GENERAL_PROXY_SUPPORT + bool proxy_retry = false; +#else + const bool proxy_retry = false; +#endif + do { + socket_connect (&sock->sd, + &sock->info.lsa->local, + sock->bind_local, + &sock->info.lsa->actual.dest, + sock->remote_list, + remote_dynamic, + &remote_changed, + sock->connect_retry_seconds, + sock->connect_timeout, + sock->connect_retry_max, + signal_received); + + if (*signal_received) + goto done; + + if (false) + ; #ifdef ENABLE_HTTP_PROXY - else if (sock->http_proxy) - { - establish_http_proxy_passthru (sock->http_proxy, - sock->sd, - sock->proxy_dest_host, - sock->proxy_dest_port, - &sock->stream_buf.residual, - signal_received); - } + else if (sock->http_proxy) + { + proxy_retry = establish_http_proxy_passthru (sock->http_proxy, + sock->sd, + sock->proxy_dest_host, + sock->proxy_dest_port, + &sock->stream_buf.residual, + signal_received); + } #endif #ifdef ENABLE_SOCKS - else if (sock->socks_proxy) - { - establish_socks_proxy_passthru (sock->socks_proxy, - sock->sd, - sock->proxy_dest_host, - sock->proxy_dest_port, - signal_received); - } + else if (sock->socks_proxy) + { + establish_socks_proxy_passthru (sock->socks_proxy, + sock->sd, + sock->proxy_dest_host, + sock->proxy_dest_port, + signal_received); + } #endif + if (proxy_retry) + { + openvpn_close_socket (sock->sd); + sock->sd = create_socket_tcp (); + } + } while (proxy_retry); } #ifdef ENABLE_SOCKS else if (sock->info.proto == PROTO_UDPv4 && sock->socks_proxy) @@ -1386,7 +1399,7 @@ link_socket_init_phase2 (struct link_socket *sock, goto done; } #endif - + if (*signal_received) goto done; @@ -60,10 +60,25 @@ struct socks_proxy_info * new_socks_proxy (const char *server, int port, bool retry, + struct auto_proxy_info *auto_proxy_info, struct gc_arena *gc) { struct socks_proxy_info *p; + + if (auto_proxy_info) + { + if (!server) + { + if (!auto_proxy_info->socks.server) + return NULL; + + server = auto_proxy_info->socks.server; + port = auto_proxy_info->socks.port; + } + } + ALLOC_OBJ_CLEAR_GC (p, struct socks_proxy_info, gc); + ASSERT (server); ASSERT (legal_ipv4_port (port)); @@ -50,6 +50,7 @@ void socks_adjust_frame_parameters (struct frame *frame, int proto); struct socks_proxy_info *new_socks_proxy (const char *server, int port, bool retry, + struct auto_proxy_info *auto_proxy_info, struct gc_arena *gc); void establish_socks_proxy_passthru (struct socks_proxy_info *p, @@ -266,6 +266,7 @@ #ifdef WIN32 #include <iphlpapi.h> +#include <WinInet.h> #endif #ifdef HAVE_SYS_MMAN_H @@ -436,6 +437,13 @@ socket_defined (const socket_descriptor_t sd) #endif /* + * Should we include code common to all proxy methods? + */ +#if defined(ENABLE_HTTP_PROXY) || defined(ENABLE_SOCKS) +#define GENERAL_PROXY_SUPPORT +#endif + +/* * Do we have PKCS11 capability? */ #if defined(USE_PKCS11) && defined(USE_CRYPTO) && defined(USE_SSL) |