v1.7.0 release

This commit is contained in:
seajee
2025-09-06 01:30:42 +02:00
parent 42d126f01a
commit a9462d0880
6 changed files with 372 additions and 135 deletions

View File

@@ -13,7 +13,7 @@ int main(void)
SockAddr addr = sock_addr("0.0.0.0", 6969); SockAddr addr = sock_addr("0.0.0.0", 6969);
if (!sock_bind(server, addr)) { err = "bind"; goto defer; } if (!sock_bind(server, addr)) { err = "bind"; goto defer; }
if (!sock_listen(server, 16)) { err = "listen"; goto defer; } if (!sock_listen(server)) { err = "listen"; goto defer; }
Sock *client = sock_accept(server); Sock *client = sock_accept(server);
if (client == NULL) { err = "accept"; goto defer; } if (client == NULL) { err = "accept"; goto defer; }

View File

@@ -12,7 +12,7 @@ int main(void)
SockAddr addr = sock_addr("::", 6969); SockAddr addr = sock_addr("::", 6969);
if (!sock_bind(server, addr)) { err = true; goto close; } if (!sock_bind(server, addr)) { err = true; goto close; }
if (!sock_listen(server, 16)) { err = true; goto close; } if (!sock_listen(server)) { err = true; goto close; }
Sock *client = sock_accept(server); Sock *client = sock_accept(server);
if (client == NULL) { err = true; goto close; } if (client == NULL) { err = true; goto close; }

View File

@@ -5,12 +5,16 @@
int main(void) int main(void)
{ {
SockAddr addr = sock_addr("example.com", 80); const char *domain_name = "example.com";
if (addr.type == SOCK_ADDR_INVALID) { SockAddrList addr_list = sock_dns(domain_name, 80, SOCK_IPV4, SOCK_TCP);
fprintf(stderr, "ERROR: Could not resolve address %s\n", addr.str); if (addr_list.count == 0) {
fprintf(stderr, "ERROR: Could not resolve address %s\n", domain_name);
return 1; return 1;
} }
SockAddr addr = addr_list.items[0];
sock_addr_list_free(&addr_list);
printf("%s:%d\n", addr.str, addr.port); printf("%s:%d\n", addr.str, addr.port);
Sock *s = sock_create(addr.type, SOCK_TCP); Sock *s = sock_create(addr.type, SOCK_TCP);
@@ -30,7 +34,8 @@ int main(void)
const char *req = const char *req =
"GET / HTTP/1.1\r\n" "GET / HTTP/1.1\r\n"
"Host: example.com\r\n" "Host: example.com\r\n"
"Connection: close\r\n\r\n"; "Connection: close\r\n"
"\r\n";
sock_send(s, req, strlen(req)); sock_send(s, req, strlen(req));

26
examples/06-dig.c Normal file
View File

@@ -0,0 +1,26 @@
#define SOCK_IMPLEMENTATION
#include "sock.h"
int main(int argc, char **argv)
{
const char *program_name = argv[0];
if (argc <= 1) {
fprintf(stderr, "USAGE: %s <address>\n", program_name);
fprintf(stderr, "ERROR: No address was provided\n");
return 1;
}
const char *arg_address = argv[1];
SockAddrList addrs = sock_dns(arg_address, 0, 0, SOCK_TCP);
for (size_t i = 0; i < addrs.count; ++i) {
SockAddr it = addrs.items[i];
printf("%s\n", it.str);
}
sock_addr_list_free(&addrs);
return 0;
}

View File

@@ -138,7 +138,7 @@ int main(void)
printf("INFO: Bind socket\n"); printf("INFO: Bind socket\n");
if (!sock_listen(server, 10)) { if (!sock_listen(server)) {
fprintf(stderr, "ERROR: Could not listen on socket\n"); fprintf(stderr, "ERROR: Could not listen on socket\n");
sock_close(server); sock_close(server);
return EXIT_FAILURE; return EXIT_FAILURE;

438
sock.h
View File

@@ -1,10 +1,160 @@
// sock.h - v1.6.3 - MIT License - https://github.com/seajee/sock.h
/***************************************************************\
# #
# @@@@@ #
# @ @ #
# @@@@@ #
# @ @ sock.h - v1.7.0 #
# @ @ MIT License #
# @@% .@ @ #
# @@ @ @ https://github.com/seajee/sock.h #
# @ \ .@@ #
# @ @@@# #
# #
\***************************************************************/
// sock.h - A single header library for network sockets management.
//
// [License and changelog]
//
// See end of file.
//
// [Single header library usage]
//
// Including this header file as such does what you normally expect.
//
// If you also need to include all of the function implementations you
// need to #define SOCK_IMPLEMENTATION before including the header file
// like this:
//
// #define SOCK_IMPLEMENTATION
// #include "sock.h"
//
// [Structure documentation]
//
// Sock: can be treated as a normal socket
//
// SockAddr: an address composed of an IP (v4 or v6) and a port
//
// The fields you will commonly refer to in this structure are:
//
// SockAddr addr;
// addr.port; // The port of the SockAddr
// addr.str; // String representation of the address
//
// SockAddrList: a dynamic array of SockAddr
//
// A SockAddrList can be iterated like so:
//
// SockAddrList list = ...;
// for (size_t i = 0; i < list.count; ++i) {
// SockAddr addr = list.items[i];
// // Try to connect to addr or just inspect it
// }
//
// [Function documentation]
//
// In this library all of the major functions are abstraction layers to common
// standard functions, so their usage remain the same. Structures and utility
// functions are put in place to simplify the usage of the standard C API for
// socket and address handling.
//
// Sock related functions:
//
// Sock *sock_create(SockAddrType domain, SockType type)
//
// Allocates and initializes a Sock with the corresponding domain and type.
// When you're done using it you should close it with sock_close(). Returns
// NULL on error.
//
// bool sock_bind(Sock *sock, SockAddr addr)
//
// Binds a sock to the specified address. Returns false on error.
//
// bool sock_listen(Sock *sock)
//
// Makes a sock listen for incoming connections. Returns false on error.
//
// Sock *sock_accept(Sock *sock)
//
// Accepts a new connection on a sock in a blocking way. For handling new
// connections asynchronously you can use sock_async_accept(). Returns NULL on
// error.
//
// bool sock_async_accept(Sock *sock, SockThreadCallback fn, void *user_data)
//
// Same as sock_accept but creates a new pthread that will own the client
// sock. It will be the callback job to close the new sock. A function
// callback shall be passed with the following signature:
// void callback(Sock *sock, void *user_data)
// Returns false on error.
//
// bool sock_connect(Sock *sock, SockAddr addr)
//
// Connects a sock on a connection-mode sock (e.g. TCP). Returns false on
// error.
//
// ssize_t sock_send(Sock *sock, const void *buf, size_t size)
//
// Same as send() but with socks: sends the content of buf to the specified
// sock. On success returns the number of bytes sent. On error a negative
// number shall be returned.
//
// ssize_t sock_recv(Sock *sock, void *buf, size_t size)
//
// Same as recv() but with socks: receives a message from sock end writes it
// into the specified buffer. On sucess returns the number of bytes received.
// On error a negative number shall be returned.
//
// ssize_t sock_sendto(Sock *sock, const void *buf, size_t size, SockAddr addr)
//
// Same as sendto() but with socks: the return value works the same way as
// sock_send(). The addr parameter specifies the address to send the message
// to.
//
// ssize_t sock_recvfrom(Sock *sock, void *buf, size_t size, SockAddr *addr)
//
// Same as recvfrom() but with socks: the return value works the same way as
// sock_recv(). The addr parameter will be filled with the address information
// of the sender.
//
// void sock_close(Sock *sock);
//
// Closes a sock and releases its memory.
//
// void sock_log_error(const Sock *sock)
//
// Prints the last error message of the specified Sock in stderr. This
// functions uses errno to get the error message.
//
// SockAddr related functions:
//
// SockAddr sock_addr(const char *addr, int port)
//
// Given a valid IP (v4 or v6) address as a string and a port returns a
// correctly initialized SockAddr structure. On invalid input, the resulting
// SockAddr type will be set to SOCK_ADDR_INVALID.
//
// SockAddrList sock_dns(const char *addr,
// int port, SockAddrType addr_hint, SockType sock_hint)
//
// This functions queries information about the specified address (e.g.
// "example.com") and returns a list of possible valid SockAddr. The only
// required parameter is addr, while the rest are optional hints that can be
// set to 0. Hints can be provided to further filter the resulting
// SockAddrList. An empty list will be returned in case of no results or on
// errors. Freeing the list is required after using it with
// sock_addr_list_free().
//
// void sock_addr_list_free(SockAddrList *list)
//
// Releases the memory of the specified SockAddrList.
#ifndef SOCK_H_ #ifndef SOCK_H_
#define SOCK_H_ #define SOCK_H_
#include <arpa/inet.h> #include <arpa/inet.h>
#include <assert.h>
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
#include <pthread.h> #include <pthread.h>
@@ -16,14 +166,16 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#define SOCK_ADDR_LIST_INITIAL_CAPACITY 16
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { // Prevent name mangling extern "C" { // Prevent name mangling
#endif // __cplusplus #endif // __cplusplus
typedef enum { typedef enum {
SOCK_ADDR_INVALID = 0, SOCK_ADDR_INVALID = 0,
SOCK_IPV4, SOCK_IPV4 = AF_INET,
SOCK_IPV6 SOCK_IPV6 = AF_INET6
} SockAddrType; } SockAddrType;
typedef struct { typedef struct {
@@ -38,17 +190,23 @@ typedef struct {
socklen_t len; socklen_t len;
} SockAddr; } SockAddr;
typedef struct {
SockAddr *items; // Dynamic array of SockAddr
size_t count;
size_t capacity;
} SockAddrList;
typedef enum { typedef enum {
SOCK_TYPE_INVALID = 0, SOCK_TYPE_INVALID = 0,
SOCK_TCP, SOCK_TCP = SOCK_STREAM,
SOCK_UDP SOCK_UDP = SOCK_DGRAM
} SockType; } SockType;
typedef struct { typedef struct {
SockType type; // Socket type SockType type; // Socket type
SockAddr addr; // Socket address SockAddr addr; // Socket address
int fd; // File descriptor int fd; // File descriptor
char *last_error; // Last error about this socket int last_errno; // Last error about this socket
} Sock; } Sock;
typedef void (*SockThreadCallback)(Sock *sock, void *user_data); typedef void (*SockThreadCallback)(Sock *sock, void *user_data);
@@ -65,11 +223,17 @@ Sock *sock_create(SockAddrType domain, SockType type);
// Create a SockAddr structure from primitives // Create a SockAddr structure from primitives
SockAddr sock_addr(const char *addr, int port); SockAddr sock_addr(const char *addr, int port);
// Get all possible addresses from DNS with optional hints
SockAddrList sock_dns(const char *addr, int port, SockAddrType addr_hint, SockType sock_hint);
// Free a SockAddrList structure
void sock_addr_list_free(SockAddrList *list);
// Bind a socket to a specific address // Bind a socket to a specific address
bool sock_bind(Sock *sock, SockAddr addr); bool sock_bind(Sock *sock, SockAddr addr);
// Make the socket listen to incoming connections // Make the socket listen to incoming connections
bool sock_listen(Sock *sock, int backlog); bool sock_listen(Sock *sock);
// Accept connections from a socket // Accept connections from a socket
Sock *sock_accept(Sock *sock); Sock *sock_accept(Sock *sock);
@@ -100,6 +264,7 @@ void sock_log_error(const Sock *sock);
// Private functions // Private functions
void *sock__accept_thread(void *data); void *sock__accept_thread(void *data);
void sock__convert_addr(SockAddr *addr);
#ifdef __cplusplus #ifdef __cplusplus
} }
@@ -122,29 +287,7 @@ Sock *sock_create(SockAddrType domain, SockType type)
memset(sock, 0, sizeof(*sock)); memset(sock, 0, sizeof(*sock));
sock->type = type; sock->type = type;
sock->fd = socket(domain, type, 0);
int sock_domain = 0;
int sock_type = 0;
switch (domain) {
case SOCK_IPV4: sock_domain = AF_INET; break;
case SOCK_IPV6: sock_domain = AF_INET6; break;
default: {
free(sock);
return NULL;
} break;
}
switch (type) {
case SOCK_TCP: sock_type = SOCK_STREAM; break;
case SOCK_UDP: sock_type = SOCK_DGRAM; break;
default: {
free(sock);
return NULL;
} break;
}
sock->fd = socket(sock_domain, sock_type, 0);
if (sock->fd < 0) { if (sock->fd < 0) {
free(sock); free(sock);
return NULL; return NULL;
@@ -153,6 +296,7 @@ Sock *sock_create(SockAddrType domain, SockType type)
int enable = 1; int enable = 1;
if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR, if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR,
&enable, sizeof(enable)) < 0) { &enable, sizeof(enable)) < 0) {
close(sock->fd);
free(sock); free(sock);
return NULL; return NULL;
} }
@@ -169,46 +313,116 @@ SockAddr sock_addr(const char *addr, int port)
return sa; return sa;
} }
sa.port = port;
if (inet_pton(AF_INET, addr, &sa.ipv4.sin_addr) == 1) { if (inet_pton(AF_INET, addr, &sa.ipv4.sin_addr) == 1) {
sa.type = SOCK_IPV4; sa.type = SOCK_IPV4;
sa.ipv4.sin_family = AF_INET; sa.ipv4.sin_family = AF_INET;
sa.ipv4.sin_port = htons(port); sa.ipv4.sin_port = htons(port);
sa.len = sizeof(sa.ipv4); sa.len = sizeof(sa.ipv4);
strncpy(sa.str, addr, sizeof(sa.str)); snprintf(sa.str, sizeof(sa.str), "%s", addr);
} else if (inet_pton(AF_INET6, addr, &sa.ipv6.sin6_addr) == 1) { } else if (inet_pton(AF_INET6, addr, &sa.ipv6.sin6_addr) == 1) {
sa.type = SOCK_IPV6; sa.type = SOCK_IPV6;
sa.ipv6.sin6_family = AF_INET6; sa.ipv6.sin6_family = AF_INET6;
sa.ipv6.sin6_port = htons(port); sa.ipv6.sin6_port = htons(port);
sa.len = sizeof(sa.ipv6); sa.len = sizeof(sa.ipv6);
strncpy(sa.str, addr, sizeof(sa.str)); snprintf(sa.str, sizeof(sa.str), "%s", addr);
} else { }
struct addrinfo *res;
if (getaddrinfo(addr, NULL, NULL, &res) != 0) {
// TODO: specific errors from getaddrinfo are ignored for logging
sa.type = SOCK_ADDR_INVALID;
return sa; return sa;
} }
if (res->ai_family == AF_INET) { SockAddrList sock_dns(const char *addr, int port, SockAddrType addr_hint, SockType sock_hint)
{
SockAddrList list;
memset(&list, 0, sizeof(list));
if (addr == NULL) {
return list;
}
list.items = (SockAddr*)malloc(
sizeof(*list.items) * SOCK_ADDR_LIST_INITIAL_CAPACITY);
if (list.items == NULL) {
return list;
}
list.capacity = SOCK_ADDR_LIST_INITIAL_CAPACITY;
struct addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = (addr_hint != SOCK_ADDR_INVALID ? addr_hint : AF_UNSPEC);
hints.ai_socktype = sock_hint;
char port_str[8];
snprintf(port_str, sizeof(port_str), "%d", port);
struct addrinfo *res;
if (getaddrinfo(addr, port_str, &hints, &res) != 0) {
return list;
}
for (struct addrinfo *ai = res; ai != NULL; ai = ai->ai_next) {
SockAddr sa;
memset(&sa, 0, sizeof(sa));
bool skip = false;
switch (ai->ai_family) {
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in*)ai->ai_addr;
sa.type = SOCK_IPV4; sa.type = SOCK_IPV4;
sa.ipv4 = *(struct sockaddr_in*)res->ai_addr; sa.port = ntohs(sin->sin_port);
sa.ipv4.sin_port = htons(port); sa.ipv4 = *sin;
sa.len = sizeof(sa.ipv4); sa.len = sizeof(sa.ipv4);
inet_ntop(AF_INET, &sa.ipv4.sin_addr, sa.str, sizeof(sa.str)); inet_ntop(AF_INET, &sa.ipv4.sin_addr, sa.str, sizeof(sa.str));
} else { } break;
case AF_INET6: {
struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ai->ai_addr;
sa.type = SOCK_IPV6; sa.type = SOCK_IPV6;
sa.ipv6 = *(struct sockaddr_in6*)res->ai_addr; sa.port = ntohs(sin6->sin6_port);
sa.ipv6.sin6_port = htons(port); sa.ipv6 = *sin6;
sa.len = sizeof(sa.ipv6); sa.len = sizeof(sa.ipv6);
inet_ntop(AF_INET6, &sa.ipv6.sin6_addr, sa.str, sizeof(sa.str)); inet_ntop(AF_INET6, &sa.ipv6.sin6_addr, sa.str, sizeof(sa.str));
} break;
// Unreachable address family, skip address
default: {
skip = true;
} break;
}
if (skip) {
continue;
}
if (list.count >= list.capacity) {
list.capacity *= 2;
SockAddr *new_items = (SockAddr*)realloc(
list.items, list.capacity * sizeof(*list.items));
if (new_items == NULL) {
sock_addr_list_free(&list);
return list;
}
list.items = new_items;
}
list.items[list.count++] = sa;
} }
freeaddrinfo(res); freeaddrinfo(res);
return list;
} }
sa.port = port; void sock_addr_list_free(SockAddrList *list)
{
if (list == NULL) {
return;
}
return sa; free(list->items);
list->count = 0;
list->capacity = 0;
} }
bool sock_bind(Sock *sock, SockAddr addr) bool sock_bind(Sock *sock, SockAddr addr)
@@ -218,7 +432,7 @@ bool sock_bind(Sock *sock, SockAddr addr)
} }
if (bind(sock->fd, &addr.sockaddr, addr.len) < 0) { if (bind(sock->fd, &addr.sockaddr, addr.len) < 0) {
sock->last_error = strerror(errno); sock->last_errno = errno;
return false; return false;
} }
@@ -227,14 +441,14 @@ bool sock_bind(Sock *sock, SockAddr addr)
return true; return true;
} }
bool sock_listen(Sock *sock, int backlog) bool sock_listen(Sock *sock)
{ {
if (sock == NULL) { if (sock == NULL) {
return false; return false;
} }
if (listen(sock->fd, backlog) < 0) { if (listen(sock->fd, SOMAXCONN) < 0) {
sock->last_error = strerror(errno); sock->last_errno = errno;
return false; return false;
} }
@@ -247,47 +461,25 @@ Sock *sock_accept(Sock *sock)
return NULL; return NULL;
} }
int fd = accept(sock->fd, &sock->addr.sockaddr, &sock->addr.len);
if (fd < 0) {
sock->last_error = strerror(errno);
return NULL;
}
Sock *res = (Sock*)malloc(sizeof(*res)); Sock *res = (Sock*)malloc(sizeof(*res));
if (res == NULL) { if (res == NULL) {
sock->last_error = strerror(errno); sock->last_errno = errno;
return NULL; return NULL;
} }
memset(res, 0, sizeof(*res)); memset(res, 0, sizeof(*res));
res->addr.len = sock->addr.len;
int fd = accept(sock->fd, &res->addr.sockaddr, &res->addr.len);
if (fd < 0) {
free(res);
sock->last_errno = errno;
return NULL;
}
res->type = sock->type; res->type = sock->type;
res->fd = fd; res->fd = fd;
sock__convert_addr(&res->addr);
SockAddr *addr = &res->addr;
addr->len = sizeof(addr->sockaddr);
getpeername(res->fd, &addr->sockaddr, &addr->len);
// TODO: This is duplicated code from sock_recvfrom
switch (addr->sockaddr.sa_family) {
case AF_INET: {
struct sockaddr_in *ipv4 = &addr->ipv4;
addr->type = SOCK_IPV4;
addr->port = ntohs(ipv4->sin_port);
addr->len = sizeof(*ipv4);
inet_ntop(AF_INET, &ipv4->sin_addr, addr->str, sizeof(addr->str));
} break;
case AF_INET6: {
struct sockaddr_in6 *ipv6 = &addr->ipv6;
addr->type = SOCK_IPV6;
addr->port = ntohs(ipv6->sin6_port);
addr->len = sizeof(*ipv6);
inet_ntop(AF_INET6, &ipv6->sin6_addr, addr->str, sizeof(addr->str));
} break;
default: {
assert(false && "Unreachable address family");
} break;
}
return res; return res;
} }
@@ -307,7 +499,7 @@ bool sock_async_accept(Sock *sock, SockThreadCallback fn, void *user_data)
(SockThreadData*)malloc(sizeof(*thread_data)); (SockThreadData*)malloc(sizeof(*thread_data));
if (thread_data == NULL) { if (thread_data == NULL) {
sock_close(client); sock_close(client);
sock->last_error = strerror(errno); sock->last_errno = errno;
return false; return false;
} }
@@ -319,7 +511,7 @@ bool sock_async_accept(Sock *sock, SockThreadCallback fn, void *user_data)
if (pthread_create(&thread, NULL, sock__accept_thread, thread_data) != 0) { if (pthread_create(&thread, NULL, sock__accept_thread, thread_data) != 0) {
free(thread_data); free(thread_data);
sock_close(client); sock_close(client);
sock->last_error = strerror(errno); sock->last_errno = errno;
return false; return false;
} }
@@ -335,6 +527,7 @@ bool sock_connect(Sock *sock, SockAddr addr)
} }
if (connect(sock->fd, &addr.sockaddr, addr.len) < 0) { if (connect(sock->fd, &addr.sockaddr, addr.len) < 0) {
sock->last_errno = errno;
return false; return false;
} }
@@ -376,39 +569,27 @@ ssize_t sock_recvfrom(Sock *sock, void *buf, size_t size, SockAddr *addr)
return -1; return -1;
} }
struct sockaddr_storage sa_storage;
struct sockaddr *sa = NULL; struct sockaddr *sa = NULL;
socklen_t *len = NULL; socklen_t sa_len = 0;
socklen_t *len_ptr = NULL;
if (addr != NULL) { if (addr != NULL) {
sa = &addr->sockaddr; sa = (struct sockaddr*)&sa_storage;
len = &sock->addr.len; sa_len = sizeof(sa_storage);
len_ptr = &sa_len;
} }
ssize_t res = recvfrom(sock->fd, buf, size, 0, sa, len); ssize_t res = recvfrom(sock->fd, buf, size, 0, sa, len_ptr);
if (res < 0 || addr == NULL) { if (res < 0 || addr == NULL) {
return res; return res;
} }
switch (addr->sockaddr.sa_family) { if (addr != NULL) {
case AF_INET: { memset(addr, 0, sizeof(*addr));
struct sockaddr_in *ipv4 = &addr->ipv4; memcpy(&addr->sockaddr, sa, sa_len);
addr->type = SOCK_IPV4; addr->len = sa_len;
addr->port = ntohs(ipv4->sin_port); sock__convert_addr(addr);
addr->len = sizeof(*ipv4);
inet_ntop(AF_INET, &ipv4->sin_addr, addr->str, sizeof(addr->str));
} break;
case AF_INET6: {
struct sockaddr_in6 *ipv6 = &addr->ipv6;
addr->type = SOCK_IPV6;
addr->port = ntohs(ipv6->sin6_port);
addr->len = sizeof(*ipv6);
inet_ntop(AF_INET6, &ipv6->sin6_addr, addr->str, sizeof(addr->str));
} break;
default: {
assert(false && "Unreachable address family");
} break;
} }
return res; return res;
@@ -432,13 +613,7 @@ void sock_log_error(const Sock *sock)
return; return;
} }
if (sock->last_error == NULL) { fprintf(stderr, "SOCK ERROR: %s\n", strerror(sock->last_errno));
fprintf(stderr, "SOCK ERROR: socket is empty. errno says: %s\n",
strerror(errno));
return;
}
fprintf(stderr, "SOCK ERROR: %s\n", sock->last_error);
} }
void *sock__accept_thread(void *data) void *sock__accept_thread(void *data)
@@ -460,6 +635,35 @@ void *sock__accept_thread(void *data)
return NULL; return NULL;
} }
void sock__convert_addr(SockAddr *addr)
{
if (addr == NULL) {
return;
}
sa_family_t family = addr->sockaddr.sa_family;
switch (family) {
case AF_INET: {
struct sockaddr_in *ipv4 = &addr->ipv4;
addr->type = SOCK_IPV4;
addr->port = ntohs(ipv4->sin_port);
addr->len = sizeof(*ipv4);
inet_ntop(family, &ipv4->sin_addr, addr->str, sizeof(addr->str));
} break;
case AF_INET6: {
struct sockaddr_in6 *ipv6 = &addr->ipv6;
addr->type = SOCK_IPV6;
addr->port = ntohs(ipv6->sin6_port);
addr->len = sizeof(*ipv6);
inet_ntop(family, &ipv6->sin6_addr, addr->str, sizeof(addr->str));
} break;
default: {
addr->type = SOCK_ADDR_INVALID;
} break;
}
}
#ifdef __cplusplus #ifdef __cplusplus
} }
@@ -470,6 +674,8 @@ void *sock__accept_thread(void *data)
/* /*
Revision history: Revision history:
1.7.0 (2025-09-06) Header now includes documentation; New sock_dns()
function; major improvements and refactoring
1.6.3 (2025-08-24) Change SockThreadFn -> SockThreadCallback 1.6.3 (2025-08-24) Change SockThreadFn -> SockThreadCallback
1.6.2 (2025-08-03) Log errno error if sock last error is NULL 1.6.2 (2025-08-03) Log errno error if sock last error is NULL
1.6.1 (2025-08-03) Log errno error if sock is NULL in sock_log_error 1.6.1 (2025-08-03) Log errno error if sock is NULL in sock_log_error