OpenVPN
dns.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2022-2024 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include "syshead.h"
29
30#include "dns.h"
31#include "socket.h"
32
40static bool
41dns_server_port_parse(in_port_t *port, char *port_str)
42{
43 char *endptr;
44 errno = 0;
45 unsigned long tmp = strtoul(port_str, &endptr, 10);
46 if (errno || *endptr != '\0' || tmp == 0 || tmp > UINT16_MAX)
47 {
48 return false;
49 }
50 *port = (in_port_t)tmp;
51 return true;
52}
53
54bool
55dns_server_addr_parse(struct dns_server *server, const char *addr)
56{
57 if (!addr)
58 {
59 return false;
60 }
61
62 char addrcopy[INET6_ADDRSTRLEN] = {0};
63 size_t copylen = 0;
64 in_port_t port = 0;
65 sa_family_t af;
66
67 char *first_colon = strchr(addr, ':');
68 char *last_colon = strrchr(addr, ':');
69
70 if (!first_colon || first_colon == last_colon)
71 {
72 /* IPv4 address with optional port, e.g. 1.2.3.4 or 1.2.3.4:853 */
73 if (last_colon)
74 {
75 if (last_colon == addr || !dns_server_port_parse(&port, last_colon + 1))
76 {
77 return false;
78 }
79 copylen = first_colon - addr;
80 }
81 af = AF_INET;
82 }
83 else
84 {
85 /* IPv6 address with optional port, e.g. ab::cd or [ab::cd]:853 */
86 if (addr[0] == '[')
87 {
88 addr += 1;
89 char *bracket = last_colon - 1;
90 if (*bracket != ']' || bracket == addr || !dns_server_port_parse(&port, last_colon + 1))
91 {
92 return false;
93 }
94 copylen = bracket - addr;
95 }
96 af = AF_INET6;
97 }
98
99 /* Copy the address part into a temporary buffer and use that */
100 if (copylen)
101 {
102 if (copylen >= sizeof(addrcopy))
103 {
104 return false;
105 }
106 strncpy(addrcopy, addr, copylen);
107 addr = addrcopy;
108 }
109
110 struct addrinfo *ai = NULL;
111 if (openvpn_getaddrinfo(0, addr, NULL, 0, NULL, af, &ai) != 0)
112 {
113 return false;
114 }
115
116 if (server->addr_count >= SIZE(server->addr))
117 {
118 return false;
119 }
120
121 if (ai->ai_family == AF_INET)
122 {
123 struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
124 server->addr[server->addr_count].in.a4.s_addr = sin->sin_addr.s_addr;
125 }
126 else
127 {
128 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
129 server->addr[server->addr_count].in.a6 = sin6->sin6_addr;
130 }
131
132 server->addr[server->addr_count].family = af;
133 server->addr[server->addr_count].port = port;
134 server->addr_count += 1;
135
136 freeaddrinfo(ai);
137 return true;
138}
139
140void
141dns_domain_list_append(struct dns_domain **entry, char **domains, struct gc_arena *gc)
142{
143 /* Fast forward to the end of the list */
144 while (*entry)
145 {
146 entry = &((*entry)->next);
147 }
148
149 /* Append all domains to the end of the list */
150 while (*domains)
151 {
152 ALLOC_OBJ_CLEAR_GC(*entry, struct dns_domain, gc);
153 struct dns_domain *new = *entry;
154 new->name = *domains++;
155 entry = &new->next;
156 }
157}
158
159bool
160dns_server_priority_parse(long *priority, const char *str, bool pulled)
161{
162 char *endptr;
163 const long min = pulled ? 0 : INT8_MIN;
164 const long max = INT8_MAX;
165 long prio = strtol(str, &endptr, 10);
166 if (*endptr != '\0' || prio < min || prio > max)
167 {
168 return false;
169 }
170 *priority = prio;
171 return true;
172}
173
174struct dns_server *
175dns_server_get(struct dns_server **entry, long priority, struct gc_arena *gc)
176{
177 struct dns_server *obj = *entry;
178 while (true)
179 {
180 if (!obj || obj->priority > priority)
181 {
182 ALLOC_OBJ_CLEAR_GC(*entry, struct dns_server, gc);
183 (*entry)->next = obj;
184 (*entry)->priority = priority;
185 return *entry;
186 }
187 else if (obj->priority == priority)
188 {
189 return obj;
190 }
191 entry = &obj->next;
192 obj = *entry;
193 }
194}
195
196bool
197dns_options_verify(int msglevel, const struct dns_options *o)
198{
199 const struct dns_server *server =
200 o->servers ? o->servers : o->servers_prepull;
201 while (server)
202 {
203 if (server->addr_count == 0)
204 {
205 msg(msglevel, "ERROR: dns server %ld does not have an address assigned", server->priority);
206 return false;
207 }
208 server = server->next;
209 }
210 return true;
211}
212
213static struct dns_domain *
214clone_dns_domains(const struct dns_domain *domain, struct gc_arena *gc)
215{
216 struct dns_domain *new_list = NULL;
217 struct dns_domain **new_entry = &new_list;
218
219 while (domain)
220 {
221 ALLOC_OBJ_CLEAR_GC(*new_entry, struct dns_domain, gc);
222 struct dns_domain *new_domain = *new_entry;
223 *new_domain = *domain;
224 new_entry = &new_domain->next;
225 domain = domain->next;
226 }
227
228 return new_list;
229}
230
231static struct dns_server *
232clone_dns_servers(const struct dns_server *server, struct gc_arena *gc)
233{
234 struct dns_server *new_list = NULL;
235 struct dns_server **new_entry = &new_list;
236
237 while (server)
238 {
239 ALLOC_OBJ_CLEAR_GC(*new_entry, struct dns_server, gc);
240 struct dns_server *new_server = *new_entry;
241 *new_server = *server;
242 new_server->domains = clone_dns_domains(server->domains, gc);
243 new_entry = &new_server->next;
244 server = server->next;
245 }
246
247 return new_list;
248}
249
250struct dns_options
251clone_dns_options(const struct dns_options *o, struct gc_arena *gc)
252{
253 struct dns_options clone;
254
255 memset(&clone, 0, sizeof(clone));
257 clone.servers = clone_dns_servers(o->servers, gc);
259
260 return clone;
261}
262
263void
265{
266 o->servers_prepull = o->servers;
267 o->servers = NULL;
268}
269
270void
272{
273 struct dns_server **entry = &o->servers;
274 struct dns_server *server = *entry;
275 struct dns_server *server_pp = o->servers_prepull;
276
277 while (server && server_pp)
278 {
279 if (server->priority > server_pp->priority)
280 {
281 /* Merge static server in front of pulled one */
282 struct dns_server *next_pp = server_pp->next;
283 server_pp->next = server;
284 *entry = server_pp;
285 server = *entry;
286 server_pp = next_pp;
287 }
288 else if (server->priority == server_pp->priority)
289 {
290 /* Pulled server overrides static one */
291 server_pp = server_pp->next;
292 }
293 entry = &server->next;
294 server = *entry;
295 }
296
297 /* Append remaining local servers */
298 if (server_pp)
299 {
300 *entry = server_pp;
301 }
302
303 o->servers_prepull = NULL;
304}
305
306static const char *
308{
309 switch (dnssec)
310 {
311 case DNS_SECURITY_YES:
312 return "yes";
313
315 return "optional";
316
317 case DNS_SECURITY_NO:
318 return "no";
319
320 default:
321 return "unset";
322 }
323}
324
325static const char *
327{
328 switch (transport)
329 {
331 return "DoH";
332
334 return "DoT";
335
337 return "plain";
338
339 default:
340 return "unset";
341 }
342}
343
344static void
346 const char *format, int i, int j,
347 const char *value)
348{
349 char name[64];
350 bool name_ok = false;
351
352 if (j < 0)
353 {
354 name_ok = snprintf(name, sizeof(name), format, i);
355 }
356 else
357 {
358 name_ok = snprintf(name, sizeof(name), format, i, j);
359 }
360
361 if (!name_ok)
362 {
363 msg(M_WARN, "WARNING: dns option setenv name buffer overflow");
364 }
365
366 setenv_str(es, name, value);
367}
368
369void
370setenv_dns_options(const struct dns_options *o, struct env_set *es)
371{
372 struct gc_arena gc = gc_new();
373 const struct dns_server *s;
374 const struct dns_domain *d;
375 int i, j;
376
377 for (i = 1, d = o->search_domains; d != NULL; i++, d = d->next)
378 {
379 setenv_dns_option(es, "dns_search_domain_%d", i, -1, d->name);
380 }
381
382 for (i = 1, s = o->servers; s != NULL; i++, s = s->next)
383 {
384 for (j = 0; j < s->addr_count; ++j)
385 {
386 if (s->addr[j].family == AF_INET)
387 {
388 setenv_dns_option(es, "dns_server_%d_address_%d", i, j + 1,
389 print_in_addr_t(s->addr[j].in.a4.s_addr, IA_NET_ORDER, &gc));
390 }
391 else
392 {
393 setenv_dns_option(es, "dns_server_%d_address_%d", i, j + 1,
394 print_in6_addr(s->addr[j].in.a6, 0, &gc));
395 }
396 if (s->addr[j].port)
397 {
398 setenv_dns_option(es, "dns_server_%d_port_%d", i, j + 1,
399 print_in_port_t(s->addr[j].port, &gc));
400 }
401 }
402
403 if (s->domains)
404 {
405 for (j = 1, d = s->domains; d != NULL; j++, d = d->next)
406 {
407 setenv_dns_option(es, "dns_server_%d_resolve_domain_%d", i, j, d->name);
408 }
409 }
410
411 if (s->dnssec)
412 {
413 setenv_dns_option(es, "dns_server_%d_dnssec", i, -1,
414 dnssec_value(s->dnssec));
415 }
416
417 if (s->transport)
418 {
419 setenv_dns_option(es, "dns_server_%d_transport", i, -1,
421 }
422 if (s->sni)
423 {
424 setenv_dns_option(es, "dns_server_%d_sni", i, -1, s->sni);
425 }
426 }
427
428 gc_free(&gc);
429}
430
431void
433{
434 struct gc_arena gc = gc_new();
435
436 int i = 1;
437 struct dns_server *server = o->servers_prepull ? o->servers_prepull : o->servers;
438 while (server)
439 {
440 msg(D_SHOW_PARMS, " DNS server #%d:", i++);
441
442 for (int j = 0; j < server->addr_count; ++j)
443 {
444 const char *addr;
445 const char *fmt_port;
446 if (server->addr[j].family == AF_INET)
447 {
448 addr = print_in_addr_t(server->addr[j].in.a4.s_addr, IA_NET_ORDER, &gc);
449 fmt_port = " address = %s:%s";
450 }
451 else
452 {
453 addr = print_in6_addr(server->addr[j].in.a6, 0, &gc);
454 fmt_port = " address = [%s]:%s";
455 }
456
457 if (server->addr[j].port)
458 {
459 const char *port = print_in_port_t(server->addr[j].port, &gc);
460 msg(D_SHOW_PARMS, fmt_port, addr, port);
461 }
462 else
463 {
464 msg(D_SHOW_PARMS, " address = %s", addr);
465 }
466 }
467
468 if (server->dnssec)
469 {
470 msg(D_SHOW_PARMS, " dnssec = %s", dnssec_value(server->dnssec));
471 }
472
473 if (server->transport)
474 {
475 msg(D_SHOW_PARMS, " transport = %s", transport_value(server->transport));
476 }
477 if (server->sni)
478 {
479 msg(D_SHOW_PARMS, " sni = %s", server->sni);
480 }
481
482 struct dns_domain *domain = server->domains;
483 if (domain)
484 {
485 msg(D_SHOW_PARMS, " resolve domains:");
486 while (domain)
487 {
488 msg(D_SHOW_PARMS, " %s", domain->name);
489 domain = domain->next;
490 }
491 }
492
493 server = server->next;
494 }
495
496 struct dns_domain *search_domain = o->search_domains;
497 if (search_domain)
498 {
499 msg(D_SHOW_PARMS, " DNS search domains:");
500 while (search_domain)
501 {
502 msg(D_SHOW_PARMS, " %s", search_domain->name);
503 search_domain = search_domain->next;
504 }
505 }
506
507 gc_free(&gc);
508}
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Definition buffer.h:1097
static void gc_free(struct gc_arena *a)
Definition buffer.h:1033
static struct gc_arena gc_new(void)
Definition buffer.h:1025
bool dns_options_verify(int msglevel, const struct dns_options *o)
Checks validity of DNS options.
Definition dns.c:197
void dns_domain_list_append(struct dns_domain **entry, char **domains, struct gc_arena *gc)
Appends DNS domain parameters to a linked list.
Definition dns.c:141
static const char * dnssec_value(const enum dns_security dnssec)
Definition dns.c:307
static void setenv_dns_option(struct env_set *es, const char *format, int i, int j, const char *value)
Definition dns.c:345
void dns_options_postprocess_pull(struct dns_options *o)
Merges pulled DNS servers with static ones into an ordered list.
Definition dns.c:271
bool dns_server_addr_parse(struct dns_server *server, const char *addr)
Parses a string IPv4 or IPv6 address and optional colon separated port, into a in_addr or in6_addr re...
Definition dns.c:55
struct dns_server * dns_server_get(struct dns_server **entry, long priority, struct gc_arena *gc)
Find or create DNS server with priority in a linked list.
Definition dns.c:175
static struct dns_server * clone_dns_servers(const struct dns_server *server, struct gc_arena *gc)
Definition dns.c:232
static struct dns_domain * clone_dns_domains(const struct dns_domain *domain, struct gc_arena *gc)
Definition dns.c:214
static const char * transport_value(const enum dns_server_transport transport)
Definition dns.c:326
bool dns_server_priority_parse(long *priority, const char *str, bool pulled)
Parses a string DNS server priority and validates it.
Definition dns.c:160
struct dns_options clone_dns_options(const struct dns_options *o, struct gc_arena *gc)
Makes a deep copy of the passed DNS options.
Definition dns.c:251
void setenv_dns_options(const struct dns_options *o, struct env_set *es)
Puts the DNS options into an environment set.
Definition dns.c:370
void show_dns_options(const struct dns_options *o)
Prints configured DNS options.
Definition dns.c:432
static bool dns_server_port_parse(in_port_t *port, char *port_str)
Parses a string as port and stores it.
Definition dns.c:41
void dns_options_preprocess_pull(struct dns_options *o)
Saves and resets the server options, so that pulled ones don't mix in.
Definition dns.c:264
dns_security
Definition dns.h:30
@ DNS_SECURITY_NO
Definition dns.h:32
@ DNS_SECURITY_YES
Definition dns.h:33
@ DNS_SECURITY_OPTIONAL
Definition dns.h:34
dns_server_transport
Definition dns.h:37
@ DNS_TRANSPORT_PLAIN
Definition dns.h:39
@ DNS_TRANSPORT_TLS
Definition dns.h:41
@ DNS_TRANSPORT_HTTPS
Definition dns.h:40
void setenv_str(struct env_set *es, const char *name, const char *value)
Definition env_set.c:283
#define D_SHOW_PARMS
Definition errlevel.h:96
#define SIZE(x)
Definition basic.h:30
#define msg(flags,...)
Definition error.h:144
#define M_WARN
Definition error.h:91
int openvpn_getaddrinfo(unsigned int flags, const char *hostname, const char *servname, int resolve_retry_seconds, struct signal_info *sig_info, int ai_family, struct addrinfo **res)
Definition socket.c:453
const char * print_in_port_t(in_port_t port, struct gc_arena *gc)
Definition socket.c:3010
const char * print_in6_addr(struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
Definition socket.c:2994
const char * print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
Definition socket.c:2974
#define IA_NET_ORDER
Definition socket.h:402
struct dns_domain * next
Definition dns.h:45
const char * name
Definition dns.h:46
struct dns_server * servers
Definition dns.h:73
struct dns_server * servers_prepull
Definition dns.h:72
struct dns_domain * search_domains
Definition dns.h:71
struct in_addr a4
Definition dns.h:52
union dns_server_addr::@0 in
sa_family_t family
Definition dns.h:55
struct in6_addr a6
Definition dns.h:53
in_port_t port
Definition dns.h:56
struct dns_server_addr addr[8]
Definition dns.h:63
enum dns_security dnssec
Definition dns.h:65
struct dns_server * next
Definition dns.h:60
long priority
Definition dns.h:61
size_t addr_count
Definition dns.h:62
struct dns_domain * domains
Definition dns.h:64
enum dns_server_transport transport
Definition dns.h:66
const char * sni
Definition dns.h:67
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:117
unsigned short sa_family_t
Definition syshead.h:395
struct env_set * es
struct gc_arena gc
Definition test_ssl.c:155