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 [[ ${#DNS[@]} -gt 0 ]] || return 0
if [[ $(resolvconf --version 2>/dev/null) == openresolv\ * ]]; then 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 else
echo "[#] mount \`${DNS[*]}' /etc/resolv.conf" >&2 echo "[#] mount \`${DNS[*]}' /etc/resolv.conf" >&2
[[ -e /etc/resolv.conf ]] || touch /etc/resolv.conf [[ -e /etc/resolv.conf ]] || touch /etc/resolv.conf
@ -15,6 +17,7 @@ set_dns() {
_EOF _EOF
printf 'nameserver %s\n' "${DNS[@]}" printf 'nameserver %s\n' "${DNS[@]}"
[[ ${#DNS_SEARCH[@]} -eq 0 ]] || printf 'search %s\n' "${DNS_SEARCH[*]}"
} | unshare -m --propagation shared bash -c "$(cat <<-_EOF } | unshare -m --propagation shared bash -c "$(cat <<-_EOF
set -e set -e
context="\$(stat -c %C /etc/resolv.conf 2>/dev/null)" || unset context 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); 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 { enum field {
InterfaceSection, InterfaceSection,
PrivateKey, PrivateKey,
@ -451,7 +446,12 @@ static void highlight_multivalue_value(struct highlight_span_array *ret, const s
{ {
switch (section) { switch (section) {
case DNS: 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; break;
case Address: case Address:
case AllowedIPs: { 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. to be assigned to the interface. May be specified multiple times.
.IP \(bu .IP \(bu
DNS \(em a comma-separated list of IP (v4 or v6) addresses to be set as the interface's 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 -a tun.\fIINTERFACE\fP -m 0 -x` and upon bringing it down, this runs
`resolvconf -d tun.\fIINTERFACE\fP`. If these particular invocations of `resolvconf -d tun.\fIINTERFACE\fP`. If these particular invocations of
.BR resolvconf (8) .BR resolvconf (8)

View file

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

View file

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

View file

@ -16,6 +16,7 @@ INTERFACE=""
ADDRESSES=( ) ADDRESSES=( )
MTU="" MTU=""
DNS=( ) DNS=( )
DNS_SEARCH=( )
TABLE="" TABLE=""
PRE_UP=( ) PRE_UP=( )
POST_UP=( ) POST_UP=( )
@ -60,7 +61,7 @@ clean_temp() {
} }
parse_options() { parse_options() {
local interface_section=0 line key value stripped path local interface_section=0 line key value stripped path v
CONFIG_FILE="$1" CONFIG_FILE="$1"
if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then
for path in "${CONFIG_SEARCH_PATHS[@]}"; do for path in "${CONFIG_SEARCH_PATHS[@]}"; do
@ -84,7 +85,9 @@ parse_options() {
case "$key" in case "$key" in
Address) ADDRESSES+=( ${value//,/ } ); continue ;; Address) ADDRESSES+=( ${value//,/ } ); continue ;;
MTU) MTU="$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 ;; Table) TABLE="$value"; continue ;;
PreUp) PRE_UP+=( "$value" ); continue ;; PreUp) PRE_UP+=( "$value" ); continue ;;
PreDown) PRE_DOWN+=( "$value" ); continue ;; PreDown) PRE_DOWN+=( "$value" ); continue ;;
@ -297,7 +300,9 @@ monitor_daemon() {
HAVE_SET_DNS=0 HAVE_SET_DNS=0
set_dns() { set_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0 [[ ${#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 HAVE_SET_DNS=1
} }

View file

@ -16,6 +16,7 @@ INTERFACE=""
ADDRESSES=( ) ADDRESSES=( )
MTU="" MTU=""
DNS=( ) DNS=( )
DNS_SEARCH=( )
TABLE="" TABLE=""
PRE_UP=( ) PRE_UP=( )
POST_UP=( ) POST_UP=( )
@ -37,7 +38,7 @@ die() {
} }
parse_options() { parse_options() {
local interface_section=0 line key value stripped local interface_section=0 line key value stripped v
CONFIG_FILE="$1" CONFIG_FILE="$1"
[[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf" [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]] && CONFIG_FILE="/etc/wireguard/$CONFIG_FILE.conf"
[[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist" [[ -e $CONFIG_FILE ]] || die "\`$CONFIG_FILE' does not exist"
@ -56,7 +57,9 @@ parse_options() {
case "$key" in case "$key" in
Address) ADDRESSES+=( ${value//,/ } ); continue ;; Address) ADDRESSES+=( ${value//,/ } ); continue ;;
MTU) MTU="$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 ;; Table) TABLE="$value"; continue ;;
PreUp) PRE_UP+=( "$value" ); continue ;; PreUp) PRE_UP+=( "$value" ); continue ;;
PreDown) PRE_DOWN+=( "$value" ); continue ;; PreDown) PRE_DOWN+=( "$value" ); continue ;;
@ -150,7 +153,9 @@ resolvconf_iface_prefix() {
HAVE_SET_DNS=0 HAVE_SET_DNS=0
set_dns() { set_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0 [[ ${#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 HAVE_SET_DNS=1
} }

View file

@ -16,6 +16,7 @@ INTERFACE=""
ADDRESSES=( ) ADDRESSES=( )
MTU="" MTU=""
DNS=( ) DNS=( )
DNS_SEARCH=( )
TABLE="" TABLE=""
PRE_UP=( ) PRE_UP=( )
POST_UP=( ) POST_UP=( )
@ -56,7 +57,9 @@ parse_options() {
case "$key" in case "$key" in
Address) ADDRESSES+=( ${value//,/ } ); continue ;; Address) ADDRESSES+=( ${value//,/ } ); continue ;;
MTU) MTU="$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 ;; Table) TABLE="$value"; continue ;;
PreUp) PRE_UP+=( "$value" ); continue ;; PreUp) PRE_UP+=( "$value" ); continue ;;
PreDown) PRE_DOWN+=( "$value" ); continue ;; PreDown) PRE_DOWN+=( "$value" ); continue ;;
@ -270,7 +273,9 @@ set_dns() {
[[ ${#DNS[@]} -gt 0 ]] || return 0 [[ ${#DNS[@]} -gt 0 ]] || return 0
# TODO: this is a horrible way of doing it. Has OpenBSD no resolvconf? # 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 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() { unset_dns() {