| /* |
| * OpenVPN -- An application to securely tunnel IP networks |
| * over a single TCP/UDP port, with support for SSL/TLS-based |
| * session authentication and key exchange, |
| * packet encryption, packet authentication, and |
| * packet compression. |
| * |
| * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 |
| * as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #elif defined(_MSC_VER) |
| #include "config-msvc.h" |
| #endif |
| |
| #include "syshead.h" |
| |
| #include "push.h" |
| #include "options.h" |
| #include "ssl.h" |
| #include "ssl_verify.h" |
| #include "manage.h" |
| |
| #include "memdbg.h" |
| |
| #if P2MP |
| |
| static char push_reply_cmd[] = "PUSH_REPLY"; |
| |
| /* |
| * Auth username/password |
| * |
| * Client received an authentication failed message from server. |
| * Runs on client. |
| */ |
| void |
| receive_auth_failed(struct context *c, const struct buffer *buffer) |
| { |
| msg(M_VERB0, "AUTH: Received control message: %s", BSTR(buffer)); |
| c->options.no_advance = true; |
| |
| if (c->options.pull) |
| { |
| /* Before checking how to react on AUTH_FAILED, first check if the |
| * failed auth might be the result of an expired auth-token. |
| * Note that a server restart will trigger a generic AUTH_FAILED |
| * instead an AUTH_FAILED,SESSION so handle all AUTH_FAILED message |
| * identical for this scenario */ |
| if (ssl_clean_auth_token()) |
| { |
| c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */ |
| c->sig->signal_text = "auth-failure (auth-token)"; |
| } |
| else |
| { |
| switch (auth_retry_get()) |
| { |
| case AR_NONE: |
| c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */ |
| break; |
| |
| case AR_INTERACT: |
| ssl_purge_auth(false); |
| |
| case AR_NOINTERACT: |
| c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */ |
| break; |
| |
| default: |
| ASSERT(0); |
| } |
| c->sig->signal_text = "auth-failure"; |
| } |
| #ifdef ENABLE_MANAGEMENT |
| if (management) |
| { |
| const char *reason = NULL; |
| struct buffer buf = *buffer; |
| if (buf_string_compare_advance(&buf, "AUTH_FAILED,") && BLEN(&buf)) |
| { |
| reason = BSTR(&buf); |
| } |
| management_auth_failure(management, UP_TYPE_AUTH, reason); |
| } |
| #endif |
| /* |
| * Save the dynamic-challenge text even when management is defined |
| */ |
| { |
| #ifdef ENABLE_CLIENT_CR |
| struct buffer buf = *buffer; |
| if (buf_string_match_head_str(&buf, "AUTH_FAILED,CRV1:") && BLEN(&buf)) |
| { |
| buf_advance(&buf, 12); /* Length of "AUTH_FAILED," substring */ |
| ssl_put_auth_challenge(BSTR(&buf)); |
| } |
| #endif |
| } |
| } |
| } |
| |
| /* |
| * Act on received restart message from server |
| */ |
| void |
| server_pushed_signal(struct context *c, const struct buffer *buffer, const bool restart, const int adv) |
| { |
| if (c->options.pull) |
| { |
| struct buffer buf = *buffer; |
| const char *m = ""; |
| if (buf_advance(&buf, adv) && buf_read_u8(&buf) == ',' && BLEN(&buf)) |
| { |
| m = BSTR(&buf); |
| } |
| |
| /* preserve cached passwords? */ |
| /* advance to next server? */ |
| { |
| bool purge = true; |
| |
| if (m[0] == '[') |
| { |
| int i; |
| for (i = 1; m[i] != '\0' && m[i] != ']'; ++i) |
| { |
| if (m[i] == 'P') |
| { |
| purge = false; |
| } |
| else if (m[i] == 'N') |
| { |
| /* next server? */ |
| c->options.no_advance = false; |
| } |
| } |
| } |
| if (purge) |
| { |
| ssl_purge_auth(true); |
| } |
| } |
| |
| if (restart) |
| { |
| msg(D_STREAM_ERRORS, "Connection reset command was pushed by server ('%s')", m); |
| c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */ |
| c->sig->signal_text = "server-pushed-connection-reset"; |
| } |
| else |
| { |
| msg(D_STREAM_ERRORS, "Halt command was pushed by server ('%s')", m); |
| c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- server-pushed halt */ |
| c->sig->signal_text = "server-pushed-halt"; |
| } |
| #ifdef ENABLE_MANAGEMENT |
| if (management) |
| { |
| management_notify(management, "info", c->sig->signal_text, m); |
| } |
| #endif |
| } |
| } |
| |
| #if P2MP_SERVER |
| /** |
| * Add an option to the given push list by providing a format string. |
| * |
| * The string added to the push options is allocated in o->gc, so the caller |
| * does not have to preserve anything. |
| * |
| * @param gc GC arena where options are allocated |
| * @param push_list Push list containing options |
| * @param msglevel The message level to use when printing errors |
| * @param fmt Format string for the option |
| * @param ... Format string arguments |
| * |
| * @return true on success, false on failure. |
| */ |
| static bool push_option_fmt(struct gc_arena *gc, struct push_list *push_list, |
| int msglevel, const char *fmt, ...) |
| #ifdef __GNUC__ |
| #if __USE_MINGW_ANSI_STDIO |
| __attribute__ ((format(gnu_printf, 4, 5))) |
| #else |
| __attribute__ ((format(__printf__, 4, 5))) |
| #endif |
| #endif |
| ; |
| |
| /* |
| * Send auth failed message from server to client. |
| */ |
| void |
| send_auth_failed(struct context *c, const char *client_reason) |
| { |
| struct gc_arena gc = gc_new(); |
| static const char auth_failed[] = "AUTH_FAILED"; |
| size_t len; |
| |
| schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM); |
| |
| len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed); |
| if (len > PUSH_BUNDLE_SIZE) |
| { |
| len = PUSH_BUNDLE_SIZE; |
| } |
| |
| { |
| struct buffer buf = alloc_buf_gc(len, &gc); |
| buf_printf(&buf, auth_failed); |
| if (client_reason) |
| { |
| buf_printf(&buf, ",%s", client_reason); |
| } |
| send_control_channel_string(c, BSTR(&buf), D_PUSH); |
| } |
| |
| gc_free(&gc); |
| } |
| |
| /* |
| * Send restart message from server to client. |
| */ |
| void |
| send_restart(struct context *c, const char *kill_msg) |
| { |
| schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM); |
| send_control_channel_string(c, kill_msg ? kill_msg : "RESTART", D_PUSH); |
| } |
| |
| #endif /* if P2MP_SERVER */ |
| |
| /* |
| * Push/Pull |
| */ |
| |
| void |
| incoming_push_message(struct context *c, const struct buffer *buffer) |
| { |
| struct gc_arena gc = gc_new(); |
| unsigned int option_types_found = 0; |
| int status; |
| |
| msg(D_PUSH, "PUSH: Received control message: '%s'", sanitize_control_message(BSTR(buffer), &gc)); |
| |
| status = process_incoming_push_msg(c, |
| buffer, |
| c->options.pull, |
| pull_permission_mask(c), |
| &option_types_found); |
| |
| if (status == PUSH_MSG_ERROR) |
| { |
| msg(D_PUSH_ERRORS, "WARNING: Received bad push/pull message: %s", sanitize_control_message(BSTR(buffer), &gc)); |
| } |
| else if (status == PUSH_MSG_REPLY || status == PUSH_MSG_CONTINUATION) |
| { |
| c->options.push_option_types_found |= option_types_found; |
| |
| /* delay bringing tun/tap up until --push parms received from remote */ |
| if (status == PUSH_MSG_REPLY) |
| { |
| if (!do_up(c, true, c->options.push_option_types_found)) |
| { |
| msg(D_PUSH_ERRORS, "Failed to open tun/tap interface"); |
| goto error; |
| } |
| } |
| event_timeout_clear(&c->c2.push_request_interval); |
| } |
| else if (status == PUSH_MSG_REQUEST) |
| { |
| if (c->options.mode == MODE_SERVER) |
| { |
| struct frame *frame_fragment = NULL; |
| #ifdef ENABLE_FRAGMENT |
| if (c->options.ce.fragment) |
| { |
| frame_fragment = &c->c2.frame_fragment; |
| } |
| #endif |
| struct tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE]; |
| if (!tls_session_update_crypto_params(session, &c->options, |
| &c->c2.frame, frame_fragment)) |
| { |
| msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed"); |
| goto error; |
| } |
| } |
| } |
| |
| goto cleanup; |
| error: |
| register_signal(c, SIGUSR1, "process-push-msg-failed"); |
| cleanup: |
| gc_free(&gc); |
| } |
| |
| bool |
| send_push_request(struct context *c) |
| { |
| const int max_push_requests = c->options.handshake_window / PUSH_REQUEST_INTERVAL; |
| if (++c->c2.n_sent_push_requests <= max_push_requests) |
| { |
| return send_control_channel_string(c, "PUSH_REQUEST", D_PUSH); |
| } |
| else |
| { |
| msg(D_STREAM_ERRORS, "No reply from server after sending %d push requests", max_push_requests); |
| c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- server-pushed connection reset */ |
| c->sig->signal_text = "no-push-reply"; |
| return false; |
| } |
| } |
| |
| #if P2MP_SERVER |
| |
| /** |
| * Prepare push options, based on local options and available peer info. |
| * |
| * @param context context structure storing data for VPN tunnel |
| * @param gc gc arena for allocating push options |
| * @param push_list push list to where options are added |
| * |
| * @return true on success, false on failure. |
| */ |
| static bool |
| prepare_push_reply(struct context *c, struct gc_arena *gc, |
| struct push_list *push_list) |
| { |
| const char *optstr = NULL; |
| struct tls_multi *tls_multi = c->c2.tls_multi; |
| const char *const peer_info = tls_multi->peer_info; |
| struct options *o = &c->options; |
| |
| /* ipv6 */ |
| if (c->c2.push_ifconfig_ipv6_defined && !o->push_ifconfig_ipv6_blocked) |
| { |
| push_option_fmt(gc, push_list, M_USAGE, "ifconfig-ipv6 %s/%d %s", |
| print_in6_addr(c->c2.push_ifconfig_ipv6_local, 0, gc), |
| c->c2.push_ifconfig_ipv6_netbits, |
| print_in6_addr(c->c2.push_ifconfig_ipv6_remote, |
| 0, gc)); |
| } |
| |
| /* ipv4 */ |
| if (c->c2.push_ifconfig_defined && c->c2.push_ifconfig_local |
| && c->c2.push_ifconfig_remote_netmask) |
| { |
| in_addr_t ifconfig_local = c->c2.push_ifconfig_local; |
| if (c->c2.push_ifconfig_local_alias) |
| { |
| ifconfig_local = c->c2.push_ifconfig_local_alias; |
| } |
| push_option_fmt(gc, push_list, M_USAGE, "ifconfig %s %s", |
| print_in_addr_t(ifconfig_local, 0, gc), |
| print_in_addr_t(c->c2.push_ifconfig_remote_netmask, |
| 0, gc)); |
| } |
| |
| /* Send peer-id if client supports it */ |
| optstr = peer_info ? strstr(peer_info, "IV_PROTO=") : NULL; |
| if (optstr) |
| { |
| int proto = 0; |
| int r = sscanf(optstr, "IV_PROTO=%d", &proto); |
| if ((r == 1) && (proto >= 2)) |
| { |
| push_option_fmt(gc, push_list, M_USAGE, "peer-id %d", |
| tls_multi->peer_id); |
| tls_multi->use_peer_id = true; |
| } |
| } |
| |
| /* Push cipher if client supports Negotiable Crypto Parameters */ |
| if (tls_peer_info_ncp_ver(peer_info) >= 2 && o->ncp_enabled) |
| { |
| /* if we have already created our key, we cannot *change* our own |
| * cipher -> so log the fact and push the "what we have now" cipher |
| * (so the client is always told what we expect it to use) |
| */ |
| const struct tls_session *session = &tls_multi->session[TM_ACTIVE]; |
| if (session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized) |
| { |
| msg( M_INFO, "PUSH: client wants to negotiate cipher (NCP), but " |
| "server has already generated data channel keys, " |
| "re-sending previously negotiated cipher '%s'", |
| o->ciphername ); |
| } |
| else |
| { |
| /* Push the first cipher from --ncp-ciphers to the client. |
| * TODO: actual negotiation, instead of server dictatorship. */ |
| char *push_cipher = string_alloc(o->ncp_ciphers, &o->gc); |
| o->ciphername = strtok(push_cipher, ":"); |
| } |
| push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername); |
| } |
| else if (o->ncp_enabled) |
| { |
| tls_poor_mans_ncp(o, tls_multi->remote_ciphername); |
| } |
| |
| /* If server uses --auth-gen-token and we have an auth token |
| * to send to the client |
| */ |
| if (false == tls_multi->auth_token_sent && NULL != tls_multi->auth_token) |
| { |
| push_option_fmt(gc, push_list, M_USAGE, |
| "auth-token %s", tls_multi->auth_token); |
| tls_multi->auth_token_sent = true; |
| } |
| return true; |
| } |
| |
| static bool |
| send_push_options(struct context *c, struct buffer *buf, |
| struct push_list *push_list, int safe_cap, |
| bool *push_sent, bool *multi_push) |
| { |
| struct push_entry *e = push_list->head; |
| |
| while (e) |
| { |
| if (e->enable) |
| { |
| const int l = strlen(e->option); |
| if (BLEN(buf) + l >= safe_cap) |
| { |
| buf_printf(buf, ",push-continuation 2"); |
| { |
| const bool status = send_control_channel_string(c, BSTR(buf), D_PUSH); |
| if (!status) |
| { |
| return false; |
| } |
| *push_sent = true; |
| *multi_push = true; |
| buf_reset_len(buf); |
| buf_printf(buf, "%s", push_reply_cmd); |
| } |
| } |
| if (BLEN(buf) + l >= safe_cap) |
| { |
| msg(M_WARN, "--push option is too long"); |
| return false; |
| } |
| buf_printf(buf, ",%s", e->option); |
| } |
| e = e->next; |
| } |
| return true; |
| } |
| |
| static bool |
| send_push_reply(struct context *c, struct push_list *per_client_push_list) |
| { |
| struct gc_arena gc = gc_new(); |
| struct buffer buf = alloc_buf_gc(PUSH_BUNDLE_SIZE, &gc); |
| bool multi_push = false; |
| const int extra = 84; /* extra space for possible trailing ifconfig and push-continuation */ |
| const int safe_cap = BCAP(&buf) - extra; |
| bool push_sent = false; |
| |
| buf_printf(&buf, "%s", push_reply_cmd); |
| |
| /* send options which are common to all clients */ |
| if (!send_push_options(c, &buf, &c->options.push_list, safe_cap, |
| &push_sent, &multi_push)) |
| { |
| goto fail; |
| } |
| |
| /* send client-specific options */ |
| if (!send_push_options(c, &buf, per_client_push_list, safe_cap, |
| &push_sent, &multi_push)) |
| { |
| goto fail; |
| } |
| |
| if (multi_push) |
| { |
| buf_printf(&buf, ",push-continuation 1"); |
| } |
| |
| if (BLEN(&buf) > sizeof(push_reply_cmd)-1) |
| { |
| const bool status = send_control_channel_string(c, BSTR(&buf), D_PUSH); |
| if (!status) |
| { |
| goto fail; |
| } |
| push_sent = true; |
| } |
| |
| /* If nothing have been pushed, send an empty push, |
| * as the client is expecting a response |
| */ |
| if (!push_sent) |
| { |
| bool status = false; |
| |
| buf_reset_len(&buf); |
| buf_printf(&buf, "%s", push_reply_cmd); |
| status = send_control_channel_string(c, BSTR(&buf), D_PUSH); |
| if (!status) |
| { |
| goto fail; |
| } |
| } |
| |
| gc_free(&gc); |
| return true; |
| |
| fail: |
| gc_free(&gc); |
| return false; |
| } |
| |
| static void |
| push_option_ex(struct gc_arena *gc, struct push_list *push_list, |
| const char *opt, bool enable, int msglevel) |
| { |
| if (!string_class(opt, CC_ANY, CC_COMMA)) |
| { |
| msg(msglevel, "PUSH OPTION FAILED (illegal comma (',') in string): '%s'", opt); |
| } |
| else |
| { |
| struct push_entry *e; |
| ALLOC_OBJ_CLEAR_GC(e, struct push_entry, gc); |
| e->enable = true; |
| e->option = opt; |
| if (push_list->head) |
| { |
| ASSERT(push_list->tail); |
| push_list->tail->next = e; |
| push_list->tail = e; |
| } |
| else |
| { |
| ASSERT(!push_list->tail); |
| push_list->head = e; |
| push_list->tail = e; |
| } |
| } |
| } |
| |
| void |
| push_option(struct options *o, const char *opt, int msglevel) |
| { |
| push_option_ex(&o->gc, &o->push_list, opt, true, msglevel); |
| } |
| |
| void |
| clone_push_list(struct options *o) |
| { |
| if (o->push_list.head) |
| { |
| const struct push_entry *e = o->push_list.head; |
| push_reset(o); |
| while (e) |
| { |
| push_option_ex(&o->gc, &o->push_list, |
| string_alloc(e->option, &o->gc), true, M_FATAL); |
| e = e->next; |
| } |
| } |
| } |
| |
| void |
| push_options(struct options *o, char **p, int msglevel, struct gc_arena *gc) |
| { |
| const char **argv = make_extended_arg_array(p, gc); |
| char *opt = print_argv(argv, gc, 0); |
| push_option(o, opt, msglevel); |
| } |
| |
| static bool |
| push_option_fmt(struct gc_arena *gc, struct push_list *push_list, |
| int msglevel, const char *format, ...) |
| { |
| va_list arglist; |
| char tmp[256] = {0}; |
| int len; |
| va_start(arglist, format); |
| len = vsnprintf(tmp, sizeof(tmp), format, arglist); |
| va_end(arglist); |
| if (len > sizeof(tmp)-1) |
| { |
| return false; |
| } |
| push_option_ex(gc, push_list, string_alloc(tmp, gc), true, msglevel); |
| return true; |
| } |
| |
| void |
| push_reset(struct options *o) |
| { |
| CLEAR(o->push_list); |
| } |
| |
| void |
| push_remove_option(struct options *o, const char *p) |
| { |
| msg(D_PUSH_DEBUG, "PUSH_REMOVE searching for: '%s'", p); |
| |
| /* ifconfig-ipv6 is special, as not part of the push list */ |
| if (streq( p, "ifconfig-ipv6" )) |
| { |
| o->push_ifconfig_ipv6_blocked = true; |
| return; |
| } |
| |
| if (o && o->push_list.head) |
| { |
| struct push_entry *e = o->push_list.head; |
| |
| /* cycle through the push list */ |
| while (e) |
| { |
| if (e->enable |
| && strncmp( e->option, p, strlen(p) ) == 0) |
| { |
| msg(D_PUSH_DEBUG, "PUSH_REMOVE removing: '%s'", e->option); |
| e->enable = false; |
| } |
| |
| e = e->next; |
| } |
| } |
| } |
| #endif /* if P2MP_SERVER */ |
| |
| #if P2MP_SERVER |
| int |
| process_incoming_push_request(struct context *c) |
| { |
| int ret = PUSH_MSG_ERROR; |
| |
| #ifdef ENABLE_ASYNC_PUSH |
| c->c2.push_request_received = true; |
| #endif |
| if (tls_authentication_status(c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED) |
| { |
| const char *client_reason = tls_client_reason(c->c2.tls_multi); |
| send_auth_failed(c, client_reason); |
| ret = PUSH_MSG_AUTH_FAILURE; |
| } |
| else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED) |
| { |
| time_t now; |
| |
| openvpn_time(&now); |
| if (c->c2.sent_push_reply_expiry > now) |
| { |
| ret = PUSH_MSG_ALREADY_REPLIED; |
| } |
| else |
| { |
| /* per-client push options - peer-id, cipher, ifconfig, ipv6-ifconfig */ |
| struct push_list push_list; |
| struct gc_arena gc = gc_new(); |
| |
| CLEAR(push_list); |
| if (prepare_push_reply(c, &gc, &push_list) |
| && send_push_reply(c, &push_list)) |
| { |
| ret = PUSH_MSG_REQUEST; |
| c->c2.sent_push_reply_expiry = now + 30; |
| } |
| gc_free(&gc); |
| } |
| } |
| else |
| { |
| ret = PUSH_MSG_REQUEST_DEFERRED; |
| } |
| |
| return ret; |
| } |
| #endif /* if P2MP_SERVER */ |
| |
| static void |
| push_update_digest(md_ctx_t *ctx, struct buffer *buf, const struct options *opt) |
| { |
| char line[OPTION_PARM_SIZE]; |
| while (buf_parse(buf, ',', line, sizeof(line))) |
| { |
| /* peer-id might change on restart and this should not trigger reopening tun */ |
| if (strprefix(line, "peer-id ")) |
| { |
| continue; |
| } |
| /* tun reopen only needed if cipher change can change tun MTU */ |
| if (strprefix(line, "cipher ") && !opt->ce.tun_mtu_defined) |
| { |
| continue; |
| } |
| md_ctx_update(ctx, (const uint8_t *) line, strlen(line)+1); |
| } |
| } |
| |
| int |
| process_incoming_push_msg(struct context *c, |
| const struct buffer *buffer, |
| bool honor_received_options, |
| unsigned int permission_mask, |
| unsigned int *option_types_found) |
| { |
| int ret = PUSH_MSG_ERROR; |
| struct buffer buf = *buffer; |
| |
| #if P2MP_SERVER |
| if (buf_string_compare_advance(&buf, "PUSH_REQUEST")) |
| { |
| ret = process_incoming_push_request(c); |
| } |
| else |
| #endif |
| |
| if (honor_received_options && buf_string_compare_advance(&buf, "PUSH_REPLY")) |
| { |
| const uint8_t ch = buf_read_u8(&buf); |
| if (ch == ',') |
| { |
| struct buffer buf_orig = buf; |
| if (!c->c2.pulled_options_digest_init_done) |
| { |
| c->c2.pulled_options_state = md_ctx_new(); |
| md_ctx_init(c->c2.pulled_options_state, md_kt_get("SHA256")); |
| c->c2.pulled_options_digest_init_done = true; |
| } |
| if (!c->c2.did_pre_pull_restore) |
| { |
| pre_pull_restore(&c->options, &c->c2.gc); |
| c->c2.did_pre_pull_restore = true; |
| } |
| if (apply_push_options(&c->options, |
| &buf, |
| permission_mask, |
| option_types_found, |
| c->c2.es)) |
| { |
| push_update_digest(c->c2.pulled_options_state, &buf_orig, |
| &c->options); |
| switch (c->options.push_continuation) |
| { |
| case 0: |
| case 1: |
| md_ctx_final(c->c2.pulled_options_state, c->c2.pulled_options_digest.digest); |
| md_ctx_cleanup(c->c2.pulled_options_state); |
| md_ctx_free(c->c2.pulled_options_state); |
| c->c2.pulled_options_state = NULL; |
| c->c2.pulled_options_digest_init_done = false; |
| ret = PUSH_MSG_REPLY; |
| break; |
| |
| case 2: |
| ret = PUSH_MSG_CONTINUATION; |
| break; |
| } |
| } |
| } |
| else if (ch == '\0') |
| { |
| ret = PUSH_MSG_REPLY; |
| } |
| /* show_settings (&c->options); */ |
| } |
| return ret; |
| } |
| |
| #if P2MP_SERVER |
| |
| /* |
| * Remove iroutes from the push_list. |
| */ |
| void |
| remove_iroutes_from_push_route_list(struct options *o) |
| { |
| if (o && o->push_list.head && o->iroutes) |
| { |
| struct gc_arena gc = gc_new(); |
| struct push_entry *e = o->push_list.head; |
| |
| /* cycle through the push list */ |
| while (e) |
| { |
| char *p[MAX_PARMS]; |
| bool enable = true; |
| |
| /* parse the push item */ |
| CLEAR(p); |
| if (e->enable |
| && parse_line(e->option, p, SIZE(p), "[PUSH_ROUTE_REMOVE]", 1, D_ROUTE_DEBUG, &gc)) |
| { |
| /* is the push item a route directive? */ |
| if (p[0] && !strcmp(p[0], "route") && !p[3]) |
| { |
| /* get route parameters */ |
| bool status1, status2; |
| const in_addr_t network = getaddr(GETADDR_HOST_ORDER, p[1], 0, &status1, NULL); |
| const in_addr_t netmask = getaddr(GETADDR_HOST_ORDER, p[2] ? p[2] : "255.255.255.255", 0, &status2, NULL); |
| |
| /* did route parameters parse correctly? */ |
| if (status1 && status2) |
| { |
| const struct iroute *ir; |
| |
| /* does route match an iroute? */ |
| for (ir = o->iroutes; ir != NULL; ir = ir->next) |
| { |
| if (network == ir->network && netmask == netbits_to_netmask(ir->netbits >= 0 ? ir->netbits : 32)) |
| { |
| enable = false; |
| break; |
| } |
| } |
| } |
| } |
| |
| /* should we copy the push item? */ |
| e->enable = enable; |
| if (!enable) |
| { |
| msg(D_PUSH, "REMOVE PUSH ROUTE: '%s'", e->option); |
| } |
| } |
| |
| e = e->next; |
| } |
| |
| gc_free(&gc); |
| } |
| } |
| |
| #endif /* if P2MP_SERVER */ |
| |
| #endif /* if P2MP */ |