Browse code

PUSH_UPDATE: Added update_option() function.

When the function receives an option to update, it first checks whether it has
already received an option of the same type within the same update message.
If it has already received it, it simply calls add_option(), otherwise it
deletes all the values already present for that option first.

Change-Id: Ia45c99e6df7b3ad24020c10b8a9b3577984ecdc2
Signed-off-by: Marco Baffo <marco@mandelbit.com>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20250729104110.27704-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg32408.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Marco Baffo authored on 2025/07/29 19:41:01
Showing 1 changed files
... ...
@@ -5360,6 +5360,20 @@ remove_option(struct context *c,
5360 5360
               struct env_set *es);
5361 5361
 
5362 5362
 static void
5363
+update_option(struct context *c,
5364
+              struct options *options,
5365
+              char *p[],
5366
+              bool is_inline,
5367
+              const char *file,
5368
+              int line,
5369
+              const int level,
5370
+              const int msglevel,
5371
+              const unsigned int permission_mask,
5372
+              unsigned int *option_types_found,
5373
+              struct env_set *es,
5374
+              unsigned int *update_options_found);
5375
+
5376
+static void
5363 5377
 read_config_file(struct options *options,
5364 5378
                  const char *file,
5365 5379
                  int level,
... ...
@@ -5545,6 +5559,7 @@ apply_push_options(struct context *c,
5545 5545
     int line_num = 0;
5546 5546
     const char *file = "[PUSH-OPTIONS]";
5547 5547
     const int msglevel = D_PUSH_ERRORS|M_OPTERR;
5548
+    unsigned int update_options_found = 0;
5548 5549
 
5549 5550
     while (buf_parse(buf, ',', line, sizeof(line)))
5550 5551
     {
... ...
@@ -5590,6 +5605,11 @@ apply_push_options(struct context *c,
5590 5590
                 remove_option(c, options, p, false, file, line_num, msglevel,
5591 5591
                               permission_mask, option_types_found, es);
5592 5592
             }
5593
+            else
5594
+            {
5595
+                update_option(c, options, p, false, file, line_num, 0, msglevel,
5596
+                              permission_mask, option_types_found, es, &update_options_found);
5597
+            }
5593 5598
         }
5594 5599
     }
5595 5600
     return true;
... ...
@@ -5728,6 +5748,13 @@ msglevel_forward_compatible(struct options *options, const int msglevel)
5728 5728
     return options->forward_compatible ? M_WARN : msglevel;
5729 5729
 }
5730 5730
 
5731
+#define RESET_OPTION_ROUTES(option_ptr, field) \
5732
+    if (option_ptr)                        \
5733
+    {                                      \
5734
+        option_ptr->field = NULL;          \
5735
+        option_ptr->flags = 0;             \
5736
+    }
5737
+
5731 5738
 /**
5732 5739
  * @brief Resets options found in the PUSH_UPDATE message that are preceded by the `-` flag.
5733 5740
  *        This function is used in push-updates to reset specified options.
... ...
@@ -5782,11 +5809,7 @@ remove_option(struct context *c,
5782 5782
             delete_routes_v4(c->c1.route_list, c->c1.tuntap,
5783 5783
                              ROUTE_OPTION_FLAGS(&c->options),
5784 5784
                              es, &c->net_ctx);
5785
-            if (options->routes)
5786
-            {
5787
-                options->routes->routes = NULL;
5788
-                options->routes->flags = 0;
5789
-            }
5785
+            RESET_OPTION_ROUTES(options->routes, routes);
5790 5786
         }
5791 5787
     }
5792 5788
     else if (streq(p[0], "route-ipv6") && !p[1])
... ...
@@ -5797,11 +5820,7 @@ remove_option(struct context *c,
5797 5797
             delete_routes_v6(c->c1.route_ipv6_list, c->c1.tuntap,
5798 5798
                              ROUTE_OPTION_FLAGS(&c->options),
5799 5799
                              es, &c->net_ctx);
5800
-            if (options->routes_ipv6)
5801
-            {
5802
-                options->routes_ipv6->routes_ipv6 = NULL;
5803
-                options->routes_ipv6->flags = 0;
5804
-            }
5800
+            RESET_OPTION_ROUTES(options->routes_ipv6, routes_ipv6);
5805 5801
         }
5806 5802
     }
5807 5803
     else if (streq(p[0], "route-gateway") && !p[1])
... ...
@@ -5921,6 +5940,303 @@ err:
5921 5921
     msg(msglevel, "Error occurred trying to remove %s option", p[0]);
5922 5922
 }
5923 5923
 
5924
+
5925
+static bool
5926
+check_route_option(struct options *options, char *p[], const int msglevel, bool pull_mode)
5927
+{
5928
+    rol_check_alloc(options);
5929
+    if (pull_mode)
5930
+    {
5931
+        if (!ip_or_dns_addr_safe(p[1], options->allow_pull_fqdn) && !is_special_addr(p[1])) /* FQDN -- may be DNS name */
5932
+        {
5933
+            msg(msglevel, "route parameter network/IP '%s' must be a valid address", p[1]);
5934
+            return false;
5935
+        }
5936
+        if (p[2] && !ip_addr_dotted_quad_safe(p[2])) /* FQDN -- must be IP address */
5937
+        {
5938
+            msg(msglevel, "route parameter netmask '%s' must be an IP address", p[2]);
5939
+            return false;
5940
+        }
5941
+        if (p[3] && !ip_or_dns_addr_safe(p[3], options->allow_pull_fqdn) && !is_special_addr(p[3])) /* FQDN -- may be DNS name */
5942
+        {
5943
+            msg(msglevel, "route parameter gateway '%s' must be a valid address", p[3]);
5944
+            return false;
5945
+        }
5946
+    }
5947
+    return true;
5948
+}
5949
+
5950
+
5951
+static bool
5952
+check_route6_option(struct options *options, char *p[], const int msglevel, bool pull_mode)
5953
+{
5954
+    rol6_check_alloc(options);
5955
+    if (pull_mode)
5956
+    {
5957
+        if (!ipv6_addr_safe_hexplusbits(p[1]))
5958
+        {
5959
+            msg(msglevel, "route-ipv6 parameter network/IP '%s' must be a valid address", p[1]);
5960
+            return false;
5961
+        }
5962
+        if (p[2] && !ipv6_addr_safe(p[2]))
5963
+        {
5964
+            msg(msglevel, "route-ipv6 parameter gateway '%s' must be a valid address", p[2]);
5965
+            return false;
5966
+        }
5967
+        /* p[3] is metric, if present */
5968
+    }
5969
+    return true;
5970
+}
5971
+
5972
+static bool
5973
+check_dns_option(struct options *options, char *p[], const int msglevel, bool pull_mode)
5974
+{
5975
+    if (streq(p[1], "search-domains") && p[2])
5976
+    {
5977
+        dns_domain_list_append(&options->dns_options.search_domains, &p[2], &options->dns_options.gc);
5978
+    }
5979
+    else if (streq(p[1], "server") && p[2] && p[3] && p[4])
5980
+    {
5981
+        long priority;
5982
+        if (!dns_server_priority_parse(&priority, p[2], pull_mode))
5983
+        {
5984
+            msg(msglevel, "--dns server: invalid priority value '%s'", p[2]);
5985
+            return false;
5986
+        }
5987
+
5988
+        struct dns_server *server = dns_server_get(&options->dns_options.servers, priority, &options->dns_options.gc);
5989
+
5990
+        if (streq(p[3], "address") && p[4])
5991
+        {
5992
+            for (int i = 4; p[i]; ++i)
5993
+            {
5994
+                if (!dns_server_addr_parse(server, p[i]))
5995
+                {
5996
+                    msg(msglevel, "--dns server %ld: malformed address or maximum exceeded '%s'", priority, p[i]);
5997
+                    return false;
5998
+                }
5999
+            }
6000
+        }
6001
+        else if (streq(p[3], "resolve-domains"))
6002
+        {
6003
+            dns_domain_list_append(&server->domains, &p[4], &options->dns_options.gc);
6004
+        }
6005
+        else if (streq(p[3], "dnssec") && !p[5])
6006
+        {
6007
+            if (streq(p[4], "yes"))
6008
+            {
6009
+                server->dnssec = DNS_SECURITY_YES;
6010
+            }
6011
+            else if (streq(p[4], "no"))
6012
+            {
6013
+                server->dnssec = DNS_SECURITY_NO;
6014
+            }
6015
+            else if (streq(p[4], "optional"))
6016
+            {
6017
+                server->dnssec = DNS_SECURITY_OPTIONAL;
6018
+            }
6019
+            else
6020
+            {
6021
+                msg(msglevel, "--dns server %ld: malformed dnssec value '%s'", priority, p[4]);
6022
+                return false;
6023
+            }
6024
+        }
6025
+        else if (streq(p[3], "transport") && !p[5])
6026
+        {
6027
+            if (streq(p[4], "plain"))
6028
+            {
6029
+                server->transport = DNS_TRANSPORT_PLAIN;
6030
+            }
6031
+            else if (streq(p[4], "DoH"))
6032
+            {
6033
+                server->transport = DNS_TRANSPORT_HTTPS;
6034
+            }
6035
+            else if (streq(p[4], "DoT"))
6036
+            {
6037
+                server->transport = DNS_TRANSPORT_TLS;
6038
+            }
6039
+            else
6040
+            {
6041
+                msg(msglevel, "--dns server %ld: malformed transport value '%s'", priority, p[4]);
6042
+                return false;
6043
+            }
6044
+        }
6045
+        else if (streq(p[3], "sni") && !p[5])
6046
+        {
6047
+            server->sni = p[4];
6048
+        }
6049
+        else
6050
+        {
6051
+            msg(msglevel, "--dns server %ld: unknown option type '%s' or missing or unknown parameter", priority, p[3]);
6052
+            return false;
6053
+        }
6054
+    }
6055
+    else
6056
+    {
6057
+        msg(msglevel, "--dns: unknown option type '%s' or missing or unknown parameter", p[1]);
6058
+        return false;
6059
+    }
6060
+    return true;
6061
+}
6062
+
6063
+/**
6064
+ * @brief Processes an option to update. It first checks whether it has already
6065
+ *        received an option of the same type within the same update message.
6066
+ *        If the option has already been received, it calls add_option().
6067
+ *        Otherwise, it deletes all existing values related to that option before calling add_option().
6068
+ *
6069
+ * @param c The context structure.
6070
+ * @param options A pointer to the options structure.
6071
+ * @param p An array of strings containing the options and their parameters.
6072
+ * @param is_inline A boolean indicating if the option is inline.
6073
+ * @param file The file where the function is called.
6074
+ * @param line The line number where the function is called.
6075
+ * @param level The level of the option.
6076
+ * @param msglevel The message level for logging.
6077
+ * @param permission_mask The permission mask used by VERIFY_PERMISSION().
6078
+ * @param option_types_found A pointer to the variable where the flags corresponding to the options found are stored.
6079
+ * @param es The environment set structure.
6080
+ * @param update_options_found A pointer to the variable where the flags corresponding to the update options found are stored,
6081
+ *                             used to check if an option of the same type has already been processed by update_option() within the same push-update message.
6082
+ */
6083
+static void
6084
+update_option(struct context *c,
6085
+              struct options *options,
6086
+              char *p[],
6087
+              bool is_inline,
6088
+              const char *file,
6089
+              int line,
6090
+              const int level,
6091
+              const int msglevel,
6092
+              const unsigned int permission_mask,
6093
+              unsigned int *option_types_found,
6094
+              struct env_set *es,
6095
+              unsigned int *update_options_found)
6096
+{
6097
+    const bool pull_mode = BOOL_CAST(permission_mask & OPT_P_PULL_MODE);
6098
+    ASSERT(MAX_PARMS >= 7);
6099
+
6100
+    if (streq(p[0], "route") && p[1] && !p[5])
6101
+    {
6102
+        if (!(*update_options_found & OPT_P_U_ROUTE))
6103
+        {
6104
+            VERIFY_PERMISSION(OPT_P_ROUTE);
6105
+            if (!check_route_option(options, p, msglevel, pull_mode))
6106
+            {
6107
+                goto err;
6108
+            }
6109
+            if (c->c1.route_list)
6110
+            {
6111
+                delete_routes_v4(c->c1.route_list, c->c1.tuntap,
6112
+                                 ROUTE_OPTION_FLAGS(&c->options),
6113
+                                 es, &c->net_ctx);
6114
+                RESET_OPTION_ROUTES(options->routes, routes);
6115
+            }
6116
+            *update_options_found |= OPT_P_U_ROUTE;
6117
+        }
6118
+    }
6119
+    else if (streq(p[0], "route-ipv6") && p[1] && !p[4])
6120
+    {
6121
+        if (!(*update_options_found & OPT_P_U_ROUTE6))
6122
+        {
6123
+            VERIFY_PERMISSION(OPT_P_ROUTE);
6124
+            if (!check_route6_option(options, p, msglevel, pull_mode))
6125
+            {
6126
+                goto err;
6127
+            }
6128
+            if (c->c1.route_ipv6_list)
6129
+            {
6130
+                delete_routes_v6(c->c1.route_ipv6_list, c->c1.tuntap,
6131
+                                 ROUTE_OPTION_FLAGS(&c->options),
6132
+                                 es, &c->net_ctx);
6133
+                RESET_OPTION_ROUTES(options->routes_ipv6, routes_ipv6);
6134
+            }
6135
+            *update_options_found |= OPT_P_U_ROUTE6;
6136
+        }
6137
+    }
6138
+    else if (streq(p[0], "redirect-gateway") || streq(p[0], "redirect-private"))
6139
+    {
6140
+        if (!(*update_options_found & OPT_P_U_REDIR_GATEWAY))
6141
+        {
6142
+            VERIFY_PERMISSION(OPT_P_ROUTE);
6143
+            if (options->routes)
6144
+            {
6145
+                options->routes->flags = 0;
6146
+            }
6147
+            if (options->routes_ipv6)
6148
+            {
6149
+                options->routes_ipv6->flags = 0;
6150
+            }
6151
+            *update_options_found |= OPT_P_U_REDIR_GATEWAY;
6152
+        }
6153
+    }
6154
+    else if (streq(p[0], "dns") && p[1])
6155
+    {
6156
+        if (!(*update_options_found & OPT_P_U_DNS))
6157
+        {
6158
+            VERIFY_PERMISSION(OPT_P_DHCPDNS);
6159
+            if (!check_dns_option(options, p, msglevel, pull_mode))
6160
+            {
6161
+                goto err;
6162
+            }
6163
+            gc_free(&options->dns_options.gc);
6164
+            CLEAR(options->dns_options);
6165
+            *update_options_found |= OPT_P_U_DNS;
6166
+        }
6167
+    }
6168
+#if defined(_WIN32) || defined(TARGET_ANDROID)
6169
+    else if (streq(p[0], "dhcp-option") && p[1] && !p[3])
6170
+    {
6171
+        if (!(*update_options_found & OPT_P_U_DHCP))
6172
+        {
6173
+            struct tuntap_options *o = &options->tuntap_options;
6174
+            VERIFY_PERMISSION(OPT_P_DHCPDNS);
6175
+
6176
+            o->domain = NULL;
6177
+            o->netbios_scope = NULL;
6178
+            o->netbios_node_type = 0;
6179
+            o->dns6_len = 0;
6180
+            CLEAR(o->dns6);
6181
+            o->dns_len = 0;
6182
+            CLEAR(o->dns);
6183
+            o->wins_len = 0;
6184
+            CLEAR(o->wins);
6185
+            o->ntp_len = 0;
6186
+            CLEAR(o->ntp);
6187
+            o->nbdd_len = 0;
6188
+            CLEAR(o->nbdd);
6189
+            while (o->domain_search_list_len-- > 0)
6190
+            {
6191
+                o->domain_search_list[o->domain_search_list_len] = NULL;
6192
+            }
6193
+            o->disable_nbt = 0;
6194
+            o->dhcp_options = 0;
6195
+#if defined(TARGET_ANDROID)
6196
+            o->http_proxy_port = 0;
6197
+            o->http_proxy = NULL;
6198
+#endif
6199
+            *update_options_found |= OPT_P_U_DHCP;
6200
+        }
6201
+    }
6202
+#else /* if defined(_WIN32) || defined(TARGET_ANDROID) */
6203
+    else if (streq(p[0], "dhcp-option") && p[1] && !p[3])
6204
+    {
6205
+        if (!(*update_options_found & OPT_P_U_DHCP))
6206
+        {
6207
+            VERIFY_PERMISSION(OPT_P_DHCPDNS);
6208
+            delete_all_dhcp_fo(options, &es->list);
6209
+            *update_options_found |= OPT_P_U_DHCP;
6210
+        }
6211
+    }
6212
+#endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */
6213
+    add_option(options, p, is_inline, file, line,
6214
+               level, msglevel, permission_mask,
6215
+               option_types_found, es);
6216
+    return;
6217
+err:
6218
+    msg(msglevel, "Error occurred trying to update %s option", p[0]);
6219
+}
6220
+
5924 6221
 static void
5925 6222
 set_user_script(struct options *options,
5926 6223
                 const char **script,
... ...
@@ -7284,45 +7600,18 @@ add_option(struct options *options,
7284 7284
     else if (streq(p[0], "route") && p[1] && !p[5])
7285 7285
     {
7286 7286
         VERIFY_PERMISSION(OPT_P_ROUTE);
7287
-        rol_check_alloc(options);
7288
-        if (pull_mode)
7287
+        if (!check_route_option(options, p, msglevel, pull_mode))
7289 7288
         {
7290
-            if (!ip_or_dns_addr_safe(p[1], options->allow_pull_fqdn) && !is_special_addr(p[1])) /* FQDN -- may be DNS name */
7291
-            {
7292
-                msg(msglevel, "route parameter network/IP '%s' must be a valid address", p[1]);
7293
-                goto err;
7294
-            }
7295
-            if (p[2] && !ip_addr_dotted_quad_safe(p[2])) /* FQDN -- must be IP address */
7296
-            {
7297
-                msg(msglevel, "route parameter netmask '%s' must be an IP address", p[2]);
7298
-                goto err;
7299
-            }
7300
-            if (p[3] && !ip_or_dns_addr_safe(p[3], options->allow_pull_fqdn) && !is_special_addr(p[3])) /* FQDN -- may be DNS name */
7301
-            {
7302
-                msg(msglevel, "route parameter gateway '%s' must be a valid address", p[3]);
7303
-                goto err;
7304
-            }
7305
-            /* p[4] is metric, if specified */
7289
+            goto err;
7306 7290
         }
7307 7291
         add_route_to_option_list(options->routes, p[1], p[2], p[3], p[4], options->route_default_table_id);
7308 7292
     }
7309 7293
     else if (streq(p[0], "route-ipv6") && p[1] && !p[4])
7310 7294
     {
7311 7295
         VERIFY_PERMISSION(OPT_P_ROUTE);
7312
-        rol6_check_alloc(options);
7313
-        if (pull_mode)
7296
+        if (!check_route6_option(options, p, msglevel, pull_mode))
7314 7297
         {
7315
-            if (!ipv6_addr_safe_hexplusbits(p[1]))
7316
-            {
7317
-                msg(msglevel, "route-ipv6 parameter network/IP '%s' must be a valid address", p[1]);
7318
-                goto err;
7319
-            }
7320
-            if (p[2] && !ipv6_addr_safe(p[2]))
7321
-            {
7322
-                msg(msglevel, "route-ipv6 parameter gateway '%s' must be a valid address", p[2]);
7323
-                goto err;
7324
-            }
7325
-            /* p[3] is metric, if specified */
7298
+            goto err;
7326 7299
         }
7327 7300
         add_route_ipv6_to_option_list(options->routes_ipv6, p[1], p[2], p[3], options->route_default_table_id);
7328 7301
     }
... ...
@@ -8410,90 +8699,8 @@ add_option(struct options *options,
8410 8410
     else if (streq(p[0], "dns") && p[1])
8411 8411
     {
8412 8412
         VERIFY_PERMISSION(OPT_P_DHCPDNS);
8413
-
8414
-        if (streq(p[1], "search-domains") && p[2])
8415
-        {
8416
-            dns_domain_list_append(&options->dns_options.search_domains, &p[2], &options->dns_options.gc);
8417
-        }
8418
-        else if (streq(p[1], "server") && p[2] && p[3] && p[4])
8419
-        {
8420
-            long priority;
8421
-            if (!dns_server_priority_parse(&priority, p[2], pull_mode))
8422
-            {
8423
-                msg(msglevel, "--dns server: invalid priority value '%s'", p[2]);
8424
-                goto err;
8425
-            }
8426
-
8427
-            struct dns_server *server = dns_server_get(&options->dns_options.servers, priority, &options->dns_options.gc);
8428
-
8429
-            if (streq(p[3], "address") && p[4])
8430
-            {
8431
-                for (int i = 4; p[i]; ++i)
8432
-                {
8433
-                    if (!dns_server_addr_parse(server, p[i]))
8434
-                    {
8435
-                        msg(msglevel, "--dns server %ld: malformed address or maximum exceeded '%s'", priority, p[i]);
8436
-                        goto err;
8437
-                    }
8438
-                }
8439
-            }
8440
-            else if (streq(p[3], "resolve-domains"))
8441
-            {
8442
-                dns_domain_list_append(&server->domains, &p[4], &options->dns_options.gc);
8443
-            }
8444
-            else if (streq(p[3], "dnssec") && !p[5])
8445
-            {
8446
-                if (streq(p[4], "yes"))
8447
-                {
8448
-                    server->dnssec = DNS_SECURITY_YES;
8449
-                }
8450
-                else if (streq(p[4], "no"))
8451
-                {
8452
-                    server->dnssec = DNS_SECURITY_NO;
8453
-                }
8454
-                else if (streq(p[4], "optional"))
8455
-                {
8456
-                    server->dnssec = DNS_SECURITY_OPTIONAL;
8457
-                }
8458
-                else
8459
-                {
8460
-                    msg(msglevel, "--dns server %ld: malformed dnssec value '%s'", priority, p[4]);
8461
-                    goto err;
8462
-                }
8463
-            }
8464
-            else if (streq(p[3], "transport") && !p[5])
8465
-            {
8466
-                if (streq(p[4], "plain"))
8467
-                {
8468
-                    server->transport = DNS_TRANSPORT_PLAIN;
8469
-                }
8470
-                else if (streq(p[4], "DoH"))
8471
-                {
8472
-                    server->transport = DNS_TRANSPORT_HTTPS;
8473
-                }
8474
-                else if (streq(p[4], "DoT"))
8475
-                {
8476
-                    server->transport = DNS_TRANSPORT_TLS;
8477
-                }
8478
-                else
8479
-                {
8480
-                    msg(msglevel, "--dns server %ld: malformed transport value '%s'", priority, p[4]);
8481
-                    goto err;
8482
-                }
8483
-            }
8484
-            else if (streq(p[3], "sni") && !p[5])
8485
-            {
8486
-                server->sni = p[4];
8487
-            }
8488
-            else
8489
-            {
8490
-                msg(msglevel, "--dns server %ld: unknown option type '%s' or missing or unknown parameter", priority, p[3]);
8491
-                goto err;
8492
-            }
8493
-        }
8494
-        else
8413
+        if (!check_dns_option(options, p, msglevel, pull_mode))
8495 8414
         {
8496
-            msg(msglevel, "--dns: unknown option type '%s' or missing or unknown parameter", p[1]);
8497 8415
             goto err;
8498 8416
         }
8499 8417
     }