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: aae05ac7a8938e06de8d256e6acc9a29d83c2834 $
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  */
461 
462  /*
463  * Run the user shutdown before we run ours.
464  */
465  if (my->user_shutdown) {
466  if (my->user_shutdown(bio) < 0) return -1;
467  }
468 
469  return unlink(my->info.socket.unix.path);
470 }
471 
472 /** Bind to a Unix domain socket.
473  *
474  * @todo - this function only does a tiny bit of what fr_server_domain_socket_peercred() and
475  * fr_server_domain_socket_perm() do. Those functions do a lot more sanity checks.
476  *
477  * The main question is whether or not those checks are useful. In many cases, fchmod() and fchown() are not
478  * possible on Unix sockets, so we shouldn't bother doing them,
479  *
480  * Note that the listeners generally call these functions with wrappers of fr_suid_up() and fr_suid_down().
481  * So these functions are running as "root", and will create files owned as "root".
482  */
484 {
485  int dirfd, rcode;
486  char const *filename, *p;
487  socklen_t sunlen;
488  struct sockaddr_un sun;
489 
490  if (!cfg->path) {
491  fr_strerror_const("Failed to specify path");
492  return -1;
493  }
494 
495  /*
496  * The UID and GID should be taken automatically from the "user" and "group" settings in
497  * mainconfig. There is no reason to set them to anything else.
498  */
499  if (cfg->uid == (uid_t) -1) {
500  fr_strerror_printf("Failed opening domain socket %s: no UID specified", cfg->path);
501  return -1;
502  }
503 
504  if (cfg->gid == (gid_t) -1) {
505  fr_strerror_printf("Failed opening domain socket %s: no GID specified", cfg->path);
506  return -1;
507  }
508 
509  /*
510  * Opening 'foo.sock' is OK.
511  */
512  p = strrchr(cfg->path, '/');
513  if (!p) {
514  dirfd = AT_FDCWD;
515  filename = cfg->path;
516 
517  } else if (p == cfg->path) {
518  /*
519  * Opening '/foo.sock' is dumb.
520  */
521  fr_strerror_printf("Failed opening domain socket %s: cannot exist at file system root", p);
522  return -1;
523 
524  } else if (fr_bio_fd_socket_unix_mkdir(&dirfd, &filename, cfg) < 0) {
525  return -1;
526  }
527 
528  /*
529  * Verify and/or clean up the domain socket.
530  */
531  if (fr_bio_fd_socket_unix_verify(dirfd, filename, cfg) < 0) {
532  fail:
533  if (dirfd != AT_FDCWD) close(dirfd);
534  return -1;
535  }
536 
537 #ifdef HAVE_BINDAT
538  /*
539  * The best function to use here is bindat(), but only quite recent versions of FreeBSD actually
540  * have it, and it's definitely not POSIX.
541  *
542  * If we use bindat(), we pass a relative pathname.
543  */
544  if (fr_filename_to_sockaddr(&sun, &sunlen, filename) < 0) goto fail;
545 
546  rcode = bindat(dirfd, my->info.socket.fd, (struct sockaddr *) &sun, sunlen);
547 #else
548  /*
549  * For bind(), we pass the full path.
550  */
551  if (fr_filename_to_sockaddr(&sun, &sunlen, cfg->path) < 0) goto fail;
552 
553  rcode = bind(my->info.socket.fd, (struct sockaddr *) &sun, sunlen);
554 #endif
555  if (rcode < 0) {
556  /*
557  * @todo - if EADDRINUSE, then the socket exists. Try connect(), and if that fails,
558  * delete the socket and try again. This may be simpler than the checks above.
559  */
560  fr_strerror_printf("Failed binding to domain socket %s: %s", cfg->path, fr_syserror(errno));
561  goto fail;
562  }
563 
564 #ifdef __linux__
565  /*
566  * Linux supports chown && chmod for sockets.
567  */
568  if (fchmod(my->info.socket.fd, S_IREAD | S_IWRITE | S_IEXEC | S_IRGRP | S_IWGRP | S_IXGRP) < 0) {
569  fr_strerror_printf("Failed changing permission for domain socket %s: %s", cfg->path, fr_syserror(errno));
570  goto fail;
571  }
572 
573  /*
574  * This is a NOOP if we're chowning a file owned by ourselves to our own UID / GID.
575  *
576  * Otherwise if we're running as root, it will set ownership to the correct user.
577  */
578  if (fchown(my->info.socket.fd, cfg->uid, cfg->gid) < 0) {
579  fr_strerror_printf("Failed changing ownershipt for domain directory %s: %s", cfg->path, fr_syserror(errno));
580  goto fail;
581  }
582 
583 #endif
584 
585  /*
586  * Socket is open. We need to clean it up on shutdown.
587  */
588  if (my->cb.shutdown) my->user_shutdown = my->cb.shutdown;
589  my->cb.shutdown = fr_bio_fd_unix_shutdown;
590 
591  return 0;
592 }
593 
594 #ifdef SO_BINDTODEVICE
595 /** Linux bind to device by name.
596  *
597  */
599 {
600  /*
601  * ifindex isn't set, do nothing.
602  */
603  if (!my->info.socket.inet.ifindex) return 0;
604 
605  if (!cfg->interface) return 0;
606 
607  /*
608  * The internet hints that CAP_NET_RAW is required to use SO_BINDTODEVICE.
609  *
610  * This function also sets fr_strerror() on failure, which will be seen if the bind fails. If
611  * the bind succeeds, then we don't really care that the capability change has failed. We must
612  * already have that capability.
613  */
614 #ifdef HAVE_CAPABILITY_H
615  (void)fr_cap_enable(CAP_NET_RAW, CAP_EFFECTIVE);
616 #endif
617 
618  if (setsockopt(my->info.socket.fd, SOL_SOCKET, SO_BINDTODEVICE, cfg->interface, strlen(cfg->interface)) < 0) {
619  fr_strerror_printf("Failed setting SO_BINDTODEVICE for %s: %s", cfg->interface, fr_syserror(errno));
620  return -1;
621  }
622 
623  return 0;
624 }
625 
626 #elif defined(IP_BOUND_IF) || defined(IPV6_BOUND_IF)
627 /** *BSD bind to interface by index.
628  *
629  */
631 {
632  int opt, rcode;
633 
634  if (!my->info.socket.inet.ifindex) return 0;
635 
636  opt = my->info.socket.inet.ifindex;
637 
638  switch (my->info.socket.af) {
639  case AF_LOCAL:
640  rcode = setsockopt(my->info.socket.fd, IPPROTO_IP, IP_BOUND_IF, &opt, sizeof(opt));
641  break;
642 
643  case AF_INET6:
644  rcode = setsockopt(my->info.socket.fd, IPPROTO_IPV6, IPV6_BOUND_IF, &opt, sizeof(opt));
645  break;
646 
647  default:
648  rcode = -1;
649  errno = EAFNOSUPPORT;
650  break;
651  }
652 
653  fr_strerror_printf("Failed setting IP_BOUND_IF: %s", fr_syserror(errno));
654  return rcode;
655 }
656 #else
657 
658 /** This system is missing SO_BINDTODEVICE, IP_BOUND_IF, IPV6_BOUND_IF
659  *
660  * @todo - FreeBSD IP_RECVIF and IP_SENDIF
661  *
662  * Except that has to be done in recvmsg() and sendmsg(). And it only works on datagram sockets.
663  *
664  * cmsg_len = sizeof(struct sockaddr_dl)
665  * cmsg_level = IPPROTO_IP
666  * cmsg_type = IP_RECVIF
667  */
669 {
670  fr_strerror_const("Bind to interface is not supported on this platform");
671  return -1;
672 }
673 
674 /* bind to device */
675 #endif
676 
678 {
679  socklen_t salen;
680  struct sockaddr_storage salocal;
681 
682  fr_assert((my->info.socket.af == AF_INET) || (my->info.socket.af == AF_INET6));
683 
684 #ifdef HAVE_CAPABILITY_H
685  /*
686  * If we're binding to a special port as non-root, then
687  * check capabilities. If we're root, we already have
688  * equivalent capabilities so we don't need to check.
689  */
690  if ((my->info.socket.inet.src_port < 1024) && (geteuid() != 0)) {
691  (void)fr_cap_enable(CAP_NET_BIND_SERVICE, CAP_EFFECTIVE);
692  }
693 #endif
694 
695  if (fr_bio_fd_socket_bind_to_device(my, cfg) < 0) return -1;
696 
697  /*
698  * Bind to the IP + interface.
699  */
700  if (fr_ipaddr_to_sockaddr(&salocal, &salen, &my->info.socket.inet.src_ipaddr, my->info.socket.inet.src_port) < 0) return -1;
701 
702  if (bind(my->info.socket.fd, (struct sockaddr *) &salocal, salen) < 0) {
703  fr_strerror_printf("Failed binding to socket: %s", fr_syserror(errno));
704  return -1;
705  }
706 
707  /*
708  * The source IP may have changed, so get the new one.
709  */
710  return fr_bio_fd_socket_name(my);
711 }
712 
713 /** Opens a socket and updates sock->fd
714  *
715  * If the socket is asynchronous, it also calls connect()
716  */
718 {
719  int fd;
720  int rcode;
721  fr_bio_fd_t *my = talloc_get_type_abort(bio, fr_bio_fd_t);
722 
724 
725  my->info = (fr_bio_fd_info_t) {
726  .socket = {
727  .type = cfg->socket_type,
728  },
729  .cfg = cfg,
730  };
731 
732  if (!cfg->path && !cfg->filename) {
733  int protocol;
734 
735  my->info.socket.af = cfg->src_ipaddr.af;
736  my->info.socket.inet.src_ipaddr = cfg->src_ipaddr;
737  my->info.socket.inet.dst_ipaddr = cfg->dst_ipaddr;
738  my->info.socket.inet.src_port = cfg->src_port;
739  my->info.socket.inet.dst_port = cfg->dst_port;
740 
741  if (cfg->socket_type == SOCK_STREAM) {
742  protocol = IPPROTO_TCP;
743  } else {
744  protocol = IPPROTO_UDP;
745  }
746 
747  if (cfg->interface) {
748  my->info.socket.inet.ifindex = if_nametoindex(cfg->interface);
749 
750  if (!my->info.socket.inet.ifindex) {
751  fr_strerror_printf_push("Failed finding interface %s: %s", cfg->interface, fr_syserror(errno));
752  return -1;
753  }
754  }
755 
756  fd = socket(my->info.socket.af, my->info.socket.type, protocol);
757  if (fd < 0) {
758  fr_strerror_printf("Failed opening socket: %s", fr_syserror(errno));
759  return -1;
760  }
761 
762  } else if (cfg->path) {
763  my->info.socket.af = AF_LOCAL;
764  my->info.socket.type = SOCK_STREAM;
765  my->info.socket.unix.path = cfg->path;
766 
767  fd = socket(my->info.socket.af, my->info.socket.type, 0);
768  if (fd < 0) {
769  fr_strerror_printf("Failed opening domain socket %s: %s", cfg->path, fr_syserror(errno));
770  return -1;
771  }
772 
773  } else {
774  if (cfg->type != FR_BIO_FD_CONNECTED) {
775  fr_strerror_printf("Can only use connected sockets for file IO");
776  return -1;
777  }
778 
779  /*
780  * Filenames overload the #fr_socket_t for now.
781  */
782  my->info.socket.af = AF_FILE_BIO;
783  my->info.socket.type = SOCK_STREAM;
784  my->info.socket.unix.path = cfg->filename;
785 
786  /*
787  * Allow hacks for stdout and stderr
788  */
789  if (strcmp(cfg->filename, "/dev/stdout") == 0) {
790  if (cfg->flags != O_WRONLY) {
791  fail_dev:
792  fr_strerror_printf("Cannot read from %s", cfg->filename);
793  return -1;
794  }
795 
796  fd = dup(STDOUT_FILENO);
797 
798  } else if (strcmp(cfg->filename, "/dev/stderr") == 0) {
799  if (cfg->flags != O_WRONLY) goto fail_dev;
800 
801  fd = dup(STDERR_FILENO);
802 
803  } else {
804  fd = open(cfg->filename, cfg->flags, cfg->perm);
805  }
806  if (fd < 0) {
807  fr_strerror_printf("Failed opening file %s: %s", cfg->filename, fr_syserror(errno));
808  return -1;
809  }
810  }
811 
812  /*
813  * Set it to be non-blocking if required.
814  */
815  if (cfg->async && (fr_nonblock(fd) < 0)) {
816  fr_strerror_printf("Failed opening setting O_NONBLOCK: %s", fr_syserror(errno));
817 
818  fail:
819  my->info.socket = (fr_socket_t) {
820  .fd = -1,
821  };
823  my->info.cfg = NULL;
824  close(fd);
825  return -1;
826  }
827 
828 #ifdef FD_CLOEXEC
829  /*
830  * We don't want child processes inheriting these file descriptors.
831  */
832  rcode = fcntl(fd, F_GETFD);
833  if (rcode >= 0) {
834  if (fcntl(fd, F_SETFD, rcode | FD_CLOEXEC) < 0) {
835  fr_strerror_printf("Failed opening setting FD_CLOEXE: %s", fr_syserror(errno));
836  goto fail;
837  }
838  }
839 #endif
840 
841  /*
842  * Initialize the bio information before calling the various setup functions.
843  */
845 
846  /*
847  * Set the FD so that the subsequent calls can use it.
848  */
849  my->info.socket.fd = fd;
850 
851  /*
852  * Set the type, too.
853  */
854  my->info.type = cfg->type;
855 
856  /*
857  * Do sanity checks, bootstrap common socket options, bind to the socket, and initialize the read
858  * / write functions.
859  */
860  switch (cfg->type) {
861  /*
862  * Unconnected UDP or datagram AF_LOCAL server sockets.
863  */
865  if (my->info.socket.type != SOCK_DGRAM) {
866  fr_strerror_const("Failed configuring socket: unconnected sockets must be UDP");
867  return -1;
868  }
869 
870  switch (my->info.socket.af) {
871  case AF_LOCAL:
872  if (fr_bio_fd_common_datagram(fd, &my->info.socket, cfg) < 0) goto fail;
873  break;
874 
875  case AF_FILE_BIO:
876  fr_strerror_const("Filenames must use the connected API");
877  goto fail;
878 
879  case AF_INET:
880  case AF_INET6:
881  /* sets SO_REUSEPORT, too */
882  if (fr_bio_fd_server_udp(fd, &my->info.socket, cfg) < 0) goto fail;
883  break;
884 
885  default:
886  fr_strerror_const("Unsupported address family for unconnected sockets");
887  goto fail;
888  }
889 
890  if (fr_bio_fd_socket_bind(my, cfg) < 0) goto fail;
891 
892  if (fr_bio_fd_init_common(my) < 0) goto fail;
893  break;
894 
895  /*
896  * A connected client: UDP, TCP, AF_LOCAL, or AF_FILE_BIO
897  */
898  case FR_BIO_FD_CONNECTED:
899  if (my->info.socket.type == SOCK_DGRAM) {
900  rcode = fr_bio_fd_common_datagram(fd, &my->info.socket, cfg); /* we don't use SO_REUSEPORT for clients */
901  if (rcode < 0) goto fail;
902 
903  } else if ((my->info.socket.af == AF_INET) || (my->info.socket.af == AF_INET6)) {
904  rcode = fr_bio_fd_common_tcp(fd, &my->info.socket, cfg);
905  if (rcode < 0) goto fail;
906  }
907 
908  switch (my->info.socket.af) {
909  case AF_LOCAL:
910  if (fr_bio_fd_socket_bind_unix(my, cfg) < 0) goto fail;
911  break;
912 
913  case AF_FILE_BIO:
914  break;
915 
916  case AF_INET:
917  case AF_INET6:
918  if (fr_bio_fd_socket_bind(my, cfg) < 0) goto fail;
919  break;
920 
921  default:
922  return -1;
923  }
924 
925  if (fr_bio_fd_init_connected(my) < 0) goto fail;
926  break;
927 
928  /*
929  * Server socket which listens for new stream connections
930  */
931  case FR_BIO_FD_ACCEPT:
932  fr_assert(my->info.socket.type == SOCK_STREAM);
933 
934  switch (my->info.socket.af) {
935  case AF_INET:
936  if (fr_bio_fd_server_ipv4(fd, &my->info.socket, cfg) < 0) goto fail;
937 
938  if (fr_bio_fd_socket_bind(my, cfg) < 0) goto fail;
939  break;
940 
941  case AF_INET6:
942  if (fr_bio_fd_server_ipv6(fd, &my->info.socket, cfg) < 0) goto fail;
943 
944  if (fr_bio_fd_socket_bind(my, cfg) < 0) goto fail;
945  break;
946 
947  case AF_LOCAL:
948  if (fr_bio_fd_socket_bind_unix(my, cfg) < 0) goto fail;
949  break;
950 
951  default:
952  fr_strerror_const("Unsupported address family for accept() socket");
953  goto fail;
954  }
955 
956  if (fr_bio_fd_init_accept(my) < 0) goto fail;
957  break;
958  }
959 
960  return 0;
961 }
Definition: base.h:103
#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:646
int fr_bio_fd_init_accept(fr_bio_fd_t *my)
Definition: fd.c:966
int fr_bio_fd_init_connected(fr_bio_fd_t *my)
Definition: fd.c:782
int fr_filename_to_sockaddr(struct sockaddr_un *sun, socklen_t *sunlen, char const *filename)
Definition: fd.c:628
int fr_bio_fd_init_common(fr_bio_fd_t *my)
Definition: fd.c:845
fr_socket_t socket
as connected socket
Definition: fd.h:108
uint16_t src_port
our port
Definition: fd.h:84
@ FR_BIO_FD_CONNECTED
connected client sockets (UDP or TCP)
Definition: fd.h:64
@ FR_BIO_FD_UNCONNECTED
unconnected UDP / datagram only
Definition: fd.h:61
@ FR_BIO_FD_ACCEPT
returns new fd in buffer on fr_bio_read()
Definition: fd.h:65
fr_bio_fd_type_t type
type of the socket
Definition: fd.h:110
fr_bio_fd_state_t state
connecting, open, closed, etc.
Definition: fd.h:112
uint32_t recv_buff
How big the kernel's receive buffer should be.
Definition: fd.h:89
@ FR_BIO_FD_STATE_CLOSED
Definition: fd.h:55
@ FR_BIO_FD_STATE_CONNECTING
Definition: fd.h:57
@ FR_BIO_FD_STATE_OPEN
error states must be before this
Definition: fd.h:56
fr_ipaddr_t dst_ipaddr
their IP address
Definition: fd.h:82
fr_bio_fd_type_t type
accept, connected, unconnected, etc.
Definition: fd.h:77
char const * path
for Unix domain sockets
Definition: fd.h:92
uint32_t send_buff
How big the kernel's send buffer should be.
Definition: fd.h:90
char const * filename
for files
Definition: fd.h:97
uid_t uid
who owns the socket
Definition: fd.h:94
fr_bio_fd_config_t const * cfg
so we know what was asked, vs what was granted.
Definition: fd.h:118
gid_t gid
who owns the socket
Definition: fd.h:95
char const * interface
for binding to an interface
Definition: fd.h:87
mode_t perm
permissions for domain sockets
Definition: fd.h:93
#define AF_FILE_BIO
Definition: fd.h:37
int socket_type
SOCK_STREAM or SOCK_DGRAM.
Definition: fd.h:79
uint16_t dst_port
their port
Definition: fd.h:85
bool tcp_delay
We do tcp_nodelay by default.
Definition: fd.h:101
bool async
is it async
Definition: fd.h:100
int flags
O_RDONLY, etc.
Definition: fd.h:98
fr_ipaddr_t src_ipaddr
our IP address
Definition: fd.h:81
Configuration for sockets.
Definition: fd.h:76
Run-time status of the socket.
Definition: fd.h:107
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 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
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:483
static int fr_bio_fd_socket_bind(fr_bio_fd_t *my, fr_bio_fd_config_t const *cfg)
Definition: fd_open.c:677
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_unix_shutdown(fr_bio_t *bio)
Definition: fd_open.c:448
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:668
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:717
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
fr_bio_callback_t user_shutdown
user shutdown
Definition: fd_priv.h:37
fr_bio_fd_info_t info
Definition: fd_priv.h:39
Our FD bio structure.
Definition: fd_priv.h:35
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:393
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:1378
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:284
static fr_bio_t * bio
Definition: radclient-ng.c:86
#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 af
AF_INET, AF_INET6, or AF_UNIX.
Definition: socket.h:78
int fd
File descriptor if this is a live socket.
Definition: socket.h:81
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