Browse code

dns: deal with --dhcp-options when --dns is active

Since --dns settings overrule DNS related --dhcp-options,
remove the latter when values were defined via --dns.

To stay as backward compatible as possible, we add foreign_options to
the script hook environment from the --dns values when a --up script
is defined. In that case the default --dns-updown is not run, even
when --dns values are present, to prevent double DNS configuration.
This way an existing --up script that deals with DNS can run, without
the immediate need to change after an openvpn upgrade and a server
pushing --dns options.

If you specify a custom --dns-updown, or force running the default
dns-updown that comes with openvpn, those compat env vars are not set
for --up scripts and the dns-updown command is run, even when there's
an --up script present.

Since Android uses the DNS values from tuntap_options, we always
override those with --dns stuff unconditionally. Also on Windows when
--ip-win32 is dynamic or adaptive, since DHCP relies on these as well.

Change-Id: I635c4018fb43b5976a39b6a90cb2e9cb2570cd6a
Signed-off-by: Heiko Hund <heiko@ist.eigentlich.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
Message-Id: <20250618124835.24737-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg31922.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Heiko Hund authored on 2025/06/18 21:48:29
Showing 3 changed files
... ...
@@ -691,7 +691,8 @@ run_updown_runner(bool up, struct options *o, const struct tuntap *tt, struct dn
691 691
 static void
692 692
 run_up_down_command(bool up, struct options *o, const struct tuntap *tt, struct dns_updown_runner_info *updown_runner)
693 693
 {
694
-    if (!o->dns_options.updown)
694
+    struct dns_options *dns = &o->dns_options;
695
+    if (!dns->updown || (o->up_script && !dns->user_set_updown))
695 696
     {
696 697
         return;
697 698
     }
... ...
@@ -701,7 +702,7 @@ run_up_down_command(bool up, struct options *o, const struct tuntap *tt, struct
701 701
     if (!updown_runner->required)
702 702
     {
703 703
         /* Run dns updown directly */
704
-        status = do_run_up_down_command(up, NULL, &o->dns_options, tt);
704
+        status = do_run_up_down_command(up, NULL, dns, tt);
705 705
     }
706 706
     else
707 707
     {
... ...
@@ -852,6 +853,14 @@ run_dns_up_down(bool up, struct options *o, const struct tuntap *tt, struct dns_
852 852
     {
853 853
         return;
854 854
     }
855
+#ifdef _WIN32
856
+    /* Don't use iservice in DHCP mode */
857
+    struct tuntap_options *tto = &o->tuntap_options;
858
+    if (tto->ip_win32_type == IPW32_SET_DHCP_MASQ || tto->ip_win32_type == IPW32_SET_ADAPTIVE)
859
+    {
860
+        return;
861
+    }
862
+#endif
855 863
 
856 864
     /* Warn about adding servers of unsupported AF */
857 865
     const struct dns_server *s = o->dns_options.servers;
... ...
@@ -76,7 +76,28 @@ struct dns_updown_runner_info {
76 76
 #endif
77 77
 };
78 78
 
79
+#ifndef N_DHCP_ADDR
80
+#define N_DHCP_ADDR 4
81
+#endif
82
+
83
+#ifndef N_SEARCH_LIST_LEN
84
+#define N_SEARCH_LIST_LEN 10
85
+#endif
86
+
87
+struct dhcp_options {
88
+    in_addr_t dns[N_DHCP_ADDR];
89
+    int dns_len;
90
+
91
+    struct in6_addr dns6[N_DHCP_ADDR];
92
+    int dns6_len;
93
+
94
+    const char *domain;
95
+    const char *domain_search_list[N_SEARCH_LIST_LEN];
96
+    int domain_search_list_len;
97
+};
98
+
79 99
 struct dns_options {
100
+    struct dhcp_options from_dhcp;
80 101
     struct dns_domain *search_domains;
81 102
     struct dns_server *servers_prepull;
82 103
     struct dns_server *servers;
... ...
@@ -1328,7 +1328,6 @@ show_tuntap_options(const struct tuntap_options *o)
1328 1328
 #endif /* ifndef ENABLE_SMALL */
1329 1329
 #endif /* ifdef _WIN32 */
1330 1330
 
1331
-#if defined(_WIN32) || defined(TARGET_ANDROID)
1332 1331
 static void
1333 1332
 dhcp_option_dns6_parse(const char *parm, struct in6_addr *dns6_list, int *len, int msglevel)
1334 1333
 {
... ...
@@ -1371,150 +1370,6 @@ dhcp_option_address_parse(const char *name, const char *parm, in_addr_t *array,
1371 1371
     }
1372 1372
 }
1373 1373
 
1374
-/*
1375
- * If DNS options are set use these for TUN/TAP options as well.
1376
- * Applies to DNS, DNS6 and DOMAIN-SEARCH.
1377
- * Existing options will be discarded.
1378
- */
1379
-static void
1380
-tuntap_options_copy_dns(struct options *o)
1381
-{
1382
-    struct tuntap_options *tt = &o->tuntap_options;
1383
-    struct dns_options *dns = &o->dns_options;
1384
-
1385
-    if (dns->search_domains)
1386
-    {
1387
-        tt->domain_search_list_len = 0;
1388
-        const struct dns_domain *domain = dns->search_domains;
1389
-        while (domain && tt->domain_search_list_len < N_SEARCH_LIST_LEN)
1390
-        {
1391
-            tt->domain_search_list[tt->domain_search_list_len++] = domain->name;
1392
-            domain = domain->next;
1393
-        }
1394
-        if (domain)
1395
-        {
1396
-            msg(M_WARN, "WARNING: couldn't copy all --dns search-domains to --dhcp-option");
1397
-        }
1398
-        tt->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED;
1399
-    }
1400
-
1401
-    if (dns->servers)
1402
-    {
1403
-        tt->dns_len = 0;
1404
-        tt->dns6_len = 0;
1405
-        bool overflow = false;
1406
-        const struct dns_server *server = dns->servers;
1407
-        while (server)
1408
-        {
1409
-            for (int i = 0; i < server->addr_count; ++i)
1410
-            {
1411
-                if (server->addr[i].family == AF_INET)
1412
-                {
1413
-                    if (tt->dns_len >= N_DHCP_ADDR)
1414
-                    {
1415
-                        overflow = true;
1416
-                        continue;
1417
-                    }
1418
-                    tt->dns[tt->dns_len++] = ntohl(server->addr[i].in.a4.s_addr);
1419
-                }
1420
-                else
1421
-                {
1422
-                    if (tt->dns6_len >= N_DHCP_ADDR)
1423
-                    {
1424
-                        overflow = true;
1425
-                        continue;
1426
-                    }
1427
-                    tt->dns6[tt->dns6_len++] = server->addr[i].in.a6;
1428
-                }
1429
-            }
1430
-            server = server->next;
1431
-        }
1432
-        if (overflow)
1433
-        {
1434
-            msg(M_WARN, "WARNING: couldn't copy all --dns server addresses to --dhcp-option");
1435
-        }
1436
-        tt->dhcp_options |= DHCP_OPTIONS_DHCP_OPTIONAL;
1437
-    }
1438
-}
1439
-#else /* if defined(_WIN32) || defined(TARGET_ANDROID) */
1440
-static void
1441
-foreign_options_copy_dns(struct options *o, struct env_set *es)
1442
-{
1443
-    const struct dns_domain *domain = o->dns_options.search_domains;
1444
-    const struct dns_server *server = o->dns_options.servers;
1445
-    if (!domain && !server)
1446
-    {
1447
-        return;
1448
-    }
1449
-
1450
-    /* reset the index since we're starting all over again */
1451
-    int opt_max = o->foreign_option_index;
1452
-    o->foreign_option_index = 0;
1453
-
1454
-    for (int i = 1; i <= opt_max; ++i)
1455
-    {
1456
-        char name[32];
1457
-        snprintf(name, sizeof(name), "foreign_option_%d", i);
1458
-
1459
-        const char *env_str = env_set_get(es, name);
1460
-        const char *value = strchr(env_str, '=') + 1;
1461
-        if ((domain && strstr(value, "dhcp-option DOMAIN-SEARCH") == value)
1462
-            || (server && strstr(value, "dhcp-option DNS") == value))
1463
-        {
1464
-            setenv_del(es, name);
1465
-        }
1466
-        else
1467
-        {
1468
-            setenv_foreign_option(o, &value, 1, es);
1469
-        }
1470
-    }
1471
-
1472
-    struct gc_arena gc = gc_new();
1473
-
1474
-    while (server)
1475
-    {
1476
-        for (size_t i = 0; i < server->addr_count; ++i)
1477
-        {
1478
-            if (server->addr[i].family == AF_INET)
1479
-            {
1480
-                const char *argv[] = {
1481
-                    "dhcp-option",
1482
-                    "DNS",
1483
-                    print_in_addr_t(server->addr[i].in.a4.s_addr, 0, &gc)
1484
-                };
1485
-                setenv_foreign_option(o, argv, 3, es);
1486
-            }
1487
-            else
1488
-            {
1489
-                const char *argv[] = {
1490
-                    "dhcp-option",
1491
-                    "DNS6",
1492
-                    print_in6_addr(server->addr[i].in.a6, 0, &gc)
1493
-                };
1494
-                setenv_foreign_option(o, argv, 3, es);
1495
-            }
1496
-        }
1497
-        server = server->next;
1498
-    }
1499
-    while (domain)
1500
-    {
1501
-        const char *argv[] = { "dhcp-option", "DOMAIN-SEARCH", domain->name };
1502
-        setenv_foreign_option(o, argv, 3, es);
1503
-        domain = domain->next;
1504
-    }
1505
-
1506
-    gc_free(&gc);
1507
-
1508
-    /* remove old leftover entries */
1509
-    while (o->foreign_option_index < opt_max)
1510
-    {
1511
-        char name[32];
1512
-        snprintf(name, sizeof(name), "foreign_option_%d", opt_max--);
1513
-        setenv_del(es, name);
1514
-    }
1515
-}
1516
-#endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */
1517
-
1518 1374
 #ifndef ENABLE_SMALL
1519 1375
 static const char *
1520 1376
 print_vlan_accept(enum vlan_acceptable_frames mode)
... ...
@@ -3603,6 +3458,260 @@ options_process_mutate_prf(struct options *o)
3603 3603
     }
3604 3604
 }
3605 3605
 
3606
+#if defined(_WIN32) || defined(TARGET_ANDROID)
3607
+/**
3608
+ * @brief Postprocess DNS related settings
3609
+ *
3610
+ * Set TUN/TAP DNS options with values from either --dns
3611
+ * or --dhcp-option.
3612
+ *
3613
+ * @param o     pointer to the options struct
3614
+ */
3615
+static void
3616
+tuntap_options_postprocess_dns(struct options *o)
3617
+{
3618
+    struct dns_options *dns = &o->dns_options;
3619
+    struct tuntap_options *tt = &o->tuntap_options;
3620
+    if (!dns->servers)
3621
+    {
3622
+        /* Copy --dhcp-options to tuntap_options */
3623
+        struct dhcp_options *dhcp = &dns->from_dhcp;
3624
+        assert(sizeof(dhcp->dns) == sizeof(tt->dns));
3625
+        assert(sizeof(dhcp->dns6) == sizeof(tt->dns6));
3626
+        assert(sizeof(dhcp->domain_search_list) == sizeof(tt->domain_search_list));
3627
+
3628
+        tt->domain = dhcp->domain;
3629
+        tt->dns_len = dhcp->dns_len;
3630
+        tt->dns6_len = dhcp->dns6_len;
3631
+
3632
+        memcpy(tt->dns, dhcp->dns, sizeof(tt->dns));
3633
+        memcpy(tt->dns6, dhcp->dns6, sizeof(tt->dns6));
3634
+
3635
+        tt->domain_search_list_len = dhcp->domain_search_list_len;
3636
+        for (size_t i = 0; i < SIZE(tt->domain_search_list); ++i)
3637
+        {
3638
+            tt->domain_search_list[i] = dhcp->domain_search_list[i];
3639
+        }
3640
+
3641
+        return;
3642
+    }
3643
+
3644
+#if defined(_WIN32)
3645
+    if (tt->ip_win32_type != IPW32_SET_DHCP_MASQ && tt->ip_win32_type != IPW32_SET_ADAPTIVE)
3646
+    {
3647
+        return;     /* Not in DHCP mode */
3648
+    }
3649
+#endif /* if defined(_WIN32) */
3650
+
3651
+    /* Copy --dns options to tuntap_options */
3652
+    const struct dns_domain *d = dns->search_domains;
3653
+    while (d && tt->domain_search_list_len + 1 < N_SEARCH_LIST_LEN)
3654
+    {
3655
+        tt->domain_search_list[tt->domain_search_list_len++] = d->name;
3656
+        d = d->next;
3657
+    }
3658
+    if (d)
3659
+    {
3660
+        msg(M_WARN, "WARNING: couldn't copy all --dns search-domains to TUN/TAP");
3661
+    }
3662
+
3663
+    const struct dns_server *s = dns->servers;
3664
+    while (s)
3665
+    {
3666
+        bool non_standard_server_port = false;
3667
+        for (int i = 0; i < s->addr_count; ++i)
3668
+        {
3669
+            if (s->addr[i].port && s->addr[i].port != 53)
3670
+            {
3671
+                non_standard_server_port = true;
3672
+                break;
3673
+            }
3674
+        }
3675
+        if ((s->transport && s->transport != DNS_TRANSPORT_PLAIN)
3676
+            || (s->dnssec && s->dnssec != DNS_SECURITY_NO)
3677
+            || non_standard_server_port)
3678
+        {
3679
+            /* Skip servers requiring unsupported config to be set */
3680
+            s = s->next;
3681
+        }
3682
+        else
3683
+        {
3684
+            bool overflow = false;
3685
+            for (int i = 0; i < s->addr_count; ++i)
3686
+            {
3687
+                if (s->addr[i].family == AF_INET && tt->dns_len + 1 < N_DHCP_ADDR)
3688
+                {
3689
+                    tt->dns[tt->dns_len++] = s->addr[i].in.a4.s_addr;
3690
+                }
3691
+                else if (tt->dns6_len + 1 < N_DHCP_ADDR)
3692
+                {
3693
+                    tt->dns6[tt->dns6_len++] = s->addr[i].in.a6;
3694
+                }
3695
+                else
3696
+                {
3697
+                    overflow = true;
3698
+                }
3699
+            }
3700
+            if (overflow)
3701
+            {
3702
+                msg(M_WARN, "WARNING: couldn't copy all --dns server addresses to TUN/TAP");
3703
+            }
3704
+            return;
3705
+        }
3706
+    }
3707
+}
3708
+
3709
+#else /* if defined(_WIN32) || defined(TARGET_ANDROID) */
3710
+
3711
+/**
3712
+ * @brief Postprocess DNS related settings
3713
+ *
3714
+ * Discard existing --dhcp-options from the env if needed and possibly
3715
+ * replace them with values from --dns. If no --dns servers are set copy
3716
+ * the --dhcp-option values over for --dns-updown runs.
3717
+ *
3718
+ * @param o     pointer to the options struct
3719
+ * @param es    env set to modify potentially
3720
+ */
3721
+static void
3722
+dhcp_options_postprocess_dns(struct options *o, struct env_set *es)
3723
+{
3724
+    struct gc_arena gc = gc_new();
3725
+    struct dns_options *dns = &o->dns_options;
3726
+
3727
+    if (dns->servers || dns->user_set_updown)
3728
+    {
3729
+        /* Clean up env from --dhcp-option DNS config */
3730
+        struct buffer name = alloc_buf_gc(OPTION_PARM_SIZE, &gc);
3731
+        struct buffer value = alloc_buf_gc(OPTION_PARM_SIZE, &gc);
3732
+
3733
+        const int fo_count = o->foreign_option_index;
3734
+        o->foreign_option_index = 0;
3735
+
3736
+        for (int i = 1; i <= fo_count; ++i)
3737
+        {
3738
+            buf_clear(&name);
3739
+            buf_printf(&name, "foreign_option_%d", i);
3740
+            const char *env_str = env_set_get(es, BSTR(&name));
3741
+            const char *item_val = strchr(env_str, '=') + 1;
3742
+            buf_clear(&value);
3743
+            buf_printf(&value, "%s", item_val);
3744
+
3745
+            /* Remove foreign option item from env set */
3746
+            env_set_del(es, BSTR(&name));
3747
+
3748
+            item_val = BSTR(&value);
3749
+            if (strncmp(item_val, "dhcp-option ", 12) != 0
3750
+                || (strncmp(item_val + 12, "ADAPTER-DOMAIN-SUFFIX ", 22) != 0
3751
+                    && strncmp(item_val + 12, "DOMAIN-SEARCH ", 14) != 0
3752
+                    && strncmp(item_val + 12, "DOMAIN ", 7) != 0
3753
+                    && strncmp(item_val + 12, "DNS6 ", 5) != 0
3754
+                    && strncmp(item_val + 12, "DNS ", 4) != 0))
3755
+            {
3756
+                /* Re-set the item with potentially updated name */
3757
+                buf_clear(&name);
3758
+                buf_printf(&name, "foreign_option_%d", ++o->foreign_option_index);
3759
+                setenv_str(es, BSTR(&name), BSTR(&value));
3760
+            }
3761
+        }
3762
+    }
3763
+
3764
+    if (!dns->servers)
3765
+    {
3766
+        /* Copy --dhcp-options to dns_options */
3767
+        struct dhcp_options *dhcp = &dns->from_dhcp;
3768
+
3769
+        if (dhcp->dns_len || dhcp->dns6_len)
3770
+        {
3771
+            struct dns_domain **entry = &dns->search_domains;
3772
+            ALLOC_OBJ_CLEAR_GC(*entry, struct dns_domain, &dns->gc);
3773
+            struct dns_domain *new = *entry;
3774
+            new->name = dhcp->domain;
3775
+            entry = &new->next;
3776
+
3777
+            for (size_t i = 0; i < dhcp->domain_search_list_len; ++i)
3778
+            {
3779
+                ALLOC_OBJ_CLEAR_GC(*entry, struct dns_domain, &dns->gc);
3780
+                struct dns_domain *new = *entry;
3781
+                new->name = dhcp->domain_search_list[i];
3782
+                entry = &new->next;
3783
+            }
3784
+
3785
+            struct dns_server *server = dns_server_get(&dns->servers, 0, &dns->gc);
3786
+            const size_t max_addrs = SIZE(server->addr);
3787
+            for (size_t i = 0; i < dhcp->dns_len && server->addr_count < max_addrs; ++i)
3788
+            {
3789
+                server->addr[server->addr_count].in.a4.s_addr = htonl(dhcp->dns[i]);
3790
+                server->addr[server->addr_count].family = AF_INET;
3791
+                server->addr_count += 1;
3792
+            }
3793
+            for (size_t i = 0; i < dhcp->dns6_len && server->addr_count < max_addrs; ++i)
3794
+            {
3795
+                server->addr[server->addr_count].in.a6 = dhcp->dns6[i];
3796
+                server->addr[server->addr_count].family = AF_INET6;
3797
+                server->addr_count += 1;
3798
+            }
3799
+        }
3800
+    }
3801
+    else if (o->up_script && !dns->user_set_updown)
3802
+    {
3803
+        /* Set foreign option env vars from --dns config */
3804
+        const char *p[] = { "dhcp-option", NULL, NULL };
3805
+        size_t p_len = sizeof(p) / sizeof(p[0]);
3806
+
3807
+        p[1] = "DOMAIN";
3808
+        const struct dns_domain *d = dns->search_domains;
3809
+        while (d)
3810
+        {
3811
+            p[2] = d->name;
3812
+            setenv_foreign_option(o, (const char **)p, p_len, es);
3813
+            d = d->next;
3814
+        }
3815
+
3816
+        const struct dns_server *s = dns->servers;
3817
+        while (s)
3818
+        {
3819
+            bool non_standard_server_port = false;
3820
+            for (int i = 0; i < s->addr_count; ++i)
3821
+            {
3822
+                if (s->addr[i].port && s->addr[i].port != 53)
3823
+                {
3824
+                    non_standard_server_port = true;
3825
+                    break;
3826
+                }
3827
+            }
3828
+            if ((s->transport && s->transport != DNS_TRANSPORT_PLAIN)
3829
+                || (s->dnssec && s->dnssec != DNS_SECURITY_NO)
3830
+                || non_standard_server_port)
3831
+            {
3832
+                /* Skip servers requiring unsupported config to be set */
3833
+                s = s->next;
3834
+            }
3835
+            else
3836
+            {
3837
+                for (int i = 0; i < s->addr_count; ++i)
3838
+                {
3839
+                    if (s->addr[i].family == AF_INET)
3840
+                    {
3841
+                        p[1] = "DNS";
3842
+                        p[2] = print_in_addr_t(s->addr[i].in.a4.s_addr, IA_NET_ORDER, &gc);
3843
+                    }
3844
+                    else
3845
+                    {
3846
+                        p[1] = "DNS6";
3847
+                        p[2] = print_in6_addr(s->addr[i].in.a6, 0, &gc);
3848
+                    }
3849
+                    setenv_foreign_option(o, (const char **)p, p_len, es);
3850
+                }
3851
+                break;
3852
+            }
3853
+        }
3854
+    }
3855
+
3856
+    gc_free(&gc);
3857
+}
3858
+#endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */
3859
+
3606 3860
 static void
3607 3861
 options_postprocess_mutate(struct options *o, struct env_set *es)
3608 3862
 {
... ...
@@ -3786,9 +3895,9 @@ options_postprocess_mutate(struct options *o, struct env_set *es)
3786 3786
     else
3787 3787
     {
3788 3788
 #if defined(_WIN32) || defined(TARGET_ANDROID)
3789
-        tuntap_options_copy_dns(o);
3789
+        tuntap_options_postprocess_dns(o);
3790 3790
 #else
3791
-        foreign_options_copy_dns(o, es);
3791
+        dhcp_options_postprocess_dns(o, es);
3792 3792
 #endif
3793 3793
     }
3794 3794
     if (o->auth_token_generate && !o->auth_token_renewal)
... ...
@@ -4171,9 +4280,9 @@ options_postprocess_pull(struct options *o, struct env_set *es)
4171 4171
     {
4172 4172
         dns_options_postprocess_pull(&o->dns_options);
4173 4173
 #if defined(_WIN32) || defined(TARGET_ANDROID)
4174
-        tuntap_options_copy_dns(o);
4174
+        tuntap_options_postprocess_dns(o);
4175 4175
 #else
4176
-        foreign_options_copy_dns(o, es);
4176
+        dhcp_options_postprocess_dns(o, es);
4177 4177
 #endif
4178 4178
     }
4179 4179
     return success;
... ...
@@ -8162,18 +8271,43 @@ add_option(struct options *options,
8162 8162
             goto err;
8163 8163
         }
8164 8164
     }
8165
-#if defined(_WIN32) || defined(TARGET_ANDROID)
8166 8165
     else if (streq(p[0], "dhcp-option") && p[1])
8167 8166
     {
8167
+        struct dhcp_options *dhcp = &options->dns_options.from_dhcp;
8168
+#if defined(_WIN32) || defined(TARGET_ANDROID)
8168 8169
         struct tuntap_options *o = &options->tuntap_options;
8170
+#endif
8169 8171
         VERIFY_PERMISSION(OPT_P_DHCPDNS);
8170 8172
 
8171
-        if ((streq(p[1], "DOMAIN") || streq(p[1], "ADAPTER_DOMAIN_SUFFIX"))
8172
-            && p[2] && !p[3])
8173
+        if ((streq(p[1], "DOMAIN") || streq(p[1], "ADAPTER_DOMAIN_SUFFIX")) && p[2] && !p[3])
8173 8174
         {
8174
-            o->domain = p[2];
8175
-            o->dhcp_options |= DHCP_OPTIONS_DHCP_OPTIONAL;
8175
+            dhcp->domain = p[2];
8176
+        }
8177
+        else if (streq(p[1], "DOMAIN-SEARCH") && p[2] && !p[3])
8178
+        {
8179
+            if (dhcp->domain_search_list_len < N_SEARCH_LIST_LEN)
8180
+            {
8181
+                dhcp->domain_search_list[dhcp->domain_search_list_len++] = p[2];
8182
+            }
8183
+            else
8184
+            {
8185
+                msg(msglevel, "--dhcp-option %s: maximum of %d search entries can be specified",
8186
+                    p[1], N_SEARCH_LIST_LEN);
8187
+            }
8188
+        }
8189
+        else if ((streq(p[1], "DNS") || streq(p[1], "DNS6")) && p[2] && !p[3]
8190
+                 && (!strstr(p[2], ":") || ipv6_addr_safe(p[2])))
8191
+        {
8192
+            if (strstr(p[2], ":"))
8193
+            {
8194
+                dhcp_option_dns6_parse(p[2], dhcp->dns6, &dhcp->dns6_len, msglevel);
8195
+            }
8196
+            else
8197
+            {
8198
+                dhcp_option_address_parse("DNS", p[2], dhcp->dns, &dhcp->dns_len, msglevel);
8199
+            }
8176 8200
         }
8201
+#if defined(_WIN32) || defined(TARGET_ANDROID)
8177 8202
         else if (streq(p[1], "NBS") && p[2] && !p[3])
8178 8203
         {
8179 8204
             o->netbios_scope = p[2];
... ...
@@ -8191,23 +8325,9 @@ add_option(struct options *options,
8191 8191
             o->netbios_node_type = t;
8192 8192
             o->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED;
8193 8193
         }
8194
-        else if ((streq(p[1], "DNS") || streq(p[1], "DNS6")) && p[2] && !p[3]
8195
-                 && (!strstr(p[2], ":") || ipv6_addr_safe(p[2])))
8196
-        {
8197
-            if (strstr(p[2], ":"))
8198
-            {
8199
-                dhcp_option_dns6_parse(p[2], o->dns6, &o->dns6_len, msglevel);
8200
-            }
8201
-            else
8202
-            {
8203
-                dhcp_option_address_parse("DNS", p[2], o->dns, &o->dns_len, msglevel);
8204
-                o->dhcp_options |= DHCP_OPTIONS_DHCP_OPTIONAL;
8205
-            }
8206
-        }
8207 8194
         else if (streq(p[1], "WINS") && p[2] && !p[3])
8208 8195
         {
8209 8196
             dhcp_option_address_parse("WINS", p[2], o->wins, &o->wins_len, msglevel);
8210
-            o->dhcp_options |= DHCP_OPTIONS_DHCP_OPTIONAL;
8211 8197
         }
8212 8198
         else if (streq(p[1], "NTP") && p[2] && !p[3])
8213 8199
         {
... ...
@@ -8219,19 +8339,6 @@ add_option(struct options *options,
8219 8219
             dhcp_option_address_parse("NBDD", p[2], o->nbdd, &o->nbdd_len, msglevel);
8220 8220
             o->dhcp_options |= DHCP_OPTIONS_DHCP_REQUIRED;
8221 8221
         }
8222
-        else if (streq(p[1], "DOMAIN-SEARCH") && p[2] && !p[3])
8223
-        {
8224
-            if (o->domain_search_list_len < N_SEARCH_LIST_LEN)
8225
-            {
8226
-                o->domain_search_list[o->domain_search_list_len++] = p[2];
8227
-            }
8228
-            else
8229
-            {
8230
-                msg(msglevel, "--dhcp-option %s: maximum of %d search entries can be specified",
8231
-                    p[1], N_SEARCH_LIST_LEN);
8232
-            }
8233
-            o->dhcp_options |= DHCP_OPTIONS_DHCP_OPTIONAL;
8234
-        }
8235 8222
         else if (streq(p[1], "DISABLE-NBT") && !p[2])
8236 8223
         {
8237 8224
             o->disable_nbt = 1;
... ...
@@ -8249,8 +8356,10 @@ add_option(struct options *options,
8249 8249
             msg(msglevel, "--dhcp-option: unknown option type '%s' or missing or unknown parameter", p[1]);
8250 8250
             goto err;
8251 8251
         }
8252
-    }
8252
+#else /* if defined(_WIN32) || defined(TARGET_ANDROID) */
8253
+        setenv_foreign_option(options, (const char **)p, 3, es);
8253 8254
 #endif /* if defined(_WIN32) || defined(TARGET_ANDROID) */
8255
+    }
8254 8256
 #ifdef _WIN32
8255 8257
     else if (streq(p[0], "show-adapters") && !p[1])
8256 8258
     {