wg-quick: support dns search domains

If DNS= has an IP in it, treat it as a DNS server. If DNS= has a non-IP
in it, treat it as a DNS search domain.

Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
This commit is contained in:
Jason A. Donenfeld 2020-05-08 23:15:50 -06:00
parent 238ca40591
commit 7f236c7957
8 changed files with 88 additions and 36 deletions

View file

@ -2,7 +2,9 @@ set_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0
if [[ $(resolvconf --version 2>/dev/null) == openresolv\ * ]]; then
printf 'nameserver %s\n' "${DNS[@]}" | cmd resolvconf -a "$INTERFACE" -m 0 -x
{ printf 'nameserver %s\n' "${DNS[@]}"
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
} | cmd resolvconf -a "$INTERFACE" -m 0 -x
else
echo "[#] mount \`${DNS[*]}' /etc/resolv.conf" >&2
[[ -e /etc/resolv.conf ]] || touch /etc/resolv.conf
@ -15,6 +17,7 @@ set_dns() {
_EOF
printf 'nameserver %s\n' "${DNS[@]}"
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
} | unshare -m --propagation shared bash -c "$(cat <<-_EOF
set -e
context="\$(stat -c %C /etc/resolv.conf 2>/dev/null)" || unset context

View file

@ -337,11 +337,6 @@ static bool is_valid_network(string_span_t s)
return is_valid_ipv4(s) || is_valid_ipv6(s);
}
static bool is_valid_dns(string_span_t s)
{
return is_valid_ipv4(s) || is_valid_ipv6(s);
}
enum field {
InterfaceSection,
PrivateKey,
@ -451,7 +446,12 @@ static void highlight_multivalue_value(struct highlight_span_array *ret, const s
{
switch (section) {
case DNS:
append_highlight_span(ret, parent.s, s, is_valid_dns(s) ? HighlightIP : HighlightError);
if (is_valid_ipv4(s) || is_valid_ipv6(s))
append_highlight_span(ret, parent.s, s, HighlightIP);
else if (is_valid_hostname(s))
append_highlight_span(ret, parent.s, s, HighlightHost);
else
append_highlight_span(ret, parent.s, s, HighlightError);
break;
case Address:
case AllowedIPs: {

View file

@ -76,7 +76,8 @@ Address \(em a comma-separated list of IP (v4 or v6) addresses (optionally with
to be assigned to the interface. May be specified multiple times.
.IP \(bu
DNS \(em a comma-separated list of IP (v4 or v6) addresses to be set as the interface's
DNS servers. May be specified multiple times. Upon bringing the interface up, this runs
DNS servers, or non-IP hostnames to be set as the interface's DNS search domains. May be
specified multiple times. Upon bringing the interface up, this runs
`resolvconf -a tun.\fIINTERFACE\fP -m 0 -x` and upon bringing it down, this runs
`resolvconf -d tun.\fIINTERFACE\fP`. If these particular invocations of
.BR resolvconf (8)

View file

@ -837,37 +837,49 @@ static void set_dnses(unsigned int netid, const char *dnses)
if (len > (1<<16))
return;
_cleanup_free_ char *mutable = xstrdup(dnses);
_cleanup_free_ char *shell_arglist = xmalloc(len * 4 + 1);
_cleanup_free_ char *function_arglist = xmalloc(len * 4 + 1);
_cleanup_free_ char *dns_shell_arglist = xmalloc(len * 4 + 1);
_cleanup_free_ char *dns_search_shell_arglist = xmalloc(len * 4 + 1);
_cleanup_free_ char *dns_function_arglist = xmalloc(len * 4 + 1);
_cleanup_free_ char *dns_search_function_arglist = xmalloc(len * 4 + 1);
_cleanup_free_ char *arg = xmalloc(len + 4);
_cleanup_free_ char **dns_list = NULL;
_cleanup_free_ char **dns_search_list = NULL;
_cleanup_binder_ AIBinder *handle = NULL;
size_t dns_list_size = 0;
_cleanup_regfree_ regex_t regex_ipnothost = { 0 };
size_t dns_list_size = 0, dns_search_list_size = 0;
bool is_ip;
if (!len)
return;
xregcomp(&regex_ipnothost, "^[a-zA-Z0-9_=+.-]{1,15}$", REG_EXTENDED | REG_NOSUB);
for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
if (strchr(dns, '\'') || strchr(dns, '\\'))
continue;
++dns_list_size;
++*(!regexec(&regex_ipnothost, dns, 0, NULL, 0) ? &dns_list_size : &dns_search_list_size);
}
if (!dns_list_size)
return;
dns_list = xcalloc(dns_list_size + 1, sizeof(*dns_list));
dns_search_list = xcalloc(dns_search_list_size + 1, sizeof(*dns_search_list));
free(mutable);
mutable = xstrdup(dnses);
shell_arglist[0] = '\0';
function_arglist[0] = '\0';
dns_shell_arglist[0] = '\0';
dns_search_shell_arglist[0] = '\0';
dns_function_arglist[0] = '\0';
dns_search_function_arglist[0] = '\0';
dns_list_size = 0;
dns_search_list_size = 0;
for (char *dns = strtok(mutable, ", \t\n"); dns; dns = strtok(NULL, ", \t\n")) {
if (strchr(dns, '\'') || strchr(dns, '\\'))
continue;
is_ip = !regexec(&regex_ipnothost, dns, 0, NULL, 0);
snprintf(arg, len + 3, "'%s' ", dns);
strncat(shell_arglist, arg, len * 4 - 1);
snprintf(arg, len + 2, function_arglist[0] == '\0' ? "%s" : ", %s", dns);
strncat(function_arglist, arg, len * 4 - 1);
dns_list[dns_list_size++] = dns;
strncat(is_ip ? dns_shell_arglist : dns_search_shell_arglist, arg, len * 4 - 1);
snprintf(arg, len + 2, (is_ip ? dns_function_arglist[0] : dns_search_function_arglist[0]) == '\0' ? "%s" : ", %s", dns);
strncat(is_ip ? dns_function_arglist : dns_search_function_arglist, arg, len * 4 - 1);
*(is_ip ? &dns_list[dns_list_size++] : &dns_search_list[dns_search_list_size++]) = dns;
}
if ((handle = dnsresolver_get_handle())) {
@ -889,15 +901,16 @@ static void set_dnses(unsigned int netid, const char *dnses)
.base_timeout_msec = DNSRESOLVER_BASE_TIMEOUT,
.retry_count = DNSRESOLVER_RETRY_COUNT,
.servers = dns_list,
.domains = (char *[]){NULL},
.domains = dns_search_list,
.tls_name = "",
.tls_servers = (char *[]){NULL},
.tls_fingerprints = (char *[]){NULL}
};
printf("[#] <binder>::dnsResolver->setResolverConfiguration(%u, [%s], [], %d, %d, %d, %d, %d, %d, [], [])\n",
netid, function_arglist, DNSRESOLVER_SAMPLE_VALIDITY, DNSRESOLVER_SUCCESS_THRESHOLD,
DNSRESOLVER_MIN_SAMPLES, DNSRESOLVER_MAX_SAMPLES, DNSRESOLVER_BASE_TIMEOUT, DNSRESOLVER_RETRY_COUNT);
printf("[#] <binder>::dnsResolver->setResolverConfiguration(%u, [%s], [%s], %d, %d, %d, %d, %d, %d, [], [])\n",
netid, dns_function_arglist, dns_search_function_arglist, DNSRESOLVER_SAMPLE_VALIDITY,
DNSRESOLVER_SUCCESS_THRESHOLD, DNSRESOLVER_MIN_SAMPLES, DNSRESOLVER_MAX_SAMPLES,
DNSRESOLVER_BASE_TIMEOUT, DNSRESOLVER_RETRY_COUNT);
status = dnsresolver_set_resolver_configuration(handle, &params);
if (status != 0) {
@ -905,7 +918,7 @@ static void set_dnses(unsigned int netid, const char *dnses)
exit(ENONET);
}
} else
cndc("resolver setnetdns %u '' %s", netid, shell_arglist);
cndc("resolver setnetdns %u '%s' %s", netid, dns_search_shell_arglist, dns_shell_arglist);
}
static void add_addr(const char *iface, const char *addr)

View file

@ -18,6 +18,7 @@ INTERFACE=""
ADDRESSES=( )
MTU=""
DNS=( )
DNS_SEARCH=( )
TABLE=""
PRE_UP=( )
POST_UP=( )
@ -43,7 +44,7 @@ die() {
CONFIG_SEARCH_PATHS=( /etc/wireguard /usr/local/etc/wireguard )
parse_options() {
local interface_section=0 line key value stripped path
local interface_section=0 line key value stripped path v
CONFIG_FILE="$1"
if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then
for path in "${CONFIG_SEARCH_PATHS[@]}"; do
@ -67,7 +68,9 @@ parse_options() {
case "$key" in
Address) ADDRESSES+=( ${value//,/ } ); continue ;;
MTU) MTU="$value"; continue ;;
DNS) DNS+=( ${value//,/ } ); continue ;;
DNS) for v in ${value//,/ }; do
[[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
done; continue ;;
Table) TABLE="$value"; continue ;;
PreUp) PRE_UP+=( "$value" ); continue ;;
PreDown) PRE_DOWN+=( "$value" ); continue ;;
@ -213,6 +216,7 @@ collect_endpoints() {
}
declare -A SERVICE_DNS
declare -A SERVICE_DNS_SEARCH
collect_new_service_dns() {
local service get_response
local -A found_services
@ -223,10 +227,16 @@ collect_new_service_dns() {
get_response="$(cmd networksetup -getdnsservers "$service")"
[[ $get_response == *" "* ]] && get_response="Empty"
[[ -n $get_response ]] && SERVICE_DNS["$service"]="$get_response"
get_response="$(cmd networksetup -getsearchdomains "$service")"
[[ $get_response == *" "* ]] && get_response="Empty"
[[ -n $get_response ]] && SERVICE_DNS_SEARCH["$service"]="$get_response"
done; } < <(networksetup -listallnetworkservices)
for service in "${!SERVICE_DNS[@]}"; do
[[ -n ${found_services["$service"]} ]] || unset SERVICE_DNS["$service"]
if ! [[ -n ${found_services["$service"]} ]]; then
unset SERVICE_DNS["$service"]
unset SERVICE_DNS_SEARCH["$service"]
fi
done
}
@ -287,7 +297,14 @@ set_dns() {
for service in "${!SERVICE_DNS[@]}"; do
while read -r response; do
[[ $response == *Error* ]] && echo "$response" >&2
done < <(cmd networksetup -setdnsservers "$service" "${DNS[@]}")
done < <(
cmd networksetup -setdnsservers "$service" "${DNS[@]}"
if [[ ${#DNS_SEARCH[@]} -eq 0 ]]; then
cmd networksetup -setsearchdomains "$service" Empty
else
cmd networksetup -setsearchdomains "$service" "${DNS_SEARCH[@]}"
fi
)
done
}
@ -296,7 +313,10 @@ del_dns() {
for service in "${!SERVICE_DNS[@]}"; do
while read -r response; do
[[ $response == *Error* ]] && echo "$response" >&2
done < <(cmd networksetup -setdnsservers "$service" ${SERVICE_DNS["$service"]} || true)
done < <(
cmd networksetup -setdnsservers "$service" ${SERVICE_DNS["$service"]} || true
cmd networksetup -setsearchdomains "$service" ${SERVICE_DNS_SEARCH["$service"]} || true
)
done
}

View file

@ -16,6 +16,7 @@ INTERFACE=""
ADDRESSES=( )
MTU=""
DNS=( )
DNS_SEARCH=( )
TABLE=""
PRE_UP=( )
POST_UP=( )
@ -60,7 +61,7 @@ clean_temp() {
}
parse_options() {
local interface_section=0 line key value stripped path
local interface_section=0 line key value stripped path v
CONFIG_FILE="$1"
if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then
for path in "${CONFIG_SEARCH_PATHS[@]}"; do
@ -84,7 +85,9 @@ parse_options() {
case "$key" in
Address) ADDRESSES+=( ${value//,/ } ); continue ;;
MTU) MTU="$value"; continue ;;
DNS) DNS+=( ${value//,/ } ); continue ;;
DNS) for v in ${value//,/ }; do
[[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
done; continue ;;
Table) TABLE="$value"; continue ;;
PreUp) PRE_UP+=( "$value" ); continue ;;
PreDown) PRE_DOWN+=( "$value" ); continue ;;
@ -297,7 +300,9 @@ monitor_daemon() {
HAVE_SET_DNS=0
set_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0
printf 'nameserver %s\n' "${DNS[@]}" | cmd resolvconf -a "$INTERFACE" -x
{ printf 'nameserver %s\n' "${DNS[@]}"
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
} | cmd resolvconf -a "$INTERFACE" -x
HAVE_SET_DNS=1
}

View file

@ -16,6 +16,7 @@ INTERFACE=""
ADDRESSES=( )
MTU=""
DNS=( )
DNS_SEARCH=( )
TABLE=""
PRE_UP=( )
POST_UP=( )
@ -37,7 +38,7 @@ die() {
}
parse_options() {
local interface_section=0 line key value stripped
local interface_section=0 line key value stripped v
CONFIG_FILE="$1"
[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
[[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
@ -56,7 +57,9 @@ parse_options() {
case "$key" in
Address) ADDRESSES+=( ${value//,/ } ); continue ;;
MTU) MTU="$value"; continue ;;
DNS) DNS+=( ${value//,/ } ); continue ;;
DNS) for v in ${value//,/ }; do
[[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
done; continue ;;
Table) TABLE="$value"; continue ;;
PreUp) PRE_UP+=( "$value" ); continue ;;
PreDown) PRE_DOWN+=( "$value" ); continue ;;
@ -150,7 +153,9 @@ resolvconf_iface_prefix() {
HAVE_SET_DNS=0
set_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0
printf 'nameserver %s\n' "${DNS[@]}" | cmd resolvconf -a "$(resolvconf_iface_prefix)$INTERFACE" -m 0 -x
{ printf 'nameserver %s\n' "${DNS[@]}"
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
} | cmd resolvconf -a "$(resolvconf_iface_prefix)$INTERFACE" -m 0 -x
HAVE_SET_DNS=1
}

View file

@ -16,6 +16,7 @@ INTERFACE=""
ADDRESSES=( )
MTU=""
DNS=( )
DNS_SEARCH=( )
TABLE=""
PRE_UP=( )
POST_UP=( )
@ -56,7 +57,9 @@ parse_options() {
case "$key" in
Address) ADDRESSES+=( ${value//,/ } ); continue ;;
MTU) MTU="$value"; continue ;;
DNS) DNS+=( ${value//,/ } ); continue ;;
DNS) for v in ${value//,/ }; do
[[ $v =~ (^[0-9.]+$)|(^.*:.*$) ]] && DNS+=( $v ) || DNS_SEARCH+=( $v )
done; continue ;;
Table) TABLE="$value"; continue ;;
PreUp) PRE_UP+=( "$value" ); continue ;;
PreDown) PRE_DOWN+=( "$value" ); continue ;;
@ -270,7 +273,9 @@ set_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0
# TODO: this is a horrible way of doing it. Has OpenBSD no resolvconf?
cmd cp /etc/resolv.conf "/etc/resolv.conf.wg-quick-backup.$INTERFACE"
cmd printf 'nameserver %s\n' "${DNS[@]}" > /etc/resolv.conf
{ cmd printf 'nameserver %s\n' "${DNS[@]}"
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || cmd printf 'search %s\n' "${DNS_SEARCH[*]}"
} > /etc/resolv.conf
}
unset_dns() {