M TODO.md => TODO.md +1 -1
@@ 1,6 1,6 @@
# TODO
-- in "sa", replace alloc functions with init functions
+- check address family in `sa_from_sockaddr_in()` and friends
- check if `bind()` works with sizeof struct sockaddr_storage
- test listening on unix and ipv6 sockets
- handle linux "unnamed" and "abstract" unix sockets
M src/libood/ood/inet/socket.c => src/libood/ood/inet/socket.c +1 -0
@@ 44,6 44,7 @@ ood_inet_socket_accept(struct ood_inet_socket *listener, struct ood_inet_socket
errno = 0;
int fd = accept(listener->fd, &client_address.generic, &len);
if (fd >= 0) {
+ sa_update(&client_address);
*connection_out = alloc_connection(fd, &client_address);
if (*connection_out) {
return ood_okay;
M src/sa/README.md => src/sa/README.md +1 -0
@@ 55,6 55,7 @@ Typical usage of `sa` looks like this (sans error handling):
socklen_t len;
sa_prepare(&client_address, &len);
int client_fd = accept(fd, &client_address.generic, &len);
+ sa_update(&client_address);
Socket addresses are frequently logged, so `sa` creates a string representation
at initialization time. Use the `sa_str()` function to get the correct string
M src/sa/sa.c => src/sa/sa.c +47 -14
@@ 6,6 6,36 @@
#include <stdio.h>
+static bool
+update_ipv4_str(union sa *address)
+{
+ const char *ipv4_str = inet_ntop(AF_INET, &address->ipv4.sin_addr,
+ address->ipv4_str, SA_IPV4_STR_SIZE);
+ if (!ipv4_str) return false;
+
+ char *s = address->ipv4_str + strlen(address->ipv4_str);
+ int bytes_written = sprintf(s, ":%i", ntohs(address->ipv4.sin_port));
+ if (bytes_written < 0) return false;
+
+ return true;
+}
+
+
+static bool
+update_ipv6_str(union sa *address)
+{
+ char const *ipv6_str = inet_ntop(AF_INET6, &address->ipv6.sin6_addr,
+ address->ipv6_str, SA_IPV6_STR_SIZE);
+ if (!ipv6_str) return false;
+
+ char *s = address->ipv6_str + strlen(address->ipv6_str);
+ int bytes_written = sprintf(s, ":%i", ntohs(address->ipv6.sin6_port));
+ if (bytes_written < 0) return false;
+
+ return true;
+}
+
+
bool
sa_unspec(union sa *address)
{
@@ 102,13 132,7 @@ sa_from_sockaddr_in(union sa *address, struct sockaddr_in const *ipv4_address)
address->ipv4 = *ipv4_address;
- const char *ipv4_str = inet_ntop(AF_INET, &ipv4_address->sin_addr,
- address->ipv4_str, SA_IPV4_STR_SIZE);
- if (!ipv4_str) return false;
-
- char *s = address->ipv4_str + strlen(address->ipv4_str);
- int bytes_written = sprintf(s, ":%i", ntohs(ipv4_address->sin_port));
- if (bytes_written < 0) return false;
+ if (!update_ipv4_str(address)) return false;
return true;
}
@@ 123,13 147,7 @@ sa_from_sockaddr_in6(union sa *address, struct sockaddr_in6 const *ipv6_address)
address->ipv6 = *ipv6_address;
- char const *ipv6_str = inet_ntop(AF_INET6, &ipv6_address->sin6_addr,
- address->ipv6_str, SA_IPV6_STR_SIZE);
- if (!ipv6_str) return false;
-
- char *s = address->ipv6_str + strlen(address->ipv6_str);
- int bytes_written = sprintf(s, ":%i", ntohs(ipv6_address->sin6_port));
- if (bytes_written < 0) return false;
+ if (!update_ipv6_str(address)) return false;
return true;
}
@@ 158,6 176,21 @@ sa_prepare(union sa *address, socklen_t *len)
}
+bool
+sa_update(union sa *address)
+{
+ if (!address) return false;
+
+ if (AF_INET == address->generic.sa_family) {
+ if (!update_ipv4_str(address)) return false;
+ } else if (AF_INET6 == address->generic.sa_family) {
+ if (!update_ipv6_str(address)) return false;
+ }
+
+ return true;
+}
+
+
extern sa_family_t
sa_family(union sa const *address);
M src/sa/sa.h => src/sa/sa.h +7 -1
@@ 103,12 103,18 @@ bool
sa_from_sockaddr_un(union sa *address, struct sockaddr_un const *local_address);
-// # Initialize an `sa` union and length for receiving a socket address
+// # Prepare an `sa` union and length for receiving a socket address
bool
sa_prepare(union sa *address, socklen_t *len);
+// # Update an `sa` union after receiving a socket address
+
+bool
+sa_update(union sa *address);
+
+
// # Get info about a socket address
inline sa_family_t
M src/sa/sa_tests.c => src/sa/sa_tests.c +103 -0
@@ 547,6 547,102 @@ test_sa_prepare_for_null(void)
static void
+test_sa_update_for_unspecified()
+{
+ union sa address = {
+ .generic = {
+ .sa_family = AF_UNSPEC,
+ }
+ };
+
+ bool success = sa_update(&address);
+
+ assert(success);
+ assert(STR_EQ("(unspecified)", sa_str(&address)));
+}
+
+
+static void
+test_sa_update_for_ipv4()
+{
+ union sa address = {
+ .ipv4 = {
+ .sin_family=AF_INET,
+ .sin_addr=(struct in_addr){
+ .s_addr=htonl(INADDR_LOOPBACK)
+ },
+ .sin_port=htons(8080),
+ }
+ };
+
+ bool success = sa_update(&address);
+
+ assert(success);
+ assert(STR_EQ("127.0.0.1:8080", sa_str(&address)));
+}
+
+
+static void
+test_sa_update_for_ipv6()
+{
+ union sa address = {
+ .ipv6 = {
+ .sin6_family=AF_INET6,
+ .sin6_addr=in6addr_loopback,
+ .sin6_port=htons(8080),
+ }
+ };
+
+ bool success = sa_update(&address);
+
+ assert(success);
+ assert(STR_EQ("::1:8080", sa_str(&address)));
+}
+
+
+static void
+test_sa_update_for_local()
+{
+ union sa address = {
+ .local = {
+ .sun_family = AF_UNIX,
+ .sun_path = "/var/dood/http",
+ }
+ };
+
+ bool success = sa_update(&address);
+
+ assert(success);
+ assert(STR_EQ("/var/dood/http", sa_str(&address)));
+}
+
+
+static void
+test_sa_update_for_unsupported()
+{
+ union sa address = {
+ .generic = {
+ .sa_family = 0xff,
+ }
+ };
+
+ bool success = sa_update(&address);
+
+ assert(success);
+ assert(STR_EQ("(unsupported)", sa_str(&address)));
+}
+
+
+static void
+test_sa_update_for_null()
+{
+ bool success = sa_update(NULL);
+
+ assert(!success);
+}
+
+
+static void
test_sa_family_for_unspecified(void)
{
union sa address = {
@@ 748,6 844,13 @@ main(int argc, char *argv[])
test_sa_prepare();
test_sa_prepare_for_null();
+ test_sa_update_for_unspecified();
+ test_sa_update_for_ipv4();
+ test_sa_update_for_ipv6();
+ test_sa_update_for_local();
+ test_sa_update_for_unsupported();
+ test_sa_update_for_null();
+
test_sa_family_for_unspecified();
test_sa_family_for_ipv4();
test_sa_family_for_ipv6();