The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: c9ec70f70cf00f6aa184b9a0a93220351986d7db $
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 
29 #include <sys/stat.h>
30 #include <net/if.h>
31 #include <fcntl.h>
32 #include <libgen.h>
33 #include <netinet/tcp.h>
34 
35 /** Initialize common datagram information
36  *
37  */
38 static int fr_bio_fd_common_tcp(int fd, UNUSED fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
39 {
40  int on = 1;
41 
42 #ifdef SO_KEEPALIVE
43  /*
44  * TCP keepalives are always a good idea. Too many people put firewalls between critical
45  * systems, and then the firewalls drop live TCP streams.
46  */
47  if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) {
48  fr_strerror_printf("Failed setting SO_KEEPALIVE: %s", fr_syserror(errno));
49  return -1;
50  }
51 #endif
52 
53 #ifdef TCP_NODELAY
54  /*
55  * Add some defines for *BSD, and Solaris systems.
56  */
57 # if !defined(SOL_TCP) && defined(IPPROTO_TCP)
58 # define SOL_TCP IPPROTO_TCP
59 # endif
60 
61  /*
62  * Also set TCP_NODELAY, to force the data to be written quickly.
63  *
64  * We buffer full packets in memory before we write them, so there's no reason for the kernel to
65  * sit around waiting for more data from us.
66  */
67  if (!cfg->tcp_delay) {
68  if (setsockopt(fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) {
69  fr_strerror_printf("Failed setting TCP_NODELAY: %s", fr_syserror(errno));
70  return -1;
71  }
72  }
73 #endif
74 
75  return 0;
76 }
77 
78 
79 /** Initialize common datagram information
80  *
81  */
82 static int fr_bio_fd_common_datagram(int fd, UNUSED fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
83 {
84  int on = 1;
85 
86 #ifdef SO_TIMESTAMPNS
87  /*
88  * Enable receive timestamps, these should reflect
89  * when the packet was received, not when it was read
90  * from the socket.
91  */
92  if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS, &on, sizeof(int)) < 0) {
93  fr_strerror_printf("Failed setting SO_TIMESTAMPNS: %s", fr_syserror(errno));
94  return -1;
95  }
96 
97 #elif defined(SO_TIMESTAMP)
98  /*
99  * Enable receive timestamps, these should reflect
100  * when the packet was received, not when it was read
101  * from the socket.
102  */
103  if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &on, sizeof(int)) < 0) {
104  fr_strerror_printf("Failed setting SO_TIMESTAMP: %s", fr_syserror(errno));
105  return -1;
106  }
107 #endif
108 
109 #ifdef SO_RCVBUF
110  if (cfg->recv_buff) {
111  int opt = cfg->recv_buff;
112 
113  if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) < 0) {
114  fr_strerror_printf("Failed setting SO_RCVBUF: %s", fr_syserror(errno));
115  return -1;
116  }
117  }
118 #endif
119 
120 #ifdef SO_SNDBUF
121  if (cfg->send_buff) {
122  int opt = cfg->send_buff;
123 
124  if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) < 0) {
125  fr_strerror_printf("Failed setting SO_SNDBUF: %s", fr_syserror(errno));
126  return -1;
127  }
128  }
129 #endif
130 
131  return 0;
132 }
133 
134 /** Initialize a UDP server socket.
135  *
136  */
137 static int fr_bio_fd_server_udp(int fd, fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
138 {
139 #ifdef SO_REUSEPORT
140  int on = 1;
141 
142  /*
143  * Set SO_REUSEPORT before bind, so that all sockets can
144  * listen on the same destination IP address.
145  */
146  if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) {
147  fr_strerror_printf("Failed setting SO_REUSEPORT: %s", fr_syserror(errno));
148  return -1;
149  }
150 #endif
151 
152  return fr_bio_fd_common_datagram(fd, sock, cfg);
153 }
154 
155 /** Initialize a TCP server socket.
156  *
157  */
158 static int fr_bio_fd_server_tcp(int fd, UNUSED fr_socket_t const *sock)
159 {
160  int on = 1;
161 
162  if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
163  fr_strerror_printf("Failed setting SO_REUSEADDR: %s", fr_syserror(errno));
164  return -1;
165  }
166 
167  return 0;
168 }
169 
170 /** Initialize an IPv4 server socket.
171  *
172  */
173 static int fr_bio_fd_server_ipv4(int fd, fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
174 {
175  int flag;
176 
177 #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
178  /*
179  * Disable PMTU discovery. On Linux, this also makes sure that the "don't
180  * fragment" flag is zero.
181  */
182  flag = IP_PMTUDISC_DONT;
183 
184  if (setsockopt(fd, IPPROTO_IP, IP_MTU_DISCOVER, &flag, sizeof(flag)) < 0) {
185  fr_strerror_printf("Failed setting IP_MTU_DISCOVER: %s", fr_syserror(errno));
186  return -1;
187  }
188 #endif
189 
190 #if defined(IP_DONTFRAG)
191  /*
192  * Ensure that the "don't fragment" flag is zero.
193  */
194  flag = 0;
195 
196  if (setsockopt(fd, IPPROTO_IP, IP_DONTFRAG, &flag, sizeof(flag)) < 0) {
197  fr_strerror_printf("Failed setting IP_DONTFRAG: %s", fr_syserror(errno));
198  return -1;
199  }
200 #endif
201 
202  /*
203  * And set up any UDP / TCP specific information.
204  */
205  if (sock->type == SOCK_DGRAM) return fr_bio_fd_server_udp(fd, sock, cfg);
206 
207  return fr_bio_fd_server_tcp(fd, sock);
208 }
209 
210 /** Initialize an IPv6 server socket.
211  *
212  */
213 static int fr_bio_fd_server_ipv6(int fd, fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
214 {
215 #ifdef IPV6_V6ONLY
216  /*
217  * Don't allow v4 packets on v6 connections.
218  */
219  if (IN6_IS_ADDR_UNSPECIFIED(UNCONST(struct in6_addr *, &sock->inet.src_ipaddr.addr.v6))) {
220  int on = 1;
221 
222  if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof(on)) < 0) {
223  fr_strerror_printf("Failed setting IPV6_ONLY: %s", fr_syserror(errno));
224  return -1;
225  }
226  }
227 #endif /* IPV6_V6ONLY */
228 
229  /*
230  * And set up any UDP / TCP specific information.
231  */
232  if (sock->type == SOCK_DGRAM) return fr_bio_fd_server_udp(fd, sock, cfg);
233 
234  return fr_bio_fd_server_tcp(fd, sock);
235 }
236 
237 /** Verify or clean up a pre-existing domain socket.
238  *
239  */
240 static int fr_bio_fd_socket_unix_verify(int dirfd, char const *filename, fr_bio_fd_config_t const *cfg)
241 {
242  int fd;
243  struct stat buf;
244 
245  /*
246  * See if the socket exits. If there's an error opening it, that's an issue.
247  *
248  * If it doesn't exist, that's fine.
249  */
250  if (fstatat(dirfd, filename, &buf, AT_SYMLINK_NOFOLLOW) < 0) {
251  if (errno != ENOENT) {
252  fr_strerror_printf("Failed opening domain socket %s: %s", cfg->path, fr_syserror(errno));
253  return -1;
254  }
255 
256  return 0;
257  }
258 
259  /*
260  * If it exists, it must be a socket.
261  */
262  if (!S_ISSOCK(buf.st_mode)) {
263  fr_strerror_printf("Failed open domain socket %s: it is not a socket", filename);
264  return -1;
265  }
266 
267  /*
268  * Refuse to open sockets not owned by us. This prevents configurations from stomping on each
269  * other.
270  */
271  if (buf.st_uid != cfg->uid) {
272  fr_strerror_printf("Failed opening domain socket %s: incorrect UID", cfg->path);
273  return -1;
274  }
275 
276  /*
277  * The file exists,and someone is listening. We can't claim it for ourselves.
278  *
279  * Note that this function calls connect(), but connect() always returns immediately for domain
280  * sockets.
281  *
282  * @todo - redo that function here, with separate checks for permission errors vs anything else.
283  */
284  fd = fr_socket_client_unix(cfg->path, false);
285  if (fd >= 0) {
286  close(fd);
287  fr_strerror_printf("Failed creating domain socket %s: It is currently active", cfg->path);
288  return -1;
289  }
290 
291  /*
292  * It exists, but no one is listening. Delete it so that we can re-bind to it.
293  */
294  if (unlinkat(dirfd, filename, 0) < 0) {
295  fr_strerror_printf("Failed removing pre-existing domain socket %s: %s",
296  cfg->path, fr_syserror(errno));
297  return -1;
298  }
299 
300  return 0;
301 }
302 
303 /*
304  * We normally can't call fchmod() or fchown() on sockets, as they don't really exist in the file system.
305  * Instead, we enforce those permissions on the parent directory of the socket.
306  */
307 static int fr_bio_fd_socket_unix_mkdir(int *dirfd, char const **filename, fr_bio_fd_config_t const *cfg)
308 {
309  mode_t perm;
310  int parent_fd, fd;
311  char const *path = cfg->path;
312  char *p, *dir = NULL;
313  char *slashes[2];
314 
315  perm = S_IREAD | S_IWRITE | S_IEXEC;
316  perm |= S_IRGRP | S_IWGRP | S_IXGRP;
317 
318  /*
319  * The parent directory exists. Ensure that it has the correct ownership and permissions.
320  *
321  * If the parent directory exists, then it enforces access, and we can create the domain socket
322  * within it.
323  */
324  if (fr_dirfd(dirfd, filename, path) == 0) {
325  struct stat buf;
326 
327  if (fstat(*dirfd, &buf) < 0) {
328  fr_strerror_printf("Failed reading parent directory for file %s: %s", path, fr_syserror(errno));
329  fail:
330  talloc_free(dir);
331  close(*dirfd);
332  return -1;
333  }
334 
335  if (buf.st_uid != cfg->uid) {
336  fr_strerror_printf("Failed reading parent directory for file %s: Incorrect UID", path);
337  goto fail;
338  }
339 
340  if (buf.st_gid != cfg->gid) {
341  fr_strerror_printf("Failed reading parent directory for file %s: Incorrect GID", path);
342  goto fail;
343  }
344 
345  /*
346  * We don't have the correct permissions on the directory, so we fix them.
347  *
348  * @todo - allow for "other" to read/write if we do authentication on the socket?
349  */
350  if (fchmod(*dirfd, perm) < 0) {
351  fr_strerror_printf("Failed setting parent directory permissions for file %s: %s", path, fr_syserror(errno));
352  goto fail;
353  }
354 
355  return 0;
356  }
357 
358  dir = talloc_strdup(NULL, path);
359  if (!dir) goto fail;
360 
361  /*
362  * Find the last two directory separators.
363  */
364  slashes[0] = slashes[1] = NULL;
365  for (p = dir; *p != '\0'; p++) {
366  if (*p == '/') {
367  slashes[0] = slashes[1];
368  slashes[1] = p;
369  }
370  }
371 
372  /*
373  * There's only one / in the path, we can't do anything.
374  *
375  * Opening 'foo/bar.sock' might be useful, but isn't normally a good idea.
376  */
377  if (!slashes[0]) {
378  fr_strerror_printf("Failed parsing filename %s: it is not absolute", path);
379  goto fail;
380  }
381 
382  /*
383  * Ensure that the grandparent directory exists.
384  *
385  * /var/run/radiusd/foo.sock
386  *
387  * slashes[0] points to the slash after 'run'.
388  *
389  * slashes[1] points to the slash after 'radiusd', which doesn't exist.
390  */
391  *slashes[0] = '\0';
392 
393  /*
394  * If the grandparent doesn't exist, then we don't create it.
395  *
396  * These checks minimize the possibility that a misconfiguration by user "radiusd" can cause a
397  * suid-root binary top create a directory in the wrong place. These checks are only necessary
398  * if the unix domain socket is opened as root.
399  */
400  parent_fd = open(dir, O_DIRECTORY | O_NOFOLLOW);
401  if (parent_fd < 0) {
402  fr_strerror_printf("Failed opening directory %s: %s", dir, fr_syserror(errno));
403  goto fail;
404  }
405 
406  /*
407  * Create the parent directory.
408  */
409  *slashes[0] = '/';
410  *slashes[1] = '\0';
411  if (mkdirat(parent_fd, dir, 0700) < 0) {
412  fr_strerror_printf("Failed creating directory %s: %s", dir, fr_syserror(errno));
413  close_parent:
414  close(parent_fd);
415  goto fail;
416  }
417 
418  fd = openat(parent_fd, dir, O_DIRECTORY);
419  if (fd < 0) {
420  fr_strerror_printf("Failed opening directory %s: %s", dir, fr_syserror(errno));
421  goto close_parent;
422  }
423 
424  if (fchmod(fd, perm) < 0) {
425  fr_strerror_printf("Failed changing permission for directory %s: %s", dir, fr_syserror(errno));
426  close_fd:
427  close(fd);
428  goto close_parent;
429  }
430 
431  /*
432  * This is a NOOP if we're chowning a file owned by ourselves to our own UID / GID.
433  *
434  * Otherwise if we're running as root, it will set ownership to the correct user.
435  */
436  if (fchown(fd, cfg->uid, cfg->gid) < 0) {
437  fr_strerror_printf("Failed changing ownershipt for directory %s: %s", dir, fr_syserror(errno));
438  goto close_fd;
439  }
440 
441  talloc_free(dir);
442  close(fd);
443  close(parent_fd);
444 
445  return 0;
446 }
447 
449 {
450  fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
451 
452  /*
453  * The bio must be open in order to shut it down.
454  *
455  * Unix domain sockets are deleted when the bio is closed.
456  *
457  * Unix domain sockets are never in the "connecting" state, because connect() always returns
458  * immediately.
459  */
460  fr_assert(my->info.state == FR_BIO_FD_STATE_OPEN);
461 
462  /*
463  * Run the user shutdown before we run ours.
464  */
465  if (my->user_shutdown) my->user_shutdown(bio);
466 
467  (void) unlink(my->info.socket.unix.path);
468 }
469 
470 /** Bind to a Unix domain socket.
471  *
472  * @todo - this function only does a tiny bit of what fr_server_domain_socket_peercred() and
473  * fr_server_domain_socket_perm() do. Those functions do a lot more sanity checks.
474  *
475  * The main question is whether or not those checks are useful. In many cases, fchmod() and fchown() are not
476  * possible on Unix sockets, so we shouldn't bother doing them,
477  *
478  * Note that the listeners generally call these functions with wrappers of fr_suid_up() and fr_suid_down().
479  * So these functions are running as "root", and will create files owned as "root".
480  */
482 {
483  int dirfd, rcode;
484  char const *filename, *p;
485  socklen_t sunlen;
486  struct sockaddr_un sun;
487 
488  if (!cfg->path) {
489  fr_strerror_const("Failed to specify path");
490  return -1;
491  }
492 
493  /*
494  * The UID and GID should be taken automatically from the "user" and "group" settings in
495  * mainconfig. There is no reason to set them to anything else.
496  */
497  if (cfg->uid == (uid_t) -1) {
498  fr_strerror_printf("Failed opening domain socket %s: no UID specified", cfg->path);
499  return -1;
500  }
501 
502  if (cfg->gid == (gid_t) -1) {
503  fr_strerror_printf("Failed opening domain socket %s: no GID specified", cfg->path);
504  return -1;
505  }
506 
507  /*
508  * Opening 'foo.sock' is OK.
509  */
510  p = strrchr(cfg->path, '/');
511  if (!p) {
512  dirfd = AT_FDCWD;
513  filename = cfg->path;
514 
515  } else if (p == cfg->path) {
516  /*
517  * Opening '/foo.sock' is dumb.
518  */
519  fr_strerror_printf("Failed opening domain socket %s: cannot exist at file system root", p);
520  return -1;
521 
522  } else if (fr_bio_fd_socket_unix_mkdir(&dirfd, &filename, cfg) < 0) {
523  return -1;
524  }
525 
526  /*
527  * Verify and/or clean up the domain socket.
528  */
529  if (fr_bio_fd_socket_unix_verify(dirfd, filename, cfg) < 0) {
530  fail:
531  if (dirfd != AT_FDCWD) close(dirfd);
532  return -1;
533  }
534 
535 #ifdef HAVE_BINDAT
536  /*
537  * The best function to use here is bindat(), but only quite recent versions of FreeBSD actually
538  * have it, and it's definitely not POSIX.
539  *
540  * If we use bindat(), we pass a relative pathname.
541  */
542  if (fr_filename_to_sockaddr(&sun, &sunlen, filename) < 0) goto fail;
543 
544  rcode = bindat(dirfd, my->info.socket.fd, (struct sockaddr *) &sun, sunlen);
545 #else
546  /*
547  * For bind(), we pass the full path.
548  */
549  if (fr_filename_to_sockaddr(&sun, &sunlen, cfg->path) < 0) goto fail;
550 
551  rcode = bind(my->info.socket.fd, (struct sockaddr *) &sun, sunlen);
552 #endif
553  if (rcode < 0) {
554  /*
555  * @todo - if EADDRINUSE, then the socket exists. Try connect(), and if that fails,
556  * delete the socket and try again. This may be simpler than the checks above.
557  */
558  fr_strerror_printf("Failed binding to domain socket %s: %s", cfg->path, fr_syserror(errno));
559  goto fail;
560  }
561 
562 #ifdef __linux__
563  /*
564  * Linux supports chown && chmod for sockets.
565  */
566  if (fchmod(my->info.socket.fd, S_IREAD | S_IWRITE | S_IEXEC | S_IRGRP | S_IWGRP | S_IXGRP) < 0) {
567  fr_strerror_printf("Failed changing permission for domain socket %s: %s", cfg->path, fr_syserror(errno));
568  goto fail;
569  }
570 
571  /*
572  * This is a NOOP if we're chowning a file owned by ourselves to our own UID / GID.
573  *
574  * Otherwise if we're running as root, it will set ownership to the correct user.
575  */
576  if (fchown(my->info.socket.fd, cfg->uid, cfg->gid) < 0) {
577  fr_strerror_printf("Failed changing ownershipt for domain directory %s: %s", cfg->path, fr_syserror(errno));
578  goto fail;
579  }
580 
581 #endif
582 
583  /*
584  * Socket is open. We need to clean it up on shutdown.
585  */
586  if (my->cb.shutdown) my->user_shutdown = my->cb.shutdown;
587  my->cb.shutdown = fr_bio_fd_unix_shutdown;
588 
589  return 0;
590 }
591 
592 #ifdef SO_BINDTODEVICE
593 /** Linux bind to device by name.
594  *
595  */
597 {
598  /*
599  * ifindex isn't set, do nothing.
600  */
601  if (!my->info.socket.inet.ifindex) return 0;
602 
603  if (!cfg->interface) return 0;
604 
605  /*
606  * The internet hints that CAP_NET_RAW is required to use SO_BINDTODEVICE.
607  *
608  * This function also sets fr_strerror() on failure, which will be seen if the bind fails. If
609  * the bind succeeds, then we don't really care that the capability change has failed. We must
610  * already have that capability.
611  */
612 #ifdef HAVE_CAPABILITY_H
613  (void)fr_cap_enable(CAP_NET_RAW, CAP_EFFECTIVE);
614 #endif
615 
616  if (setsockopt(my->info.socket.fd, SOL_SOCKET, SO_BINDTODEVICE, cfg->interface, strlen(cfg->interface)) < 0) {
617  fr_strerror_printf("Failed setting SO_BINDTODEVICE for %s: %s", cfg->interface, fr_syserror(errno));
618  return -1;
619  }
620 
621  return 0;
622 }
623 
624 #elif defined(IP_BOUND_IF) || defined(IPV6_BOUND_IF)
625 /** *BSD bind to interface by index.
626  *
627  */
629 {
630  int opt, rcode;
631 
632  if (!my->info.socket.inet.ifindex) return 0;
633 
634  opt = my->info.socket.inet.ifindex;
635 
636  switch (my->info.socket.af) {
637  case AF_LOCAL:
638  rcode = setsockopt(my->info.socket.fd, IPPROTO_IP, IP_BOUND_IF, &opt, sizeof(opt));
639  break;
640 
641  case AF_INET6:
642  rcode = setsockopt(my->info.socket.fd, IPPROTO_IPV6, IPV6_BOUND_IF, &opt, sizeof(opt));
643  break;
644 
645  default:
646  rcode = -1;
647  errno = EAFNOSUPPORT;
648  break;
649  }
650 
651  fr_strerror_printf("Failed setting IP_BOUND_IF: %s", fr_syserror(errno));
652  return rcode;
653 }
654 #else
655 
656 /** This system is missing SO_BINDTODEVICE, IP_BOUND_IF, IPV6_BOUND_IF
657  *
658  * @todo - FreeBSD IP_RECVIF and IP_SENDIF
659  *
660  * Except that has to be done in recvmsg() and sendmsg(). And it only works on datagram sockets.
661  *
662  * cmsg_len = sizeof(struct sockaddr_dl)
663  * cmsg_level = IPPROTO_IP
664  * cmsg_type = IP_RECVIF
665  */
667 {
668  fr_strerror_const("Bind to interface is not supported on this platform");
669  return -1;
670 }
671 
672 /* bind to device */
673 #endif
674 
676 {
677  socklen_t salen;
678  struct sockaddr_storage salocal;
679 
680  fr_assert((my->info.socket.af == AF_INET) || (my->info.socket.af == AF_INET6));
681 
682 #ifdef HAVE_CAPABILITY_H
683  /*
684  * If we're binding to a special port as non-root, then
685  * check capabilities. If we're root, we already have
686  * equivalent capabilities so we don't need to check.
687  */
688  if ((my->info.socket.inet.src_port < 1024) && (geteuid() != 0)) {
689  (void)fr_cap_enable(CAP_NET_BIND_SERVICE, CAP_EFFECTIVE);
690  }
691 #endif
692 
693  if (fr_bio_fd_socket_bind_to_device(my, cfg) < 0) return -1;
694 
695  /*
696  * Bind to the IP + interface.
697  */
698  if (fr_ipaddr_to_sockaddr(&salocal, &salen, &my->info.socket.inet.src_ipaddr, my->info.socket.inet.src_port) < 0) return -1;
699 
700  if (bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen) < 0) {
701  fr_strerror_printf("Failed binding to socket: %s", fr_syserror(errno));
702  return -1;
703  }
704 
705  /*
706  * The source IP may have changed, so get the new one.
707  */
708  return fr_bio_fd_socket_name(my);
709 }
710 
711 /** Opens a socket and updates sock->fd
712  *
713  * If the socket is asynchronous, it also calls connect()
714  */
716 {
717  int fd;
718  int rcode;
719  fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
720 
722 
723  my->info = (fr_bio_fd_info_t) {
724  .socket = {
725  .type = cfg->socket_type,
726  },
727  .cfg = cfg,
728  };
729 
730  if (!cfg->path && !cfg->filename) {
731  int protocol;
732 
733  my->info.socket.af = cfg->src_ipaddr.af;
734  my->info.socket.inet.src_ipaddr = cfg->src_ipaddr;
735  my->info.socket.inet.dst_ipaddr = cfg->dst_ipaddr;
736  my->info.socket.inet.src_port = cfg->src_port;
737  my->info.socket.inet.dst_port = cfg->dst_port;
738 
739  if (cfg->socket_type == SOCK_STREAM) {
740  protocol = IPPROTO_TCP;
741  } else {
742  protocol = IPPROTO_UDP;
743  }
744 
745  if (cfg->interface) {
746  my->info.socket.inet.ifindex = if_nametoindex(cfg->interface);
747 
748  if (!my->info.socket.inet.ifindex) {
749  fr_strerror_printf_push("Failed finding interface %s: %s", cfg->interface, fr_syserror(errno));
750  return -1;
751  }
752  }
753 
754  /*
755  * It's already opened, so we don't need to do that.
756  */
757  if (cfg->type == FR_BIO_FD_ACCEPTED) {
758  fd = my->info.socket.fd;
759  fr_assert(fd >= 0);
760 
761  } else {
762  fd = socket(my->info.socket.af, my->info.socket.type, protocol);
763  if (fd < 0) {
764  fr_strerror_printf("Failed opening socket: %s", fr_syserror(errno));
765  return -1;
766  }
767  }
768 
769  } else if (cfg->path) {
770  my->info.socket.af = AF_LOCAL;
771  my->info.socket.type = SOCK_STREAM;
772  my->info.socket.unix.path = cfg->path;
773 
774  fd = socket(my->info.socket.af, my->info.socket.type, 0);
775  if (fd < 0) {
776  fr_strerror_printf("Failed opening domain socket %s: %s", cfg->path, fr_syserror(errno));
777  return -1;
778  }
779 
780  } else {
781  if (cfg->type != FR_BIO_FD_CONNECTED) {
782  fr_strerror_printf("Can only use connected sockets for file IO");
783  return -1;
784  }
785 
786  /*
787  * Filenames overload the #fr_socket_t for now.
788  */
789  my->info.socket.af = AF_FILE_BIO;
790  my->info.socket.type = SOCK_STREAM;
791  my->info.socket.unix.path = cfg->filename;
792 
793  /*
794  * Allow hacks for stdout and stderr
795  */
796  if (strcmp(cfg->filename, "/dev/stdout") == 0) {
797  if (cfg->flags != O_WRONLY) {
798  fail_dev:
799  fr_strerror_printf("Cannot read from %s", cfg->filename);
800  return -1;
801  }
802 
803  fd = dup(STDOUT_FILENO);
804 
805  } else if (strcmp(cfg->filename, "/dev/stderr") == 0) {
806  if (cfg->flags != O_WRONLY) goto fail_dev;
807 
808  fd = dup(STDERR_FILENO);
809 
810  } else {
811  /*
812  * Minor hacks so that we have only _one_ source of open / mkdir
813  */
814  my->info.socket.fd = -1;
815 
816  fd = fr_bio_fd_reopen(bio);
817  }
818  if (fd < 0) {
819  fr_strerror_printf("Failed opening file %s: %s", cfg->filename, fr_syserror(errno));
820  return -1;
821  }
822  }
823 
824  /*
825  * Set it to be non-blocking if required.
826  */
827  if (cfg->async && (fr_nonblock(fd) < 0)) {
828  fr_strerror_printf("Failed opening setting O_NONBLOCK: %s", fr_syserror(errno));
829 
830  fail:
831  my->info.socket = (fr_socket_t) {
832  .fd = -1,
833  };
834  my->info.state = FR_BIO_FD_STATE_CLOSED;
835  my->info.cfg = NULL;
836  close(fd);
837  return -1;
838  }
839 
840 #ifdef FD_CLOEXEC
841  /*
842  * We don't want child processes inheriting these file descriptors.
843  */
844  rcode = fcntl(fd, F_GETFD);
845  if (rcode >= 0) {
846  if (fcntl(fd, F_SETFD, rcode | FD_CLOEXEC) < 0) {
847  fr_strerror_printf("Failed opening setting FD_CLOEXE: %s", fr_syserror(errno));
848  goto fail;
849  }
850  }
851 #endif
852 
853  /*
854  * Initialize the bio information before calling the various setup functions.
855  */
856  my->info.state = FR_BIO_FD_STATE_CONNECTING;
857 
858  /*
859  * Set the FD so that the subsequent calls can use it.
860  */
861  my->info.socket.fd = fd;
862 
863  /*
864  * Set the type, too.
865  */
866  my->info.type = cfg->type;
867 
868  /*
869  * Do sanity checks, bootstrap common socket options, bind to the socket, and initialize the read
870  * / write functions.
871  */
872  switch (cfg->type) {
873  /*
874  * Unconnected UDP or datagram AF_LOCAL server sockets.
875  */
877  if (my->info.socket.type != SOCK_DGRAM) {
878  fr_strerror_const("Failed configuring socket: unconnected sockets must be UDP");
879  return -1;
880  }
881 
882  switch (my->info.socket.af) {
883  case AF_LOCAL:
884  if (fr_bio_fd_common_datagram(fd, &my->info.socket, cfg) < 0) goto fail;
885  break;
886 
887  case AF_FILE_BIO:
888  fr_strerror_const("Filenames must use the connected API");
889  goto fail;
890 
891  case AF_INET:
892  case AF_INET6:
893  /* sets SO_REUSEPORT, too */
894  if (fr_bio_fd_server_udp(fd, &my->info.socket, cfg) < 0) goto fail;
895  break;
896 
897  default:
898  fr_strerror_const("Unsupported address family for unconnected sockets");
899  goto fail;
900  }
901 
902  if (fr_bio_fd_socket_bind(my, cfg) < 0) goto fail;
903 
904  if (fr_bio_fd_init_common(my) < 0) goto fail;
905  break;
906 
907  /*
908  * A connected client: UDP, TCP, AF_LOCAL, or AF_FILE_BIO
909  */
910  case FR_BIO_FD_CONNECTED:
911  if (my->info.socket.type == SOCK_DGRAM) {
912  rcode = fr_bio_fd_common_datagram(fd, &my->info.socket, cfg); /* we don't use SO_REUSEPORT for clients */
913  if (rcode < 0) goto fail;
914 
915  } else if ((my->info.socket.af == AF_INET) || (my->info.socket.af == AF_INET6)) {
916  rcode = fr_bio_fd_common_tcp(fd, &my->info.socket, cfg);
917  if (rcode < 0) goto fail;
918  }
919 
920  switch (my->info.socket.af) {
921  case AF_LOCAL:
922  if (fr_bio_fd_socket_bind_unix(my, cfg) < 0) goto fail;
923  break;
924 
925  case AF_FILE_BIO:
926  break;
927 
928  case AF_INET:
929  case AF_INET6:
930  if (fr_bio_fd_socket_bind(my, cfg) < 0) goto fail;
931  break;
932 
933  default:
934  return -1;
935  }
936 
937  if (fr_bio_fd_init_connected(my) < 0) goto fail;
938  break;
939 
940  case FR_BIO_FD_ACCEPTED:
941 #ifdef SO_NOSIGPIPE
942  /*
943  * Although the server ignore SIGPIPE, some operating systems like BSD and OSX ignore the
944  * ignoring.
945  *
946  * Fortunately, those operating systems usually support SO_NOSIGPIPE. We set that to prevent
947  * them raising the signal in the first place.
948  */
949  {
950  int on = 1;
951 
952  setsockopt(my->info.socket.fd, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
953  }
954 #endif
955 
956  my->info.type = FR_BIO_FD_CONNECTED;
957 
958  if (fr_bio_fd_init_common(my) < 0) goto fail;
959  break;
960 
961  /*
962  * Server socket which listens for new stream connections
963  */
964  case FR_BIO_FD_LISTEN:
965  fr_assert(my->info.socket.type == SOCK_STREAM);
966 
967  switch (my->info.socket.af) {
968  case AF_INET:
969  if (fr_bio_fd_server_ipv4(fd, &my->info.socket, cfg) < 0) goto fail;
970 
971  if (fr_bio_fd_socket_bind(my, cfg) < 0) goto fail;
972  break;
973 
974  case AF_INET6:
975  if (fr_bio_fd_server_ipv6(fd, &my->info.socket, cfg) < 0) goto fail;
976 
977  if (fr_bio_fd_socket_bind(my, cfg) < 0) goto fail;
978  break;
979 
980  case AF_LOCAL:
981  if (fr_bio_fd_socket_bind_unix(my, cfg) < 0) goto fail;
982  break;
983 
984  default:
985  fr_strerror_const("Unsupported address family for accept() socket");
986  goto fail;
987  }
988 
989  if (fr_bio_fd_init_listen(my) < 0) goto fail;
990  break;
991  }
992 
993  return 0;
994 }
995 
996 /** Reopen a file BIO
997  *
998  * e.g. for log files.
999  */
1001 {
1002  fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
1003  fr_bio_fd_config_t const *cfg = my->info.cfg;
1004  int fd;
1005 
1006  if (my->info.socket.af != AF_FILE_BIO) {
1007  fr_strerror_const("Cannot reopen a non-file BIO");
1008  return -1;
1009  }
1010 
1011  if (!cfg->mkdir) {
1012  fd = open(cfg->filename, cfg->flags, cfg->perm);
1013  if (fd < 0) {
1014  fr_strerror_printf("Failed opening file %s: %s", cfg->filename, fr_syserror(errno));
1015  return -1;
1016  }
1017 
1018  } else if (fr_mkdir(&fd, cfg->filename, strlen(cfg->filename), cfg->perm,
1020  .uid = cfg->uid,
1021  .gid = cfg->gid,
1022  }) < 0) {
1023  return -1;
1024  }
1025 
1026  /*
1027  * We're boot-strapping, just set the new FD and return.
1028  */
1029  if (my->info.socket.fd < 0) {
1030  return fd;
1031  }
1032 
1033  /*
1034  * Replace the FD rather than swapping it out with a new one. This is potentially more
1035  * thread-safe.
1036  */
1037  if (dup2(fd, my->info.socket.fd) < 0) {
1038  close(fd);
1039  fr_strerror_printf("Failed reopening file - %s", fr_syserror(errno));
1040  return -1;
1041  }
1042 
1043  close(fd);
1044  return my->info.socket.fd;
1045 }
Definition: base.h:112
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define UNUSED
Definition: build.h:313
int fr_bio_fd_socket_name(fr_bio_fd_t *my)
Definition: fd.c:630
int fr_bio_fd_init_connected(fr_bio_fd_t *my)
Definition: fd.c:783
int fr_filename_to_sockaddr(struct sockaddr_un *sun, socklen_t *sunlen, char const *filename)
Definition: fd.c:612
int fr_bio_fd_init_listen(fr_bio_fd_t *my)
Definition: fd.c:964
int fr_bio_fd_init_common(fr_bio_fd_t *my)
Definition: fd.c:846
uint16_t src_port
our port
Definition: fd.h:86
@ FR_BIO_FD_ACCEPTED
temporarily until it's connected.
Definition: fd.h:68
@ FR_BIO_FD_CONNECTED
connected client sockets (UDP or TCP)
Definition: fd.h:65
@ FR_BIO_FD_UNCONNECTED
unconnected UDP / datagram only
Definition: fd.h:62
@ FR_BIO_FD_LISTEN
returns new fd in buffer on fr_bio_read() or fr_bio_fd_accept()
Definition: fd.h:66
uint32_t recv_buff
How big the kernel's receive buffer should be.
Definition: fd.h:91
bool mkdir
make intermediate directories
Definition: fd.h:98
@ FR_BIO_FD_STATE_CLOSED
Definition: fd.h:56
@ FR_BIO_FD_STATE_CONNECTING
Definition: fd.h:58
@ FR_BIO_FD_STATE_OPEN
error states must be before this
Definition: fd.h:57
fr_ipaddr_t dst_ipaddr
their IP address
Definition: fd.h:84
fr_bio_fd_type_t type
accept, connected, unconnected, etc.
Definition: fd.h:79
char const * path
for Unix domain sockets
Definition: fd.h:94
uint32_t send_buff
How big the kernel's send buffer should be.
Definition: fd.h:92
char const * filename
for files
Definition: fd.h:100
uid_t uid
who owns the socket
Definition: fd.h:96
gid_t gid
who owns the socket
Definition: fd.h:97
char const * interface
for binding to an interface
Definition: fd.h:89
mode_t perm
permissions for domain sockets
Definition: fd.h:95
#define AF_FILE_BIO
Definition: fd.h:38
int socket_type
SOCK_STREAM or SOCK_DGRAM.
Definition: fd.h:81
uint16_t dst_port
their port
Definition: fd.h:87
bool tcp_delay
We do tcp_nodelay by default.
Definition: fd.h:104
bool async
is it async
Definition: fd.h:103
int flags
O_RDONLY, etc.
Definition: fd.h:101
fr_ipaddr_t src_ipaddr
our IP address
Definition: fd.h:83
Configuration for sockets.
Definition: fd.h:78
Run-time status of the socket.
Definition: fd.h:110
fr_bio_shutdown & my
Definition: fd_errno.h:59
static int fr_bio_fd_socket_unix_mkdir(int *dirfd, char const **filename, fr_bio_fd_config_t const *cfg)
Definition: fd_open.c:307
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:240
static void fr_bio_fd_unix_shutdown(fr_bio_t *bio)
Definition: fd_open.c:448
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:213
int fr_bio_fd_reopen(fr_bio_t *bio)
Reopen a file BIO.
Definition: fd_open.c:1000
static int fr_bio_fd_server_tcp(int fd, UNUSED fr_socket_t const *sock)
Initialize a TCP server socket.
Definition: fd_open.c:158
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:481
static int fr_bio_fd_socket_bind(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Definition: fd_open.c:675
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:173
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:38
static int fr_bio_fd_socket_bind_to_device(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
This system is missing SO_BINDTODEVICE, IP_BOUND_IF, IPV6_BOUND_IF.
Definition: fd_open.c:666
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:715
static int fr_bio_fd_server_udp(int fd, fr_socket_t const *sock, fr_bio_fd_config_t const *cfg)
Initialize a UDP server socket.
Definition: fd_open.c:137
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:82
Our FD bio structure.
Definition: fd_priv.h:35
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
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:1392
int af
Address family.
Definition: inet.h:64
talloc_free(reap)
unsigned int mode_t
Definition: merged_model.c:21
int fr_nonblock(UNUSED int fd)
Definition: misc.c:293
#define FD_CLOEXEC
int fr_socket_client_unix(UNUSED char const *path, UNUSED bool async)
Definition: socket.c:564
fr_assert(0)
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: syserror.c:243
close(uq->fd)
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:577
#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