The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
Loading...
Searching...
No Matches
fd_open.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: c0d76071ad0e43dc406af51f833961884a52fb56 $
19 * @file lib/bio/fd_open.c
20 * @brief BIO abstractions for opening file descriptors
21 *
22 * @copyright 2024 Network RADIUS SAS (legal@networkradius.com)
23 */
24
25#include <freeradius-devel/bio/fd_priv.h>
26#include <freeradius-devel/util/file.h>
27#include <freeradius-devel/util/cap.h>
28#include <freeradius-devel/util/rand.h>
29
30#include <sys/stat.h>
31#include <net/if.h>
32#include <libgen.h>
33#include <netinet/tcp.h>
34#include <netinet/in.h>
35
36/*
37 * @todo - allow for the server to start when DNS fails at start time.
38 *
39 * - add a flag indicating whether client DNS failures are fatal.
40 * - DNS failures are _always_ fatal for the server side
41 * - add an internal enum indicating what type of socket it is, so that we don't need to parse it at run time.
42 * - move the code in fd_open() doing cross-checks to a new routine, and call that new routine from fd_alloc().
43 * so that it can set enum type even if the ipaddr is unset
44 * - add a dst_hostname field to fr_bio_fd_config_t
45 * - in fd_config.c, add a custom parser to save the raw value into dst_hostname
46 * if the DNS lookup fails, set AF_INET=AF_UNSPEC, and set the enum as appropriate.
47 * - in fd_open.c, if enum is FR_BIO_FD_IP_UNSET, do DNS lookup, and fail if there's no DNS.
48 */
49
50static int fr_bio_fd_common_rcvsndbuf(int fd, fr_bio_fd_config_t const *cfg)
51{
52#ifdef SO_RCVBUF
53 if (cfg->recv_buff && cfg->recv_buff <= INT_MAX) {
54 int opt;
55
56 opt = (int) cfg->recv_buff;
57
58 if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
59 fr_strerror_printf("Failed setting SO_RCVBUF: %s", fr_syserror(errno));
60 return -1;
61 }
62 }
63#endif
64
65#ifdef SO_SNDBUF
66 if (cfg->send_buff && cfg->send_buff <= INT_MAX) {
67 int opt;
68
69 opt = (int) cfg->send_buff;
70
71 if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
72 fr_strerror_printf("Failed setting SO_SNDBUF: %s", fr_syserror(errno));
73 return -1;
74 }
75 }
76#endif
77
78 return 0;
79}
80
81/** Initialize common datagram information
82 *
83 */
84static int fr_bio_fd_common_tcp(int fd, UNUSED fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
85{
86 int on = 1;
87
88#ifdef SO_KEEPALIVE
89 /*
90 * TCP keepalives are always a good idea. Too many people put firewalls between critical
91 * systems, and then the firewalls drop live TCP streams.
92 */
93 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) {
94 fr_strerror_printf("Failed setting SO_KEEPALIVE: %s", fr_syserror(errno));
95 return -1;
96 }
97#endif
98
99#ifdef TCP_NODELAY
100 /*
101 * Add some defines for *BSD, and Solaris systems.
102 */
103# if !defined(SOL_TCP) && defined(IPPROTO_TCP)
104# define SOL_TCP IPPROTO_TCP
105# endif
106
107 /*
108 * Also set TCP_NODELAY, to force the data to be written quickly.
109 *
110 * We buffer full packets in memory before we write them, so there's no reason for the kernel to
111 * sit around waiting for more data from us.
112 */
113 if (!cfg->tcp_delay) {
114 if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) {
115 fr_strerror_printf("Failed setting TCP_NODELAY: %s", fr_syserror(errno));
116 return -1;
117 }
118 }
119#endif
120
121 return fr_bio_fd_common_rcvsndbuf(fd, cfg);
122}
123
124
125/** Initialize common datagram information
126 *
127 */
128static int fr_bio_fd_common_datagram(int fd, UNUSED fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
129{
130 int on = 1;
131
132#ifdef SO_TIMESTAMPNS
133 /*
134 * Enable receive timestamps, these should reflect
135 * when the packet was received, not when it was read
136 * from the socket.
137 */
138 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, &on, sizeof(int)) < 0) {
139 fr_strerror_printf("Failed setting SO_TIMESTAMPNS: %s", fr_syserror(errno));
140 return -1;
141 }
142
143#elif defined(SO_TIMESTAMP)
144 /*
145 * Enable receive timestamps, these should reflect
146 * when the packet was received, not when it was read
147 * from the socket.
148 */
149 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(int)) < 0) {
150 fr_strerror_printf("Failed setting SO_TIMESTAMP: %s", fr_syserror(errno));
151 return -1;
152 }
153#endif
154
155 return fr_bio_fd_common_rcvsndbuf(fd, cfg);
156}
157
158/** Initialize a UDP socket.
159 *
160 */
161static int fr_bio_fd_common_udp(int fd, fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
162{
163#ifdef SO_REUSEPORT
164 /*
165 * Servers re-use ports by default. And clients, too, if they ask nicely.
166 */
167 if (cfg->reuse_port) {
168 int on = 1;
169
170 /*
171 * Set SO_REUSEPORT before bind, so that all sockets can
172 * listen on the same destination IP address.
173 */
174 if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
175 fr_strerror_printf("Failed setting SO_REUSEPORT: %s", fr_syserror(errno));
176 return -1;
177 }
178 }
179#endif
180
181 /*
182 * IPv4 fragments packets. IPv6 does not.
183 *
184 * Many systems set the "don't fragment" bit by default for IPv4. This setting means that "too
185 * large" packets are silently discarded in the network.
186 *
187 * Many local networks support UDP fragmentation, so we just send packets and hope for the best.
188 * In order to support local networks, we disable the "don't fragment" bit.
189 *
190 * The wider Internet does not support UDP fragmentation. This means that fragmented packets are
191 * silently discarded.
192 *
193 * @todo - add more code to properly handle EMSGSIZE for PMTUD. We can then also do
194 * getsockopt(fd,IP_MTU,&integer) to get the current path MTU. It can only be used after the
195 * socket has been connected. It should also be called after an EMSGSIZE error is returned.
196 *
197 * getsockopt(fd,IP_MTU,&integer) can also be used for unconnected sockets, if we also set
198 * IP_RECVERR. In which case the errors are put into an error queue. This is only for Linux.
199 */
200 if ((sock->af == AF_INET) && cfg->exceed_mtu) {
201#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
202 /*
203 * Disable PMTU discovery. On Linux, this also makes sure that the "don't
204 * fragment" flag is zero.
205 */
206 {
207 int flag = IP_PMTUDISC_DONT;
208
209 if (setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &flag, sizeof(flag)) < 0) {
210 fr_strerror_printf("Failed setting IP_MTU_DISCOVER: %s", fr_syserror(errno));
211 return -1;
212 }
213 }
214#endif
215
216#if defined(IP_DONTFRAG)
217 /*
218 * Ensure that the "don't fragment" flag is zero.
219 */
220 {
221 int off = 0;
222
223 if (setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &off, sizeof(off)) < 0) {
224 fr_strerror_printf("Failed setting IP_DONTFRAG: %s", fr_syserror(errno));
225 return -1;
226 }
227 }
228#endif
229 }
230
231 return fr_bio_fd_common_datagram(fd, sock, cfg);
232}
233
234/** Initialize a TCP server socket.
235 *
236 */
237static int fr_bio_fd_server_tcp(int fd, UNUSED fr_socket_t const *sock)
238{
239 int on = 1;
240
241 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
242 fr_strerror_printf("Failed setting SO_REUSEADDR: %s", fr_syserror(errno));
243 return -1;
244 }
245
246 return 0;
247}
248
249/** Stop a wildcard IPv6 listener from accepting IPv4 packets.
250 *
251 */
252#ifdef IPV6_V6ONLY
253static int fr_bio_fd_server_v6only(int fd, fr_socket_t const *sock)
254{
255 if (IN6_IS_ADDR_UNSPECIFIED(UNCONST(struct in6_addr *, &sock->inet.src_ipaddr.addr.v6))) {
256 int on = 1;
257
258 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)) < 0) {
259 fr_strerror_printf("Failed setting IPV6_ONLY: %s", fr_syserror(errno));
260 return -1;
261 }
262 }
263
264 return 0;
265}
266#else
267static int fr_bio_fd_server_v6only(UNUSED int fd, UNUSED fr_socket_t const *sock)
268{
269 return 0;
270}
271#endif /* IPV6_V6ONLY */
272
273/** Verify or clean up a pre-existing domain socket.
274 *
275 */
276static int fr_bio_fd_socket_unix_verify(int dirfd, char const *filename, fr_bio_fd_config_t const *cfg, uid_t uid)
277{
278 int fd;
279 struct stat buf;
280
281 /*
282 * See if the socket exits. If there's an error opening it, that's an issue.
283 *
284 * If it doesn't exist, that's fine.
285 */
286 if (fstatat(dirfd, filename, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
287 if (errno != ENOENT) {
288 fr_strerror_printf("Failed opening domain socket %s: %s", cfg->path, fr_syserror(errno));
289 return -1;
290 }
291
292 return 0;
293 }
294
295 /*
296 * If it exists, it must be a socket.
297 */
298 if (!S_ISSOCK(buf.st_mode)) {
299 fr_strerror_printf("Failed open domain socket %s: it is not a socket", filename);
300 return -1;
301 }
302
303 /*
304 * Refuse to open sockets not owned by us. This prevents configurations from stomping on each
305 * other.
306 */
307 if (buf.st_uid != uid) {
308 fr_strerror_printf("Failed opening domain socket %s: incorrect UID (have %u, expected %u)",
309 cfg->path, buf.st_uid, uid);
310 return -1;
311 }
312
313 /*
314 * The file exists,and someone is listening. We can't claim it for ourselves.
315 *
316 * Note that this function calls connect(), but connect() always returns immediately for domain
317 * sockets.
318 *
319 * @todo - redo that function here, with separate checks for permission errors vs anything else.
320 */
321 fd = fr_socket_client_unix(cfg->path, false);
322 if (fd >= 0) {
323 close(fd);
324 fr_strerror_printf("Failed creating domain socket %s: It is currently active", cfg->path);
325 return -1;
326 }
327
328 /*
329 * It exists, but no one is listening. Delete it so that we can re-bind to it.
330 */
331 if (unlinkat(dirfd, filename, 0) < 0) {
332 fr_strerror_printf("Failed removing pre-existing domain socket %s: %s",
333 cfg->path, fr_syserror(errno));
334 return -1;
335 }
336
337 return 0;
338}
339
340/*
341 * We normally can't call fchmod() or fchown() on sockets, as they don't really exist in the file system.
342 * Instead, we enforce those permissions on the parent directory of the socket.
343 */
344static int fr_bio_fd_socket_unix_mkdir(int *dirfd, char const **filename, fr_bio_fd_config_t const *cfg,
345 mode_t dir_perm, uid_t uid)
346{
347 int parent_fd, fd;
348 char const *path = cfg->path;
349 char *p, *dir = NULL;
350 char *slashes[2];
351
352 /*
353 * The parent directory exists. Ensure that it has the correct ownership and permissions.
354 *
355 * If the parent directory exists, then it enforces access, and we can create the domain socket
356 * within it.
357 */
358 if (fr_dirfd(dirfd, filename, path) == 0) {
359 struct stat buf;
360
361 fr_assert(*filename);
362 fr_assert(*dirfd >= 0);
363
364 if (fstat(*dirfd, &buf) < 0) {
365 fr_strerror_printf("Failed reading parent directory for file %s: %s", path, fr_syserror(errno));
366 fail:
367 talloc_free(dir);
368 close(*dirfd);
369 return -1;
370 }
371
372 if (buf.st_uid != uid) {
373 fr_strerror_printf("Failed reading parent directory for file %s: Incorrect UID (have %u, expected %u)",
374 path, buf.st_uid, uid);
375 goto fail;
376 }
377
378 if (buf.st_gid != cfg->gid) {
379 fr_strerror_printf("Failed reading parent directory for file %s: Incorrect GID (have %u, expected %u)",
380 path, buf.st_gid, cfg->gid);
381 goto fail;
382 }
383
384 /*
385 * We don't have the correct permissions on the directory, so we fix them.
386 *
387 * @todo - allow for "other" to read/write if we do authentication on the socket?
388 */
389 if (fchmod(*dirfd, dir_perm) < 0) {
390 fr_strerror_printf("Failed setting parent directory permissions for file %s: %s", path, fr_syserror(errno));
391 goto fail;
392 }
393
394 return 0;
395 }
396
397 /*
398 * We have to create the directories.
399 */
400 dir = talloc_strdup(NULL, path);
401 if (!dir) goto fail;
402
403 /*
404 * Find the last two directory separators.
405 */
406 slashes[0] = slashes[1] = NULL;
407 for (p = dir; *p != '\0'; p++) {
408 if (*p == '/') {
409 slashes[0] = slashes[1];
410 slashes[1] = p;
411 }
412 }
413
414 /*
415 * There's only one / in the path, we can't do anything.
416 *
417 * Opening 'foo/bar.sock' might be useful, but isn't normally a good idea.
418 */
419 if (!slashes[0]) {
420 fr_strerror_printf("Failed parsing filename %s: it is not absolute", path);
421 goto fail;
422 }
423
424 /*
425 * Ensure that the grandparent directory exists.
426 *
427 * /var/run/radiusd/foo.sock
428 *
429 * slashes[0] points to the slash after 'run'.
430 *
431 * slashes[1] points to the slash after 'radiusd', which doesn't exist.
432 */
433 *slashes[0] = '\0';
434
435 /*
436 * If the grandparent doesn't exist, then we don't create it.
437 *
438 * These checks minimize the possibility that a misconfiguration by user "radiusd" can cause a
439 * suid-root binary top create a directory in the wrong place. These checks are only necessary
440 * if the unix domain socket is opened as root.
441 */
442 parent_fd = open(dir, O_DIRECTORY | O_NOFOLLOW);
443 if (parent_fd < 0) {
444 fr_strerror_printf("Failed opening directory %s: %s", dir, fr_syserror(errno));
445 goto fail;
446 }
447
448 /*
449 * Create the parent directory.
450 */
451 *slashes[0] = '/';
452 *slashes[1] = '\0';
453 if (mkdirat(parent_fd, slashes[0] + 1, 0700) < 0) {
454 fr_strerror_printf("Failed creating directory %s: %s", slashes[0], fr_syserror(errno));
455 close_parent:
456 close(parent_fd);
457 goto fail;
458 }
459
460 fd = openat(parent_fd, slashes[0] + 1, O_DIRECTORY);
461 if (fd < 0) {
462 fr_strerror_printf("Failed opening directory %s: %s", slashes[0] + 1, fr_syserror(errno));
463 goto close_parent;
464 }
465
466 if (fchmod(fd, dir_perm) < 0) {
467 fr_strerror_printf("Failed changing permission for domain socket %s: %s", cfg->path, fr_syserror(errno));
468 close_fd:
469 close(fd);
470 goto close_parent;
471 }
472
473 /*
474 * This is a NOOP if we're chowning a file owned by ourselves to the correct uid / gid
475 *
476 * Otherwise if we're running as root, it will set ownership to the correct user.
477 */
478 if (fchown(fd, uid, cfg->gid) < 0) {
479 fr_strerror_printf("Failed changing ownership for domain socket %s: %s", dir, fr_syserror(errno));
480 goto close_fd;
481 }
482
483 *dirfd = fd;
484 path = strrchr(cfg->path, '/');
485 if (path) path++;
486
487 *filename = path;
488
489 talloc_free(dir);
490 close(parent_fd);
491
492 return 0;
493}
494
495/** Remove the Unix domain socket which this bio created.
496 *
497 * Called from fr_bio_fd_shutdown() for every Unix domain bio.
498 */
500{
501 fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
502
503 /*
504 * Called after fr_bio_fd_close(), which marks the bio state as closed.
505 *
506 * Unix domain sockets are never in the "connecting" state, because connect() always returns
507 * immediately.
508 */
510
511 /*
512 * The master socket (FR_BIO_FD_LISTEN) is the one which opens / creates the path, so it's
513 * respinsible for removing it. Child sockets (FR_BIO_FD_CONNECTED) still point to the path, but
514 * they don't own it.
515 */
516 if (my->info.type != FR_BIO_FD_LISTEN) return 0;
517
518 /*
519 * We should already own this file, so we can remove it. If we can't remove it, then we can't
520 * recover. We might as well return success, so the application can run its own shutdown routine.
521 */
522 (void) unlink(my->info.socket.unix.path);
523
524 return 0;
525}
526
527/*
528 * The socket is readable and writable by the owner and the group. When cfg->perm is non-zero,
529 * the socket keeps only those read and write bits that cfg->perm also sets. Any other bit in
530 * cfg->perm has no effect. A cfg->perm of zero leaves the socket readable and writable by the
531 * owner and the group.
532 *
533 * The user is always able to read and write the files.
534 */
535#define FILE_PERMS(_cfg) \
536 do { \
537 file_perm = (_cfg)->perm ? (_cfg)->perm : S_IRGRP | S_IWGRP; \
538 file_perm |= S_IRUSR | S_IWUSR; \
539 dir_perm = file_perm & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_ISGID | S_ISVTX); \
540 file_perm &= (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); \
541 if ((dir_perm & (S_IRUSR | S_IWUSR)) != 0) dir_perm |= S_IXUSR; \
542 if ((dir_perm & (S_IRGRP | S_IWGRP)) != 0) dir_perm |= S_IXGRP; \
543 } while (0)
544
545
546/** Bind to a Unix domain socket.
547 *
548 * @todo - this function only does a tiny bit of what fr_server_domain_socket_peercred() and
549 * fr_server_domain_socket_perm() do. Those functions do a lot more sanity checks.
550 *
551 * The main question is whether or not those checks are useful. In many cases, fchmod() and fchown() are not
552 * possible on Unix sockets, so we shouldn't bother doing them,
553 *
554 * Note that the listeners generally call these functions with wrappers of fr_suid_up() and fr_suid_down().
555 * So these functions are running as "root", and will create files owned as "root".
556 */
558{
559 int dirfd, rcode;
560 mode_t file_perm, dir_perm;
561 char const *filename, *p;
562 socklen_t sunlen;
563 uid_t uid;
564 struct sockaddr_un sun;
565
566 if (!cfg->path) {
567 fr_strerror_const("Failed to specify path");
568 return -1;
569 }
570
571 /*
572 * The UID and GID are taken from the configuration.
573 */
574 uid = cfg->uid;
575 if (uid == (uid_t) -1) {
576 uid = getuid();
577 }
578
579 if (cfg->gid == (gid_t) -1) {
580 fr_strerror_printf("Failed opening domain socket %s: no GID specified", cfg->path);
581 return -1;
582 }
583
584 FILE_PERMS(cfg);
585
586 /*
587 * Opening 'foo.sock' is OK.
588 */
589 p = strrchr(cfg->path, '/');
590 if (!p) {
591 dirfd = AT_FDCWD;
592 filename = cfg->path;
593
594 } else if (p == cfg->path) {
595 /*
596 * Opening '/foo.sock' is dumb.
597 */
598 fr_strerror_printf("Failed opening domain socket %s: cannot exist at file system root", p);
599 return -1;
600
601 } else if (fr_bio_fd_socket_unix_mkdir(&dirfd, &filename, cfg, dir_perm, uid) < 0) {
602 return -1;
603 }
604
605 /*
606 * Verify and/or clean up the domain socket.
607 */
608 if (fr_bio_fd_socket_unix_verify(dirfd, filename, cfg, uid) < 0) {
609 fail:
610 if (dirfd != AT_FDCWD) close(dirfd);
611 return -1;
612 }
613
614#ifdef HAVE_BINDAT
615 /*
616 * The best function to use here is bindat(), but only quite recent versions of FreeBSD actually
617 * have it, and it's definitely not POSIX.
618 *
619 * If we use bindat(), we pass a relative pathname.
620 */
621 if (fr_filename_to_sockaddr(&sun, &sunlen, filename) < 0) goto fail;
622
623 rcode = bindat(dirfd, my->info.socket.fd, (struct sockaddr *) &sun, sunlen);
624#else
625 /*
626 * For bind(), we pass the full path.
627 */
628 if (fr_filename_to_sockaddr(&sun, &sunlen, cfg->path) < 0) goto fail;
629
630 rcode = bind(my->info.socket.fd, (struct sockaddr *) &sun, sunlen);
631#endif
632 if (rcode < 0) {
633 /*
634 * @todo - if EADDRINUSE, then the socket exists. Try connect(), and if that fails,
635 * delete the socket and try again. This may be simpler than the checks above.
636 */
637 fr_strerror_printf("Failed binding to domain socket %s: %s", cfg->path, fr_syserror(errno));
638 goto fail;
639 }
640
641#ifdef __linux__
642 /*
643 * Linux supports chown && chmod for sockets.
644 */
645 if (fchmod(my->info.socket.fd, file_perm) < 0) {
646 fr_strerror_printf("Failed changing permission for domain socket %s: %s", cfg->path, fr_syserror(errno));
647 goto fail;
648 }
649
650 /*
651 * This is a NOOP if we're chowning a file owned by ourselves to our own UID / GID.
652 *
653 * Otherwise if we're running as root, it will set ownership to the correct user.
654 */
655 if (fchown(my->info.socket.fd, uid, cfg->gid) < 0) {
656 fr_strerror_printf("Failed changing ownership for domain socket %s: %s", cfg->path, fr_syserror(errno));
657 goto fail;
658 }
659
660#endif
661
662 if (dirfd != AT_FDCWD) close(dirfd);
663
664 return 0;
665}
666
667/*
668 * Use the OSX native versions on OSX.
669 */
670#ifdef __APPLE__
671#undef SO_BINDTODEVICE
672#endif
673
674#ifdef SO_BINDTODEVICE
675/** Linux bind to device by name.
676 *
677 */
679{
680 /*
681 * ifindex isn't set, do nothing.
682 */
683 if (!my->info.socket.inet.ifindex) return 0;
684
685 if (!cfg->interface) return 0;
686
687 /*
688 * The internet hints that CAP_NET_RAW is required to use SO_BINDTODEVICE.
689 *
690 * This function also sets fr_strerror() on failure, which will be seen if the bind fails. If
691 * the bind succeeds, then we don't really care that the capability change has failed. We must
692 * already have that capability.
693 */
694#ifdef HAVE_CAPABILITY_H
695 (void)fr_cap_enable(CAP_NET_RAW, CAP_EFFECTIVE);
696#endif
697
698 if (setsockopt(my->info.socket.fd, SOL_SOCKET, SO_BINDTODEVICE, cfg->interface, strlen(cfg->interface)) < 0) {
699 fr_strerror_printf("Failed setting SO_BINDTODEVICE for %s: %s", cfg->interface, fr_syserror(errno));
700 return -1;
701 }
702
703 return 0;
704}
705
706#elif defined(IP_BOUND_IF) || defined(IPV6_BOUND_IF)
707/** OSX bind to interface by index.
708 *
709 */
711{
712 int opt, rcode;
713
714 if (!my->info.socket.inet.ifindex) return 0;
715
716 opt = my->info.socket.inet.ifindex;
717
718 switch (my->info.socket.af) {
719 case AF_INET:
720 rcode = setsockopt(my->info.socket.fd, IPPROTO_IP, IP_BOUND_IF, &opt, sizeof(opt));
721 break;
722
723 case AF_INET6:
724 rcode = setsockopt(my->info.socket.fd, IPPROTO_IPV6, IPV6_BOUND_IF, &opt, sizeof(opt));
725 break;
726
727 default:
728 rcode = -1;
729 errno = EAFNOSUPPORT;
730 break;
731 }
732
733 if (rcode < 0) fr_strerror_printf("Failed setting IP_BOUND_IF: %s", fr_syserror(errno));
734 return rcode;
735}
736#else
737
738/** This system is missing SO_BINDTODEVICE, IP_BOUND_IF, IPV6_BOUND_IF
739 *
740 * @todo - FreeBSD IP_RECVIF and IP_SENDIF
741 *
742 * Except that has to be done in recvmsg() and sendmsg(). And it only works on datagram sockets.
743 *
744 * cmsg_len = sizeof(struct sockaddr_dl)
745 * cmsg_level = IPPROTO_IP
746 * cmsg_type = IP_RECVIF
747 */
749{
750 if (!my->info.socket.inet.ifindex) return 0;
751
752 fr_strerror_const("Bind to interface is not supported on this platform");
753 return -1;
754}
755
756/* bind to device */
757#endif
758
760{
761 bool do_suid;
762 int rcode;
763 socklen_t salen;
764 struct sockaddr_storage salocal;
765
766 fr_assert((my->info.socket.af == AF_INET) || (my->info.socket.af == AF_INET6));
767
768 /*
769 * Ranges must be a high value.
770 */
771 if (my->info.cfg->src_port_start) {
772 if (my->info.cfg->src_port_start < 1024) {
773 fr_strerror_const("Cannot set src_port_start in the range 1..1023");
774 return -1;
775 }
776
777 if (my->info.cfg->src_port_end <= my->info.cfg->src_port_start) {
778 fr_strerror_const("Invalid range src_port_end <= src_port_start");
779 return -1;
780 }
781 }
782
783 /*
784 * Source port is in the restricted range, and we're not root, update permissions / capabilities
785 * so that the bind is permitted.
786 */
787 do_suid = (my->info.socket.inet.src_port > 0) && (my->info.socket.inet.src_port < 1024) && (geteuid() != 0);
788
789#ifdef HAVE_CAPABILITY_H
790 /*
791 * If we can set the capabilities, then we don't need to do SUID.
792 */
793 if (do_suid) do_suid = (fr_cap_enable(CAP_NET_BIND_SERVICE, CAP_EFFECTIVE) < 0);
794#endif
795
796 /*
797 * SUID up before we bind to the interface. If we can set the capabilities above, then should
798 * also be able to set the capabilities to bind to an interface.
799 */
800 if (do_suid) fr_suid_up();
801
802 if (fr_bio_fd_socket_bind_to_device(my, cfg) < 0) {
803 down:
804 if (do_suid) fr_suid_down();
805 return -1;
806 }
807
808 /*
809 * Get the sockaddr for bind()
810 */
811 if (fr_ipaddr_to_sockaddr(&salocal, &salen, &my->info.socket.inet.src_ipaddr, my->info.socket.inet.src_port) < 0) goto down;
812
813 /*
814 * We don't have a source port range, just bind to whatever source port that we're given.
815 */
816 if (!my->info.cfg->src_port_start) {
817 rcode = bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen);
818 if (do_suid) fr_suid_down();
819
820 if (rcode < 0) {
821 fr_strerror_printf("Failed binding to socket: %s", fr_syserror(errno));
822 return -1;
823 }
824
825 } else {
826 uint16_t src_port, current, range;
827 struct sockaddr_in *sin = (struct sockaddr_in *) &salocal;
828
832
833 range = my->info.cfg->src_port_end - my->info.cfg->src_port_start;
834 src_port = fr_rand() % range;
835
836 /*
837 * We only care about sin_port, which is in the same location for sockaddr_in and sockaddr_in6.
838 */
839 fr_assert(sin->sin_port == 0);
840
841 sin->sin_port = htons(my->info.cfg->src_port_start + src_port);
842
843 /*
844 * We've picked a random port in what is hopefully a large range. If that works, we're
845 * done.
846 */
847 rcode = bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen);
848 if (rcode == 0) {
849 if (do_suid) fr_suid_down();
850 goto done;
851 }
852
853 /*
854 * Hunt & peck. Which is horrible.
855 */
856 current = src_port;
857 while (true) {
858 if (current == range) {
859 current = 0;
860 } else {
861 current++;
862 }
863
864 /*
865 * We've already checked this, so stop.
866 */
867 if (current == src_port) break;
868
869 sin->sin_port = htons(my->info.cfg->src_port_start + current);
870
871 rcode = bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen);
872 if (rcode == 0) {
873 if (do_suid) fr_suid_down();
874 goto done;
875 }
876 }
877
878 /*
879 * The error is a good hint at _why_ we failed to bind.
880 * We expect errno to be EADDRINUSE, anything else is a surprise.
881 */
882 if (do_suid) fr_suid_down();
883 fr_strerror_printf("Failed binding port between 'src_port_start' and 'src_port_end': %s", fr_syserror(errno));
884 return -1;
885 }
886
887 /*
888 * The IP and/or port may have changed, so get the new one.
889 */
890done:
891 return fr_bio_fd_socket_name(my);
892}
893
894/** Set the name of an FD BIO
895 *
896 */
898{
899 fr_bio_fd_config_t const *cfg = my->info.cfg;
900
901 switch (my->info.type) {
903 return;
904
906 fr_assert(cfg->socket_type == SOCK_DGRAM);
907
908 switch (my->info.socket.af) {
909 case AF_INET:
910 case AF_INET6:
911 my->info.name = fr_asprintf(my, "proto udp local %pV port %u",
912 fr_box_ipaddr(my->info.socket.inet.src_ipaddr),
913 my->info.socket.inet.src_port);
914 break;
915
916 case AF_LOCAL:
917 my->info.name = fr_asprintf(my, "proto unix (datagram) filename %s",
918 cfg->path);
919 break;
920
921 default:
922 fr_assert(0);
923 my->info.name = "??? invalid BIO ???";
924 break;
925 }
926 break;
927
929 switch (my->info.socket.af) {
930 case AF_INET:
931 case AF_INET6:
932 my->info.name = fr_asprintf(my, "proto %s local %pV port %u remote %pV port %u",
933 (cfg->socket_type == SOCK_DGRAM) ? "udp" : "tcp",
934 fr_box_ipaddr(my->info.socket.inet.src_ipaddr),
935 my->info.socket.inet.src_port,
936 fr_box_ipaddr(my->info.socket.inet.dst_ipaddr),
937 my->info.socket.inet.dst_port);
938 break;
939
940 case AF_LOCAL:
941 my->info.name = fr_asprintf(my, "proto unix %sfilename %s",
942 (cfg->socket_type == SOCK_DGRAM) ? "(datagram) " : "",
943 cfg->path);
944 break;
945
946 case AF_FR_FILENAME:
947 fr_assert(cfg->socket_type == SOCK_STREAM);
948
949 if ((cfg->flags & O_ACCMODE) == O_RDONLY) {
950 my->info.name = fr_asprintf(my, "proto file (read-only) filename %s ",
951 cfg->filename);
952
953 } else if ((cfg->flags & O_ACCMODE) == O_WRONLY) {
954 my->info.name = fr_asprintf(my, "proto file (write-only) filename %s",
955 cfg->filename);
956 } else {
957 my->info.name = fr_asprintf(my, "proto file (read-write) filename %s",
958 cfg->filename);
959 }
960 break;
961
962 default:
963 fr_assert(0);
964 my->info.name = "??? invalid BIO ???";
965 break;
966 }
967 break;
968
969 case FR_BIO_FD_LISTEN:
970 switch (my->info.socket.af) {
971 case AF_INET:
972 case AF_INET6:
973 my->info.name = fr_asprintf(my, "proto %s local %pV port %u",
974 (cfg->socket_type == SOCK_DGRAM) ? "udp" : "tcp",
975 fr_box_ipaddr(my->info.socket.inet.src_ipaddr),
976 my->info.socket.inet.src_port);
977 break;
978
979 case AF_LOCAL:
980 my->info.name = fr_asprintf(my, "proto unix filename %s",
981 cfg->path);
982 break;
983
984 default:
985 fr_assert(0);
986 my->info.name = "??? invalid BIO ???";
987 break;
988 }
989 break;
990 }
991}
992
993/** Checks the configuration without modifying anything.
994 *
995 */
997{
998 /*
999 * Files and Unix domain sockets are named by a path, and have no IP addresses to check.
1000 */
1001 switch (cfg->transport_type) {
1003 fr_strerror_const("No transport was specified");
1004 return -1;
1005
1007 if (!cfg->filename) {
1008 fr_strerror_const("No filename was specified for a file");
1009 return -1;
1010 }
1011 return 0;
1012
1014 if (!cfg->path) {
1015 fr_strerror_const("No path was specified for a Unix domain socket");
1016 return -1;
1017 }
1018 return 0;
1019
1022 break;
1023 }
1024
1025 /*
1026 * Sanitize the IP addresses.
1027 *
1028 */
1029 switch (cfg->type) {
1030 case FR_BIO_FD_INVALID:
1031 fr_strerror_const("No connection type was specified");
1032 return -1;
1033
1035 /*
1036 * Ensure that we have a destination address.
1037 */
1038 if (cfg->dst_ipaddr.af == AF_UNSPEC) {
1039 fr_strerror_const("Destination IP address must be specified for a connected socket");
1040 return -1;
1041 }
1042
1043 if (!cfg->dst_port) {
1044 fr_strerror_const("Destination port must be specified for a connected socket");
1045 return -1;
1046 }
1047
1048 /*
1049 * The source IP has to be the same address family as the destination IP.
1050 */
1051 if ((cfg->src_ipaddr.af != AF_UNSPEC) && (cfg->src_ipaddr.af != cfg->dst_ipaddr.af)) {
1052 fr_strerror_printf("Source and destination IP addresses are not from the same IP address family");
1053 return -1;
1054 }
1055 break;
1056
1057 case FR_BIO_FD_LISTEN:
1058 if (!cfg->src_port) {
1059 fr_strerror_const("Source port must be specified for a server socket");
1060 return -1;
1061 }
1063
1064 /*
1065 * Unconnected sockets can use a source port, but don't need one.
1066 */
1068 if (cfg->src_ipaddr.af == AF_UNSPEC) {
1069 fr_strerror_const("Source IP address must be specified for an unconnected socket");
1070 return -1;
1071 }
1072 break;
1073 }
1074
1075 return 0;
1076}
1077
1078/** Open a TCP or UDP socket
1079 *
1080 * The IP addresses and ports come from the configuration. The socket is not bound or connected
1081 * here, as that depends on the #fr_bio_fd_type_t.
1082 *
1083 * @param my the FD bio
1084 * @param cfg the configuration
1085 * @return
1086 * - <0 on error
1087 * - the file descriptor
1088 */
1090{
1091 int fd;
1092 int protocol;
1093
1094 if (fr_bio_fd_check_config(cfg) < 0) return -1;
1095
1096 my->info.socket.af = cfg->src_ipaddr.af;
1097 my->info.socket.inet.src_ipaddr = cfg->src_ipaddr;
1098 my->info.socket.inet.dst_ipaddr = cfg->dst_ipaddr;
1099 my->info.socket.inet.src_port = cfg->src_port;
1100 my->info.socket.inet.dst_port = cfg->dst_port;
1101
1102 /*
1103 * Sanitize the IP addresses.
1104 *
1105 */
1106 switch (cfg->type) {
1107 case FR_BIO_FD_INVALID:
1108 return -1;
1109
1111 /*
1112 * No source specified, just bootstrap it from the destination.
1113 */
1114 if (my->info.socket.inet.src_ipaddr.af == AF_UNSPEC) {
1115 my->info.socket.inet.src_ipaddr = (fr_ipaddr_t) {
1116 .af = my->info.socket.inet.dst_ipaddr.af,
1117 .prefix = (my->info.socket.inet.dst_ipaddr.af == AF_INET) ? 32 : 128,
1118 };
1119
1120 /*
1121 * Set the main socket AF too.
1122 */
1123 my->info.socket.af = my->info.socket.inet.dst_ipaddr.af;
1124 }
1125
1126#ifdef SO_REUSEPORT
1127 /*
1128 * Connected UDP sockets really only matter when writing packets. They allow the
1129 * system to use write() instead of sendto().
1130 *
1131 * However for reading packets, POSIX says that the OS does NOT check the source
1132 * IP. Instead, it just delivers any packet with the correct dst IP/port. Even
1133 * worse, if multiple sockets re-use the same dst IP/port, packets are delivered
1134 * to a _random_ socket.
1135 *
1136 * As a result, we cannot use wildcard sockets with SO_REUSEPORT, and UDP.
1137 *
1138 * However, recent OS (macOS 24.6.0 (Sequoia) and Linux 6.12, and FreeBSD) all
1139 * support connected UDP sockets, and check the 4-tuple, so that only packets for
1140 * the connection get delivered to the socket.
1141 *
1142 * For now, we leave this check alone. The caller should really be passing in a
1143 * specific source IP address, and not a wildcard for connected UDP sockets.
1144 */
1145 if (cfg->reuse_port && (cfg->transport_type == FR_BIO_FD_TRANSPORT_UDP) &&
1146 fr_ipaddr_is_inaddr_any(&my->info.socket.inet.dst_ipaddr)) { /* checks AF, so we're OK */
1147 fr_strerror_const("Cannot set 'reuse_port' for connected UDP sockets with wildcard IP");
1148 return -1;
1149 }
1150#endif
1151 break;
1152
1154 case FR_BIO_FD_LISTEN:
1155 fr_assert(my->info.socket.inet.src_ipaddr.af != AF_UNSPEC);
1156 break;
1157 }
1158
1160 protocol = IPPROTO_TCP;
1161 } else {
1162 protocol = IPPROTO_UDP;
1163 }
1164
1165 if (cfg->interface) {
1166 my->info.socket.inet.ifindex = if_nametoindex(cfg->interface);
1167
1168 if (!my->info.socket.inet.ifindex) {
1169 fr_strerror_printf_push("Failed finding interface %s: %s", cfg->interface, fr_syserror(errno));
1170 return -1;
1171 }
1172 }
1173
1174 fd = socket(my->info.socket.af, my->info.socket.type, protocol);
1175 if (fd < 0) {
1176 fr_strerror_printf("Failed opening socket: %s", fr_syserror(errno));
1177 return fr_bio_error(GENERIC);
1178 }
1179
1180 return fd;
1181}
1182
1183/** Open a file
1184 *
1185 * The names /dev/stdout, /dev/stderr, and /dev/stdin are duplicates of the matching file
1186 * descriptor, and are not opened by name.
1187 *
1188 * @param my the FD bio
1189 * @param cfg the configuration
1190 * @return
1191 * - <0 on error
1192 * - the file descriptor
1193 */
1195{
1196 int fd;
1197
1198 if (cfg->type != FR_BIO_FD_CONNECTED) {
1199 fr_strerror_printf("Can only use connected sockets for file IO");
1200 return -1;
1201 }
1202
1203 /*
1204 * Filenames overload the #fr_socket_t for now.
1205 */
1207 my->info.socket.type = SOCK_STREAM;
1208 my->info.socket.file.path = cfg->filename;
1209
1210 /*
1211 * Allow hacks for stdout and stderr
1212 */
1213 if (strcmp(cfg->filename, "/dev/stdout") == 0) {
1214 if ((cfg->flags & O_ACCMODE) != O_WRONLY) {
1215 fail_dev:
1216 fr_strerror_printf("Cannot read from %s", cfg->filename);
1217 return -1;
1218 }
1219
1220 fd = dup(STDOUT_FILENO);
1221
1222 } else if (strcmp(cfg->filename, "/dev/stderr") == 0) {
1223 if ((cfg->flags & O_ACCMODE) != O_WRONLY) goto fail_dev;
1224
1225 fd = dup(STDERR_FILENO);
1226
1227 } else if (strcmp(cfg->filename, "/dev/stdin") == 0) {
1228 if ((cfg->flags & O_ACCMODE) != O_RDONLY) {
1229 fr_strerror_printf("Cannot write to %s", cfg->filename);
1230 return -1;
1231 }
1232
1233 fd = dup(STDIN_FILENO);
1234
1235 } else {
1236 /*
1237 * Minor hacks so that we have only _one_ source of open / mkdir
1238 */
1239 my->info.socket.fd = -1;
1240
1241 fd = fr_bio_fd_reopen(&my->bio);
1242 }
1243 if (fd < 0) {
1244 fr_strerror_printf("Failed opening file %s: %s", cfg->filename, fr_syserror(errno));
1245 return fr_bio_error(GENERIC);
1246 }
1247
1248 return fd;
1249}
1250
1251/** Open a Unix domain socket
1252 *
1253 * The socket is not bound or connected here, as that depends on the #fr_bio_fd_type_t.
1254 *
1255 * Permissions are
1256 *
1257 * @param my the FD bio
1258 * @param cfg the configuration
1259 * @return
1260 * - <0 on error
1261 * - the file descriptor
1262 */
1264{
1265 int fd;
1266
1267 my->info.socket.af = AF_LOCAL;
1268 my->info.socket.type = SOCK_STREAM;
1269 my->info.socket.unix.path = cfg->path;
1270
1271 fd = socket(my->info.socket.af, my->info.socket.type, 0);
1272 if (fd < 0) {
1273 fr_strerror_printf("Failed opening domain socket %s: %s", cfg->path, fr_syserror(errno));
1274 return fr_bio_error(GENERIC);
1275 }
1276
1277 return fd;
1278}
1279
1280/** Opens a socket and updates sock->fd
1281 *
1282 * If the socket is asynchronous, it also calls connect()
1283 *
1284 * The caller must supply a cfg pointer which is long-lived. The FD BIO doesn't copy it, or its contents.
1285 * fr_bio_fd_accept() uses the same pointer for accepted sockets.
1286 *
1287 * @param bio the FD bio
1288 * @param cfg configuration for the socket, which the caller owns and must keep alive
1289 * @return
1290 * - <0 on error
1291 * - 0 on success
1292 */
1294{
1295 int fd;
1296 int rcode = -1;
1297 fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
1298
1300
1301 my->info = (fr_bio_fd_info_t) {
1302 .socket = {
1303 .type = cfg->socket_type,
1304 .fd = -1,
1305 },
1306 .cfg = cfg,
1307 };
1308
1309 /*
1310 * The transport says what kind of thing to open. Each one names what it opens in its own
1311 * field, and has its own setup.
1312 */
1313 switch (cfg->transport_type) {
1315 fr_strerror_const("No transport was specified");
1316 return -1;
1317
1320 fd = fr_bio_fd_socket_open(my, cfg);
1321 break;
1322
1324 fd = fr_bio_fd_unix_open(my, cfg);
1325 break;
1326
1328 fd = fr_bio_fd_file_open(my, cfg);
1329 break;
1330 }
1331
1332 if (fd < 0) return fd;
1333 my->info.socket.fd = fd;
1334
1335 /*
1336 * Set it to be non-blocking if required.
1337 */
1338 if (cfg->async && (fr_nonblock(fd) < 0)) {
1339 fr_strerror_printf("Failed setting O_NONBLOCK: %s", fr_syserror(errno));
1340 rcode = fr_bio_error(GENERIC);
1341
1342 fail:
1343 if (my->info.socket.fd >= 0) close(fd);
1344
1345 my->info.socket = (fr_socket_t) {
1346 .fd = -1,
1347 };
1349 my->info.cfg = NULL;
1350 return rcode;
1351 }
1352
1353 if (fr_cloexec(fd) < 0) goto fail;
1354
1355 /*
1356 * Initialize the bio information before calling the various setup functions.
1357 */
1359
1360 /*
1361 * Set the FD so that the subsequent calls can use it.
1362 */
1363 my->info.socket.fd = fd;
1364
1365 /*
1366 * Set the type, too.
1367 */
1368 my->info.type = cfg->type;
1369
1370 /*
1371 * Check that the bio type makes sense for the socket which was opened.
1372 */
1373 switch (cfg->type) {
1374 case FR_BIO_FD_INVALID:
1375 fr_strerror_const("No connection type was specified");
1376 rcode = -1;
1377 goto fail;
1378
1379 /*
1380 * Unconnected UDP or datagram AF_LOCAL server sockets.
1381 */
1383 if (my->info.socket.type != SOCK_DGRAM) {
1384 fr_strerror_const("Failed configuring socket: unconnected sockets must be UDP");
1385 rcode = -1;
1386 goto fail;
1387 }
1388 break;
1389
1390 /*
1391 * Server socket which listens for new connections.
1392 */
1393 case FR_BIO_FD_LISTEN:
1394 if ((my->info.socket.type == SOCK_DGRAM) && !cfg->reuse_port) {
1395 fr_strerror_const("reuseport must be set for datagram sockets");
1396 rcode = -1;
1397 goto fail;
1398 }
1399 break;
1400
1401 /*
1402 * A connected client: UDP, TCP, AF_LOCAL, or AF_FR_FILENAME.
1403 */
1405 break;
1406 }
1407
1408 /*
1409 * Set the socket options, and then bind the socket. Which options to set, and how to bind,
1410 * depend on the address family.
1411 */
1412 switch (my->info.socket.af) {
1413 case AF_INET:
1414 case AF_INET6:
1415 /*
1416 * A wildcard IPv6 listener should not also accept IPv4 packets.
1417 */
1418 if ((cfg->type == FR_BIO_FD_LISTEN) && (my->info.socket.af == AF_INET6) &&
1419 ((rcode = fr_bio_fd_server_v6only(fd, &my->info.socket)) < 0)) goto fail;
1420
1421 if (my->info.socket.type == SOCK_DGRAM) {
1422 if ((rcode = fr_bio_fd_common_udp(fd, &my->info.socket, cfg)) < 0) goto fail;
1423
1424 } else if (cfg->type == FR_BIO_FD_LISTEN) {
1425 /*
1426 * A listening socket is never read or written, so it needs only the one
1427 * option which lets us re-bind to the same address.
1428 */
1429 if ((rcode = fr_bio_fd_server_tcp(fd, &my->info.socket)) < 0) goto fail;
1430
1431 } else {
1432 if ((rcode = fr_bio_fd_common_tcp(fd, &my->info.socket, cfg)) < 0) goto fail;
1433 }
1434
1435 if ((rcode = fr_bio_fd_socket_bind(my, cfg)) < 0) goto fail;
1436 break;
1437
1438 case AF_LOCAL:
1439 if (my->info.socket.type == SOCK_DGRAM) {
1440 if ((rcode = fr_bio_fd_common_datagram(fd, &my->info.socket, cfg)) < 0) goto fail;
1441 }
1442
1443 /*
1444 * Don't call bind for clients.
1445 */
1446 if (cfg->type != FR_BIO_FD_LISTEN) break;
1447
1448 if ((rcode = fr_bio_fd_socket_unix_bind(my, cfg)) < 0) goto fail;
1449 break;
1450
1451 /*
1452 * A file is opened by name, so there is nothing to set and nothing to bind.
1453 */
1454 case AF_FR_FILENAME:
1455 break;
1456
1457 default:
1458 fr_strerror_const("Unsupported address family");
1459 rcode = -1;
1460 goto fail;
1461 }
1462
1463 /*
1464 * Initialize the read / write functions.
1465 */
1466 switch (cfg->type) {
1468 rcode = fr_bio_fd_init_common(my);
1469 break;
1470
1472 rcode = fr_bio_fd_init_connected(my);
1473 break;
1474
1475 case FR_BIO_FD_LISTEN:
1476 rcode = fr_bio_fd_init_listen(my);
1477 break;
1478
1479 case FR_BIO_FD_INVALID: /* checked above */
1480 fr_assert(0);
1481 rcode = -1;
1482 break;
1483 }
1484 if (rcode < 0) goto fail;
1485
1486 /*
1487 * Set the name of the BIO.
1488 */
1489 fr_bio_fd_name(my);
1490
1491 return 0;
1492}
1493
1494/** Reopen a file BIO
1495 *
1496 * e.g. for log files.
1497 *
1498 * This will change the FD which is in the BIO.
1499 */
1501{
1502 fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
1503 fr_bio_fd_config_t const *cfg = my->info.cfg;
1504 int fd, flags;
1505 mode_t file_perm, dir_perm;
1506
1507 if (my->info.socket.af != AF_FR_FILENAME) {
1508 fr_strerror_const("Cannot reopen a non-file BIO");
1509 return -1;
1510 }
1511
1512 /*
1513 * Create it if necessary.
1514 */
1515 flags = cfg->flags;
1516 if ((flags & O_ACCMODE) != O_RDONLY) flags |= O_CREAT;
1517
1518 FILE_PERMS(cfg);
1519
1520 if (!cfg->mkdir) {
1521 /*
1522 * Client BIOs writing to a file, and therefore need to create it.
1523 */
1524 do_open:
1525 fd = open(cfg->filename, flags, file_perm);
1526 if (fd < 0) {
1527 failed_open:
1528 fr_strerror_printf("Failed opening file %s: %s", cfg->filename, fr_syserror(errno));
1529 return -1;
1530 }
1531
1532 } else {
1533 /*
1534 * We make the parent directory if told to, AND if there's a '/' in the path.
1535 */
1536 char const *p = strrchr(cfg->filename, '/');
1537 int dir_fd;
1538
1539 if (!p) goto do_open;
1540
1541 if (fr_mkdir(&dir_fd, cfg->filename, (size_t) (p - cfg->filename), dir_perm, fr_mkdir_chown,
1542 &(fr_mkdir_chown_t) {
1543 .uid = cfg->uid != (uid_t) -1 ? cfg->uid : getuid(),
1544 .gid = cfg->gid != (gid_t) -1 ? cfg->gid : getgid(),
1545 }) < 0) {
1546 return -1;
1547 }
1548
1549 fd = openat(dir_fd, p + 1, flags, file_perm);
1550 if (fd < 0) {
1551 close(dir_fd);
1552 goto failed_open;
1553 }
1554
1555 close(dir_fd);
1556 }
1557
1558 /*
1559 * We're boot-strapping, just set the new FD and return.
1560 */
1561 if (my->info.socket.fd < 0) {
1562 my->info.socket.fd = fd;
1563 return fd;
1564 }
1565
1566 /*
1567 * Close the previous my->info.socket.fd, and make it instead a duplicate of our new FD.
1568 */
1569 if (dup2(fd, my->info.socket.fd) < 0) {
1570 close(fd);
1571 fr_strerror_printf("Failed reopening file - %s", fr_syserror(errno));
1572 return -1;
1573 }
1574
1575 /*
1576 * Close our duplicate FD, and leave the old one as a duplicate of the new one.
1577 */
1578 close(fd);
1579 return my->info.socket.fd;
1580}
#define fr_bio_error(_x)
Definition base.h:200
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:203
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:422
#define UNUSED
Definition build.h:415
#define CAP_EFFECTIVE
Definition cap.h:48
int fr_bio_fd_socket_name(fr_bio_fd_t *my)
Definition fd.c:686
int fr_bio_fd_init_connected(fr_bio_fd_t *my)
Definition fd.c:881
int fr_filename_to_sockaddr(struct sockaddr_un *sun, socklen_t *sunlen, char const *filename)
Definition fd.c:668
int fr_bio_fd_init_listen(fr_bio_fd_t *my)
Definition fd.c:990
int fr_bio_fd_init_common(fr_bio_fd_t *my)
Definition fd.c:949
fr_socket_t socket
as connected socket
Definition fd.h:145
char const * name
printable name of this BIO
Definition fd.h:150
uint16_t src_port
our port
Definition fd.h:104
fr_bio_fd_type_t type
type of the socket
Definition fd.h:147
fr_bio_fd_state_t state
connecting, open, closed, etc.
Definition fd.h:148
uint16_t src_port_start
limit source port ranges for client BIOs
Definition fd.h:107
uint32_t recv_buff
How big the kernel's receive buffer should be.
Definition fd.h:114
bool mkdir
make intermediate directories
Definition fd.h:121
fr_ipaddr_t dst_ipaddr
their IP address
Definition fd.h:102
fr_bio_fd_type_t type
accept, connected, unconnected, etc.
Definition fd.h:93
char const * path
for Unix domain sockets
Definition fd.h:117
uint32_t send_buff
How big the kernel's send buffer should be.
Definition fd.h:115
char const * filename
for files
Definition fd.h:123
uid_t uid
who owns the socket
Definition fd.h:119
@ FR_BIO_FD_CONNECTED
connected client sockets (UDP or TCP)
Definition fd.h:63
@ FR_BIO_FD_INVALID
not set
Definition fd.h:59
@ FR_BIO_FD_UNCONNECTED
unconnected UDP / datagram only
Definition fd.h:60
@ FR_BIO_FD_LISTEN
call fr_bio_fd_accept() for each new connection
Definition fd.h:64
fr_bio_fd_config_t const * cfg
so we know what was asked, vs what was granted.
Definition fd.h:158
@ FR_BIO_FD_TRANSPORT_TCP
TCP over IPv4 or IPv6.
Definition fd.h:79
@ FR_BIO_FD_TRANSPORT_UDP
UDP over IPv4 or IPv6.
Definition fd.h:80
@ FR_BIO_FD_TRANSPORT_INVALID
not set
Definition fd.h:77
@ FR_BIO_FD_TRANSPORT_UNIX
Unix domain socket, named by 'path'.
Definition fd.h:81
@ FR_BIO_FD_TRANSPORT_FILE
regular file, named by 'filename'
Definition fd.h:78
gid_t gid
who owns the socket
Definition fd.h:120
char const * interface
for binding to an interface
Definition fd.h:110
uint16_t src_port_end
limit source port ranges for client BIOs
Definition fd.h:108
int socket_type
SOCK_STREAM or SOCK_DGRAM.
Definition fd.h:95
uint16_t dst_port
their port
Definition fd.h:105
bool tcp_delay
We do tcp_nodelay by default.
Definition fd.h:127
fr_bio_fd_transport_t transport_type
which transport, as a number instead of a name
Definition fd.h:97
@ FR_BIO_FD_STATE_CLOSED
Definition fd.h:53
@ FR_BIO_FD_STATE_CONNECTING
Definition fd.h:55
bool reuse_port
whether or not we re-use the same destination port for datagram sockets
Definition fd.h:99
bool exceed_mtu
Whether we allow packets which exceed the local MTU.
Definition fd.h:128
bool async
is it async
Definition fd.h:126
int flags
O_RDONLY, etc.
Definition fd.h:124
fr_ipaddr_t src_ipaddr
our IP address
Definition fd.h:101
Configuration for sockets.
Definition fd.h:92
Run-time status of the socket.
Definition fd.h:144
static int fr_bio_fd_socket_unix_bind(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Bind to a Unix domain socket.
Definition fd_open.c:557
static int fr_bio_fd_socket_bind_to_device(fr_bio_fd_t *my, UNUSED fr_bio_fd_config_t const *cfg)
This system is missing SO_BINDTODEVICE, IP_BOUND_IF, IPV6_BOUND_IF.
Definition fd_open.c:748
int fr_bio_fd_unix_shutdown(fr_bio_t *bio)
Remove the Unix domain socket which this bio created.
Definition fd_open.c:499
static int fr_bio_fd_socket_unix_mkdir(int *dirfd, char const **filename, fr_bio_fd_config_t const *cfg, mode_t dir_perm, uid_t uid)
Definition fd_open.c:344
static int fr_bio_fd_common_rcvsndbuf(int fd, fr_bio_fd_config_t const *cfg)
Definition fd_open.c:50
static int fr_bio_fd_common_udp(int fd, fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
Initialize a UDP socket.
Definition fd_open.c:161
int fr_bio_fd_reopen(fr_bio_t *bio)
Reopen a file BIO.
Definition fd_open.c:1500
static int fr_bio_fd_server_tcp(int fd, UNUSED fr_socket_t const *sock)
Initialize a TCP server socket.
Definition fd_open.c:237
static int fr_bio_fd_unix_open(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Open a Unix domain socket.
Definition fd_open.c:1263
#define FILE_PERMS(_cfg)
Definition fd_open.c:535
static int fr_bio_fd_server_v6only(UNUSED int fd, UNUSED fr_socket_t const *sock)
Stop a wildcard IPv6 listener from accepting IPv4 packets.
Definition fd_open.c:267
void fr_bio_fd_name(fr_bio_fd_t *my)
Set the name of an FD BIO.
Definition fd_open.c:897
static int fr_bio_fd_socket_bind(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Definition fd_open.c:759
static int fr_bio_fd_socket_unix_verify(int dirfd, char const *filename, fr_bio_fd_config_t const *cfg, uid_t uid)
Verify or clean up a pre-existing domain socket.
Definition fd_open.c:276
static int fr_bio_fd_common_tcp(int fd, UNUSED fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
Initialize common datagram information.
Definition fd_open.c:84
static int fr_bio_fd_file_open(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Open a file.
Definition fd_open.c:1194
int fr_bio_fd_open(fr_bio_t *bio, fr_bio_fd_config_t const *cfg)
Opens a socket and updates sock->fd.
Definition fd_open.c:1293
int fr_bio_fd_check_config(fr_bio_fd_config_t const *cfg)
Checks the configuration without modifying anything.
Definition fd_open.c:996
static int fr_bio_fd_socket_open(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Open a TCP or UDP socket.
Definition fd_open.c:1089
static int fr_bio_fd_common_datagram(int fd, UNUSED fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
Initialize common datagram information.
Definition fd_open.c:128
fr_bio_fd_info_t info
Definition fd_priv.h:38
Our FD bio structure.
Definition fd_priv.h:35
talloc_free(hp)
int fr_ipaddr_is_inaddr_any(fr_ipaddr_t const *ipaddr)
Determine if an address is the INADDR_ANY address for its address family.
Definition inet.c:62
int fr_ipaddr_to_sockaddr(struct sockaddr_storage *sa, socklen_t *salen, fr_ipaddr_t const *ipaddr, uint16_t port)
Convert our internal ip address representation to a sockaddr.
Definition inet.c:1399
int af
Address family.
Definition inet.h:64
#define AF_FR_FILENAME
Definition inet.h:105
IPv4/6 prefix.
ssize_t fr_mkdir(int *fd_out, char const *path, ssize_t len, mode_t mode, fr_mkdir_func_t func, void *uctx)
Create directories that are missing in the specified path.
Definition file.c:218
int fr_mkdir_chown(int fd, char const *path, void *uctx)
Callback for the common case of chown() of the directory.
Definition file.c:38
int fr_dirfd(int *dirfd, char const **filename, char const *pathname)
From a pathname, return fd and filename needed for *at() functions.
Definition file.c:411
unsigned short uint16_t
int fr_nonblock(UNUSED int fd)
Definition misc.c:293
fr_suid_t fr_suid_up
Definition misc.c:533
int fr_cloexec(UNUSED int fd)
Definition misc.c:331
fr_suid_t fr_suid_down
Definition misc.c:534
char * fr_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Special version of asprintf which implements custom format specifiers.
Definition print.c:883
#define fr_assert(_expr)
Definition rad_assert.h:37
static bool done
Definition radclient.c:80
uint32_t fr_rand(void)
Return a 32-bit random number.
Definition rand.c:128
int fr_socket_client_unix(UNUSED char const *path, UNUSED bool async)
Definition socket.c:541
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
#define talloc_strdup(_ctx, _str)
Definition talloc.h:149
int af
AF_INET, AF_INET6, or AF_UNIX.
Definition socket.h:83
int fd
File descriptor if this is a live socket.
Definition socket.h:86
int type
SOCK_STREAM, SOCK_DGRAM, etc.
Definition socket.h:84
Holds information necessary for binding or connecting to a socket.
Definition socket.h:60
void fr_strerror_clear(void)
Clears all pending messages from the talloc pools.
Definition strerror.c:581
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition strerror.h:84
#define fr_strerror_const(_msg)
Definition strerror.h:223
#define fr_box_ipaddr(_val)
Definition value.h:348