25#include <freeradius-devel/bio/fd_priv.h>
26#include <freeradius-devel/bio/fd_read.h>
27#include <freeradius-devel/bio/fd_write.h>
28#include <freeradius-devel/bio/null.h>
35# define SOL_IP IPPROTO_IP
49# ifdef IPV6_RECVPKTINFO
50# include <linux/version.h>
51# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)
52# ifdef IPV6_2292PKTINFO
53# undef IPV6_RECVPKTINFO
55# define IPV6_RECVPKTINFO IPV6_2292PKTINFO
56# define IPV6_PKTINFO IPV6_2292PKTINFO
60# elif defined(IPV6_2292PKTINFO)
61# define IPV6_RECVPKTINFO IPV6_2292PKTINFO
74# if !defined(IPV6_RECVPKTINFO) && defined(IPV6_PKTINFO)
75# define IPV6_RECVPKTINFO IPV6_PKTINFO
81# elif !defined(IPV6_PKTINFO)
82# undef IPV6_RECVPKTINFO
86#define ADDR_INIT do { \
87 addr->when = fr_time(); \
88 addr->socket.type = my->info.socket.type; \
89 addr->socket.fd = -1; \
90 addr->socket.inet.ifindex = my->info.socket.inet.ifindex; \
110 if (rcode < 0)
return rcode;
182 if (rcode < 0)
return rcode;
204 struct sockaddr_storage sockaddr;
206#ifdef STATIC_ANALYZER
207 sockaddr = (
struct sockaddr_storage) {};
211 salen =
sizeof(sockaddr);
213 rcode = recvfrom(my->
info.
socket.
fd,
buffer, size, 0, (
struct sockaddr *) &sockaddr, &salen);
217 fr_assert((sockaddr.ss_family == AF_INET) || (sockaddr.ss_family == AF_INET6));
223 if (rcode == 0)
return rcode;
237 struct sockaddr_storage sockaddr;
240 salen =
sizeof(sockaddr);
242 rcode = recvfrom(my->
info.
socket.
fd,
buffer, size, 0, (
struct sockaddr *) &sockaddr, &salen);
255 if (rcode == 0)
return rcode;
307 struct sockaddr_storage sockaddr;
318 rcode = sendto(my->
info.
socket.
fd,
buffer, size, 0, (
struct sockaddr *) &sockaddr, salen);
327#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR) || defined(IPV6_PKTINFO)
332 struct sockaddr_storage from;
335#ifdef STATIC_ANALYZER
336 from.ss_family = AF_UNSPEC;
339 memset(&my->cbuf, 0,
sizeof(my->cbuf));
340 memset(&my->msgh, 0,
sizeof(
struct msghdr));
342 my->iov = (
struct iovec) {
347 my->msgh = (
struct msghdr) {
348 .msg_control = my->cbuf,
349 .msg_controllen =
sizeof(my->cbuf),
351 .msg_namelen =
sizeof(from),
360 if ((my->msgh.msg_flags & MSG_TRUNC) != 0)
return 0;
361 if ((my->msgh.msg_flags & MSG_CTRUNC) != 0)
return 0;
366 &from, my->msgh.msg_namelen);
369 if (rcode == 0)
return rcode;
376#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR)
383 struct cmsghdr *cmsg;
389 if (rcode <= 0)
return rcode;
393 for (cmsg = CMSG_FIRSTHDR(&my->msgh);
395 cmsg = CMSG_NXTHDR(&my->msgh, cmsg)) {
399 if ((cmsg->cmsg_level ==
SOL_IP) &&
400 (cmsg->cmsg_type == IP_PKTINFO)) {
401 struct in_pktinfo *i = (
struct in_pktinfo *) CMSG_DATA(cmsg);
402 struct sockaddr_in to = {
403 .sin_family = AF_INET,
404 .sin_addr = i->ipi_addr,
405 .sin_port = htons(my->
info.
socket.inet.src_port),
409 (
struct sockaddr_storage *) &to,
sizeof(
struct sockaddr_in));
410 addr->
socket.inet.ifindex = i->ipi_ifindex;
416 if ((cmsg->cmsg_level == IPPROTO_IP) &&
417 (cmsg->cmsg_type == IP_RECVDSTADDR)) {
418 struct in_addr *i = (
struct in_addr *) CMSG_DATA(cmsg);
419 struct sockaddr_in to = {
420 .sin_family = AF_INET,
422 .sin_port = htons(my->
info.
socket.inet.src_port),
426 (
struct sockaddr_storage *) &to,
sizeof(
struct sockaddr_in));
432 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMPNS)) {
436#elif defined(SO_TIMESTAMP)
437 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP)) {
456 struct cmsghdr *cmsg;
457 struct sockaddr_storage to;
462 memset(&my->cbuf, 0,
sizeof(my->cbuf));
463 memset(&my->msgh, 0,
sizeof(
struct msghdr));
467 my->iov = (
struct iovec) {
472 my->msgh = (
struct msghdr) {
473 .msg_control = my->cbuf,
476 .msg_namelen = to_len,
484 struct in_pktinfo *pkt;
486 my->msgh.msg_controllen = CMSG_SPACE(
sizeof(*pkt));
488 cmsg = CMSG_FIRSTHDR(&my->msgh);
489 cmsg->cmsg_level =
SOL_IP;
490 cmsg->cmsg_type = IP_PKTINFO;
491 cmsg->cmsg_len = CMSG_LEN(
sizeof(*pkt));
493 pkt = (
struct in_pktinfo *) CMSG_DATA(cmsg);
494 memset(pkt, 0,
sizeof(*pkt));
495 pkt->ipi_spec_dst = addr->
socket.inet.src_ipaddr.addr.v4;
496 pkt->ipi_ifindex = addr->
socket.inet.ifindex;
498#elif defined(IP_SENDSRCADDR)
501 my->msgh.msg_controllen = CMSG_SPACE(
sizeof(*
in));
503 cmsg = CMSG_FIRSTHDR(&my->msgh);
504 cmsg->cmsg_level = IPPROTO_IP;
505 cmsg->cmsg_type = IP_SENDSRCADDR;
506 cmsg->cmsg_len = CMSG_LEN(
sizeof(*
in));
508 in = (
struct in_addr *) CMSG_DATA(cmsg);
509 *
in = addr->
socket.inet.src_ipaddr.addr.v4;
522static inline int fr_bio_fd_udpfromto_init4(
int fd)
524 int proto = 0, flag = 0, opt = 1;
526#ifdef HAVE_IP_PKTINFO
533#elif defined(IP_RECVDSTADDR)
539 flag = IP_RECVDSTADDR;
542 return setsockopt(fd, proto, flag, &opt,
sizeof(opt));
546#if defined(IPV6_PKTINFO)
552 struct cmsghdr *cmsg;
558 if (rcode <= 0)
return rcode;
562 for (cmsg = CMSG_FIRSTHDR(&my->msgh);
564 cmsg = CMSG_NXTHDR(&my->msgh, cmsg)) {
567 if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
568 (cmsg->cmsg_type == IPV6_PKTINFO)) {
569 struct in6_pktinfo *i = (
struct in6_pktinfo *) CMSG_DATA(cmsg);
570 struct sockaddr_in6 to = {
571 .sin6_family = AF_INET6,
572 .sin6_addr = i->ipi6_addr,
573 .sin6_port = htons(my->
info.
socket.inet.src_port),
574 .sin6_scope_id = my->
info.
socket.inet.src_ipaddr.scope_id,
578 (
struct sockaddr_storage *) &to,
sizeof(
struct sockaddr_in6));
579 addr->
socket.inet.ifindex = i->ipi6_ifindex;
584 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMPNS)) {
588#elif defined(SO_TIMESTAMP)
589 if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SO_TIMESTAMP)) {
608 struct cmsghdr *cmsg;
609 struct sockaddr_storage to;
614 memset(&my->cbuf, 0,
sizeof(my->cbuf));
615 memset(&my->msgh, 0,
sizeof(
struct msghdr));
619 my->iov = (
struct iovec) {
624 my->msgh = (
struct msghdr) {
625 .msg_control = my->cbuf,
628 .msg_namelen = to_len,
635 struct in6_pktinfo *pkt;
637 my->msgh.msg_controllen = CMSG_SPACE(
sizeof(*pkt));
639 cmsg = CMSG_FIRSTHDR(&my->msgh);
640 cmsg->cmsg_level = IPPROTO_IPV6;
641 cmsg->cmsg_type = IPV6_PKTINFO;
642 cmsg->cmsg_len = CMSG_LEN(
sizeof(*pkt));
644 pkt = (
struct in6_pktinfo *) CMSG_DATA(cmsg);
645 memset(pkt, 0,
sizeof(*pkt));
646 pkt->ipi6_addr = addr->
socket.inet.src_ipaddr.addr.v6;
647 pkt->ipi6_ifindex = addr->
socket.inet.ifindex;
660static inline int fr_bio_fd_udpfromto_init6(
int fd)
664 return setsockopt(fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &opt,
sizeof(opt));
672 len = strlen(filename);
673 if (
len >=
sizeof(sun->sun_path)) {
674 fr_strerror_const(
"Failed parsing unix domain socket filename: Name is too long");
678 sun->sun_family = AF_LOCAL;
679 memcpy(sun->sun_path, filename,
len + 1);
681 *sunlen = SUN_LEN(sun);
689 struct sockaddr_storage salocal;
704 salen =
sizeof(salocal);
705 memset(&salocal, 0, salen);
706 if (getsockname(my->
info.
socket.
fd, (
struct sockaddr *) &salocal, &salen) < 0) {
736 if (my->cb.connected) my->cb.connected(&my->bio);
796 if (tries <= my->max_tries)
goto retry;
816 if (rcode < 0)
return rcode;
845 switch (my->
info.
cfg->
flags & (O_RDONLY | O_WRONLY | O_RDWR)) {
922 setsockopt(my->
info.
socket.
fd, SOL_SOCKET, SO_NOSIGPIPE, &on,
sizeof(on));
935 if (rcode == 0)
return 0;
937 if (rcode !=
fr_bio_error(IO_WOULD_BLOCK))
return rcode;
963#if defined(IP_PKTINFO) || defined(IP_RECVDSTADDR)
964 }
else if (my->
info.
socket.inet.src_ipaddr.
af == AF_INET) {
965 if (fr_bio_fd_udpfromto_init4(my->
info.
socket.
fd) < 0)
return -1;
967 my->bio.read = fr_bio_fd_recvfromto4;
968 my->bio.write = fr_bio_fd_sendfromto4;
971#if defined(IPV6_PKTINFO)
972 }
else if (my->
info.
socket.inet.src_ipaddr.
af == AF_INET6) {
974 if (fr_bio_fd_udpfromto_init6(my->
info.
socket.
fd) < 0)
return -1;
976 my->bio.read = fr_bio_fd_recvfromto6;
977 my->bio.write = fr_bio_fd_sendfromto6;
1052 if (!my)
return NULL;
1067 .read_blocked =
false,
1068 .write_blocked =
false,
1178 socklen_t socklen =
sizeof(error);
1193 if (getsockopt(my->
info.
socket.
fd, SOL_SOCKET, SO_ERROR, (
void *)&error, &socklen) < 0) {
1258 my->
connect.timeout(&my->bio);
1296 if (error_cb) error_cb(bio);
1307 if (connected_cb) connected_cb(bio);
1329 if (!connected_cb) {
1334 if (error_cb) error_cb(bio);
1349 my->
connect.success = connected_cb;
1351 my->
connect.timeout = timeout_cb;
1395 if (rcode >= 0)
return 0;
1411 if (rcode > 0 )
return 0;
1437 goto set_recv_buff_zero;
1460 if (setsockopt(my->
info.
socket.
fd, SOL_SOCKET, SO_RCVBUF, &opt,
sizeof(opt)) < 0) {
1482#if !defined(__linux__) && !defined(__FreeBSD__)
1483static int inline accept4(
int fd,
struct sockaddr *sockaddr, socklen_t *salen,
UNUSED int flags)
1485 fd = accept(fd, sockaddr, salen);
1488 int our_errno = errno;
1502#ifndef SOCK_NONBLOCK
1503#define SOCK_NONBLOCK 0
1507#define SOCK_CLOEXEC 0
1528#if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN)
1539 if (tries <= my->max_tries)
goto retry;
1586 out->info.socket.
fd = -1;
1613 if ((
out->info.socket.af == AF_INET) || (
out->info.socket.af == AF_INET6)) {
1615 &
out->remote_sockaddr,
out->remote_sockaddr_len);
1626 if (!
out->info.name) {
static int const char char buffer[256]
fr_bio_write_t _CONST write
write to the underlying bio
fr_bio_read_t _CONST read
read from the underlying bio
static ssize_t fr_bio_write(fr_bio_t *bio, void *packet_ctx, void const *buffer, size_t size)
Write raw data to a bio.
static fr_bio_t * fr_bio_head(fr_bio_t *bio)
void(* fr_bio_callback_t)(fr_bio_t *bio)
static int packet_ctx
the tests pass only the address of packet_ctx
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
#define fr_event_fd_insert(...)
@ FR_EVENT_FILTER_IO
Combined filter for read/write functions/.
int fr_bio_fd_connect_full(fr_bio_t *bio, fr_event_list_t *el, fr_bio_callback_t connected_cb, fr_bio_callback_t error_cb, fr_time_delta_t *timeout, fr_bio_callback_t timeout_cb)
Finalize a connect()
static ssize_t fr_bio_fd_notconn_read(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void *buffer, UNUSED size_t size)
This FD isn't connected.
static ssize_t fr_bio_fd_write(fr_bio_t *bio, UNUSED void *packet_ctx, const void *buffer, size_t size)
Write to fd.
int fr_bio_fd_socket_name(fr_bio_fd_t *my)
static void fr_bio_fd_el_connect(NDEBUG_UNUSED fr_event_list_t *el, NDEBUG_UNUSED int fd, NDEBUG_UNUSED int flags, void *uctx)
Connect callback for when the socket is writable.
static int fr_bio_fd_write_resume(fr_bio_t *bio)
fr_bio_t * fr_bio_fd_alloc(TALLOC_CTX *ctx, fr_bio_fd_config_t const *cfg, size_t offset)
Allocate a FD bio.
static ssize_t fr_bio_fd_read_discard_stream(fr_bio_t *bio, UNUSED void *packet_ctx, void *buffer, size_t size)
Discard all reads from a TCP socket.
static int accept4(int fd, struct sockaddr *sockaddr, socklen_t *salen, UNUSED int flags)
int fr_bio_fd_init_connected(fr_bio_fd_t *my)
static ssize_t fr_bio_fd_read_connected_datagram(fr_bio_t *bio, UNUSED void *packet_ctx, void *buffer, size_t size)
Connected datagram read.
static ssize_t fr_bio_fd_try_connect(fr_bio_fd_t *my)
Try to connect().
static ssize_t fr_bio_fd_recvfrom(fr_bio_t *bio, void *packet_ctx, void *buffer, size_t size)
Read from a UDP socket where we know our IP.
static int fr_bio_fd_eof(fr_bio_t *bio)
static ssize_t fr_bio_fd_sendto(fr_bio_t *bio, void *packet_ctx, const void *buffer, size_t size)
Write to a UDP socket where we know our IP.
int fr_filename_to_sockaddr(struct sockaddr_un *sun, socklen_t *sunlen, char const *filename)
int fr_bio_fd_init_listen(fr_bio_fd_t *my)
int fr_bio_fd_accept(TALLOC_CTX *ctx, fr_bio_t **out_p, fr_bio_t *bio)
Accept a new connection on a socket.
fr_bio_fd_info_t const * fr_bio_fd_info(fr_bio_t *bio)
Returns a pointer to the bio-specific information.
static int fr_bio_fd_shutdown(fr_bio_t *bio)
Orderly shutdown.
static int fr_bio_fd_accept_stream(fr_bio_fd_t *my, fr_bio_fd_t const *parent)
Accept a stream socket and initialize its flags.
static int fr_bio_fd_init_file(fr_bio_fd_t *my)
Files are a special case of connected sockets.
static void fr_bio_fd_el_timeout(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *uctx)
We have a timeout on the conenction.
int fr_bio_fd_init_common(fr_bio_fd_t *my)
int fr_bio_fd_write_only(fr_bio_t *bio)
Mark up a bio as write-only.
static ssize_t fr_bio_fd_read_discard_datagram(fr_bio_t *bio, UNUSED void *packet_ctx, void *buffer, size_t size)
Discard all reads from a UDP socket.
static void fr_bio_fd_el_error(UNUSED fr_event_list_t *el, UNUSED int fd, UNUSED int flags, int fd_errno, void *uctx)
FD error when trying to connect, give up on the BIO.
int fr_bio_fd_close(fr_bio_t *bio)
Close the FD, but leave the bio allocated and alive.
static void fr_bio_fd_set_open(fr_bio_fd_t *my)
static ssize_t fr_bio_fd_read_stream(fr_bio_t *bio, UNUSED void *packet_ctx, void *buffer, size_t size)
Stream read.
fr_socket_t socket
as connected socket
fr_bio_fd_type_t type
type of the socket
fr_bio_fd_state_t state
connecting, open, closed, etc.
int connect_errno
from connect() or other APIs
@ FR_BIO_FD_CONNECTED
connected client sockets (UDP or TCP)
@ FR_BIO_FD_INVALID
not set
@ FR_BIO_FD_UNCONNECTED
unconnected UDP / datagram only
@ FR_BIO_FD_LISTEN
call fr_bio_fd_accept() for each new connection
fr_bio_fd_config_t const * cfg
so we know what was asked, vs what was granted.
bool read_blocked
did we block on read?
@ FR_BIO_FD_TRANSPORT_TCP
TCP over IPv4 or IPv6.
uint32_t backlog
for listen()
fr_bio_fd_transport_t transport_type
which transport, as a number instead of a name
int fr_bio_fd_open(fr_bio_t *bio, fr_bio_fd_config_t const *cfg)
Opens a socket and updates sock->fd.
@ FR_BIO_FD_STATE_CONNECTING
@ FR_BIO_FD_STATE_OPEN
error states must be before this
bool backlog_is_set
Whether we were provided with a backlog.
fr_socket_t socket
socket information, including FD.
bool write_blocked
did we block on write?
Configuration for sockets.
Run-time status of the socket.
static bool fr_bio_fd_errno_retry(fr_bio_fd_t *my, ssize_t *rcode_p, int *tries_p, bool is_write)
Turn a failed read / write into a bio return code.
int fr_bio_fd_unix_shutdown(fr_bio_t *bio)
Remove the Unix domain socket which this bio created.
void fr_bio_fd_name(fr_bio_fd_t *my)
Set the name of an FD BIO.
socklen_t remote_sockaddr_len
struct fr_bio_fd_s::@18 connect
size_t offset
where fr_bio_fd_packet_ctx_t is stored
int max_tries
how many times we retry on EINTR
#define fr_bio_fd_packet_ctx(_my, _packet_ctx)
struct sockaddr_storage remote_sockaddr
for connected UDP sockets.
static bool fr_bio_fd_read_retry(fr_bio_fd_t *my, ssize_t *rcode_p, int *tries_p)
Common finalization code for the read functions.
static bool fr_bio_fd_write_retry(fr_bio_fd_t *my, ssize_t *rcode_p, int *tries_p, size_t size)
Common finalization code for the write functions.
void fr_ipaddr_get_scope_id(fr_ipaddr_t *ipaddr)
int fr_ipaddr_from_sockaddr(fr_ipaddr_t *ipaddr, uint16_t *port, struct sockaddr_storage const *sa, socklen_t salen)
Convert sockaddr to our internal ip address representation.
int fr_ipaddr_is_inaddr_any(fr_ipaddr_t const *ipaddr)
Determine if an address is the INADDR_ANY address for its address family.
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.
int fr_bio_write_blocked(fr_bio_t *bio)
Internal BIO function to tell all BIOs that it's blocked.
ssize_t fr_bio_shutdown_read(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void *buffer, UNUSED size_t size)
int fr_bio_destructor(fr_bio_t *bio)
Free this bio.
ssize_t fr_bio_shutdown_write(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
fr_bio_t * fr_bio_eof(fr_bio_t *bio)
Internal BIO function to run EOF callbacks.
int fr_bio_shutdown(fr_bio_t *bio)
Shut down a set of BIOs.
int fr_event_fd_delete(fr_event_list_t *el, int fd, fr_event_filter_t filter)
Remove a file descriptor from the event loop.
Stores all information relating to an event list.
int fr_nonblock(UNUSED int fd)
int fr_cloexec(UNUSED int fd)
ssize_t fr_bio_null_write(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
Always return 0 on write.
ssize_t fr_bio_null_read(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void *buffer, UNUSED size_t size)
Always return 0 on read.
ssize_t fr_bio_fail_read(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void *buffer, UNUSED size_t size)
Always return error on read.
ssize_t fr_bio_fail_write(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
Always return an error on write.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
static fr_time_t fr_time_from_timeval(struct timeval const *when_tv)
Convert a timeval (wallclock time) to a fr_time_t (internal time)
#define fr_time_wrap(_time)
#define fr_time_eq(_a, _b)
static fr_time_t fr_time_from_timespec(struct timespec const *when_ts)
Convert a timespec (wallclock time) to a fr_time_t (internal time)
A time delta, a difference in time measured in nanoseconds.
#define FR_TIMER_DELETE(_ev_p)
static fr_event_list_t * el
int af
AF_INET, AF_INET6, or AF_UNIX.
int fd
File descriptor if this is a live socket.
int type
SOCK_STREAM, SOCK_DGRAM, etc.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
#define fr_strerror_const(_msg)
static fr_sbuff_err_t char size_t fr_sbuff_t * in
static fr_sbuff_err_t char ** out
static fr_sbuff_err_t char size_t * len