The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
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: b238558d3ed814bd7e6c7471446a347f360e9f24 $
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 */
252static int fr_bio_fd_server_v6only(UNUSED int fd, UNUSED fr_socket_t const *sock)
253{
254#ifdef IPV6_V6ONLY
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#endif /* IPV6_V6ONLY */
264
265 return 0;
266}
267
268/** Verify or clean up a pre-existing domain socket.
269 *
270 */
271static int fr_bio_fd_socket_unix_verify(int dirfd, char const *filename, fr_bio_fd_config_t const *cfg, uid_t uid)
272{
273 int fd;
274 struct stat buf;
275
276 /*
277 * See if the socket exits. If there's an error opening it, that's an issue.
278 *
279 * If it doesn't exist, that's fine.
280 */
281 if (fstatat(dirfd, filename, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
282 if (errno != ENOENT) {
283 fr_strerror_printf("Failed opening domain socket %s: %s", cfg->path, fr_syserror(errno));
284 return -1;
285 }
286
287 return 0;
288 }
289
290 /*
291 * If it exists, it must be a socket.
292 */
293 if (!S_ISSOCK(buf.st_mode)) {
294 fr_strerror_printf("Failed open domain socket %s: it is not a socket", filename);
295 return -1;
296 }
297
298 /*
299 * Refuse to open sockets not owned by us. This prevents configurations from stomping on each
300 * other.
301 */
302 if (buf.st_uid != uid) {
303 fr_strerror_printf("Failed opening domain socket %s: incorrect UID (have %u, expected %u)",
304 cfg->path, buf.st_uid, uid);
305 return -1;
306 }
307
308 /*
309 * The file exists,and someone is listening. We can't claim it for ourselves.
310 *
311 * Note that this function calls connect(), but connect() always returns immediately for domain
312 * sockets.
313 *
314 * @todo - redo that function here, with separate checks for permission errors vs anything else.
315 */
316 fd = fr_socket_client_unix(cfg->path, false);
317 if (fd >= 0) {
318 close(fd);
319 fr_strerror_printf("Failed creating domain socket %s: It is currently active", cfg->path);
320 return -1;
321 }
322
323 /*
324 * It exists, but no one is listening. Delete it so that we can re-bind to it.
325 */
326 if (unlinkat(dirfd, filename, 0) < 0) {
327 fr_strerror_printf("Failed removing pre-existing domain socket %s: %s",
328 cfg->path, fr_syserror(errno));
329 return -1;
330 }
331
332 return 0;
333}
334
335/*
336 * We normally can't call fchmod() or fchown() on sockets, as they don't really exist in the file system.
337 * Instead, we enforce those permissions on the parent directory of the socket.
338 */
339static int fr_bio_fd_socket_unix_mkdir(int *dirfd, char const **filename, fr_bio_fd_config_t const *cfg,
340 mode_t dir_perm, uid_t uid)
341{
342 int parent_fd, fd;
343 char const *path = cfg->path;
344 char *p, *dir = NULL;
345 char *slashes[2];
346
347 /*
348 * The parent directory exists. Ensure that it has the correct ownership and permissions.
349 *
350 * If the parent directory exists, then it enforces access, and we can create the domain socket
351 * within it.
352 */
353 if (fr_dirfd(dirfd, filename, path) == 0) {
354 struct stat buf;
355
356 fr_assert(*filename);
357 fr_assert(*dirfd >= 0);
358
359 if (fstat(*dirfd, &buf) < 0) {
360 fr_strerror_printf("Failed reading parent directory for file %s: %s", path, fr_syserror(errno));
361 fail:
362 talloc_free(dir);
363 close(*dirfd);
364 return -1;
365 }
366
367 if (buf.st_uid != uid) {
368 fr_strerror_printf("Failed reading parent directory for file %s: Incorrect UID (have %u, expected %u)",
369 path, buf.st_uid, uid);
370 goto fail;
371 }
372
373 if (buf.st_gid != cfg->gid) {
374 fr_strerror_printf("Failed reading parent directory for file %s: Incorrect GID (have %u, expected %u)",
375 path, buf.st_gid, cfg->gid);
376 goto fail;
377 }
378
379 /*
380 * We don't have the correct permissions on the directory, so we fix them.
381 *
382 * @todo - allow for "other" to read/write if we do authentication on the socket?
383 */
384 if (fchmod(*dirfd, dir_perm) < 0) {
385 fr_strerror_printf("Failed setting parent directory permissions for file %s: %s", path, fr_syserror(errno));
386 goto fail;
387 }
388
389 return 0;
390 }
391
392 /*
393 * We have to create the directories.
394 */
395 dir = talloc_strdup(NULL, path);
396 if (!dir) goto fail;
397
398 /*
399 * Find the last two directory separators.
400 */
401 slashes[0] = slashes[1] = NULL;
402 for (p = dir; *p != '\0'; p++) {
403 if (*p == '/') {
404 slashes[0] = slashes[1];
405 slashes[1] = p;
406 }
407 }
408
409 /*
410 * There's only one / in the path, we can't do anything.
411 *
412 * Opening 'foo/bar.sock' might be useful, but isn't normally a good idea.
413 */
414 if (!slashes[0]) {
415 fr_strerror_printf("Failed parsing filename %s: it is not absolute", path);
416 goto fail;
417 }
418
419 /*
420 * Ensure that the grandparent directory exists.
421 *
422 * /var/run/radiusd/foo.sock
423 *
424 * slashes[0] points to the slash after 'run'.
425 *
426 * slashes[1] points to the slash after 'radiusd', which doesn't exist.
427 */
428 *slashes[0] = '\0';
429
430 /*
431 * If the grandparent doesn't exist, then we don't create it.
432 *
433 * These checks minimize the possibility that a misconfiguration by user "radiusd" can cause a
434 * suid-root binary top create a directory in the wrong place. These checks are only necessary
435 * if the unix domain socket is opened as root.
436 */
437 parent_fd = open(dir, O_DIRECTORY | O_NOFOLLOW);
438 if (parent_fd < 0) {
439 fr_strerror_printf("Failed opening directory %s: %s", dir, fr_syserror(errno));
440 goto fail;
441 }
442
443 /*
444 * Create the parent directory.
445 */
446 *slashes[0] = '/';
447 *slashes[1] = '\0';
448 if (mkdirat(parent_fd, slashes[0] + 1, 0700) < 0) {
449 fr_strerror_printf("Failed creating directory %s: %s", slashes[0], fr_syserror(errno));
450 close_parent:
451 close(parent_fd);
452 goto fail;
453 }
454
455 fd = openat(parent_fd, slashes[0] + 1, O_DIRECTORY);
456 if (fd < 0) {
457 fr_strerror_printf("Failed opening directory %s: %s", slashes[0] + 1, fr_syserror(errno));
458 goto close_parent;
459 }
460
461 if (fchmod(fd, dir_perm) < 0) {
462 fr_strerror_printf("Failed changing permission for domain socket %s: %s", cfg->path, fr_syserror(errno));
463 close_fd:
464 close(fd);
465 goto close_parent;
466 }
467
468 /*
469 * This is a NOOP if we're chowning a file owned by ourselves to the correct uid / gid
470 *
471 * Otherwise if we're running as root, it will set ownership to the correct user.
472 */
473 if (fchown(fd, uid, cfg->gid) < 0) {
474 fr_strerror_printf("Failed changing ownership for domain socket %s: %s", dir, fr_syserror(errno));
475 goto close_fd;
476 }
477
478 *dirfd = fd;
479 path = strrchr(cfg->path, '/');
480 if (path) path++;
481
482 *filename = path;
483
484 talloc_free(dir);
485 close(parent_fd);
486
487 return 0;
488}
489
490/** Remove the Unix domain socket which this bio created.
491 *
492 * Called from fr_bio_fd_shutdown() for every Unix domain bio.
493 */
495{
496 fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
497
498 /*
499 * Called after fr_bio_fd_close(), which marks the bio state as closed.
500 *
501 * Unix domain sockets are never in the "connecting" state, because connect() always returns
502 * immediately.
503 */
505
506 /*
507 * The master socket (FR_BIO_FD_LISTEN) is the one which opens / creates the path, so it's
508 * respinsible for removing it. Child sockets (FR_BIO_FD_CONNECTED) still point to the path, but
509 * they don't own it.
510 */
511 if (my->info.type != FR_BIO_FD_LISTEN) return 0;
512
513 /*
514 * We should already own this file, so we can remove it. If we can't remove it, then we can't
515 * recover. We might as well return success, so the application can run its own shutdown routine.
516 */
517 (void) unlink(my->info.socket.unix.path);
518
519 return 0;
520}
521
522/*
523 * The socket is readable and writable by the owner and the group. When cfg->perm is non-zero,
524 * the socket keeps only those read and write bits that cfg->perm also sets. Any other bit in
525 * cfg->perm has no effect. A cfg->perm of zero leaves the socket readable and writable by the
526 * owner and the group.
527 *
528 * The user is always able to read and write the files.
529 */
530#define FILE_PERMS(_cfg) \
531 do { \
532 file_perm = (_cfg)->perm ? (_cfg)->perm : S_IRGRP | S_IWGRP; \
533 file_perm |= S_IRUSR | S_IWUSR; \
534 dir_perm = file_perm & (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_ISGID | S_ISVTX); \
535 file_perm &= (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); \
536 if ((dir_perm & (S_IRUSR | S_IWUSR)) != 0) dir_perm |= S_IXUSR; \
537 if ((dir_perm & (S_IRGRP | S_IWGRP)) != 0) dir_perm |= S_IXGRP; \
538 } while (0)
539
540
541/** Bind to a Unix domain socket.
542 *
543 * @todo - this function only does a tiny bit of what fr_server_domain_socket_peercred() and
544 * fr_server_domain_socket_perm() do. Those functions do a lot more sanity checks.
545 *
546 * The main question is whether or not those checks are useful. In many cases, fchmod() and fchown() are not
547 * possible on Unix sockets, so we shouldn't bother doing them,
548 *
549 * Note that the listeners generally call these functions with wrappers of fr_suid_up() and fr_suid_down().
550 * So these functions are running as "root", and will create files owned as "root".
551 */
553{
554 int dirfd, rcode;
555 mode_t file_perm, dir_perm;
556 char const *filename, *p;
557 socklen_t sunlen;
558 uid_t uid;
559 struct sockaddr_un sun;
560
561 if (!cfg->path) {
562 fr_strerror_const("Failed to specify path");
563 return -1;
564 }
565
566 /*
567 * The UID and GID are taken from the configuration.
568 */
569 uid = cfg->uid;
570 if (uid == (uid_t) -1) {
571 uid = getuid();
572 }
573
574 if (cfg->gid == (gid_t) -1) {
575 fr_strerror_printf("Failed opening domain socket %s: no GID specified", cfg->path);
576 return -1;
577 }
578
579 FILE_PERMS(cfg);
580
581 /*
582 * Opening 'foo.sock' is OK.
583 */
584 p = strrchr(cfg->path, '/');
585 if (!p) {
586 dirfd = AT_FDCWD;
587 filename = cfg->path;
588
589 } else if (p == cfg->path) {
590 /*
591 * Opening '/foo.sock' is dumb.
592 */
593 fr_strerror_printf("Failed opening domain socket %s: cannot exist at file system root", p);
594 return -1;
595
596 } else if (fr_bio_fd_socket_unix_mkdir(&dirfd, &filename, cfg, dir_perm, uid) < 0) {
597 return -1;
598 }
599
600 /*
601 * Verify and/or clean up the domain socket.
602 */
603 if (fr_bio_fd_socket_unix_verify(dirfd, filename, cfg, uid) < 0) {
604 fail:
605 if (dirfd != AT_FDCWD) close(dirfd);
606 return -1;
607 }
608
609#ifdef HAVE_BINDAT
610 /*
611 * The best function to use here is bindat(), but only quite recent versions of FreeBSD actually
612 * have it, and it's definitely not POSIX.
613 *
614 * If we use bindat(), we pass a relative pathname.
615 */
616 if (fr_filename_to_sockaddr(&sun, &sunlen, filename) < 0) goto fail;
617
618 rcode = bindat(dirfd, my->info.socket.fd, (struct sockaddr *) &sun, sunlen);
619#else
620 /*
621 * For bind(), we pass the full path.
622 */
623 if (fr_filename_to_sockaddr(&sun, &sunlen, cfg->path) < 0) goto fail;
624
625 rcode = bind(my->info.socket.fd, (struct sockaddr *) &sun, sunlen);
626#endif
627 if (rcode < 0) {
628 /*
629 * @todo - if EADDRINUSE, then the socket exists. Try connect(), and if that fails,
630 * delete the socket and try again. This may be simpler than the checks above.
631 */
632 fr_strerror_printf("Failed binding to domain socket %s: %s", cfg->path, fr_syserror(errno));
633 goto fail;
634 }
635
636#ifdef __linux__
637 /*
638 * Linux supports chown && chmod for sockets.
639 */
640 if (fchmod(my->info.socket.fd, file_perm) < 0) {
641 fr_strerror_printf("Failed changing permission for domain socket %s: %s", cfg->path, fr_syserror(errno));
642 goto fail;
643 }
644
645 /*
646 * This is a NOOP if we're chowning a file owned by ourselves to our own UID / GID.
647 *
648 * Otherwise if we're running as root, it will set ownership to the correct user.
649 */
650 if (fchown(my->info.socket.fd, uid, cfg->gid) < 0) {
651 fr_strerror_printf("Failed changing ownership for domain socket %s: %s", cfg->path, fr_syserror(errno));
652 goto fail;
653 }
654
655#endif
656
657 if (dirfd != AT_FDCWD) close(dirfd);
658
659 return 0;
660}
661
662/*
663 * Use the OSX native versions on OSX.
664 */
665#ifdef __APPLE__
666#undef SO_BINDTODEVICE
667#endif
668
669#ifdef SO_BINDTODEVICE
670/** Linux bind to device by name.
671 *
672 */
674{
675 /*
676 * ifindex isn't set, do nothing.
677 */
678 if (!my->info.socket.inet.ifindex) return 0;
679
680 if (!cfg->interface) return 0;
681
682 /*
683 * The internet hints that CAP_NET_RAW is required to use SO_BINDTODEVICE.
684 *
685 * This function also sets fr_strerror() on failure, which will be seen if the bind fails. If
686 * the bind succeeds, then we don't really care that the capability change has failed. We must
687 * already have that capability.
688 */
689#ifdef HAVE_CAPABILITY_H
690 (void)fr_cap_enable(CAP_NET_RAW, CAP_EFFECTIVE);
691#endif
692
693 if (setsockopt(my->info.socket.fd, SOL_SOCKET, SO_BINDTODEVICE, cfg->interface, strlen(cfg->interface)) < 0) {
694 fr_strerror_printf("Failed setting SO_BINDTODEVICE for %s: %s", cfg->interface, fr_syserror(errno));
695 return -1;
696 }
697
698 return 0;
699}
700
701#elif defined(IP_BOUND_IF) || defined(IPV6_BOUND_IF)
702/** OSX bind to interface by index.
703 *
704 */
706{
707 int opt, rcode;
708
709 if (!my->info.socket.inet.ifindex) return 0;
710
711 opt = my->info.socket.inet.ifindex;
712
713 switch (my->info.socket.af) {
714 case AF_INET:
715 rcode = setsockopt(my->info.socket.fd, IPPROTO_IP, IP_BOUND_IF, &opt, sizeof(opt));
716 break;
717
718 case AF_INET6:
719 rcode = setsockopt(my->info.socket.fd, IPPROTO_IPV6, IPV6_BOUND_IF, &opt, sizeof(opt));
720 break;
721
722 default:
723 rcode = -1;
724 errno = EAFNOSUPPORT;
725 break;
726 }
727
728 if (rcode < 0) fr_strerror_printf("Failed setting IP_BOUND_IF: %s", fr_syserror(errno));
729 return rcode;
730}
731#else
732
733/** This system is missing SO_BINDTODEVICE, IP_BOUND_IF, IPV6_BOUND_IF
734 *
735 * @todo - FreeBSD IP_RECVIF and IP_SENDIF
736 *
737 * Except that has to be done in recvmsg() and sendmsg(). And it only works on datagram sockets.
738 *
739 * cmsg_len = sizeof(struct sockaddr_dl)
740 * cmsg_level = IPPROTO_IP
741 * cmsg_type = IP_RECVIF
742 */
744{
745 if (!my->info.socket.inet.ifindex) return 0;
746
747 fr_strerror_const("Bind to interface is not supported on this platform");
748 return -1;
749}
750
751/* bind to device */
752#endif
753
755{
756 bool do_suid;
757 int rcode;
758 socklen_t salen;
759 struct sockaddr_storage salocal;
760
761 fr_assert((my->info.socket.af == AF_INET) || (my->info.socket.af == AF_INET6));
762
763 /*
764 * Ranges must be a high value.
765 */
766 if (my->info.cfg->src_port_start) {
767 if (my->info.cfg->src_port_start < 1024) {
768 fr_strerror_const("Cannot set src_port_start in the range 1..1023");
769 return -1;
770 }
771
772 if (my->info.cfg->src_port_end <= my->info.cfg->src_port_start) {
773 fr_strerror_const("Invalid range src_port_end <= src_port_start");
774 return -1;
775 }
776 }
777
778 /*
779 * Source port is in the restricted range, and we're not root, update permissions / capabilities
780 * so that the bind is permitted.
781 */
782 do_suid = (my->info.socket.inet.src_port > 0) && (my->info.socket.inet.src_port < 1024) && (geteuid() != 0);
783
784#ifdef HAVE_CAPABILITY_H
785 /*
786 * If we can set the capabilities, then we don't need to do SUID.
787 */
788 if (do_suid) do_suid = (fr_cap_enable(CAP_NET_BIND_SERVICE, CAP_EFFECTIVE) < 0);
789#endif
790
791 /*
792 * SUID up before we bind to the interface. If we can set the capabilities above, then should
793 * also be able to set the capabilities to bind to an interface.
794 */
795 if (do_suid) fr_suid_up();
796
797 if (fr_bio_fd_socket_bind_to_device(my, cfg) < 0) {
798 down:
799 if (do_suid) fr_suid_down();
800 return -1;
801 }
802
803 /*
804 * Get the sockaddr for bind()
805 */
806 if (fr_ipaddr_to_sockaddr(&salocal, &salen, &my->info.socket.inet.src_ipaddr, my->info.socket.inet.src_port) < 0) goto down;
807
808 /*
809 * We don't have a source port range, just bind to whatever source port that we're given.
810 */
811 if (!my->info.cfg->src_port_start) {
812 rcode = bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen);
813 if (do_suid) fr_suid_down();
814
815 if (rcode < 0) {
816 fr_strerror_printf("Failed binding to socket: %s", fr_syserror(errno));
817 return -1;
818 }
819
820 } else {
821 uint16_t src_port, current, range;
822 struct sockaddr_in *sin = (struct sockaddr_in *) &salocal;
823
827
828 range = my->info.cfg->src_port_end - my->info.cfg->src_port_start;
829 src_port = fr_rand() % range;
830
831 /*
832 * We only care about sin_port, which is in the same location for sockaddr_in and sockaddr_in6.
833 */
834 fr_assert(sin->sin_port == 0);
835
836 sin->sin_port = htons(my->info.cfg->src_port_start + src_port);
837
838 /*
839 * We've picked a random port in what is hopefully a large range. If that works, we're
840 * done.
841 */
842 rcode = bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen);
843 if (rcode == 0) {
844 if (do_suid) fr_suid_down();
845 goto done;
846 }
847
848 /*
849 * Hunt & peck. Which is horrible.
850 */
851 current = src_port;
852 while (true) {
853 if (current == range) {
854 current = 0;
855 } else {
856 current++;
857 }
858
859 /*
860 * We've already checked this, so stop.
861 */
862 if (current == src_port) break;
863
864 sin->sin_port = htons(my->info.cfg->src_port_start + current);
865
866 rcode = bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen);
867 if (rcode == 0) {
868 if (do_suid) fr_suid_down();
869 goto done;
870 }
871 }
872
873 /*
874 * The error is a good hint at _why_ we failed to bind.
875 * We expect errno to be EADDRINUSE, anything else is a surprise.
876 */
877 if (do_suid) fr_suid_down();
878 fr_strerror_printf("Failed binding port between 'src_port_start' and 'src_port_end': %s", fr_syserror(errno));
879 return -1;
880 }
881
882 /*
883 * The IP and/or port may have changed, so get the new one.
884 */
885done:
886 return fr_bio_fd_socket_name(my);
887}
888
889/** Set the name of an FD BIO
890 *
891 */
893{
894 fr_bio_fd_config_t const *cfg = my->info.cfg;
895
896 switch (my->info.type) {
898 return;
899
901 fr_assert(cfg->socket_type == SOCK_DGRAM);
902
903 switch (my->info.socket.af) {
904 case AF_INET:
905 case AF_INET6:
906 my->info.name = fr_asprintf(my, "proto udp local %pV port %u",
907 fr_box_ipaddr(my->info.socket.inet.src_ipaddr),
908 my->info.socket.inet.src_port);
909 break;
910
911 case AF_LOCAL:
912 my->info.name = fr_asprintf(my, "proto unix (datagram) filename %s",
913 cfg->path);
914 break;
915
916 default:
917 fr_assert(0);
918 my->info.name = "??? invalid BIO ???";
919 break;
920 }
921 break;
922
924 switch (my->info.socket.af) {
925 case AF_INET:
926 case AF_INET6:
927 my->info.name = fr_asprintf(my, "proto %s local %pV port %u remote %pV port %u",
928 (cfg->socket_type == SOCK_DGRAM) ? "udp" : "tcp",
929 fr_box_ipaddr(my->info.socket.inet.src_ipaddr),
930 my->info.socket.inet.src_port,
931 fr_box_ipaddr(my->info.socket.inet.dst_ipaddr),
932 my->info.socket.inet.dst_port);
933 break;
934
935 case AF_LOCAL:
936 my->info.name = fr_asprintf(my, "proto unix %sfilename %s",
937 (cfg->socket_type == SOCK_DGRAM) ? "(datagram) " : "",
938 cfg->path);
939 break;
940
941 case AF_FR_FILENAME:
942 fr_assert(cfg->socket_type == SOCK_STREAM);
943
944 if ((cfg->flags & O_ACCMODE) == O_RDONLY) {
945 my->info.name = fr_asprintf(my, "proto file (read-only) filename %s ",
946 cfg->filename);
947
948 } else if ((cfg->flags & O_ACCMODE) == O_WRONLY) {
949 my->info.name = fr_asprintf(my, "proto file (write-only) filename %s",
950 cfg->filename);
951 } else {
952 my->info.name = fr_asprintf(my, "proto file (read-write) filename %s",
953 cfg->filename);
954 }
955 break;
956
957 default:
958 fr_assert(0);
959 my->info.name = "??? invalid BIO ???";
960 break;
961 }
962 break;
963
964 case FR_BIO_FD_LISTEN:
965 switch (my->info.socket.af) {
966 case AF_INET:
967 case AF_INET6:
968 my->info.name = fr_asprintf(my, "proto %s local %pV port %u",
969 (cfg->socket_type == SOCK_DGRAM) ? "udp" : "tcp",
970 fr_box_ipaddr(my->info.socket.inet.src_ipaddr),
971 my->info.socket.inet.src_port);
972 break;
973
974 case AF_LOCAL:
975 my->info.name = fr_asprintf(my, "proto unix filename %s",
976 cfg->path);
977 break;
978
979 default:
980 fr_assert(0);
981 my->info.name = "??? invalid BIO ???";
982 break;
983 }
984 break;
985 }
986}
987
988/** Checks the configuration without modifying anything.
989 *
990 */
992{
993 /*
994 * Files and Unix domain sockets are named by a path, and have no IP addresses to check.
995 */
996 switch (cfg->transport_type) {
998 fr_strerror_const("No transport was specified");
999 return -1;
1000
1002 if (!cfg->filename) {
1003 fr_strerror_const("No filename was specified for a file");
1004 return -1;
1005 }
1006 return 0;
1007
1009 if (!cfg->path) {
1010 fr_strerror_const("No path was specified for a Unix domain socket");
1011 return -1;
1012 }
1013 return 0;
1014
1017 break;
1018 }
1019
1020 /*
1021 * Sanitize the IP addresses.
1022 *
1023 */
1024 switch (cfg->type) {
1025 case FR_BIO_FD_INVALID:
1026 fr_strerror_const("No connection type was specified");
1027 return -1;
1028
1030 /*
1031 * Ensure that we have a destination address.
1032 */
1033 if (cfg->dst_ipaddr.af == AF_UNSPEC) {
1034 fr_strerror_const("Destination IP address must be specified for a connected socket");
1035 return -1;
1036 }
1037
1038 if (!cfg->dst_port) {
1039 fr_strerror_const("Destination port must be specified for a connected socket");
1040 return -1;
1041 }
1042
1043 /*
1044 * The source IP has to be the same address family as the destination IP.
1045 */
1046 if ((cfg->src_ipaddr.af != AF_UNSPEC) && (cfg->src_ipaddr.af != cfg->dst_ipaddr.af)) {
1047 fr_strerror_printf("Source and destination IP addresses are not from the same IP address family");
1048 return -1;
1049 }
1050 break;
1051
1052 case FR_BIO_FD_LISTEN:
1053 if (!cfg->src_port) {
1054 fr_strerror_const("Source port must be specified for a server socket");
1055 return -1;
1056 }
1058
1059 /*
1060 * Unconnected sockets can use a source port, but don't need one.
1061 */
1063 if (cfg->src_ipaddr.af == AF_UNSPEC) {
1064 fr_strerror_const("Source IP address must be specified for an unconnected socket");
1065 return -1;
1066 }
1067 break;
1068 }
1069
1070 return 0;
1071}
1072
1073/** Open a TCP or UDP socket
1074 *
1075 * The IP addresses and ports come from the configuration. The socket is not bound or connected
1076 * here, as that depends on the #fr_bio_fd_type_t.
1077 *
1078 * @param my the FD bio
1079 * @param cfg the configuration
1080 * @return
1081 * - <0 on error
1082 * - the file descriptor
1083 */
1085{
1086 int fd;
1087 int protocol;
1088
1089 if (fr_bio_fd_check_config(cfg) < 0) return -1;
1090
1091 my->info.socket.af = cfg->src_ipaddr.af;
1092 my->info.socket.inet.src_ipaddr = cfg->src_ipaddr;
1093 my->info.socket.inet.dst_ipaddr = cfg->dst_ipaddr;
1094 my->info.socket.inet.src_port = cfg->src_port;
1095 my->info.socket.inet.dst_port = cfg->dst_port;
1096
1097 /*
1098 * Sanitize the IP addresses.
1099 *
1100 */
1101 switch (cfg->type) {
1102 case FR_BIO_FD_INVALID:
1103 return -1;
1104
1106 /*
1107 * No source specified, just bootstrap it from the destination.
1108 */
1109 if (my->info.socket.inet.src_ipaddr.af == AF_UNSPEC) {
1110 my->info.socket.inet.src_ipaddr = (fr_ipaddr_t) {
1111 .af = my->info.socket.inet.dst_ipaddr.af,
1112 .prefix = (my->info.socket.inet.dst_ipaddr.af == AF_INET) ? 32 : 128,
1113 };
1114
1115 /*
1116 * Set the main socket AF too.
1117 */
1118 my->info.socket.af = my->info.socket.inet.dst_ipaddr.af;
1119 }
1120
1121#ifdef SO_REUSEPORT
1122 /*
1123 * Connected UDP sockets really only matter when writing packets. They allow the
1124 * system to use write() instead of sendto().
1125 *
1126 * However for reading packets, POSIX says that the OS does NOT check the source
1127 * IP. Instead, it just delivers any packet with the correct dst IP/port. Even
1128 * worse, if multiple sockets re-use the same dst IP/port, packets are delivered
1129 * to a _random_ socket.
1130 *
1131 * As a result, we cannot use wildcard sockets with SO_REUSEPORT, and UDP.
1132 *
1133 * However, recent OS (macOS 24.6.0 (Sequoia) and Linux 6.12, and FreeBSD) all
1134 * support connected UDP sockets, and check the 4-tuple, so that only packets for
1135 * the connection get delivered to the socket.
1136 *
1137 * For now, we leave this check alone. The caller should really be passing in a
1138 * specific source IP address, and not a wildcard for connected UDP sockets.
1139 */
1140 if (cfg->reuse_port && (cfg->transport_type == FR_BIO_FD_TRANSPORT_UDP) &&
1141 fr_ipaddr_is_inaddr_any(&my->info.socket.inet.dst_ipaddr)) { /* checks AF, so we're OK */
1142 fr_strerror_const("Cannot set 'reuse_port' for connected UDP sockets with wildcard IP");
1143 return -1;
1144 }
1145#endif
1146 break;
1147
1149 case FR_BIO_FD_LISTEN:
1150 fr_assert(my->info.socket.inet.src_ipaddr.af != AF_UNSPEC);
1151 break;
1152 }
1153
1155 protocol = IPPROTO_TCP;
1156 } else {
1157 protocol = IPPROTO_UDP;
1158 }
1159
1160 if (cfg->interface) {
1161 my->info.socket.inet.ifindex = if_nametoindex(cfg->interface);
1162
1163 if (!my->info.socket.inet.ifindex) {
1164 fr_strerror_printf_push("Failed finding interface %s: %s", cfg->interface, fr_syserror(errno));
1165 return -1;
1166 }
1167 }
1168
1169 fd = socket(my->info.socket.af, my->info.socket.type, protocol);
1170 if (fd < 0) {
1171 fr_strerror_printf("Failed opening socket: %s", fr_syserror(errno));
1172 return fr_bio_error(GENERIC);
1173 }
1174
1175 return fd;
1176}
1177
1178/** Open a file
1179 *
1180 * The names /dev/stdout, /dev/stderr, and /dev/stdin are duplicates of the matching file
1181 * descriptor, and are not opened by name.
1182 *
1183 * @param my the FD bio
1184 * @param cfg the configuration
1185 * @return
1186 * - <0 on error
1187 * - the file descriptor
1188 */
1190{
1191 int fd;
1192
1193 if (cfg->type != FR_BIO_FD_CONNECTED) {
1194 fr_strerror_printf("Can only use connected sockets for file IO");
1195 return -1;
1196 }
1197
1198 /*
1199 * Filenames overload the #fr_socket_t for now.
1200 */
1202 my->info.socket.type = SOCK_STREAM;
1203 my->info.socket.file.path = cfg->filename;
1204
1205 /*
1206 * Allow hacks for stdout and stderr
1207 */
1208 if (strcmp(cfg->filename, "/dev/stdout") == 0) {
1209 if ((cfg->flags & O_ACCMODE) != O_WRONLY) {
1210 fail_dev:
1211 fr_strerror_printf("Cannot read from %s", cfg->filename);
1212 return -1;
1213 }
1214
1215 fd = dup(STDOUT_FILENO);
1216
1217 } else if (strcmp(cfg->filename, "/dev/stderr") == 0) {
1218 if ((cfg->flags & O_ACCMODE) != O_WRONLY) goto fail_dev;
1219
1220 fd = dup(STDERR_FILENO);
1221
1222 } else if (strcmp(cfg->filename, "/dev/stdin") == 0) {
1223 if ((cfg->flags & O_ACCMODE) != O_RDONLY) {
1224 fr_strerror_printf("Cannot write to %s", cfg->filename);
1225 return -1;
1226 }
1227
1228 fd = dup(STDIN_FILENO);
1229
1230 } else {
1231 /*
1232 * Minor hacks so that we have only _one_ source of open / mkdir
1233 */
1234 my->info.socket.fd = -1;
1235
1236 fd = fr_bio_fd_reopen(&my->bio);
1237 }
1238 if (fd < 0) {
1239 fr_strerror_printf("Failed opening file %s: %s", cfg->filename, fr_syserror(errno));
1240 return fr_bio_error(GENERIC);
1241 }
1242
1243 return fd;
1244}
1245
1246/** Open a Unix domain socket
1247 *
1248 * The socket is not bound or connected here, as that depends on the #fr_bio_fd_type_t.
1249 *
1250 * Permissions are
1251 *
1252 * @param my the FD bio
1253 * @param cfg the configuration
1254 * @return
1255 * - <0 on error
1256 * - the file descriptor
1257 */
1259{
1260 int fd;
1261
1262 my->info.socket.af = AF_LOCAL;
1263 my->info.socket.type = SOCK_STREAM;
1264 my->info.socket.unix.path = cfg->path;
1265
1266 fd = socket(my->info.socket.af, my->info.socket.type, 0);
1267 if (fd < 0) {
1268 fr_strerror_printf("Failed opening domain socket %s: %s", cfg->path, fr_syserror(errno));
1269 return fr_bio_error(GENERIC);
1270 }
1271
1272 return fd;
1273}
1274
1275/** Opens a socket and updates sock->fd
1276 *
1277 * If the socket is asynchronous, it also calls connect()
1278 *
1279 * The caller must supply a cfg pointer which is long-lived. The FD BIO doesn't copy it, or its contents.
1280 * fr_bio_fd_accept() uses the same pointer for accepted sockets.
1281 *
1282 * @param bio the FD bio
1283 * @param cfg configuration for the socket, which the caller owns and must keep alive
1284 * @return
1285 * - <0 on error
1286 * - 0 on success
1287 */
1289{
1290 int fd;
1291 int rcode = -1;
1292 fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
1293
1295
1296 my->info = (fr_bio_fd_info_t) {
1297 .socket = {
1298 .type = cfg->socket_type,
1299 .fd = -1,
1300 },
1301 .cfg = cfg,
1302 };
1303
1304 /*
1305 * The transport says what kind of thing to open. Each one names what it opens in its own
1306 * field, and has its own setup.
1307 */
1308 switch (cfg->transport_type) {
1310 fr_strerror_const("No transport was specified");
1311 return -1;
1312
1315 fd = fr_bio_fd_socket_open(my, cfg);
1316 break;
1317
1319 fd = fr_bio_fd_unix_open(my, cfg);
1320 break;
1321
1323 fd = fr_bio_fd_file_open(my, cfg);
1324 break;
1325 }
1326
1327 if (fd < 0) return fd;
1328 my->info.socket.fd = fd;
1329
1330 /*
1331 * Set it to be non-blocking if required.
1332 */
1333 if (cfg->async && (fr_nonblock(fd) < 0)) {
1334 fr_strerror_printf("Failed setting O_NONBLOCK: %s", fr_syserror(errno));
1335 rcode = fr_bio_error(GENERIC);
1336
1337 fail:
1338 if (my->info.socket.fd >= 0) close(fd);
1339
1340 my->info.socket = (fr_socket_t) {
1341 .fd = -1,
1342 };
1344 my->info.cfg = NULL;
1345 return rcode;
1346 }
1347
1348 if (fr_cloexec(fd) < 0) goto fail;
1349
1350 /*
1351 * Initialize the bio information before calling the various setup functions.
1352 */
1354
1355 /*
1356 * Set the FD so that the subsequent calls can use it.
1357 */
1358 my->info.socket.fd = fd;
1359
1360 /*
1361 * Set the type, too.
1362 */
1363 my->info.type = cfg->type;
1364
1365 /*
1366 * Check that the bio type makes sense for the socket which was opened.
1367 */
1368 switch (cfg->type) {
1369 case FR_BIO_FD_INVALID:
1370 fr_strerror_const("No connection type was specified");
1371 rcode = -1;
1372 goto fail;
1373
1374 /*
1375 * Unconnected UDP or datagram AF_LOCAL server sockets.
1376 */
1378 if (my->info.socket.type != SOCK_DGRAM) {
1379 fr_strerror_const("Failed configuring socket: unconnected sockets must be UDP");
1380 rcode = -1;
1381 goto fail;
1382 }
1383 break;
1384
1385 /*
1386 * Server socket which listens for new connections.
1387 */
1388 case FR_BIO_FD_LISTEN:
1389 if ((my->info.socket.type == SOCK_DGRAM) && !cfg->reuse_port) {
1390 fr_strerror_const("reuseport must be set for datagram sockets");
1391 rcode = -1;
1392 goto fail;
1393 }
1394 break;
1395
1396 /*
1397 * A connected client: UDP, TCP, AF_LOCAL, or AF_FR_FILENAME.
1398 */
1400 break;
1401 }
1402
1403 /*
1404 * Set the socket options, and then bind the socket. Which options to set, and how to bind,
1405 * depend on the address family.
1406 */
1407 switch (my->info.socket.af) {
1408 case AF_INET:
1409 case AF_INET6:
1410 /*
1411 * A wildcard IPv6 listener should not also accept IPv4 packets.
1412 */
1413 if ((cfg->type == FR_BIO_FD_LISTEN) && (my->info.socket.af == AF_INET6) &&
1414 ((rcode = fr_bio_fd_server_v6only(fd, &my->info.socket)) < 0)) goto fail;
1415
1416 if (my->info.socket.type == SOCK_DGRAM) {
1417 if ((rcode = fr_bio_fd_common_udp(fd, &my->info.socket, cfg)) < 0) goto fail;
1418
1419 } else if (cfg->type == FR_BIO_FD_LISTEN) {
1420 /*
1421 * A listening socket is never read or written, so it needs only the one
1422 * option which lets us re-bind to the same address.
1423 */
1424 if ((rcode = fr_bio_fd_server_tcp(fd, &my->info.socket)) < 0) goto fail;
1425
1426 } else {
1427 if ((rcode = fr_bio_fd_common_tcp(fd, &my->info.socket, cfg)) < 0) goto fail;
1428 }
1429
1430 if ((rcode = fr_bio_fd_socket_bind(my, cfg)) < 0) goto fail;
1431 break;
1432
1433 case AF_LOCAL:
1434 if (my->info.socket.type == SOCK_DGRAM) {
1435 if ((rcode = fr_bio_fd_common_datagram(fd, &my->info.socket, cfg)) < 0) goto fail;
1436 }
1437
1438 /*
1439 * Don't call bind for clients.
1440 */
1441 if (cfg->type != FR_BIO_FD_LISTEN) break;
1442
1443 if ((rcode = fr_bio_fd_socket_unix_bind(my, cfg)) < 0) goto fail;
1444 break;
1445
1446 /*
1447 * A file is opened by name, so there is nothing to set and nothing to bind.
1448 */
1449 case AF_FR_FILENAME:
1450 break;
1451
1452 default:
1453 fr_strerror_const("Unsupported address family");
1454 rcode = -1;
1455 goto fail;
1456 }
1457
1458 /*
1459 * Initialize the read / write functions.
1460 */
1461 switch (cfg->type) {
1463 rcode = fr_bio_fd_init_common(my);
1464 break;
1465
1467 rcode = fr_bio_fd_init_connected(my);
1468 break;
1469
1470 case FR_BIO_FD_LISTEN:
1471 rcode = fr_bio_fd_init_listen(my);
1472 break;
1473
1474 case FR_BIO_FD_INVALID: /* checked above */
1475 fr_assert(0);
1476 rcode = -1;
1477 break;
1478 }
1479 if (rcode < 0) goto fail;
1480
1481 /*
1482 * Set the name of the BIO.
1483 */
1484 fr_bio_fd_name(my);
1485
1486 return 0;
1487}
1488
1489/** Reopen a file BIO
1490 *
1491 * e.g. for log files.
1492 *
1493 * This will change the FD which is in the BIO.
1494 */
1496{
1497 fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
1498 fr_bio_fd_config_t const *cfg = my->info.cfg;
1499 int fd, flags;
1500 mode_t file_perm, dir_perm;
1501
1502 if (my->info.socket.af != AF_FR_FILENAME) {
1503 fr_strerror_const("Cannot reopen a non-file BIO");
1504 return -1;
1505 }
1506
1507 /*
1508 * Create it if necessary.
1509 */
1510 flags = cfg->flags;
1511 if ((flags & O_ACCMODE) != O_RDONLY) flags |= O_CREAT;
1512
1513 FILE_PERMS(cfg);
1514
1515 if (!cfg->mkdir) {
1516 /*
1517 * Client BIOs writing to a file, and therefore need to create it.
1518 */
1519 do_open:
1520 fd = open(cfg->filename, flags, file_perm);
1521 if (fd < 0) {
1522 failed_open:
1523 fr_strerror_printf("Failed opening file %s: %s", cfg->filename, fr_syserror(errno));
1524 return -1;
1525 }
1526
1527 } else {
1528 /*
1529 * We make the parent directory if told to, AND if there's a '/' in the path.
1530 */
1531 char const *p = strrchr(cfg->filename, '/');
1532 int dir_fd;
1533
1534 if (!p) goto do_open;
1535
1536 if (fr_mkdir(&dir_fd, cfg->filename, (size_t) (p - cfg->filename), dir_perm, fr_mkdir_chown,
1537 &(fr_mkdir_chown_t) {
1538 .uid = cfg->uid != (uid_t) -1 ? cfg->uid : getuid(),
1539 .gid = cfg->gid != (gid_t) -1 ? cfg->gid : getgid(),
1540 }) < 0) {
1541 return -1;
1542 }
1543
1544 fd = openat(dir_fd, p + 1, flags, file_perm);
1545 if (fd < 0) {
1546 close(dir_fd);
1547 goto failed_open;
1548 }
1549
1550 close(dir_fd);
1551 }
1552
1553 /*
1554 * We're boot-strapping, just set the new FD and return.
1555 */
1556 if (my->info.socket.fd < 0) {
1557 my->info.socket.fd = fd;
1558 return fd;
1559 }
1560
1561 /*
1562 * Close the previous my->info.socket.fd, and make it instead a duplicate of our new FD.
1563 */
1564 if (dup2(fd, my->info.socket.fd) < 0) {
1565 close(fd);
1566 fr_strerror_printf("Failed reopening file - %s", fr_syserror(errno));
1567 return -1;
1568 }
1569
1570 /*
1571 * Close our duplicate FD, and leave the old one as a duplicate of the new one.
1572 */
1573 close(fd);
1574 return my->info.socket.fd;
1575}
#define fr_bio_error(_x)
Definition base.h:200
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition build.h:186
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:391
#define UNUSED
Definition build.h:384
#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
@ 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
uint16_t src_port
our port
Definition fd.h:104
@ 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_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_BIO_FD_STATE_CLOSED
Definition fd.h:53
@ FR_BIO_FD_STATE_CONNECTING
Definition fd.h:55
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_config_t const * cfg
so we know what was asked, vs what was granted.
Definition fd.h:158
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
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:552
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:743
int fr_bio_fd_unix_shutdown(fr_bio_t *bio)
Remove the Unix domain socket which this bio created.
Definition fd_open.c:494
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:339
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:1495
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:1258
#define FILE_PERMS(_cfg)
Definition fd_open.c:530
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:252
void fr_bio_fd_name(fr_bio_fd_t *my)
Set the name of an FD BIO.
Definition fd_open.c:892
static int fr_bio_fd_socket_bind(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Definition fd_open.c:754
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:271
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:1189
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:1288
int fr_bio_fd_check_config(fr_bio_fd_config_t const *cfg)
Checks the configuration without modifying anything.
Definition fd_open.c:991
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:1084
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
unsigned int mode_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:104
int fr_socket_client_unix(UNUSED char const *path, UNUSED bool async)
Definition socket.c:543
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