src/openvpn/push.c
6fbf66fa
 /*
  *  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.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
6fbf66fa
  *
  *  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.
  *
caa54ac3
  *  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.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #include "push.h"
 #include "options.h"
 #include "ssl.h"
63dc03d0
 #include "ssl_verify.h"
6fbf66fa
 #include "manage.h"
 
 #include "memdbg.h"
 
 #if P2MP
 
88c4b9d6
 static char push_reply_cmd[] = "PUSH_REPLY";
 
6fbf66fa
 /*
  * Auth username/password
  *
  * Client received an authentication failed message from server.
  * Runs on client.
  */
 void
81d882d5
 receive_auth_failed(struct context *c, const struct buffer *buffer)
6fbf66fa
 {
81d882d5
     msg(M_VERB0, "AUTH: Received control message: %s", BSTR(buffer));
     c->options.no_advance = true;
 
     if (c->options.pull)
     {
         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";
6fbf66fa
 #ifdef ENABLE_MANAGEMENT
81d882d5
         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);
         }
6fbf66fa
 #endif
81d882d5
         /*
          * Save the dynamic-challenge text even when management is defined
          */
         {
3cf9dd88
 #ifdef ENABLE_CLIENT_CR
81d882d5
             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));
             }
3cf9dd88
 #endif
81d882d5
         }
6fbf66fa
     }
 }
 
f25071b6
 /*
  * Act on received restart message from server
  */
 void
81d882d5
 server_pushed_signal(struct context *c, const struct buffer *buffer, const bool restart, const int adv)
f25071b6
 {
81d882d5
     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";
         }
e1b99e6b
 #ifdef ENABLE_MANAGEMENT
81d882d5
         if (management)
         {
             management_notify(management, "info", c->sig->signal_text, m);
         }
e1b99e6b
 #endif
f25071b6
     }
 }
 
6fbf66fa
 #if P2MP_SERVER
3212d0c0
 /**
  * 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,
81d882d5
                             int msglevel, const char *fmt, ...)
3212d0c0
 #ifdef __GNUC__
 #if __USE_MINGW_ANSI_STDIO
81d882d5
 __attribute__ ((format(gnu_printf, 4, 5)))
3212d0c0
 #else
81d882d5
 __attribute__ ((format(__printf__, 4, 5)))
3212d0c0
 #endif
 #endif
81d882d5
 ;
f25071b6
 
6fbf66fa
 /*
  * Send auth failed message from server to client.
  */
344ee918
 void
81d882d5
 send_auth_failed(struct context *c, const char *client_reason)
6fbf66fa
 {
81d882d5
     struct gc_arena gc = gc_new();
     static const char auth_failed[] = "AUTH_FAILED";
     size_t len;
5733ef66
 
81d882d5
     schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
5733ef66
 
81d882d5
     len = (client_reason ? strlen(client_reason)+1 : 0) + sizeof(auth_failed);
     if (len > PUSH_BUNDLE_SIZE)
     {
         len = PUSH_BUNDLE_SIZE;
     }
5733ef66
 
81d882d5
     {
         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);
     }
5733ef66
 
81d882d5
     gc_free(&gc);
6fbf66fa
 }
f25071b6
 
 /*
  * Send restart message from server to client.
  */
 void
81d882d5
 send_restart(struct context *c, const char *kill_msg)
f25071b6
 {
81d882d5
     schedule_exit(c, c->options.scheduled_exit_interval, SIGTERM);
     send_control_channel_string(c, kill_msg ? kill_msg : "RESTART", D_PUSH);
f25071b6
 }
 
81d882d5
 #endif /* if P2MP_SERVER */
6fbf66fa
 
 /*
  * Push/Pull
  */
 
 void
81d882d5
 incoming_push_message(struct context *c, const struct buffer *buffer)
6fbf66fa
 {
81d882d5
     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 tls_session *session = &c->c2.tls_multi->session[TM_ACTIVE];
             /* Do not regenerate keys if client send a second push request */
             if (!session->key[KS_PRIMARY].crypto_options.key_ctx_bi.initialized
                 && !tls_session_update_crypto_params(session, &c->options,
                                                      &c->c2.frame))
             {
                 msg(D_TLS_ERRORS, "TLS Error: initializing data channel failed");
                 goto error;
             }
         }
     }
 
     goto cleanup;
a17aa981
 error:
81d882d5
     register_signal(c, SIGUSR1, "process-push-msg-failed");
d728ebed
 cleanup:
81d882d5
     gc_free(&gc);
6fbf66fa
 }
 
 bool
81d882d5
 send_push_request(struct context *c)
6fbf66fa
 {
81d882d5
     const int max_push_requests = c->options.handshake_window / PUSH_REQUEST_INTERVAL;
     if (++c->c2.n_sent_push_requests <= max_push_requests)
4ff5b53f
     {
81d882d5
         return send_control_channel_string(c, "PUSH_REQUEST", D_PUSH);
4ff5b53f
     }
81d882d5
     else
4ff5b53f
     {
81d882d5
         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;
4ff5b53f
     }
6fbf66fa
 }
 
 #if P2MP_SERVER
3eee126e
 
3a5a46cf
 /**
  * Prepare push options, based on local options and available peer info.
  *
81d882d5
  * @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
3a5a46cf
  *
  * @return true on success, false on failure.
  */
 static bool
81d882d5
 prepare_push_reply(struct context *c, struct gc_arena *gc,
                    struct push_list *push_list)
3a5a46cf
 {
81d882d5
     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
6ae2f19d
         && c->c2.push_ifconfig_remote_netmask
         && !o->push_ifconfig_ipv4_blocked)
81d882d5
     {
         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);
3b9cce65
             tls_multi->use_peer_id = true;
81d882d5
         }
     }
 
     /* Push cipher if client supports Negotiable Crypto Parameters */
     if (tls_peer_info_ncp_ver(peer_info) >= 2 && o->ncp_enabled)
     {
5634cecf
         /* 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)
81d882d5
          */
         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, "
5634cecf
                  "re-sending previously negotiated cipher '%s'",
                  o->ciphername );
81d882d5
         }
         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, ":");
         }
5634cecf
         push_option_fmt(gc, push_list, M_USAGE, "cipher %s", o->ciphername);
81d882d5
     }
     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;
3a5a46cf
 }
 
 static bool
81d882d5
 send_push_options(struct context *c, struct buffer *buf,
                   struct push_list *push_list, int safe_cap,
                   bool *push_sent, bool *multi_push)
6fbf66fa
 {
81d882d5
     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;
88c4b9d6
 }
 
 static bool
81d882d5
 send_push_reply(struct context *c, struct push_list *per_client_push_list)
88c4b9d6
 {
81d882d5
     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;
     }
6fbf66fa
 
81d882d5
     /* send client-specific options */
     if (!send_push_options(c, &buf, per_client_push_list, safe_cap,
                            &push_sent, &multi_push))
3eee126e
     {
e0221d9d
         goto fail;
     }
 
81d882d5
     if (multi_push)
e0221d9d
     {
81d882d5
         buf_printf(&buf, ",push-continuation 1");
     }
e0221d9d
 
81d882d5
     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;
3eee126e
     }
6fbf66fa
 
81d882d5
     /* 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;
         }
     }
3eee126e
 
81d882d5
     gc_free(&gc);
     return true;
 
 fail:
     gc_free(&gc);
     return false;
6fbf66fa
 }
 
3eee126e
 static void
81d882d5
 push_option_ex(struct gc_arena *gc, struct push_list *push_list,
                const char *opt, bool enable, int msglevel)
6fbf66fa
 {
81d882d5
     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;
         }
3eee126e
     }
 }
 
 void
81d882d5
 push_option(struct options *o, const char *opt, int msglevel)
3eee126e
 {
81d882d5
     push_option_ex(&o->gc, &o->push_list, opt, true, msglevel);
3eee126e
 }
 
 void
81d882d5
 clone_push_list(struct options *o)
3eee126e
 {
81d882d5
     if (o->push_list.head)
3eee126e
     {
81d882d5
         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;
         }
6fbf66fa
     }
 }
 
 void
81d882d5
 push_options(struct options *o, char **p, int msglevel, struct gc_arena *gc)
eadf16a6
 {
81d882d5
     const char **argv = make_extended_arg_array(p, gc);
     char *opt = print_argv(argv, gc, 0);
     push_option(o, opt, msglevel);
eadf16a6
 }
 
81d882d5
 static bool
 push_option_fmt(struct gc_arena *gc, struct push_list *push_list,
                 int msglevel, const char *format, ...)
3a5a46cf
 {
81d882d5
     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;
3a5a46cf
 }
 
eadf16a6
 void
81d882d5
 push_reset(struct options *o)
6fbf66fa
 {
81d882d5
     CLEAR(o->push_list);
6fbf66fa
 }
970312f1
 
 void
81d882d5
 push_remove_option(struct options *o, const char *p)
970312f1
 {
81d882d5
     msg(D_PUSH_DEBUG, "PUSH_REMOVE searching for: '%s'", p);
970312f1
 
6ae2f19d
     /* ifconfig is special, as not part of the push list */
     if (streq(p, "ifconfig"))
     {
         o->push_ifconfig_ipv4_blocked = true;
         return;
     }
 
81d882d5
     /* ifconfig-ipv6 is special, as not part of the push list */
     if (streq( p, "ifconfig-ipv6" ))
970312f1
     {
81d882d5
         o->push_ifconfig_ipv6_blocked = true;
         return;
970312f1
     }
 
81d882d5
     if (o && o->push_list.head)
970312f1
     {
81d882d5
         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;
         }
970312f1
     }
 }
81d882d5
 #endif /* if P2MP_SERVER */
6fbf66fa
 
8929a395
 #if P2MP_SERVER
6fbf66fa
 int
81d882d5
 process_incoming_push_request(struct context *c)
0d1a75bf
 {
81d882d5
     int ret = PUSH_MSG_ERROR;
0d1a75bf
 
 #ifdef ENABLE_ASYNC_PUSH
81d882d5
     c->c2.push_request_received = true;
0d1a75bf
 #endif
81d882d5
     if (tls_authentication_status(c->c2.tls_multi, 0) == TLS_AUTHENTICATION_FAILED || c->c2.context_auth == CAS_FAILED)
0d1a75bf
     {
81d882d5
         const char *client_reason = tls_client_reason(c->c2.tls_multi);
         send_auth_failed(c, client_reason);
         ret = PUSH_MSG_AUTH_FAILURE;
0d1a75bf
     }
81d882d5
     else if (!c->c2.push_reply_deferred && c->c2.context_auth == CAS_SUCCEEDED)
0d1a75bf
     {
81d882d5
         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);
         }
0d1a75bf
     }
81d882d5
     else
0d1a75bf
     {
81d882d5
         ret = PUSH_MSG_REQUEST_DEFERRED;
0d1a75bf
     }
 
81d882d5
     return ret;
0d1a75bf
 }
81d882d5
 #endif /* if P2MP_SERVER */
0d1a75bf
 
3cf51f61
 static void
ec4dff3b
 push_update_digest(md_ctx_t *ctx, struct buffer *buf, const struct options *opt)
3cf51f61
 {
81d882d5
     char line[OPTION_PARM_SIZE];
     while (buf_parse(buf, ',', line, sizeof(line)))
3cf51f61
     {
81d882d5
         /* peer-id might change on restart and this should not trigger reopening tun */
ec4dff3b
         if (strprefix(line, "peer-id "))
81d882d5
         {
ec4dff3b
             continue;
         }
         /* tun reopen only needed if cipher change can change tun MTU */
         if (strprefix(line, "cipher ") && !opt->ce.tun_mtu_defined)
         {
             continue;
81d882d5
         }
a5dbf8c8
         md_ctx_update(ctx, (const uint8_t *) line, strlen(line)+1);
3cf51f61
     }
 }
 
0d1a75bf
 int
81d882d5
 process_incoming_push_msg(struct context *c,
                           const struct buffer *buffer,
                           bool honor_received_options,
                           unsigned int permission_mask,
                           unsigned int *option_types_found)
6fbf66fa
 {
81d882d5
     int ret = PUSH_MSG_ERROR;
     struct buffer buf = *buffer;
6fbf66fa
 
 #if P2MP_SERVER
81d882d5
     if (buf_string_compare_advance(&buf, "PUSH_REQUEST"))
6fbf66fa
     {
81d882d5
         ret = process_incoming_push_request(c);
6fbf66fa
     }
81d882d5
     else
6fbf66fa
 #endif
 
81d882d5
     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;
5b48e8c9
             if (!c->c2.pulled_options_digest_init_done)
81d882d5
             {
c481ef00
                 c->c2.pulled_options_state = md_ctx_new();
                 md_ctx_init(c->c2.pulled_options_state, md_kt_get("SHA256"));
5b48e8c9
                 c->c2.pulled_options_digest_init_done = true;
81d882d5
             }
             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))
             {
c481ef00
                 push_update_digest(c->c2.pulled_options_state, &buf_orig,
ec4dff3b
                                    &c->options);
81d882d5
                 switch (c->options.push_continuation)
                 {
                     case 0:
                     case 1:
c481ef00
                         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;
5b48e8c9
                         c->c2.pulled_options_digest_init_done = false;
81d882d5
                         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;
6fbf66fa
 }
 
 #if P2MP_SERVER
3eee126e
 
6fbf66fa
 /*
  * Remove iroutes from the push_list.
  */
 void
81d882d5
 remove_iroutes_from_push_route_list(struct options *o)
6fbf66fa
 {
81d882d5
     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);
6fbf66fa
     }
 }
3eee126e
 
81d882d5
 #endif /* if P2MP_SERVER */
6fbf66fa
 
81d882d5
 #endif /* if P2MP */