v1.7.0 release
This commit is contained in:
@@ -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; }
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
@@ -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
26
examples/06-dig.c
Normal 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;
|
||||||
|
}
|
||||||
@@ -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
438
sock.h
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user