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-2023 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 = ntohl(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  memset(&clone, 0, sizeof(clone));
255  clone.search_domains = clone_dns_domains(o.search_domains, gc);
256  clone.servers = clone_dns_servers(o.servers, gc);
257  clone.servers_prepull = clone_dns_servers(o.servers_prepull, gc);
258  return clone;
259 }
260 
261 void
263 {
264  o->servers_prepull = o->servers;
265  o->servers = NULL;
266 }
267 
268 void
270 {
271  struct dns_server **entry = &o->servers;
272  struct dns_server *server = *entry;
273  struct dns_server *server_pp = o->servers_prepull;
274 
275  while (server && server_pp)
276  {
277  if (server->priority > server_pp->priority)
278  {
279  /* Merge static server in front of pulled one */
280  struct dns_server *next_pp = server_pp->next;
281  server_pp->next = server;
282  *entry = server_pp;
283  server = *entry;
284  server_pp = next_pp;
285  }
286  else if (server->priority == server_pp->priority)
287  {
288  /* Pulled server overrides static one */
289  server_pp = server_pp->next;
290  }
291  entry = &server->next;
292  server = *entry;
293  }
294 
295  /* Append remaining local servers */
296  if (server_pp)
297  {
298  *entry = server_pp;
299  }
300 
301  o->servers_prepull = NULL;
302 }
303 
304 static const char *
306 {
307  switch (dnssec)
308  {
309  case DNS_SECURITY_YES:
310  return "yes";
311 
313  return "optional";
314 
315  case DNS_SECURITY_NO:
316  return "no";
317 
318  default:
319  return "unset";
320  }
321 }
322 
323 static const char *
325 {
326  switch (transport)
327  {
328  case DNS_TRANSPORT_HTTPS:
329  return "DoH";
330 
331  case DNS_TRANSPORT_TLS:
332  return "DoT";
333 
334  case DNS_TRANSPORT_PLAIN:
335  return "plain";
336 
337  default:
338  return "unset";
339  }
340 }
341 
342 static void
344  const char *format, int i, int j,
345  const char *value)
346 {
347  char name[64];
348  bool name_ok = false;
349 
350  if (j < 0)
351  {
352  name_ok = openvpn_snprintf(name, sizeof(name), format, i);
353  }
354  else
355  {
356  name_ok = openvpn_snprintf(name, sizeof(name), format, i, j);
357  }
358 
359  if (!name_ok)
360  {
361  msg(M_WARN, "WARNING: dns option setenv name buffer overflow");
362  }
363 
364  setenv_str(es, name, value);
365 }
366 
367 void
368 setenv_dns_options(const struct dns_options *o, struct env_set *es)
369 {
370  struct gc_arena gc = gc_new();
371  const struct dns_server *s;
372  const struct dns_domain *d;
373  int i, j;
374 
375  for (i = 1, d = o->search_domains; d != NULL; i++, d = d->next)
376  {
377  setenv_dns_option(es, "dns_search_domain_%d", i, -1, d->name);
378  }
379 
380  for (i = 1, s = o->servers; s != NULL; i++, s = s->next)
381  {
382  for (j = 0; j < s->addr_count; ++j)
383  {
384  if (s->addr[j].family == AF_INET)
385  {
386  setenv_dns_option(es, "dns_server_%d_address_%d", i, j + 1,
387  print_in_addr_t(s->addr[j].in.a4.s_addr, 0, &gc));
388  }
389  else
390  {
391  setenv_dns_option(es, "dns_server_%d_address_%d", i, j + 1,
392  print_in6_addr(s->addr[j].in.a6, 0, &gc));
393  }
394  if (s->addr[j].port)
395  {
396  setenv_dns_option(es, "dns_server_%d_port_%d", i, j + 1,
397  print_in_port_t(s->addr[j].port, &gc));
398  }
399  }
400 
401  if (s->domains)
402  {
403  for (j = 1, d = s->domains; d != NULL; j++, d = d->next)
404  {
405  setenv_dns_option(es, "dns_server_%d_resolve_domain_%d", i, j, d->name);
406  }
407  }
408 
409  if (s->dnssec)
410  {
411  setenv_dns_option(es, "dns_server_%d_dnssec", i, -1,
412  dnssec_value(s->dnssec));
413  }
414 
415  if (s->transport)
416  {
417  setenv_dns_option(es, "dns_server_%d_transport", i, -1,
419  }
420  if (s->sni)
421  {
422  setenv_dns_option(es, "dns_server_%d_sni", i, -1, s->sni);
423  }
424  }
425 
426  gc_free(&gc);
427 }
428 
429 void
431 {
432  struct gc_arena gc = gc_new();
433 
434  int i = 1;
435  struct dns_server *server = o->servers_prepull ? o->servers_prepull : o->servers;
436  while (server)
437  {
438  msg(D_SHOW_PARMS, " DNS server #%d:", i++);
439 
440  for (int j = 0; j < server->addr_count; ++j)
441  {
442  const char *addr;
443  const char *fmt_port;
444  if (server->addr[j].family == AF_INET)
445  {
446  addr = print_in_addr_t(server->addr[j].in.a4.s_addr, 0, &gc);
447  fmt_port = " address = %s:%s";
448  }
449  else
450  {
451  addr = print_in6_addr(server->addr[j].in.a6, 0, &gc);
452  fmt_port = " address = [%s]:%s";
453  }
454 
455  if (server->addr[j].port)
456  {
457  const char *port = print_in_port_t(server->addr[j].port, &gc);
458  msg(D_SHOW_PARMS, fmt_port, addr, port);
459  }
460  else
461  {
462  msg(D_SHOW_PARMS, " address = %s", addr);
463  }
464  }
465 
466  if (server->dnssec)
467  {
468  msg(D_SHOW_PARMS, " dnssec = %s", dnssec_value(server->dnssec));
469  }
470 
471  if (server->transport)
472  {
473  msg(D_SHOW_PARMS, " transport = %s", transport_value(server->transport));
474  }
475  if (server->sni)
476  {
477  msg(D_SHOW_PARMS, " sni = %s", server->sni);
478  }
479 
480  struct dns_domain *domain = server->domains;
481  if (domain)
482  {
483  msg(D_SHOW_PARMS, " resolve domains:");
484  while (domain)
485  {
486  msg(D_SHOW_PARMS, " %s", domain->name);
487  domain = domain->next;
488  }
489  }
490 
491  server = server->next;
492  }
493 
494  struct dns_domain *search_domain = o->search_domains;
495  if (search_domain)
496  {
497  msg(D_SHOW_PARMS, " DNS search domains:");
498  while (search_domain)
499  {
500  msg(D_SHOW_PARMS, " %s", search_domain->name);
501  search_domain = search_domain->next;
502  }
503  }
504 
505  gc_free(&gc);
506 }
dnssec_value
static const char * dnssec_value(const enum dns_security dnssec)
Definition: dns.c:305
dns_server_addr::port
in_port_t port
Definition: dns.h:56
gc_new
static struct gc_arena gc_new(void)
Definition: buffer.h:1031
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:269
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:133
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:324
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
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:343
dns_options::gc
struct gc_arena gc
Definition: dns.h:74
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:429
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:385
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:2921
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:97
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:262
ALLOC_OBJ_CLEAR_GC
#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc)
Definition: buffer.h:1103
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:2901
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:2937
env_set
Definition: env_set.h:42
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
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:368
openvpn_snprintf
bool openvpn_snprintf(char *str, size_t size, const char *format,...)
Definition: buffer.c:294
gc_free
static void gc_free(struct gc_arena *a)
Definition: buffer.h:1039
show_dns_options
void show_dns_options(const struct dns_options *o)
Prints configured DNS options.
Definition: dns.c:430
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:150
clone_dns_domains
static struct dns_domain * clone_dns_domains(const struct dns_domain *domain, struct gc_arena *gc)
Definition: dns.c:214