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