Browse code

dns: add updown script for macOS

Change-Id: Iade06a8454ccf53668deef61f07217ead8ec6c63
Signed-off-by: Heiko Hund <heiko@ist.eigentlich.net>
Acked-by: Arne Schwabe <arne-openvpn@rfc2549.org>
Message-Id: <20250621121301.27509-1-gert@greenie.muc.de>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg31942.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

Heiko Hund authored on 2025/06/21 21:12:54
Showing 3 changed files
... ...
@@ -364,8 +364,7 @@ case "$host" in
364 364
 	*-*-darwin*)
365 365
 		AC_DEFINE([TARGET_DARWIN], [1], [Are we running on Mac OS X?])
366 366
 		AC_DEFINE_UNQUOTED([TARGET_PREFIX], ["M"], [Target prefix])
367
-		AM_CONDITIONAL([ENABLE_DNS_UPDOWN], [false])
368
-		AC_SUBST([DNS_UPDOWN_TYPE], ["resolvconf_file"])
367
+		AC_SUBST([DNS_UPDOWN_TYPE], ["macos"])
369 368
 		have_tap_header="yes"
370 369
 		ac_cv_type_struct_in_pktinfo=no
371 370
 		;;
... ...
@@ -12,6 +12,7 @@ MAINTAINERCLEANFILES = \
12 12
 	$(srcdir)/Makefile.in
13 13
 
14 14
 EXTRA_DIST = \
15
+	macos-dns-updown.sh \
15 16
 	systemd-dns-updown.sh \
16 17
 	openresolv-dns-updown.sh \
17 18
 	haikuos_file-dns-updown.sh \
18 19
new file mode 100644
... ...
@@ -0,0 +1,217 @@
0
+#!/bin/bash
1
+#
2
+# dns-updown - add/remove openvpn provided DNS information
3
+#
4
+# (C) Copyright 2025 OpenVPN Inc <sales@openvpn.net>
5
+#
6
+# SPDX-License-Identifier: BSD-2-Clause
7
+#
8
+# Example env from openvpn (most are not applied):
9
+#
10
+#   dns_vars_file /tmp/openvpn_dvf_58b95c0c97b2db43afb5d745f986c53c.tmp
11
+#
12
+#      or
13
+#
14
+#   dev utun0
15
+#   script_type dns-up
16
+#   dns_search_domain_1 mycorp.in
17
+#   dns_search_domain_2 eu.mycorp.com
18
+#   dns_server_1_address_1 192.168.99.254
19
+#   dns_server_1_address_2 fd00::99:53
20
+#   dns_server_1_port_2 53
21
+#   dns_server_1_resolve_domain_1 mycorp.in
22
+#   dns_server_1_resolve_domain_2 eu.mycorp.com
23
+#   dns_server_1_dnssec true
24
+#   dns_server_1_transport DoH
25
+#   dns_server_1_sni dns.mycorp.in
26
+#
27
+
28
+[ -z "${dns_vars_file}" ] || . "${dns_vars_file}"
29
+
30
+itf_dns_key="State:/Network/Service/openvpn-${dev}/DNS"
31
+dns_backup_key="State:/Network/Service/openvpn-${dev}/DnsBackup"
32
+
33
+function primary_dns_key {
34
+    local uuid=$(echo "show State:/Network/Global/IPv4" | /usr/sbin/scutil | grep "PrimaryService" | cut -d: -f2 | xargs)
35
+    echo "Setup:/Network/Service/${uuid}/DNS"
36
+}
37
+
38
+function only_standard_server_ports {
39
+    local i=1
40
+    while :; do
41
+        local addr_var=dns_server_${n}_address_${i}
42
+        [ -n "${!addr_var}" ] || return 0
43
+
44
+        local port_var=dns_server_${n}_port_${i}
45
+        [ -z "${!port_var}" -o "${!port_var}" = "53" ] || return 1
46
+
47
+        i=$((i+1))
48
+    done
49
+}
50
+
51
+function find_compat_profile {
52
+    local n=1
53
+    while :; do
54
+        local addr_var=dns_server_${n}_address_1
55
+        [ -n "${!addr_var}" ] || {
56
+            echo "setting DNS failed, no compatible server profile"
57
+            exit 1
58
+        }
59
+
60
+        # Skip server profiles which require DNSSEC,
61
+        # secure transport or use a custom port
62
+        local dnssec_var=dns_server_${n}_dnssec
63
+        local transport_var=dns_server_${n}_transport
64
+        [ -z "${!transport_var}" -o "${!transport_var}" = "plain" ] \
65
+            && [ -z "${!dnssec_var}" -o "${!dnssec_var}" = "no" ] \
66
+            && only_standard_server_ports && break
67
+
68
+        n=$((n+1))
69
+    done
70
+    return $n
71
+}
72
+
73
+function get_search_domains {
74
+    local search_domains=""
75
+    local resolver=0
76
+    /usr/sbin/scutil --dns | while read line; do
77
+        if [[ "$line" =~ resolver.# ]]; then
78
+            resolver=$((resolver+1))
79
+        elif [ "$resolver" = 1 ] && [[ "$line" =~ search.domain ]]; then
80
+            search_domains+="$(echo $line | cut -d: -f2 | xargs) "
81
+        elif [ "$resolver" -gt 1 ]; then
82
+            echo "$search_domains"
83
+            break
84
+        fi
85
+    done
86
+}
87
+
88
+function set_search_domains {
89
+    [ -n "$1" ] || return
90
+    dns_key=$(primary_dns_key)
91
+    search_domains="${1}$(get_search_domains)"
92
+
93
+    local cmds=""
94
+    cmds+="get ${dns_key}\n"
95
+    cmds+="d.add SearchDomains * ${search_domains}\n"
96
+    cmds+="set ${dns_key}\n"
97
+    echo -e "${cmds}" | /usr/sbin/scutil
98
+}
99
+
100
+function unset_search_domains {
101
+    [ -n "$1" ] || return
102
+    dns_key=$(primary_dns_key)
103
+    search_domains="$(get_search_domains)"
104
+    search_domains=$(echo $search_domains | sed -e "s/$1//")
105
+
106
+    local cmds=""
107
+    cmds+="get ${dns_key}\n"
108
+    cmds+="d.add SearchDomains * ${search_domains}\n"
109
+    cmds+="set ${dns_key}\n"
110
+    echo -e "${cmds}" | /usr/sbin/scutil
111
+}
112
+
113
+function set_dns {
114
+    find_compat_profile
115
+    local n=$?
116
+
117
+    local i=1
118
+    local addrs=""
119
+    while :; do
120
+        local addr_var=dns_server_${n}_address_${i}
121
+        local addr="${!addr_var}"
122
+        [ -n "$addr" ] || break
123
+
124
+        local port_var=dns_server_${n}_port_${i}
125
+        if [ -n "${!port_var}" ]; then
126
+            if [[ "$addr" =~ : ]]; then
127
+                addr="[$addr]"
128
+            fi
129
+            addrs+="${addr}:${!port_var}${sni} "
130
+        else
131
+            addrs+="${addr}${sni} "
132
+        fi
133
+        i=$((i+1))
134
+    done
135
+
136
+    i=1
137
+    local match_domains=""
138
+    while :; do
139
+        domain_var=dns_server_${n}_resolve_domain_${i}
140
+        [ -n "${!domain_var}" ] || break
141
+        # Add as match domain, if it doesn't already exist
142
+        [[ "$match_domains" =~ (^| )${!domain_var}( |$) ]] \
143
+            || match_domains+="${!domain_var} "
144
+        i=$((i+1))
145
+    done
146
+
147
+    i=1
148
+    local search_domains=""
149
+    while :; do
150
+        domain_var=dns_search_domain_${i}
151
+        [ -n "${!domain_var}" ] || break
152
+        # Add as search domain, if it doesn't already exist
153
+        [[ "$search_domains" =~ (^| )${!domain_var}( |$) ]] \
154
+            || search_domains+="${!domain_var} "
155
+        i=$((i+1))
156
+    done
157
+
158
+    if [ -n "$match_domains" ]; then
159
+        local cmds=""
160
+        cmds+="d.init\n"
161
+        cmds+="d.add ServerAddresses * ${addrs}\n"
162
+        cmds+="d.add SupplementalMatchDomains * ${match_domains}\n"
163
+        cmds+="d.add SupplementalMatchDomainsNoSearch # 1\n"
164
+        cmds+="add ${itf_dns_key}\n"
165
+        echo -e "${cmds}" | /usr/sbin/scutil
166
+        set_search_domains "$search_domains"
167
+    else
168
+        local cmds=""
169
+        cmds+="get $(primary_dns_key)\n"
170
+        cmds+="set ${dns_backup_key}\n"
171
+        cmds+="d.init\n"
172
+        cmds+="d.add ServerAddresses * ${addrs}\n"
173
+        cmds+="d.add SearchDomains * ${search_domains}\n"
174
+        cmds+="d.add SearchOrder # 5000\n"
175
+        cmds+="set $(primary_dns_key)\n"
176
+        echo -e "${cmds}" | /usr/sbin/scutil
177
+    fi
178
+
179
+    /usr/bin/dscacheutil -flushcache
180
+}
181
+
182
+function unset_dns {
183
+    find_compat_profile
184
+    local n=$?
185
+
186
+    local i=1
187
+    local search_domains=""
188
+    while :; do
189
+        domain_var=dns_search_domain_${i}
190
+        [ -n "${!domain_var}" ] || break
191
+        # Add as search domain, if it doesn't already exist
192
+        [[ "$search_domains" =~ (^| )${!domain_var}( |$) ]] \
193
+            || search_domains+="${!domain_var} "
194
+        i=$((i+1))
195
+    done
196
+
197
+    domain_var=dns_server_${n}_resolve_domain_1
198
+    if [ -n "${!domain_var}" ]; then
199
+        echo "remove ${itf_dns_key}" | /usr/sbin/scutil
200
+        unset_search_domains "$search_domains"
201
+    else
202
+        local cmds=""
203
+        cmds+="get ${dns_backup_key}\n"
204
+        cmds+="set $(primary_dns_key)\n"
205
+        cmds+="remove ${dns_backup_key}\n"
206
+        echo -e "${cmds}" | /usr/sbin/scutil
207
+    fi
208
+
209
+    /usr/bin/dscacheutil -flushcache
210
+}
211
+
212
+if [ "$script_type" = "dns-up" ]; then
213
+    set_dns
214
+else
215
+    unset_dns
216
+fi