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