23#include <freeradius-devel/util/test/acutest_common_init.h>
24#include <freeradius-devel/util/test/acutest_helpers.h>
26#include <freeradius-devel/bio/bio_priv.h>
27#include <freeradius-devel/bio/fd.h>
28#include <freeradius-devel/util/syserror.h>
53 struct sockaddr_in sin = {
54 .sin_family = AF_INET,
55 .sin_addr.s_addr = htonl(INADDR_LOOPBACK),
57 socklen_t
len =
sizeof(sin);
60 fd = socket(AF_INET, SOCK_STREAM, 0);
61 if (fd < 0)
return -1;
63 if ((bind(fd, (
struct sockaddr *) &sin,
sizeof(sin)) < 0) ||
64 (listen(fd, 5) < 0) ||
65 (getsockname(fd, (
struct sockaddr *) &sin, &
len) < 0)) {
70 *port = ntohs(sin.sin_port);
103 if (listen_fd < 0)
goto done;
111 .socket_type = SOCK_STREAM,
116 .addr.v4.s_addr = htonl(INADDR_LOOPBACK),
134 TEST_MSG(
"connect_full returned %d. 0 means deferred, 1 means connected at once, <0 means failed", rcode);
135 if (rcode < 0)
goto done;
149 TEST_MSG(
"connected_count = %d, error_count = %d, timeout_count = %d, connect_errno = %d",
152 TEST_CASE(
"neither the error callback nor the timeout callback ran");
160 if (listen_fd >= 0) close(listen_fd);
175 struct sockaddr_in6 sin6 = {
176 .sin6_family = AF_INET6,
177 .sin6_addr = IN6ADDR_LOOPBACK_INIT,
182 fd = socket(AF_INET6, SOCK_DGRAM, 0);
183 if (fd < 0)
return errno;
185 if (bind(fd, (
struct sockaddr *) &sin6,
sizeof(sin6)) < 0) error = errno;
211 struct sockaddr_storage to;
216 struct timeval recv_timeout = { .tv_sec = 5 };
227 .socket_type = SOCK_DGRAM,
231 .prefix = (af == AF_INET) ? 32 : 128,
242 TEST_MSG(
"the bio did not learn the port which the kernel picked");
248 loopback = (
fr_ipaddr_t) { .
af = AF_INET, .prefix = 32, .addr.v4.s_addr = htonl(INADDR_LOOPBACK) };
250 loopback = (
fr_ipaddr_t) { .
af = AF_INET6, .prefix = 128, .addr.v6 = in6addr_loopback };
255 fd = socket(af, SOCK_DGRAM, 0);
257 if (fd < 0)
goto done;
262 rcode = sendto(fd,
"x", 1, 0, (
struct sockaddr *) &to, to_len);
266 if (rcode != 1)
goto done;
272 TEST_CHECK(setsockopt(info->
socket.
fd, SOL_SOCKET, SO_RCVTIMEO, &recv_timeout,
sizeof(recv_timeout)) == 0);
276 TEST_CASE(
"the packet is read, not dropped");
278 TEST_MSG(
"fr_bio_read returned %zd", rcode);
279 if (rcode != 1)
goto done;
281 TEST_CASE(
"the destination is the loopback address and the port of the bio");
287 if (fd >= 0) close(fd);
311 fr_bio_t *listener, *accepted = NULL;
314 fr_ipaddr_t loopback = { .
af = AF_INET, .prefix = 32, .addr.v4.s_addr = htonl(INADDR_LOOPBACK) };
315 struct sockaddr_storage to;
317 struct sockaddr_in client_addr = {};
318 socklen_t client_len =
sizeof(client_addr);
331 if (fd < 0)
goto done;
336 .socket_type = SOCK_STREAM,
339 .src_ipaddr = loopback,
346 if (!listener)
goto done;
349 TEST_CASE(
"an accept with no connection waiting returns 0, and leaves the listener open");
352 TEST_MSG(
"fr_bio_fd_accept returned %zd", rcode);
355 if ((rcode != 0) || accepted)
goto done;
358 client = socket(AF_INET, SOCK_STREAM, 0);
360 if (client < 0)
goto done;
361 TEST_CHECK(connect(client, (
struct sockaddr *) &to, to_len) == 0);
362 TEST_CHECK(getsockname(client, (
struct sockaddr *) &client_addr, &client_len) == 0);
368 pfd = (
struct pollfd) { .fd = linfo->
socket.
fd, .events = POLLIN };
371 TEST_CASE(
"an accept with a connection waiting returns 1 and a connected bio");
374 TEST_MSG(
"fr_bio_fd_accept returned %zd", rcode);
376 if ((rcode != 1) || !accepted)
goto done;
384 TEST_CASE(
"the remote end of the new bio is the client");
387 TEST_MSG(
"dst_port = %u, expected %u", ainfo->
socket.inet.dst_port, ntohs(client_addr.sin_port));
393 TEST_CASE(
"the new bio carries data in both directions");
403 pfd = (
struct pollfd) { .fd = ainfo->
socket.
fd, .events = POLLIN };
409 if (client >= 0) close(client);
static int const char char buffer[256]
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 ssize_t fr_bio_read(fr_bio_t *bio, void *packet_ctx, void *buffer, size_t size)
Read raw data from a bio.
static int packet_ctx
the tests pass only the address of packet_ctx
static void cb_timeout(fr_bio_t *bio)
static void recvfromto_test(int af)
Read one packet through an unconnected UDP bio bound to the wildcard address.
static void test_deferred_connect_success_calls_connected_cb(void)
A deferred connect which succeeds has to call the connected callback.
static void test_recvfromto6(void)
static int loopback_listen(uint16_t *port)
Open a TCP socket listening on loopback.
static void cb_error(fr_bio_t *bio)
static int connected_count
static void test_recvfromto4(void)
static void test_listen_accept(void)
Accept a TCP connection through a listening bio.
static int ipv6_loopback_usable(void)
See if the IPv6 loopback address can be used.
static void cb_connected(fr_bio_t *bio)
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()
fr_bio_t * fr_bio_fd_alloc(TALLOC_CTX *ctx, fr_bio_fd_config_t const *cfg, size_t offset)
Allocate a FD bio.
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.
fr_socket_t socket
as connected socket
char const * name
printable name of this BIO
fr_bio_fd_type_t type
type of the socket
fr_bio_fd_state_t state
connecting, open, closed, etc.
fr_ipaddr_t dst_ipaddr
their IP address
fr_bio_fd_type_t type
accept, connected, unconnected, etc.
@ FR_BIO_FD_CONNECTED
connected client sockets (UDP or TCP)
@ FR_BIO_FD_UNCONNECTED
unconnected UDP / datagram only
@ FR_BIO_FD_LISTEN
call fr_bio_fd_accept() for each new connection
@ FR_BIO_FD_TRANSPORT_TCP
TCP over IPv4 or IPv6.
@ FR_BIO_FD_TRANSPORT_UDP
UDP over IPv4 or IPv6.
@ FR_BIO_FD_STATE_OPEN
error states must be before this
fr_ipaddr_t src_ipaddr
our IP address
Configuration for sockets.
Run-time status of the socket.
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.
fr_cmp_ret_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
void fr_event_service(fr_event_list_t *el)
Service any outstanding timer or file descriptor events.
int fr_event_corral(fr_event_list_t *el, fr_time_t now, bool wait)
Gather outstanding timer and file descriptor events.
fr_event_list_t * fr_event_list_alloc(TALLOC_CTX *ctx, fr_event_status_cb_t status, void *status_uctx)
Initialise a new event list.
Stores all information relating to an event list.
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
static TALLOC_CTX * talloc_init_const(char const *name)
Allocate a top level chunk with a constant name.
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
A time delta, a difference in time measured in nanoseconds.
static fr_event_list_t * el
int fd
File descriptor if this is a live socket.
char const * fr_strerror(void)
Get the last library error.
static fr_sbuff_err_t char size_t * len