@@ -5759,6 +5759,216 @@
err:
msg(msglevel, "Error occurred trying to remove %s option", p[0]);
}
+
+static void
+update_option(struct context *c,
+ struct options *options,
+ char *p[],
+ bool is_inline,
+ const char *file,
+ int line,
+ const int level,
+ const int msglevel,
+ const unsigned int permission_mask,
+ unsigned int *option_types_found,
+ struct env_set *es,
+ unsigned int *update_options_found)
+{
+ const bool pull_mode = BOOL_CAST(permission_mask & OPT_P_PULL_MODE);
+ ASSERT(MAX_PARMS >= 7);
+
+ if (streq(p[0], "route") && p[1] && !p[5])
+ {
+ if (!(*update_options_found & OPT_P_U_ROUTE))
+ {
+ VERIFY_PERMISSION(OPT_P_ROUTE);
+ rol_check_alloc(options);
+ if (pull_mode)
+ {
+ if (!ip_or_dns_addr_safe(p[1], options->allow_pull_fqdn) && !is_special_addr(p[1])) /* FQDN -- may be DNS name */
+ {
+ msg(msglevel, "route parameter network/IP '%s' must be a valid address", p[1]);
+ goto err;
+ }
+ if (p[2] && !ip_addr_dotted_quad_safe(p[2])) /* FQDN -- must be IP address */
+ {
+ msg(msglevel, "route parameter netmask '%s' must be an IP address", p[2]);
+ goto err;
+ }
+ if (p[3] && !ip_or_dns_addr_safe(p[3], options->allow_pull_fqdn) && !is_special_addr(p[3])) /* FQDN -- may be DNS name */
+ {
+ msg(msglevel, "route parameter gateway '%s' must be a valid address", p[3]);
+ goto err;
+ }
+ }
+ if (c->c1.route_list)
+ {
+ destroy_routes_v4(c->c1.route_list, c->c1.tuntap,
+ ROUTE_OPTION_FLAGS(&c->options),
+ es, &c->net_ctx, options);
+ }
+ *update_options_found |= OPT_P_U_ROUTE;
+ }
+ }
+ else if (streq(p[0], "route-ipv6") && p[1] && !p[4])
+ {
+ if (!(*update_options_found & OPT_P_U_ROUTE6))
+ {
+ VERIFY_PERMISSION(OPT_P_ROUTE);
+ rol6_check_alloc(options);
+ if (pull_mode)
+ {
+ if (!ipv6_addr_safe_hexplusbits(p[1]))
+ {
+ msg(msglevel, "route-ipv6 parameter network/IP '%s' must be a valid address", p[1]);
+ goto err;
+ }
+ if (p[2] && !ipv6_addr_safe(p[2]))
+ {
+ msg(msglevel, "route-ipv6 parameter gateway '%s' must be a valid address", p[2]);
+ goto err;
+ }
+ /* p[3] is metric, if present */
+ }
+ if (c->c1.route_ipv6_list)
+ {
+ destroy_routes_v6(c->c1.route_ipv6_list, c->c1.tuntap,
+ ROUTE_OPTION_FLAGS(&c->options),
+ es, &c->net_ctx, options);
+ }
+ *update_options_found |= OPT_P_U_ROUTE6;
+ }
+ }
+ else if (streq(p[0], "redirect-gateway") || streq(p[0], "redirect-private"))
+ {
+ if (!(*update_options_found & OPT_P_U_REDIR_GATEWAY))
+ {
+ VERIFY_PERMISSION(OPT_P_ROUTE);
+ if (options->routes)
+ {
+ options->routes->flags = 0;
+ }
+ if (options->routes_ipv6)
+ {
+ options->routes_ipv6->flags = 0;
+ }
+ *update_options_found |= OPT_P_U_REDIR_GATEWAY;
+ }
+ }
+ else if (streq(p[0], "dns") && p[1])
+ {
+ if (!(*update_options_found & OPT_P_U_DNS))
+ {
+ VERIFY_PERMISSION(OPT_P_DHCPDNS);
+ if (streq(p[1], "server") && p[2] && p[3] && p[4])
+ {
+ long priority;
+ if (!dns_server_priority_parse(&priority, p[2], pull_mode))
+ {
+ msg(msglevel, "--dns server: invalid priority value '%s'", p[2]);
+ goto err;
+ }
+
+ struct dns_server server;
+ CLEAR(server);
+ if (streq(p[3], "address") && p[4])
+ {
+ for (int i = 4; p[i]; ++i)
+ {
+ if (!dns_server_addr_parse(&server, p[i]))
+ {
+ msg(msglevel, "--dns server %ld: malformed address or maximum exceeded '%s'", priority, p[i]);
+ goto err;
+ }
+ }
+ }
+ else if (streq(p[3], "dnssec") && !p[5])
+ {
+ if (!streq(p[4], "yes") && !streq(p[4], "no") && !streq(p[4], "optional"))
+ {
+ msg(msglevel, "--dns server %ld: malformed dnssec value '%s'", priority, p[4]);
+ goto err;
+ }
+ }
+ else if (streq(p[3], "transport") && !p[5])
+ {
+ if (!streq(p[4], "plain") && !streq(p[4], "DoH") && !streq(p[4], "DoT"))
+ {
+ msg(msglevel, "--dns server %ld: malformed transport value '%s'", priority, p[4]);
+ goto err;
+ }
+ }
+ else if (!streq(p[3], "resolve-domains")
+ && !(streq(p[3], "sni") && !p[5]))
+ {
+ msg(msglevel, "--dns server %ld: unknown option type '%s' or missing or unknown parameter", priority, p[3]);
+ goto err;
+ }
+ }
+ else if (!(streq(p[1], "search-domains") && p[2]))
+ {
+ msg(msglevel, "--dns: unknown option type '%s' or missing or unknown parameter", p[1]);
+ goto err;
+ }
+
+ gc_free(&options->dns_options.gc);
+ CLEAR(options->dns_options);
+ *update_options_found |= OPT_P_U_DNS;
+ }
+ }
+#if defined(_WIN32) || defined(TARGET_ANDROID)
+ else if (streq(p[0], "dhcp-option") && p[1] && !p[3])
+ {
+ if (!(*update_options_found & OPT_P_U_DHCP))
+ {
+ struct tuntap_options *o = &options->tuntap_options;
+ VERIFY_PERMISSION(OPT_P_DHCPDNS);
+
+ o->domain = NULL;
+ o->netbios_scope = NULL;
+ o->netbios_node_type = 0;
+ o->dns6_len = 0;
+ CLEAR(o->dns6);
+ o->dns_len = 0;
+ CLEAR(o->dns);
+ o->wins_len = 0;
+ CLEAR(o->wins);
+ o->ntp_len = 0;
+ CLEAR(o->ntp);
+ o->nbdd_len = 0;
+ CLEAR(o->nbdd);
+ while (o->domain_search_list_len-- > 0)
+ {
+ o->domain_search_list[o->domain_search_list_len] = NULL;
+ }
+ o->disable_nbt = 0;
+ o->dhcp_options = 0;
+#if defined(TARGET_ANDROID)
+ o->http_proxy_port = 0;
+ o->http_proxy = NULL;
+#endif
+ *update_options_found |= OPT_P_U_DHCP;
+ }
+ }
+#else /* if defined(_WIN32) || defined(TARGET_ANDROID) */
+ else if (streq(p[0], "dhcp-option") && p[1] && !p[3])
+ {
+ if (!(*update_options_found & OPT_P_U_DHCP))
+ {
+ VERIFY_PERMISSION(OPT_P_DHCPDNS);
+ delete_all_dhcp_fo(options, &es->list);
+ *update_options_found |= OPT_P_U_DHCP;
+ }
+ }
+#endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */
+ add_option(options, p, is_inline, file, line,
+ level, msglevel, permission_mask,
+ option_types_found, es);
+ return;
+err:
+ msg(msglevel, "Error occurred trying to update %s option", p[0]);
+}
+
bool
apply_push_options(struct context *c,
struct options *options,
@@ -5772,6 +5982,7 @@
int line_num = 0;
const char *file = "[PUSH-OPTIONS]";
const int msglevel = D_PUSH_ERRORS|M_OPTERR;
+ unsigned int update_options_found = 0;
while (buf_parse(buf, ',', line, sizeof(line)))
{
@@ -5797,6 +6008,11 @@
remove_option(c, options, p, false, file, line_num, msglevel,
permission_mask, option_types_found, es);
}
+ else
+ {
+ update_option(c, options, p, false, file, line_num, 0, msglevel,
+ permission_mask, option_types_found, es, &update_options_found);
+ }
}
}
return true;