diff options
Diffstat (limited to 'proxy.c')
-rw-r--r-- | proxy.c | 388 |
1 files changed, 289 insertions, 99 deletions
@@ -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 */ |