Coverage Report

Created: 2023-06-07 06:23

/src/bind9/lib/isc/net.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3
 *
4
 * SPDX-License-Identifier: MPL-2.0
5
 *
6
 * This Source Code Form is subject to the terms of the Mozilla Public
7
 * License, v. 2.0. If a copy of the MPL was not distributed with this
8
 * file, you can obtain one at https://mozilla.org/MPL/2.0/.
9
 *
10
 * See the COPYRIGHT file distributed with this work for additional
11
 * information regarding copyright ownership.
12
 */
13
14
#include <stdbool.h>
15
#include <sys/types.h>
16
17
#if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__)
18
#if defined(HAVE_SYS_PARAM_H)
19
#include <sys/param.h>
20
#endif /* if defined(HAVE_SYS_PARAM_H) */
21
#include <sys/sysctl.h>
22
#endif /* if defined(HAVE_SYS_SYSCTL_H) && !defined(__linux__) */
23
#include <errno.h>
24
#include <fcntl.h>
25
#include <netdb.h>
26
#include <sys/uio.h>
27
#include <unistd.h>
28
29
#include <isc/log.h>
30
#include <isc/net.h>
31
#include <isc/once.h>
32
#include <isc/strerr.h>
33
#include <isc/string.h>
34
#include <isc/util.h>
35
36
#ifndef socklen_t
37
#define socklen_t unsigned int
38
#endif /* ifndef socklen_t */
39
40
/*%
41
 * Definitions about UDP port range specification.  This is a total mess of
42
 * portability variants: some use sysctl (but the sysctl names vary), some use
43
 * system-specific interfaces, some have the same interface for IPv4 and IPv6,
44
 * some separate them, etc...
45
 */
46
47
/*%
48
 * The last resort defaults: use all non well known port space
49
 */
50
#ifndef ISC_NET_PORTRANGELOW
51
0
#define ISC_NET_PORTRANGELOW 1024
52
#endif /* ISC_NET_PORTRANGELOW */
53
#ifndef ISC_NET_PORTRANGEHIGH
54
0
#define ISC_NET_PORTRANGEHIGH 65535
55
#endif /* ISC_NET_PORTRANGEHIGH */
56
57
#ifdef HAVE_SYSCTLBYNAME
58
59
/*%
60
 * sysctl variants
61
 */
62
#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__DragonFly__)
63
#define USE_SYSCTL_PORTRANGE
64
#define SYSCTL_V4PORTRANGE_LOW  "net.inet.ip.portrange.hifirst"
65
#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.portrange.hilast"
66
#define SYSCTL_V6PORTRANGE_LOW  "net.inet.ip.portrange.hifirst"
67
#define SYSCTL_V6PORTRANGE_HIGH "net.inet.ip.portrange.hilast"
68
#endif /* if defined(__FreeBSD__) || defined(__APPLE__) || \
69
  * defined(__DragonFly__) */
70
71
#ifdef __NetBSD__
72
#define USE_SYSCTL_PORTRANGE
73
#define SYSCTL_V4PORTRANGE_LOW  "net.inet.ip.anonportmin"
74
#define SYSCTL_V4PORTRANGE_HIGH "net.inet.ip.anonportmax"
75
#define SYSCTL_V6PORTRANGE_LOW  "net.inet6.ip6.anonportmin"
76
#define SYSCTL_V6PORTRANGE_HIGH "net.inet6.ip6.anonportmax"
77
#endif /* ifdef __NetBSD__ */
78
79
#else /* !HAVE_SYSCTLBYNAME */
80
81
#ifdef __OpenBSD__
82
#define USE_SYSCTL_PORTRANGE
83
#define SYSCTL_V4PORTRANGE_LOW                                         \
84
  {                                                              \
85
    CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HIFIRSTAUTO \
86
  }
87
#define SYSCTL_V4PORTRANGE_HIGH                                       \
88
  {                                                             \
89
    CTL_NET, PF_INET, IPPROTO_IP, IPCTL_IPPORT_HILASTAUTO \
90
  }
91
/* Same for IPv6 */
92
#define SYSCTL_V6PORTRANGE_LOW  SYSCTL_V4PORTRANGE_LOW
93
#define SYSCTL_V6PORTRANGE_HIGH SYSCTL_V4PORTRANGE_HIGH
94
#endif /* ifdef __OpenBSD__ */
95
96
#endif /* HAVE_SYSCTLBYNAME */
97
98
static isc_once_t once_ipv6only = ISC_ONCE_INIT;
99
#ifdef __notyet__
100
static isc_once_t once_ipv6pktinfo = ISC_ONCE_INIT;
101
#endif /* ifdef __notyet__ */
102
103
#ifndef ISC_CMSG_IP_TOS
104
#ifdef __APPLE__
105
#define ISC_CMSG_IP_TOS 0 /* As of 10.8.2. */
106
#else       /* ! __APPLE__ */
107
#define ISC_CMSG_IP_TOS 1
108
#endif /* ! __APPLE__ */
109
#endif /* ! ISC_CMSG_IP_TOS */
110
111
static isc_once_t once = ISC_ONCE_INIT;
112
113
static isc_result_t ipv4_result = ISC_R_NOTFOUND;
114
static isc_result_t ipv6_result = ISC_R_NOTFOUND;
115
static isc_result_t unix_result = ISC_R_NOTFOUND;
116
static isc_result_t ipv6only_result = ISC_R_NOTFOUND;
117
static isc_result_t ipv6pktinfo_result = ISC_R_NOTFOUND;
118
119
static isc_result_t
120
0
try_proto(int domain) {
121
0
  int s;
122
0
  isc_result_t result = ISC_R_SUCCESS;
123
124
0
  s = socket(domain, SOCK_STREAM, 0);
125
0
  if (s == -1) {
126
0
    switch (errno) {
127
0
#ifdef EAFNOSUPPORT
128
0
    case EAFNOSUPPORT:
129
0
#endif /* ifdef EAFNOSUPPORT */
130
0
#ifdef EPFNOSUPPORT
131
0
    case EPFNOSUPPORT:
132
0
#endif /* ifdef EPFNOSUPPORT */
133
0
#ifdef EPROTONOSUPPORT
134
0
    case EPROTONOSUPPORT:
135
0
#endif /* ifdef EPROTONOSUPPORT */
136
0
#ifdef EINVAL
137
0
    case EINVAL:
138
0
#endif /* ifdef EINVAL */
139
0
      return (ISC_R_NOTFOUND);
140
0
    default:
141
0
      UNEXPECTED_SYSERROR(errno, "socket()");
142
0
      return (ISC_R_UNEXPECTED);
143
0
    }
144
0
  }
145
146
0
  if (domain == PF_INET6) {
147
0
    struct sockaddr_in6 sin6;
148
0
    unsigned int len;
149
150
    /*
151
     * Check to see if IPv6 is broken, as is common on Linux.
152
     */
153
0
    len = sizeof(sin6);
154
0
    if (getsockname(s, (struct sockaddr *)&sin6, (void *)&len) < 0)
155
0
    {
156
0
      isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
157
0
              ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
158
0
              "retrieving the address of an IPv6 "
159
0
              "socket from the kernel failed.");
160
0
      isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
161
0
              ISC_LOGMODULE_SOCKET, ISC_LOG_ERROR,
162
0
              "IPv6 is not supported.");
163
0
      result = ISC_R_NOTFOUND;
164
0
    } else {
165
0
      if (len == sizeof(struct sockaddr_in6)) {
166
0
        result = ISC_R_SUCCESS;
167
0
      } else {
168
0
        isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
169
0
                ISC_LOGMODULE_SOCKET,
170
0
                ISC_LOG_ERROR,
171
0
                "IPv6 structures in kernel and "
172
0
                "user space do not match.");
173
0
        isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
174
0
                ISC_LOGMODULE_SOCKET,
175
0
                ISC_LOG_ERROR,
176
0
                "IPv6 is not supported.");
177
0
        result = ISC_R_NOTFOUND;
178
0
      }
179
0
    }
180
0
  }
181
182
0
  (void)close(s);
183
184
0
  return (result);
185
0
}
186
187
static void
188
0
initialize_action(void) {
189
0
  ipv4_result = try_proto(PF_INET);
190
0
  ipv6_result = try_proto(PF_INET6);
191
0
  unix_result = try_proto(PF_UNIX);
192
0
}
193
194
static void
195
0
initialize(void) {
196
0
  isc_once_do(&once, initialize_action);
197
0
}
198
199
isc_result_t
200
0
isc_net_probeipv4(void) {
201
0
  initialize();
202
0
  return (ipv4_result);
203
0
}
204
205
isc_result_t
206
0
isc_net_probeipv6(void) {
207
0
  initialize();
208
0
  return (ipv6_result);
209
0
}
210
211
isc_result_t
212
0
isc_net_probeunix(void) {
213
0
  initialize();
214
0
  return (unix_result);
215
0
}
216
217
static void
218
0
try_ipv6only(void) {
219
0
#ifdef IPV6_V6ONLY
220
0
  int s, on;
221
0
#endif /* ifdef IPV6_V6ONLY */
222
0
  isc_result_t result;
223
224
0
  result = isc_net_probeipv6();
225
0
  if (result != ISC_R_SUCCESS) {
226
0
    ipv6only_result = result;
227
0
    return;
228
0
  }
229
230
#ifndef IPV6_V6ONLY
231
  ipv6only_result = ISC_R_NOTFOUND;
232
  return;
233
#else  /* ifndef IPV6_V6ONLY */
234
  /* check for TCP sockets */
235
0
  s = socket(PF_INET6, SOCK_STREAM, 0);
236
0
  if (s == -1) {
237
0
    UNEXPECTED_SYSERROR(errno, "socket()");
238
0
    ipv6only_result = ISC_R_UNEXPECTED;
239
0
    return;
240
0
  }
241
242
0
  on = 1;
243
0
  if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
244
0
    ipv6only_result = ISC_R_NOTFOUND;
245
0
    goto close;
246
0
  }
247
248
0
  close(s);
249
250
  /* check for UDP sockets */
251
0
  s = socket(PF_INET6, SOCK_DGRAM, 0);
252
0
  if (s == -1) {
253
0
    UNEXPECTED_SYSERROR(errno, "socket()");
254
0
    ipv6only_result = ISC_R_UNEXPECTED;
255
0
    return;
256
0
  }
257
258
0
  on = 1;
259
0
  if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) {
260
0
    ipv6only_result = ISC_R_NOTFOUND;
261
0
    goto close;
262
0
  }
263
264
0
  ipv6only_result = ISC_R_SUCCESS;
265
266
0
close:
267
0
  close(s);
268
0
  return;
269
0
#endif /* IPV6_V6ONLY */
270
0
}
271
272
static void
273
0
initialize_ipv6only(void) {
274
0
  isc_once_do(&once_ipv6only, try_ipv6only);
275
0
}
276
277
#ifdef __notyet__
278
static void
279
try_ipv6pktinfo(void) {
280
  int s, on;
281
  isc_result_t result;
282
  int optname;
283
284
  result = isc_net_probeipv6();
285
  if (result != ISC_R_SUCCESS) {
286
    ipv6pktinfo_result = result;
287
    return;
288
  }
289
290
  /* we only use this for UDP sockets */
291
  s = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
292
  if (s == -1) {
293
    UNEXPECTED_SYSERROR(errno, "socket()");
294
    ipv6pktinfo_result = ISC_R_UNEXPECTED;
295
    return;
296
  }
297
298
#ifdef IPV6_RECVPKTINFO
299
  optname = IPV6_RECVPKTINFO;
300
#else  /* ifdef IPV6_RECVPKTINFO */
301
  optname = IPV6_PKTINFO;
302
#endif /* ifdef IPV6_RECVPKTINFO */
303
  on = 1;
304
  if (setsockopt(s, IPPROTO_IPV6, optname, &on, sizeof(on)) < 0) {
305
    ipv6pktinfo_result = ISC_R_NOTFOUND;
306
    goto close;
307
  }
308
309
  ipv6pktinfo_result = ISC_R_SUCCESS;
310
311
close:
312
  close(s);
313
  return;
314
}
315
316
static void
317
initialize_ipv6pktinfo(void) {
318
  isc_once_do(&once_ipv6pktinfo, try_ipv6pktinfo);
319
}
320
#endif /* ifdef __notyet__ */
321
322
isc_result_t
323
0
isc_net_probe_ipv6only(void) {
324
0
  initialize_ipv6only();
325
0
  return (ipv6only_result);
326
0
}
327
328
isc_result_t
329
0
isc_net_probe_ipv6pktinfo(void) {
330
/*
331
 * XXXWPK if pktinfo were supported then we could listen on :: for ipv6 and get
332
 * the information about the destination address from pktinfo structure passed
333
 * in recvmsg but this method is not portable and libuv doesn't support it - so
334
 * we need to listen on all interfaces.
335
 * We should verify that this doesn't impact performance (we already do it for
336
 * ipv4) and either remove all the ipv6pktinfo detection code from above
337
 * or think of fixing libuv.
338
 */
339
#ifdef __notyet__
340
  initialize_ipv6pktinfo();
341
#endif /* ifdef __notyet__ */
342
0
  return (ipv6pktinfo_result);
343
0
}
344
345
#if defined(USE_SYSCTL_PORTRANGE)
346
#if defined(HAVE_SYSCTLBYNAME)
347
static isc_result_t
348
getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) {
349
  int port_low, port_high;
350
  size_t portlen;
351
  const char *sysctlname_lowport, *sysctlname_hiport;
352
353
  if (af == AF_INET) {
354
    sysctlname_lowport = SYSCTL_V4PORTRANGE_LOW;
355
    sysctlname_hiport = SYSCTL_V4PORTRANGE_HIGH;
356
  } else {
357
    sysctlname_lowport = SYSCTL_V6PORTRANGE_LOW;
358
    sysctlname_hiport = SYSCTL_V6PORTRANGE_HIGH;
359
  }
360
  portlen = sizeof(port_low);
361
  if (sysctlbyname(sysctlname_lowport, &port_low, &portlen, NULL, 0) < 0)
362
  {
363
    return (ISC_R_FAILURE);
364
  }
365
  portlen = sizeof(port_high);
366
  if (sysctlbyname(sysctlname_hiport, &port_high, &portlen, NULL, 0) < 0)
367
  {
368
    return (ISC_R_FAILURE);
369
  }
370
  if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) {
371
    return (ISC_R_RANGE);
372
  }
373
374
  *low = (in_port_t)port_low;
375
  *high = (in_port_t)port_high;
376
377
  return (ISC_R_SUCCESS);
378
}
379
#else  /* !HAVE_SYSCTLBYNAME */
380
static isc_result_t
381
getudpportrange_sysctl(int af, in_port_t *low, in_port_t *high) {
382
  int mib_lo4[4] = SYSCTL_V4PORTRANGE_LOW;
383
  int mib_hi4[4] = SYSCTL_V4PORTRANGE_HIGH;
384
  int mib_lo6[4] = SYSCTL_V6PORTRANGE_LOW;
385
  int mib_hi6[4] = SYSCTL_V6PORTRANGE_HIGH;
386
  int *mib_lo, *mib_hi, miblen;
387
  int port_low, port_high;
388
  size_t portlen;
389
390
  if (af == AF_INET) {
391
    mib_lo = mib_lo4;
392
    mib_hi = mib_hi4;
393
    miblen = sizeof(mib_lo4) / sizeof(mib_lo4[0]);
394
  } else {
395
    mib_lo = mib_lo6;
396
    mib_hi = mib_hi6;
397
    miblen = sizeof(mib_lo6) / sizeof(mib_lo6[0]);
398
  }
399
400
  portlen = sizeof(port_low);
401
  if (sysctl(mib_lo, miblen, &port_low, &portlen, NULL, 0) < 0) {
402
    return (ISC_R_FAILURE);
403
  }
404
405
  portlen = sizeof(port_high);
406
  if (sysctl(mib_hi, miblen, &port_high, &portlen, NULL, 0) < 0) {
407
    return (ISC_R_FAILURE);
408
  }
409
410
  if ((port_low & ~0xffff) != 0 || (port_high & ~0xffff) != 0) {
411
    return (ISC_R_RANGE);
412
  }
413
414
  *low = (in_port_t)port_low;
415
  *high = (in_port_t)port_high;
416
417
  return (ISC_R_SUCCESS);
418
}
419
#endif /* HAVE_SYSCTLBYNAME */
420
#endif /* USE_SYSCTL_PORTRANGE */
421
422
isc_result_t
423
0
isc_net_getudpportrange(int af, in_port_t *low, in_port_t *high) {
424
0
  int result = ISC_R_FAILURE;
425
0
#if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux)
426
0
  FILE *fp;
427
0
#endif /* if !defined(USE_SYSCTL_PORTRANGE) && defined(__linux) */
428
429
0
  REQUIRE(low != NULL && high != NULL);
430
431
#if defined(USE_SYSCTL_PORTRANGE)
432
  result = getudpportrange_sysctl(af, low, high);
433
#elif defined(__linux)
434
435
0
  UNUSED(af);
436
437
  /*
438
   * Linux local ports are address family agnostic.
439
   */
440
0
  fp = fopen("/proc/sys/net/ipv4/ip_local_port_range", "r");
441
0
  if (fp != NULL) {
442
0
    int n;
443
0
    unsigned int l, h;
444
445
0
    n = fscanf(fp, "%u %u", &l, &h);
446
0
    if (n == 2 && (l & ~0xffff) == 0 && (h & ~0xffff) == 0) {
447
0
      *low = l;
448
0
      *high = h;
449
0
      result = ISC_R_SUCCESS;
450
0
    }
451
0
    fclose(fp);
452
0
  }
453
#else  /* if defined(USE_SYSCTL_PORTRANGE) */
454
  UNUSED(af);
455
#endif /* if defined(USE_SYSCTL_PORTRANGE) */
456
457
0
  if (result != ISC_R_SUCCESS) {
458
0
    *low = ISC_NET_PORTRANGELOW;
459
0
    *high = ISC_NET_PORTRANGEHIGH;
460
0
  }
461
462
0
  return (ISC_R_SUCCESS); /* we currently never fail in this function */
463
0
}
464
465
void
466
0
isc_net_disableipv4(void) {
467
0
  initialize();
468
0
  if (ipv4_result == ISC_R_SUCCESS) {
469
0
    ipv4_result = ISC_R_DISABLED;
470
0
  }
471
0
}
472
473
void
474
0
isc_net_disableipv6(void) {
475
0
  initialize();
476
0
  if (ipv6_result == ISC_R_SUCCESS) {
477
0
    ipv6_result = ISC_R_DISABLED;
478
0
  }
479
0
}
480
481
void
482
0
isc_net_enableipv4(void) {
483
0
  initialize();
484
0
  if (ipv4_result == ISC_R_DISABLED) {
485
0
    ipv4_result = ISC_R_SUCCESS;
486
0
  }
487
0
}
488
489
void
490
0
isc_net_enableipv6(void) {
491
0
  initialize();
492
0
  if (ipv6_result == ISC_R_DISABLED) {
493
0
    ipv6_result = ISC_R_SUCCESS;
494
0
  }
495
0
}