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 
40 static bool
41 dns_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 
54 bool
55 dns_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 
140 void
141 dns_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 
159 bool
160 dns_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 
174 struct dns_server *
175 dns_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 
196 bool
197 dns_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 
213 static struct dns_domain *
214 clone_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 
231 static struct dns_server *
232 clone_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 
250 struct dns_options
251 clone_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 
263 void
265 {
266  o->servers_prepull = o->servers;
267  o->servers = NULL;
268 }
269 
270 void
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 
306 static 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 
325 static const char *
327 {
328  switch (transport)
329  {
330  case DNS_TRANSPORT_HTTPS:
331  return "DoH";
332 
333  case DNS_TRANSPORT_TLS:
334  return "DoT";
335 
336  case DNS_TRANSPORT_PLAIN:
337  return "plain";
338 
339  default:
340  return "unset";
341  }
342 }
343 
344 static 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 
369 void
370 setenv_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 
431 void
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 }
dnssec_value
static const char * dnssec_value(const enum dns_security dnssec)
Definition: dns.c:307
dns_server_addr::port
in_port_t port
Definition: dns.h:56
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1025
clone_dns_servers
static struct dns_server * clone_dns_servers(const struct dns_server *server, struct gc_arena *gc)
Definition: dns.c:232
dns_security
dns_security
Definition: dns.h:30
dns_options_postprocess_pull
void dns_options_postprocess_pull(struct dns_options *o)
Merges pulled DNS servers with static ones into an ordered list.
Definition: dns.c:271
dns_domain::next
struct dns_domain * next
Definition: dns.h:45
DNS_SECURITY_OPTIONAL
@ DNS_SECURITY_OPTIONAL
Definition: dns.h:34
es
struct env_set * es
Definition: test_pkcs11.c:141
dns_server_get
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
dns_domain_list_append
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
dns_options_verify
bool dns_options_verify(int msglevel, const struct dns_options *o)
Checks validity of DNS options.
Definition: dns.c:197
transport_value
static const char * transport_value(const enum dns_server_transport transport)
Definition: dns.c:326
dns_server_transport
dns_server_transport
Definition: dns.h:37
dns_server::dnssec
enum dns_security dnssec
Definition: dns.h:65
dns_server::transport
enum dns_server_transport transport
Definition: dns.h:66
IA_NET_ORDER
#define IA_NET_ORDER
Definition: socket.h:402
setenv_dns_option
static void setenv_dns_option(struct env_set *es, const char *format, int i, int j, const char *value)
Definition: dns.c:345
openvpn_getaddrinfo
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:469
dns_options::servers
struct dns_server * servers
Definition: dns.h:73
dns_options::servers_prepull
struct dns_server * servers_prepull
Definition: dns.h:72
sa_family_t
unsigned short sa_family_t
Definition: syshead.h:395
DNS_TRANSPORT_HTTPS
@ DNS_TRANSPORT_HTTPS
Definition: dns.h:40
dns_server::priority
long priority
Definition: dns.h:61
print_in6_addr
const char * print_in6_addr(struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
Definition: socket.c:3011
dns_server_addr_parse
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
dns_domain::name
const char * name
Definition: dns.h:46
dns_server_addr::in
union dns_server_addr::@0 in
dns_server
Definition: dns.h:59
M_WARN
#define M_WARN
Definition: error.h:91
dns_options_preprocess_pull
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
ALLOC_OBJ_CLEAR_GC
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Definition: buffer.h:1097
dns_server::domains
struct dns_domain * domains
Definition: dns.h:64
DNS_SECURITY_NO
@ DNS_SECURITY_NO
Definition: dns.h:32
dns_server_port_parse
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
dns_domain
Definition: dns.h:44
dns_options::search_domains
struct dns_domain * search_domains
Definition: dns.h:71
dns_server::addr_count
size_t addr_count
Definition: dns.h:62
DNS_TRANSPORT_PLAIN
@ DNS_TRANSPORT_PLAIN
Definition: dns.h:39
dns_server::addr
struct dns_server_addr addr[8]
Definition: dns.h:63
SIZE
#define SIZE(x)
Definition: basic.h:30
print_in_addr_t
const char * print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
Definition: socket.c:2991
syshead.h
gc_arena
Garbage collection arena used to keep track of dynamically allocated memory.
Definition: buffer.h:116
setenv_str
void setenv_str(struct env_set *es, const char *name, const char *value)
Definition: env_set.c:283
DNS_TRANSPORT_TLS
@ DNS_TRANSPORT_TLS
Definition: dns.h:41
print_in_port_t
const char * print_in_port_t(in_port_t port, struct gc_arena *gc)
Definition: socket.c:3027
env_set
Definition: env_set.h:42
D_SHOW_PARMS
#define D_SHOW_PARMS
Definition: errlevel.h:96
DNS_SECURITY_YES
@ DNS_SECURITY_YES
Definition: dns.h:33
dns_server_addr::family
sa_family_t family
Definition: dns.h:55
setenv_dns_options
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
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1033
show_dns_options
void show_dns_options(const struct dns_options *o)
Prints configured DNS options.
Definition: dns.c:432
dns_options
Definition: dns.h:70
socket.h
dns_server::sni
const char * sni
Definition: dns.h:67
config.h
dns_server::next
struct dns_server * next
Definition: dns.h:60
dns_server_addr::a6
struct in6_addr a6
Definition: dns.h:53
dns.h
dns_server_addr::a4
struct in_addr a4
Definition: dns.h:52
dns_server_priority_parse
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
msg
#define msg(flags,...)
Definition: error.h:144
clone_dns_domains
static struct dns_domain * clone_dns_domains(const struct dns_domain *domain, struct gc_arena *gc)
Definition: dns.c:214
clone_dns_options
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
gc
struct gc_arena gc
Definition: test_ssl.c:155