mirror of
https://github.com/OpenVPN/openvpn.git
synced 2026-05-28 04:03:29 -04:00
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 <heiko@ist.eigentlich.net> Acked-by: Lev Stipakov <lstipakov@gmail.com> Message-Id: <20250414180636.31936-1-gert@greenie.muc.de> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg31426.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
parent
9d551f9e05
commit
0f3b7d17d5
5 changed files with 1149 additions and 30 deletions
|
|
@ -35,6 +35,8 @@ typedef enum {
|
|||
msg_del_route,
|
||||
msg_add_dns_cfg,
|
||||
msg_del_dns_cfg,
|
||||
msg_add_nrpt_cfg,
|
||||
msg_del_nrpt_cfg,
|
||||
msg_add_nbt_cfg,
|
||||
msg_del_nbt_cfg,
|
||||
msg_flush_neighbors,
|
||||
|
|
@ -96,6 +98,23 @@ typedef struct {
|
|||
inet_address_t addr[4]; /* support up to 4 dns addresses */
|
||||
} dns_cfg_message_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
nrpt_dnssec
|
||||
} nrpt_flags_t;
|
||||
|
||||
#define NRPT_ADDR_NUM 8 /* Max. number of addresses */
|
||||
#define NRPT_ADDR_SIZE 48 /* Max. address strlen + some */
|
||||
typedef char nrpt_address_t[NRPT_ADDR_SIZE];
|
||||
typedef struct {
|
||||
message_header_t header;
|
||||
interface_t iface;
|
||||
nrpt_address_t addresses[NRPT_ADDR_NUM];
|
||||
char resolve_domains[512]; /* double \0 terminated */
|
||||
char search_domains[512];
|
||||
nrpt_flags_t flags;
|
||||
} nrpt_dns_cfg_message_t;
|
||||
|
||||
typedef struct {
|
||||
message_header_t header;
|
||||
interface_t iface;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@
|
|||
|
||||
#include "dns.h"
|
||||
#include "socket.h"
|
||||
#include "options.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "win32.h"
|
||||
#include "openvpn-msg.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Parses a string as port and stores it
|
||||
|
|
@ -428,6 +434,122 @@ setenv_dns_options(const struct dns_options *o, struct env_set *es)
|
|||
gc_free(&gc);
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static void
|
||||
make_domain_list(const char *what, const struct dns_domain *src,
|
||||
bool nrpt_domains, char *dst, size_t dst_size)
|
||||
{
|
||||
/* NRPT domains need two \0 at the end for REG_MULTI_SZ
|
||||
* and a leading '.' added in front of the domain name */
|
||||
size_t term_size = nrpt_domains ? 2 : 1;
|
||||
size_t leading_dot = nrpt_domains ? 1 : 0;
|
||||
size_t offset = 0;
|
||||
|
||||
memset(dst, 0, dst_size);
|
||||
|
||||
while (src)
|
||||
{
|
||||
size_t len = strlen(src->name);
|
||||
if (offset + leading_dot + len + term_size > dst_size)
|
||||
{
|
||||
msg(M_WARN, "WARNING: %s truncated", what);
|
||||
if (offset)
|
||||
{
|
||||
/* Remove trailing comma */
|
||||
*(dst + offset - 1) = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (leading_dot)
|
||||
{
|
||||
*(dst + offset++) = '.';
|
||||
}
|
||||
strncpy(dst + offset, src->name, len);
|
||||
offset += len;
|
||||
|
||||
src = src->next;
|
||||
if (src)
|
||||
{
|
||||
*(dst + offset++) = ',';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
run_up_down_service(bool add, const struct options *o, const struct tuntap *tt)
|
||||
{
|
||||
const struct dns_server *server = o->dns_options.servers;
|
||||
const struct dns_domain *search_domains = o->dns_options.search_domains;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (!server)
|
||||
{
|
||||
if (add)
|
||||
{
|
||||
msg(M_WARN, "WARNING: setting DNS failed, no compatible server profile");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bool only_standard_server_ports = true;
|
||||
for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
|
||||
{
|
||||
if (server->addr[i].port && server->addr[i].port != 53)
|
||||
{
|
||||
only_standard_server_ports = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((server->transport == DNS_TRANSPORT_UNSET || server->transport == DNS_TRANSPORT_PLAIN)
|
||||
&& only_standard_server_ports)
|
||||
{
|
||||
break; /* found compatible server */
|
||||
}
|
||||
|
||||
server = server->next;
|
||||
}
|
||||
|
||||
ack_message_t ack;
|
||||
nrpt_dns_cfg_message_t nrpt = {
|
||||
.header = {
|
||||
(add ? msg_add_nrpt_cfg : msg_del_nrpt_cfg),
|
||||
sizeof(nrpt_dns_cfg_message_t),
|
||||
0
|
||||
},
|
||||
.iface = { .index = tt->adapter_index, .name = "" },
|
||||
.flags = server->dnssec == DNS_SECURITY_NO ? 0 : nrpt_dnssec,
|
||||
};
|
||||
strncpynt(nrpt.iface.name, tt->actual_name, sizeof(nrpt.iface.name));
|
||||
|
||||
for (size_t i = 0; i < NRPT_ADDR_NUM; ++i)
|
||||
{
|
||||
if (server->addr[i].family == AF_UNSPEC)
|
||||
{
|
||||
/* No more addresses */
|
||||
break;
|
||||
}
|
||||
|
||||
if (inet_ntop(server->addr[i].family, &server->addr[i].in,
|
||||
nrpt.addresses[i], NRPT_ADDR_SIZE) == NULL)
|
||||
{
|
||||
msg(M_WARN, "WARNING: could not convert dns server address");
|
||||
}
|
||||
}
|
||||
|
||||
make_domain_list("dns server resolve domains", server->domains, true,
|
||||
nrpt.resolve_domains, sizeof(nrpt.resolve_domains));
|
||||
|
||||
make_domain_list("dns search domains", search_domains, false,
|
||||
nrpt.search_domains, sizeof(nrpt.search_domains));
|
||||
|
||||
send_msg_iservice(o->msg_channel, &nrpt, sizeof(nrpt), &ack, "DNS");
|
||||
}
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
void
|
||||
show_dns_options(const struct dns_options *o)
|
||||
{
|
||||
|
|
@ -506,3 +628,43 @@ show_dns_options(const struct dns_options *o)
|
|||
|
||||
gc_free(&gc);
|
||||
}
|
||||
|
||||
void
|
||||
run_dns_up_down(bool up, struct options *o, const struct tuntap *tt)
|
||||
{
|
||||
if (!o->dns_options.servers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Warn about adding servers of unsupported AF */
|
||||
const struct dns_server *s = o->dns_options.servers;
|
||||
while (up && s)
|
||||
{
|
||||
size_t bad_count = 0;
|
||||
for (size_t i = 0; i < s->addr_count; ++i)
|
||||
{
|
||||
if ((s->addr[i].family == AF_INET6 && !tt->did_ifconfig_ipv6_setup)
|
||||
|| (s->addr[i].family == AF_INET && !tt->did_ifconfig_setup))
|
||||
{
|
||||
++bad_count;
|
||||
}
|
||||
}
|
||||
if (bad_count == s->addr_count)
|
||||
{
|
||||
msg(M_WARN, "DNS server %ld only has address(es) from a family "
|
||||
"the tunnel is not configured for - it will not be reachable",
|
||||
s->priority);
|
||||
}
|
||||
else if (bad_count)
|
||||
{
|
||||
msg(M_WARN, "DNS server %ld has address(es) from a family "
|
||||
"the tunnel is not configured for", s->priority);
|
||||
}
|
||||
s = s->next;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
run_up_down_service(up, o, tt);
|
||||
#endif /* ifdef _WIN32 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "buffer.h"
|
||||
#include "env_set.h"
|
||||
#include "tun.h"
|
||||
|
||||
enum dns_security {
|
||||
DNS_SECURITY_UNSET,
|
||||
|
|
@ -146,6 +147,14 @@ void dns_options_preprocess_pull(struct dns_options *o);
|
|||
*/
|
||||
void dns_options_postprocess_pull(struct dns_options *o);
|
||||
|
||||
/**
|
||||
* Invokes the action associated with bringing DNS up or down
|
||||
* @param up Boolean to set this call to "up" when true
|
||||
* @param o Pointer to the program options
|
||||
* @param tt Pointer to the connection's tuntap struct
|
||||
*/
|
||||
void run_dns_up_down(bool up, struct options *o, const struct tuntap *tt);
|
||||
|
||||
/**
|
||||
* Puts the DNS options into an environment set.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2026,6 +2026,8 @@ do_open_tun(struct context *c, int *error_flags)
|
|||
c->c2.frame.tun_mtu, c->c2.es, &c->net_ctx);
|
||||
}
|
||||
|
||||
run_dns_up_down(true, &c->options, c->c1.tuntap);
|
||||
|
||||
/* run the up script */
|
||||
run_up_down(c->options.up_script,
|
||||
c->plugins,
|
||||
|
|
@ -2064,6 +2066,8 @@ do_open_tun(struct context *c, int *error_flags)
|
|||
/* explicitly set the ifconfig_* env vars */
|
||||
do_ifconfig_setenv(c->c1.tuntap, c->c2.es);
|
||||
|
||||
run_dns_up_down(true, &c->options, c->c1.tuntap);
|
||||
|
||||
/* run the up script if user specified --up-restart */
|
||||
if (c->options.up_restart)
|
||||
{
|
||||
|
|
@ -2152,6 +2156,8 @@ do_close_tun(struct context *c, bool force)
|
|||
adapter_index = c->c1.tuntap->adapter_index;
|
||||
#endif
|
||||
|
||||
run_dns_up_down(false, &c->options, c->c1.tuntap);
|
||||
|
||||
if (force || !(c->sig->signal_received == SIGUSR1 && c->options.persist_tun))
|
||||
{
|
||||
static_context = NULL;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue