diff options
author | James Yonan <james@openvpn.net> | 2010-05-24 22:51:16 +0000 |
---|---|---|
committer | James Yonan <james@openvpn.net> | 2010-05-24 22:51:16 +0000 |
commit | 3cf6c9328250061600b78c8a7deb0edc850e739b (patch) | |
tree | bc6032117107a8e801e8203c40b1d9533b657597 | |
parent | Minor fixes to recent HTTP proxy changes: (diff) | |
download | openvpn-3cf6c9328250061600b78c8a7deb0edc850e739b.tar.xz |
Implemented http-proxy-override and http-proxy-fallback directives to make it
easier for OpenVPN client UIs to start a pre-existing client config file with
proxy options, or to adaptively fall back to a proxy connection if a direct
connection fails.
git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@5652 e7ae566f-a301-0410-adde-c780ea21d3b5
-rw-r--r-- | buffer.h | 6 | ||||
-rw-r--r-- | forward.c | 17 | ||||
-rw-r--r-- | init.c | 221 | ||||
-rw-r--r-- | manage.c | 118 | ||||
-rw-r--r-- | manage.h | 9 | ||||
-rw-r--r-- | misc.c | 6 | ||||
-rw-r--r-- | misc.h | 5 | ||||
-rw-r--r-- | openvpn.c | 2 | ||||
-rw-r--r-- | options.c | 168 | ||||
-rw-r--r-- | options.h | 36 | ||||
-rw-r--r-- | proxy.c | 15 | ||||
-rw-r--r-- | proxy.h | 9 | ||||
-rw-r--r-- | syshead.h | 23 | ||||
-rw-r--r-- | version.m4 | 2 |
14 files changed, 565 insertions, 72 deletions
@@ -724,6 +724,12 @@ void gc_transfer (struct gc_arena *dest, struct gc_arena *src); void x_gc_free (struct gc_arena *a); +static inline bool +gc_defined (struct gc_arena *a) +{ + return a->list != NULL; +} + static inline void gc_init (struct gc_arena *a) { @@ -687,14 +687,25 @@ read_incoming_link (struct context *c) if (c->options.inetd) { c->sig->signal_received = SIGTERM; + c->sig->signal_text = "connection-reset-inetd"; msg (D_STREAM_ERRORS, "Connection reset, inetd/xinetd exit [%d]", status); } else { - c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */ - msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status); +#ifdef ENABLE_OCC + if (event_timeout_defined(&c->c2.explicit_exit_notification_interval)) + { + msg (D_STREAM_ERRORS, "Connection reset during exit notification period, ignoring [%d]", status); + openvpn_sleep(1); + } + else +#endif + { + c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- TCP connection reset */ + c->sig->signal_text = "connection-reset"; + msg (D_STREAM_ERRORS, "Connection reset, restarting [%d]", status); + } } - c->sig->signal_text = "connection-reset"; } perf_pop (); return; @@ -111,6 +111,103 @@ update_options_ce_post (struct options *options) #endif } +#if HTTP_PROXY_FALLBACK + +static bool +ce_http_proxy_fallback_defined(const struct context *c) +{ + const struct connection_list *l = c->options.connection_list; + if (l && l->current == 0) + { + int i; + for (i = 0; i < l->len; ++i) + { + if (l->array[i]->flags & CE_HTTP_PROXY_FALLBACK) + return true; + } + } + return false; +} + +static void +ce_http_proxy_fallback_start(struct context *c, const char *remote_ip_hint) +{ + const struct connection_list *l = c->options.connection_list; + if (l) + { + int i; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->flags & CE_HTTP_PROXY_FALLBACK) + { + ce->http_proxy_options = NULL; + ce->ce_http_proxy_fallback_timestamp = 0; + if (!remote_ip_hint) + remote_ip_hint = ce->remote; + } + } + } + + if (management) + management_http_proxy_fallback_notify(management, "NEED_LATER", remote_ip_hint); +} + +static bool +ce_http_proxy_fallback (struct context *c, volatile const struct connection_entry *ce) +{ + const int proxy_info_expire = 120; /* seconds before proxy info expires */ + + update_time(); + if (management) + { + if (!ce->ce_http_proxy_fallback_timestamp) + { + management_http_proxy_fallback_notify(management, "NEED_NOW", NULL); + while (!ce->ce_http_proxy_fallback_timestamp) + { + management_event_loop_n_seconds (management, 1); + if (IS_SIG (c)) + return false; + } + } + return (now < ce->ce_http_proxy_fallback_timestamp + proxy_info_expire && ce->http_proxy_options); + } + return false; +} + +static bool +management_callback_http_proxy_fallback_cmd (void *arg, const char *server, const char *port, const char *flags) +{ + struct context *c = (struct context *) arg; + const struct connection_list *l = c->options.connection_list; + int ret = false; + struct http_proxy_options *ho = parse_http_proxy_fallback (c, server, port, flags, M_WARN); + + update_time(); + if (l) + { + int i; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->flags & CE_HTTP_PROXY_FALLBACK) + { + if (ho) + { + ce->http_proxy_options = ho; + ret = true; + } + ce->ce_http_proxy_fallback_timestamp = now; + } + } + } + + return ret; +} + +#endif + /* * Initialize and possibly randomize connection list. */ @@ -141,6 +238,30 @@ init_connection_list (struct context *c) #endif } +#if 0 /* fixme -- disable for production */ +static void +show_connection_list (const struct connection_list *l) +{ + int i; + dmsg (M_INFO, "CONNECTION_LIST len=%d current=%d", + l->len, l->current); + for (i = 0; i < l->len; ++i) + { + dmsg (M_INFO, "[%d] %s:%d proto=%s http_proxy=%d", + i, + l->array[i]->remote, + l->array[i]->remote_port, + proto2ascii(l->array[i]->proto, true), + BOOL_CAST(l->array[i]->http_proxy_options)); + } +} +#else +static inline void +show_connection_list (const struct connection_list *l) +{ +} +#endif + /* * Increment to next connection entry */ @@ -151,27 +272,65 @@ next_connection_entry (struct context *c) struct connection_list *l = c->options.connection_list; if (l) { - if (l->no_advance && l->current >= 0) - { - l->no_advance = false; - } - else - { - int i; - if (++l->current >= l->len) - l->current = 0; + bool ce_defined; + struct connection_entry *ce; + int n_cycles = 0; - dmsg (D_CONNECTION_LIST, "CONNECTION_LIST len=%d current=%d", - l->len, l->current); - for (i = 0; i < l->len; ++i) - { - dmsg (D_CONNECTION_LIST, "[%d] %s:%d", - i, - l->array[i]->remote, - l->array[i]->remote_port); - } - } - c->options.ce = *l->array[l->current]; + do { + const char *remote_ip_hint = NULL; + bool advanced = false; + + ce_defined = true; + if (l->no_advance && l->current >= 0) + { + l->no_advance = false; + } + else + { + if (++l->current >= l->len) + { + l->current = 0; + ++l->n_cycles; + if (++n_cycles >= 2) + msg (M_FATAL, "No usable connection profiles are present"); + } + + advanced = true; + show_connection_list(l); + } + + ce = l->array[l->current]; + + if (c->options.remote_ip_hint && !l->n_cycles) + remote_ip_hint = c->options.remote_ip_hint; + +#if HTTP_PROXY_FALLBACK + if (advanced && ce_http_proxy_fallback_defined(c)) + ce_http_proxy_fallback_start(c, remote_ip_hint); + + if (ce->flags & CE_HTTP_PROXY_FALLBACK) + { + ce_defined = ce_http_proxy_fallback(c, ce); + if (IS_SIG (c)) + break; + } +#endif + + if (ce->flags & CE_DISABLED) + ce_defined = false; + + c->options.ce = *ce; + + if (remote_ip_hint) + c->options.ce.remote = remote_ip_hint; + +#if 0 /* fixme -- disable for production, this code simulates a network where proxy fallback is the only method to reach the OpenVPN server */ + if (!(c->options.ce.flags & CE_HTTP_PROXY_FALLBACK)) + { + c->options.ce.remote = "10.10.0.1"; /* use an unreachable address here */ + } +#endif + } while (!ce_defined); } #endif update_options_ce_post (&c->options); @@ -2774,6 +2933,9 @@ init_management_callback_p2p (struct context *c) cb.arg = c; cb.status = management_callback_status_p2p; cb.show_net = management_show_net_callback; +#if HTTP_PROXY_FALLBACK + cb.http_proxy_fallback_cmd = management_callback_http_proxy_fallback_cmd; +#endif management_set_callback (management, &cb); } #endif @@ -2898,6 +3060,17 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int c->sig->signal_text = NULL; c->sig->hard = false; + if (c->mode == CM_P2P) + init_management_callback_p2p (c); + + /* possible sleep or management hold if restart */ + if (c->mode == CM_P2P || c->mode == CM_TOP) + { + do_startup_pause (c); + if (IS_SIG (c)) + goto sig; + } + /* map in current connection entry */ next_connection_entry (c); @@ -2916,14 +3089,6 @@ init_instance (struct context *c, const struct env_set *env, const unsigned int if (c->first_time && options->mlock) do_mlockall (true); - /* possible sleep or management hold if restart */ - if (c->mode == CM_P2P || c->mode == CM_TOP) - { - do_startup_pause (c); - if (IS_SIG (c)) - goto sig; - } - #if P2MP /* get passwords if undefined */ if (auth_retry_get () == AR_INTERACT) @@ -110,6 +110,10 @@ man_help () msg (M_CLIENT, "username type u : Enter username u for a queried OpenVPN username."); msg (M_CLIENT, "verb [n] : Set log verbosity level to n, or show if n is absent."); msg (M_CLIENT, "version : Show current version number."); +#if HTTP_PROXY_FALLBACK + msg (M_CLIENT, "http-proxy-fallback <server> <port> [flags] : Enter dynamic HTTP proxy fallback info."); + msg (M_CLIENT, "http-proxy-fallback-disable : Disable HTTP proxy fallback."); +#endif msg (M_CLIENT, "END"); } @@ -204,12 +208,10 @@ man_update_io_state (struct management *man) } static void -man_output_list_push (struct management *man, const char *str) +man_output_list_push_finalize (struct management *man) { if (management_connected (man)) { - if (str) - buffer_list_push (man->connection.out, (const unsigned char *) str); man_update_io_state (man); if (!man->persist.standalone_disabled) { @@ -220,6 +222,22 @@ man_output_list_push (struct management *man, const char *str) } static void +man_output_list_push_str (struct management *man, const char *str) +{ + if (management_connected (man) && str) + { + buffer_list_push (man->connection.out, (const unsigned char *) str); + } +} + +static void +man_output_list_push (struct management *man, const char *str) +{ + man_output_list_push_str (man, str); + man_output_list_push_finalize (man); +} + +static void man_prompt (struct management *man) { if (man_password_needed (man)) @@ -256,12 +274,13 @@ man_close_socket (struct management *man, const socket_descriptor_t sd) static void virtual_output_callback_func (void *arg, const unsigned int flags, const char *str) { + struct management *man = (struct management *) arg; static int recursive_level = 0; /* GLOBAL */ + bool did_push = false; if (!recursive_level) /* don't allow recursion */ { struct gc_arena gc = gc_new (); - struct management *man = (struct management *) arg; struct log_entry e; const char *out = NULL; @@ -289,13 +308,17 @@ virtual_output_callback_func (void *arg, const unsigned int flags, const char *s | LOG_PRINT_LOG_PREFIX | LOG_PRINT_CRLF, &gc); if (out) - man_output_list_push (man, out); + { + man_output_list_push_str (man, out); + did_push = true; + } if (flags & M_FATAL) { out = log_entry_print (&e, LOG_FATAL_NOTIFY|LOG_PRINT_CRLF, &gc); if (out) { - man_output_list_push (man, out); + man_output_list_push_str (man, out); + did_push = true; man_reset_client_socket (man, true); } } @@ -304,6 +327,9 @@ virtual_output_callback_func (void *arg, const unsigned int flags, const char *s --recursive_level; gc_free (&gc); } + + if (did_push) + man_output_list_push_finalize (man); } /* @@ -998,6 +1024,31 @@ man_need (struct management *man, const char **p, const int n, unsigned int flag return true; } +#if HTTP_PROXY_FALLBACK + +static void +man_http_proxy_fallback (struct management *man, const char *server, const char *port, const char *flags) +{ + if (man->persist.callback.http_proxy_fallback_cmd) + { + const bool status = (*man->persist.callback.http_proxy_fallback_cmd)(man->persist.callback.arg, server, port, flags); + if (status) + { + msg (M_CLIENT, "SUCCESS: proxy-fallback command succeeded"); + } + else + { + msg (M_CLIENT, "ERROR: proxy-fallback command failed"); + } + } + else + { + msg (M_CLIENT, "ERROR: The proxy-fallback command is not supported by the current daemon mode"); + } +} + +#endif + static void man_dispatch_command (struct management *man, struct status_output *so, const char **p, const int nparms) { @@ -1210,6 +1261,17 @@ man_dispatch_command (struct management *man, struct status_output *so, const ch man_pkcs11_id_get (man, atoi(p[1])); } #endif +#if HTTP_PROXY_FALLBACK + else if (streq (p[0], "http-proxy-fallback")) + { + if (man_need (man, p, 2, MN_AT_LEAST)) + man_http_proxy_fallback (man, p[1], p[2], p[3]); + } + else if (streq (p[0], "http-proxy-fallback-disable")) + { + man_http_proxy_fallback (man, NULL, NULL, NULL); + } +#endif #if 1 else if (streq (p[0], "test")) { @@ -2103,7 +2165,7 @@ management_clear_callback (struct management *man) man->persist.standalone_disabled = false; man->persist.hold_release = false; CLEAR (man->persist.callback); - man_output_list_push (man, NULL); /* flush output queue */ + man_output_list_push_finalize (man); /* flush output queue */ } void @@ -2402,7 +2464,7 @@ management_io (struct management *man) net_event_win32_clear_selected_events (&man->connection.ne32, FD_ACCEPT); } } - else if (man->connection.state == MS_CC_WAIT_READ) + else if (man->connection.state == MS_CC_WAIT_READ || man->connection.state == MS_CC_WAIT_WRITE) { if (net_events & FD_READ) { @@ -2410,18 +2472,13 @@ management_io (struct management *man) ; net_event_win32_clear_selected_events (&man->connection.ne32, FD_READ); } - } - if (man->connection.state == MS_CC_WAIT_WRITE) - { if (net_events & FD_WRITE) { int status; - /* dmsg (M_INFO, "FD_WRITE set"); */ status = man_write (man); if (status < 0 && WSAGetLastError() == WSAEWOULDBLOCK) { - /* dmsg (M_INFO, "FD_WRITE cleared"); */ net_event_win32_clear_selected_events (&man->connection.ne32, FD_WRITE); } } @@ -2512,7 +2569,7 @@ man_block (struct management *man, volatile int *signal_received, const time_t e if (man_standalone_ok (man)) { - do + while (true) { event_reset (man->connection.es); management_socket_set (man, man->connection.es, NULL, NULL); @@ -2530,15 +2587,18 @@ man_block (struct management *man, volatile int *signal_received, const time_t e status = -1; break; } - /* set SIGINT signal if expiration time exceeded */ - if (expire && now >= expire) + + if (status > 0) + break; + else if (expire && now >= expire) { + /* set SIGINT signal if expiration time exceeded */ status = 0; if (signal_received) *signal_received = SIGINT; break; } - } while (status != 1); + } } return status; } @@ -2615,28 +2675,29 @@ management_event_loop_n_seconds (struct management *man, int sec) { volatile int signal_received = 0; const bool standalone_disabled_save = man->persist.standalone_disabled; - time_t expire; + time_t expire = 0; man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */ /* set expire time */ update_time (); - expire = now + sec; + if (sec) + expire = now + sec; /* if no client connection, wait for one */ man_wait_for_client_connection (man, &signal_received, expire, 0); if (signal_received) return; - /* run command processing event loop until we get our username/password */ - while (true) + /* run command processing event loop */ + do { man_standalone_event_loop (man, &signal_received, expire); if (!signal_received) man_check_for_signals (&signal_received); if (signal_received) return; - } + } while (expire); /* revert state */ man->persist.standalone_disabled = standalone_disabled_save; @@ -3028,6 +3089,19 @@ log_history_ref (const struct log_history *h, const int index) return NULL; } +#if HTTP_PROXY_FALLBACK + +void +management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint) +{ + if (remote_ip_hint) + msg (M_CLIENT, ">PROXY:%s,%s", type, remote_ip_hint); + else + msg (M_CLIENT, ">PROXY:%s", type); +} + +#endif /* HTTP_PROXY_FALLBACK */ + #else static void dummy(void) {} #endif /* ENABLE_MANAGEMENT */ @@ -170,6 +170,9 @@ struct management_callback const unsigned long cid, struct buffer_list *pf_config); /* ownership transferred */ #endif +#if HTTP_PROXY_FALLBACK + bool (*http_proxy_fallback_cmd) (void *arg, const char *server, const char *port, const char *flags); +#endif }; /* @@ -502,5 +505,11 @@ management_bytes_server (struct management *man, #endif /* MANAGEMENT_DEF_AUTH */ +#if HTTP_PROXY_FALLBACK + +void management_http_proxy_fallback_notify (struct management *man, const char *type, const char *remote_ip_hint); + +#endif /* HTTP_PROXY_FALLBACK */ + #endif #endif @@ -1374,6 +1374,9 @@ get_user_pass (struct user_pass *up, { const bool from_stdin = (!auth_file || !strcmp (auth_file, "stdin")); + if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED) + msg (M_WARN, "Note: previous '%s' credentials failed", prefix); + #ifdef ENABLE_MANAGEMENT /* * Get username/password from standard input? @@ -1382,6 +1385,9 @@ get_user_pass (struct user_pass *up, && ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT))) && management_query_user_pass_enabled (management)) { + if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED) + management_auth_failure (management, prefix, "previous auth credentials failed"); + if (!management_query_user_pass (management, up, prefix, flags)) { if ((flags & GET_USER_PASS_NOFATAL) != 0) @@ -263,12 +263,17 @@ bool get_console_input (const char *prompt, const bool echo, char *input, const #define GET_USER_PASS_NEED_OK (1<<3) #define GET_USER_PASS_NOFATAL (1<<4) #define GET_USER_PASS_NEED_STR (1<<5) +#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6) bool get_user_pass (struct user_pass *up, const char *auth_file, const char *prefix, const unsigned int flags); +void fail_user_pass (const char *prefix, + const unsigned int flags, + const char *reason); + void purge_user_pass (struct user_pass *up, const bool force); /* @@ -55,8 +55,6 @@ tunnel_point_to_point (struct context *c) if (IS_SIG (c)) return; - init_management_callback_p2p (c); - /* main event loop */ while (true) { @@ -761,7 +761,9 @@ void uninit_options (struct options *o) { if (o->gc_owned) - gc_free (&o->gc); + { + gc_free (&o->gc); + } } #ifdef ENABLE_DEBUG @@ -1412,6 +1414,137 @@ init_http_options_if_undefined (struct options *o) #endif +#if HTTP_PROXY_FALLBACK + +static struct http_proxy_options * +parse_http_proxy_override (const char *server, + const char *port, + const char *flags, + const int msglevel, + struct gc_arena *gc) +{ + if (server && port) + { + struct http_proxy_options *ho; + const int int_port = atoi(port); + + if (!legal_ipv4_port (int_port)) + { + msg (msglevel, "Bad http-proxy port number: %s", port); + return NULL; + } + + ALLOC_OBJ_CLEAR_GC (ho, struct http_proxy_options, gc); + ho->server = string_alloc(server, gc); + ho->port = int_port; + ho->retry = true; + ho->timeout = 5; + if (flags && !strcmp(flags, "nct")) + ho->auth_retry = PAR_NCT; + else + ho->auth_retry = PAR_ALL; + ho->http_version = "1.0"; + ho->user_agent = "OpenVPN-Autoproxy/1.0"; + return ho; + } + else + return NULL; +} + +struct http_proxy_options * +parse_http_proxy_fallback (struct context *c, + const char *server, + const char *port, + const char *flags, + const int msglevel) +{ + struct gc_arena gc = gc_new (); + struct http_proxy_options *hp = parse_http_proxy_override(server, port, flags, msglevel, &gc); + struct hpo_store *hpos = c->options.hpo_store; + if (!hpos) + { + ALLOC_OBJ_CLEAR_GC (hpos, struct hpo_store, &c->options.gc); + c->options.hpo_store = hpos; + } + hpos->hpo = *hp; + hpos->hpo.server = hpos->server; + strncpynt(hpos->server, hp->server, sizeof(hpos->server)); + gc_free (&gc); + return &hpos->hpo; +} + +static void +http_proxy_warn(const char *name) +{ + msg (M_WARN, "Note: option %s ignored because no TCP-based connection profiles are defined", name); +} + +void +options_postprocess_http_proxy_fallback (struct options *o) +{ + struct connection_list *l = o->connection_list; + if (l) + { + int i; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4) + { + if (l->len < CONNECTION_LIST_SIZE) + { + struct connection_entry *newce; + ALLOC_OBJ_GC (newce, struct connection_entry, &o->gc); + *newce = *ce; + newce->flags |= CE_HTTP_PROXY_FALLBACK; + newce->http_proxy_options = NULL; + newce->ce_http_proxy_fallback_timestamp = 0; + l->array[l->len++] = newce; + } + return; + } + } + } + http_proxy_warn("http-proxy-fallback"); +} + +void +options_postprocess_http_proxy_override (struct options *o) +{ + const struct connection_list *l = o->connection_list; + if (l) + { + int i; + bool succeed = false; + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->proto == PROTO_TCPv4_CLIENT || ce->proto == PROTO_TCPv4) + { + ce->http_proxy_options = o->http_proxy_override; + succeed = true; + } + } + if (succeed) + { + for (i = 0; i < l->len; ++i) + { + struct connection_entry *ce = l->array[i]; + if (ce->proto == PROTO_UDPv4) + { + ce->flags |= CE_DISABLED; + } + } + } + else + { + http_proxy_warn("http-proxy-override"); + } + } +} + +#endif + #if ENABLE_CONNECTION static struct connection_list * @@ -2095,7 +2228,7 @@ options_postprocess_mutate (struct options *o) * For compatibility with 2.0.x, map multiple --remote options * into connection list (connection lists added in 2.1). */ - if (o->remote_list->len > 1) + if (o->remote_list->len > 1 || o->force_connection_list) { const struct remote_list *rl = o->remote_list; int i; @@ -2112,7 +2245,7 @@ options_postprocess_mutate (struct options *o) *ace = ce; } } - else if (o->remote_list->len == 1) /* one --remote option specfied */ + else if (o->remote_list->len == 1) /* one --remote option specified */ { connection_entry_load_re (&o->ce, o->remote_list->array[0]); } @@ -2126,6 +2259,13 @@ options_postprocess_mutate (struct options *o) int i; for (i = 0; i < o->connection_list->len; ++i) options_postprocess_mutate_ce (o, o->connection_list->array[i]); + +#if HTTP_PROXY_FALLBACK + if (o->http_proxy_override) + options_postprocess_http_proxy_override(o); + else if (o->http_proxy_fallback) + options_postprocess_http_proxy_fallback(o); +#endif } else #endif @@ -3633,11 +3773,29 @@ add_option (struct options *options, } } #endif +#ifdef ENABLE_CONNECTION else if (streq (p[0], "remote-ip-hint") && p[1]) { - VERIFY_PERMISSION (OPT_P_GENERAL|OPT_P_CONNECTION); - // fixme + VERIFY_PERMISSION (OPT_P_GENERAL); + options->remote_ip_hint = p[1]; } +#endif +#if HTTP_PROXY_FALLBACK + else if (streq (p[0], "http-proxy-fallback")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->http_proxy_fallback = true; + options->force_connection_list = true; + } + else if (streq (p[0], "http-proxy-override") && p[1] && p[2]) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->http_proxy_override = parse_http_proxy_override(p[1], p[2], p[3], msglevel, &options->gc); + if (!options->http_proxy_override) + goto err; + options->force_connection_list = true; + } +#endif else if (streq (p[0], "remote") && p[1]) { struct remote_entry re; @@ -97,6 +97,14 @@ struct connection_entry int socks_proxy_port; bool socks_proxy_retry; #endif + +# define CE_DISABLED (1<<0) +#if HTTP_PROXY_FALLBACK +# define CE_HTTP_PROXY_FALLBACK (1<<1) + time_t ce_http_proxy_fallback_timestamp; /* time when fallback http_proxy_options was last updated */ +#endif + + unsigned int flags; }; struct remote_entry @@ -114,6 +122,7 @@ struct connection_list { int len; int current; + int n_cycles; bool no_advance; struct connection_entry *array[CONNECTION_LIST_SIZE]; }; @@ -126,6 +135,14 @@ struct remote_list #endif +#if HTTP_PROXY_FALLBACK +struct hpo_store +{ + struct http_proxy_options hpo; + char server[80]; +}; +#endif + /* Command line options */ struct options { @@ -162,14 +179,22 @@ struct options struct connection_entry ce; #ifdef ENABLE_CONNECTION + char *remote_ip_hint; struct connection_list *connection_list; struct remote_list *remote_list; + bool force_connection_list; #endif #ifdef GENERAL_PROXY_SUPPORT struct auto_proxy_info *auto_proxy_info; #endif +#if HTTP_PROXY_FALLBACK + bool http_proxy_fallback; + struct http_proxy_options *http_proxy_override; + struct hpo_store *hpo_store; /* used to store dynamic proxy info given by management interface */ +#endif + bool remote_random; const char *ipchange; const char *dev; @@ -710,4 +735,15 @@ connection_list_set_no_advance (struct options *o) #endif } +#if HTTP_PROXY_FALLBACK + +struct http_proxy_options * +parse_http_proxy_fallback (struct context *c, + const char *server, + const char *port, + const char *flags, + const int msglevel); + +#endif /* HTTP_PROXY_FALLBACK */ + #endif @@ -224,10 +224,14 @@ get_user_pass_http (struct http_proxy_info *p, const bool force) { if (!static_proxy_user_pass.defined || force) { + unsigned int flags = GET_USER_PASS_MANAGEMENT; + if (p->queried_creds) + flags |= GET_USER_PASS_PREVIOUS_CREDS_FAILED; get_user_pass (&static_proxy_user_pass, p->options.auth_file, UP_TYPE_PROXY, - GET_USER_PASS_MANAGEMENT); + flags); + p->queried_creds = true; p->up = static_proxy_user_pass; } } @@ -755,12 +759,12 @@ establish_http_proxy_passthru (struct http_proxy_info *p, realm, password, nonce, - cnonce, + (char *)cnonce, session_key); DigestCalcResponse(session_key, nonce, nonce_count, - cnonce, + (char *)cnonce, qop, http_method, uri, @@ -877,6 +881,8 @@ establish_http_proxy_passthru (struct http_proxy_info *p, goto error; } + /* SUCCESS */ + /* receive line from proxy and discard */ if (!recv_line (sd, NULL, 0, p->options.timeout, true, NULL, signal_received)) goto error; @@ -888,6 +894,9 @@ establish_http_proxy_passthru (struct http_proxy_info *p, while (recv_line (sd, NULL, 0, 2, false, lookahead, signal_received)) ; + /* reset queried_creds so that we don't think that the next creds request is due to an auth error */ + p->queried_creds = false; + #if 0 if (lookahead && BLEN (lookahead)) msg (M_INFO, "HTTP PROXY: lookahead: %s", format_hex (BPTR (lookahead), BLEN (lookahead), 0)); @@ -71,7 +71,7 @@ struct http_proxy_options { # define PAR_NO 0 /* don't support any auth retries */ # define PAR_ALL 1 /* allow all proxy auth protocols */ # define PAR_NCT 2 /* disable cleartext proxy auth protocols */ - bool auth_retry; + int auth_retry; const char *auth_method_string; const char *auth_file; @@ -79,12 +79,19 @@ struct http_proxy_options { const char *user_agent; }; +struct http_proxy_options_simple { + const char *server; + int port; + int auth_retry; +}; + struct http_proxy_info { bool defined; int auth_method; struct http_proxy_options options; struct user_pass up; char *proxy_authenticate; + bool queried_creds; }; struct http_proxy_info *http_proxy_new (const struct http_proxy_options *o, @@ -625,6 +625,22 @@ socket_defined (const socket_descriptor_t sd) #define ENABLE_INLINE_FILES 1 /* + * Support "connection" directive + */ +#if ENABLE_INLINE_FILES +#define ENABLE_CONNECTION 1 +#endif + +/* + * Should we include http proxy fallback functionality + */ +#if defined(ENABLE_CONNECTION) && defined(ENABLE_MANAGEMENT) && defined(ENABLE_HTTP_PROXY) +#define HTTP_PROXY_FALLBACK 1 +#else +#define HTTP_PROXY_FALLBACK 0 +#endif + +/* * Reduce sensitivity to system clock instability * and backtracks. */ @@ -646,11 +662,4 @@ socket_defined (const socket_descriptor_t sd) #define AUTO_USERID 0 #endif -/* - * Support "connection" directive - */ -#if ENABLE_INLINE_FILES -#define ENABLE_CONNECTION 1 -#endif - #endif @@ -1,5 +1,5 @@ dnl define the OpenVPN version -define(PRODUCT_VERSION,[2.1.1g]) +define(PRODUCT_VERSION,[2.1.1h]) dnl define the TAP version define(PRODUCT_TAP_ID,[tap0901]) define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9]) |