Skip to content

Commit 0f3b7d1

Browse files
d12fkcron2
authored andcommitted
win: implement --dns option support with NRPT
Implement support for setting options from --dns. This is hugely different than what we had so far with DNS related --dhcp-option. The main difference it that we support split DNS and DNSSEC by making use of NRPT (Name Resolution Policy Table). Also OpenVPN tries to keep local DNS resolution working when DNS is redirected into the tunnel. To prevent this from happening we have --block-outside-dns, in case you wonder. Basically we collect domains and name server addresses from network adapters and add so called exclude NRPT rules in addition to the catch all rule that is pushed by the server. All is done via the interactive service, since modifying all this requires the elevated privileges that the openvpn process hopefully doesn't have. Change-Id: I576e74f3276362606e9cbd50bb5adbebaaf209cc Signed-off-by: Heiko Hund <[email protected]> Acked-by: Lev Stipakov <[email protected]> Message-Id: <[email protected]> URL: https://www.mail-archive.com/[email protected]/msg31426.html Signed-off-by: Gert Doering <[email protected]>
1 parent 9d551f9 commit 0f3b7d1

File tree

5 files changed

+1149
-30
lines changed

5 files changed

+1149
-30
lines changed

include/openvpn-msg.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ typedef enum {
3535
msg_del_route,
3636
msg_add_dns_cfg,
3737
msg_del_dns_cfg,
38+
msg_add_nrpt_cfg,
39+
msg_del_nrpt_cfg,
3840
msg_add_nbt_cfg,
3941
msg_del_nbt_cfg,
4042
msg_flush_neighbors,
@@ -96,6 +98,23 @@ typedef struct {
9698
inet_address_t addr[4]; /* support up to 4 dns addresses */
9799
} dns_cfg_message_t;
98100

101+
102+
typedef enum {
103+
nrpt_dnssec
104+
} nrpt_flags_t;
105+
106+
#define NRPT_ADDR_NUM 8 /* Max. number of addresses */
107+
#define NRPT_ADDR_SIZE 48 /* Max. address strlen + some */
108+
typedef char nrpt_address_t[NRPT_ADDR_SIZE];
109+
typedef struct {
110+
message_header_t header;
111+
interface_t iface;
112+
nrpt_address_t addresses[NRPT_ADDR_NUM];
113+
char resolve_domains[512]; /* double \0 terminated */
114+
char search_domains[512];
115+
nrpt_flags_t flags;
116+
} nrpt_dns_cfg_message_t;
117+
99118
typedef struct {
100119
message_header_t header;
101120
interface_t iface;

src/openvpn/dns.c

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@
2929

3030
#include "dns.h"
3131
#include "socket.h"
32+
#include "options.h"
33+
34+
#ifdef _WIN32
35+
#include "win32.h"
36+
#include "openvpn-msg.h"
37+
#endif
3238

3339
/**
3440
* Parses a string as port and stores it
@@ -428,6 +434,122 @@ setenv_dns_options(const struct dns_options *o, struct env_set *es)
428434
gc_free(&gc);
429435
}
430436

437+
#ifdef _WIN32
438+
439+
static void
440+
make_domain_list(const char *what, const struct dns_domain *src,
441+
bool nrpt_domains, char *dst, size_t dst_size)
442+
{
443+
/* NRPT domains need two \0 at the end for REG_MULTI_SZ
444+
* and a leading '.' added in front of the domain name */
445+
size_t term_size = nrpt_domains ? 2 : 1;
446+
size_t leading_dot = nrpt_domains ? 1 : 0;
447+
size_t offset = 0;
448+
449+
memset(dst, 0, dst_size);
450+
451+
while (src)
452+
{
453+
size_t len = strlen(src->name);
454+
if (offset + leading_dot + len + term_size > dst_size)
455+
{
456+
msg(M_WARN, "WARNING: %s truncated", what);
457+
if (offset)
458+
{
459+
/* Remove trailing comma */
460+
*(dst + offset - 1) = '\0';
461+
}
462+
break;
463+
}
464+
465+
if (leading_dot)
466+
{
467+
*(dst + offset++) = '.';
468+
}
469+
strncpy(dst + offset, src->name, len);
470+
offset += len;
471+
472+
src = src->next;
473+
if (src)
474+
{
475+
*(dst + offset++) = ',';
476+
}
477+
}
478+
}
479+
480+
static void
481+
run_up_down_service(bool add, const struct options *o, const struct tuntap *tt)
482+
{
483+
const struct dns_server *server = o->dns_options.servers;
484+
const struct dns_domain *search_domains = o->dns_options.search_domains;
485+
486+
while (true)
487+
{
488+
if (!server)
489+
{
490+
if (add)
491+
{
492+
msg(M_WARN, "WARNING: setting DNS failed, no compatible server profile");
493+
}
494+
return;
495+
}
496+
497+
bool only_standard_server_ports = true;
498+
for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
499+
{
500+
if (server->addr[i].port && server->addr[i].port != 53)
501+
{
502+
only_standard_server_ports = false;
503+
break;
504+
}
505+
}
506+
if ((server->transport == DNS_TRANSPORT_UNSET || server->transport == DNS_TRANSPORT_PLAIN)
507+
&& only_standard_server_ports)
508+
{
509+
break; /* found compatible server */
510+
}
511+
512+
server = server->next;
513+
}
514+
515+
ack_message_t ack;
516+
nrpt_dns_cfg_message_t nrpt = {
517+
.header = {
518+
(add ? msg_add_nrpt_cfg : msg_del_nrpt_cfg),
519+
sizeof(nrpt_dns_cfg_message_t),
520+
0
521+
},
522+
.iface = { .index = tt->adapter_index, .name = "" },
523+
.flags = server->dnssec == DNS_SECURITY_NO ? 0 : nrpt_dnssec,
524+
};
525+
strncpynt(nrpt.iface.name, tt->actual_name, sizeof(nrpt.iface.name));
526+
527+
for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
528+
{
529+
if (server->addr[i].family == AF_UNSPEC)
530+
{
531+
/* No more addresses */
532+
break;
533+
}
534+
535+
if (inet_ntop(server->addr[i].family, &server->addr[i].in,
536+
nrpt.addresses[i], NRPT_ADDR_SIZE) == NULL)
537+
{
538+
msg(M_WARN, "WARNING: could not convert dns server address");
539+
}
540+
}
541+
542+
make_domain_list("dns server resolve domains", server->domains, true,
543+
nrpt.resolve_domains, sizeof(nrpt.resolve_domains));
544+
545+
make_domain_list("dns search domains", search_domains, false,
546+
nrpt.search_domains, sizeof(nrpt.search_domains));
547+
548+
send_msg_iservice(o->msg_channel, &nrpt, sizeof(nrpt), &ack, "DNS");
549+
}
550+
551+
#endif /* _WIN32 */
552+
431553
void
432554
show_dns_options(const struct dns_options *o)
433555
{
@@ -506,3 +628,43 @@ show_dns_options(const struct dns_options *o)
506628

507629
gc_free(&gc);
508630
}
631+
632+
void
633+
run_dns_up_down(bool up, struct options *o, const struct tuntap *tt)
634+
{
635+
if (!o->dns_options.servers)
636+
{
637+
return;
638+
}
639+
640+
/* Warn about adding servers of unsupported AF */
641+
const struct dns_server *s = o->dns_options.servers;
642+
while (up && s)
643+
{
644+
size_t bad_count = 0;
645+
for (size_t i = 0; i < s->addr_count; ++i)
646+
{
647+
if ((s->addr[i].family == AF_INET6 && !tt->did_ifconfig_ipv6_setup)
648+
|| (s->addr[i].family == AF_INET && !tt->did_ifconfig_setup))
649+
{
650+
++bad_count;
651+
}
652+
}
653+
if (bad_count == s->addr_count)
654+
{
655+
msg(M_WARN, "DNS server %ld only has address(es) from a family "
656+
"the tunnel is not configured for - it will not be reachable",
657+
s->priority);
658+
}
659+
else if (bad_count)
660+
{
661+
msg(M_WARN, "DNS server %ld has address(es) from a family "
662+
"the tunnel is not configured for", s->priority);
663+
}
664+
s = s->next;
665+
}
666+
667+
#ifdef _WIN32
668+
run_up_down_service(up, o, tt);
669+
#endif /* ifdef _WIN32 */
670+
}

src/openvpn/dns.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "buffer.h"
2828
#include "env_set.h"
29+
#include "tun.h"
2930

3031
enum dns_security {
3132
DNS_SECURITY_UNSET,
@@ -146,6 +147,14 @@ void dns_options_preprocess_pull(struct dns_options *o);
146147
*/
147148
void dns_options_postprocess_pull(struct dns_options *o);
148149

150+
/**
151+
* Invokes the action associated with bringing DNS up or down
152+
* @param up Boolean to set this call to "up" when true
153+
* @param o Pointer to the program options
154+
* @param tt Pointer to the connection's tuntap struct
155+
*/
156+
void run_dns_up_down(bool up, struct options *o, const struct tuntap *tt);
157+
149158
/**
150159
* Puts the DNS options into an environment set.
151160
*

src/openvpn/init.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,6 +2026,8 @@ do_open_tun(struct context *c, int *error_flags)
20262026
c->c2.frame.tun_mtu, c->c2.es, &c->net_ctx);
20272027
}
20282028

2029+
run_dns_up_down(true, &c->options, c->c1.tuntap);
2030+
20292031
/* run the up script */
20302032
run_up_down(c->options.up_script,
20312033
c->plugins,
@@ -2064,6 +2066,8 @@ do_open_tun(struct context *c, int *error_flags)
20642066
/* explicitly set the ifconfig_* env vars */
20652067
do_ifconfig_setenv(c->c1.tuntap, c->c2.es);
20662068

2069+
run_dns_up_down(true, &c->options, c->c1.tuntap);
2070+
20672071
/* run the up script if user specified --up-restart */
20682072
if (c->options.up_restart)
20692073
{
@@ -2152,6 +2156,8 @@ do_close_tun(struct context *c, bool force)
21522156
adapter_index = c->c1.tuntap->adapter_index;
21532157
#endif
21542158

2159+
run_dns_up_down(false, &c->options, c->c1.tuntap);
2160+
21552161
if (force || !(c->sig->signal_received == SIGUSR1 && c->options.persist_tun))
21562162
{
21572163
static_context = NULL;

0 commit comments

Comments
 (0)