Compare commits
13 Commits
46d7ed775d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28f4011780 | ||
|
|
077bd91f98 | ||
|
|
8c0ccb628e | ||
|
|
440a18edc6 | ||
|
|
d24dfd39a7 | ||
|
|
7e55bd0194 | ||
|
|
15334be5bf | ||
|
|
ddc2147991 | ||
|
|
e7e6f2eb9c | ||
|
|
a9462d0880 | ||
|
|
42d126f01a | ||
|
|
024377795d | ||
|
|
ad986c777a |
@@ -13,7 +13,7 @@ int main(void)
|
||||
|
||||
SockAddr addr = sock_addr("0.0.0.0", 6969);
|
||||
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);
|
||||
if (client == NULL) { err = "accept"; goto defer; }
|
||||
@@ -12,7 +12,7 @@ int main(void)
|
||||
|
||||
SockAddr addr = sock_addr("::", 6969);
|
||||
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);
|
||||
if (client == NULL) { err = true; goto close; }
|
||||
31
examples/04-hello_client_v6_udp.c
Normal file
31
examples/04-hello_client_v6_udp.c
Normal file
@@ -0,0 +1,31 @@
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define SOCK_IMPLEMENTATION
|
||||
#include "sock.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bool err = false;
|
||||
|
||||
Sock *s = sock_create(SOCK_IPV6, SOCK_UDP);
|
||||
if (s == NULL) { err = "create"; goto defer; }
|
||||
|
||||
SockAddr addr = sock_addr("::1", 6969);
|
||||
|
||||
const char *msg = "Hello from client!";
|
||||
char buf[128];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
|
||||
sock_sendto(s, msg, strlen(msg), addr);
|
||||
sock_recvfrom(s, buf, sizeof(buf), NULL);
|
||||
|
||||
printf("%s:%d: %.*s\n", addr.str, addr.port,
|
||||
(int)sizeof(buf), buf);
|
||||
|
||||
defer:
|
||||
if (err) sock_log_error(s);
|
||||
sock_close(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
32
examples/04-hello_server_v6_udp.c
Normal file
32
examples/04-hello_server_v6_udp.c
Normal file
@@ -0,0 +1,32 @@
|
||||
#include <stdio.h>
|
||||
|
||||
#define SOCK_IMPLEMENTATION
|
||||
#include "sock.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
bool err = false;
|
||||
|
||||
Sock *server = sock_create(SOCK_IPV6, SOCK_UDP);
|
||||
if (server == NULL) { err = true; goto close; }
|
||||
|
||||
SockAddr addr = sock_addr("::", 6969);
|
||||
if (!sock_bind(server, addr)) { err = true; goto close; }
|
||||
|
||||
char buf[128];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
const char *msg = "Hello from server!";
|
||||
SockAddr client_addr = {0};
|
||||
|
||||
sock_recvfrom(server, buf, sizeof(buf), &client_addr);
|
||||
sock_sendto(server, msg, strlen(msg), client_addr);
|
||||
|
||||
printf("%s:%d: %.*s\n", client_addr.str, client_addr.port,
|
||||
(int)sizeof(buf), buf);
|
||||
|
||||
close:
|
||||
if (err) sock_log_error(server);
|
||||
sock_close(server);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5,12 +5,16 @@
|
||||
|
||||
int main(void)
|
||||
{
|
||||
SockAddr addr = sock_addr("example.com", 80);
|
||||
if (addr.type == SOCK_ADDR_INVALID) {
|
||||
fprintf(stderr, "ERROR: Could not resolve address %s\n", addr.str);
|
||||
const char *domain_name = "example.com";
|
||||
SockAddrList addr_list = sock_dns(domain_name, 80, SOCK_IPV4, SOCK_TCP);
|
||||
if (addr_list.count == 0) {
|
||||
fprintf(stderr, "ERROR: Could not resolve address %s\n", domain_name);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SockAddr addr = addr_list.items[0];
|
||||
sock_addr_list_free(&addr_list);
|
||||
|
||||
printf("%s:%d\n", addr.str, addr.port);
|
||||
|
||||
Sock *s = sock_create(addr.type, SOCK_TCP);
|
||||
@@ -30,7 +34,8 @@ int main(void)
|
||||
const char *req =
|
||||
"GET / HTTP/1.1\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));
|
||||
|
||||
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");
|
||||
|
||||
if (!sock_listen(server, 10)) {
|
||||
if (!sock_listen(server)) {
|
||||
fprintf(stderr, "ERROR: Could not listen on socket\n");
|
||||
sock_close(server);
|
||||
return EXIT_FAILURE;
|
||||
613
sock.h
613
sock.h
@@ -1,14 +1,175 @@
|
||||
// sock.h - v1.6.3 - MIT License - https://github.com/seajee/sock.h
|
||||
|
||||
/***************************************************************\
|
||||
# #
|
||||
# @@@@@@ #
|
||||
# @ @ #
|
||||
# @====@ #
|
||||
# @ @ sock.h - v1.7.5 #
|
||||
# @ @ 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 = sock_dns("example.com", 0, 0, 0);
|
||||
// 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_send_all(Sock *sock, const void *buf, size_t size)
|
||||
//
|
||||
// Same as sock_send() but ensures that all of the content of buf will be
|
||||
// sent. On error a negative value is 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_recv_all(Sock *sock, void *buf, size_t size);
|
||||
//
|
||||
// Same as sock_recv() but ensures that the specified size of bytes will be
|
||||
// received. On error a negative value is 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_
|
||||
#define SOCK_H_
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <pthread.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -16,14 +177,16 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define SOCK_ADDR_LIST_INITIAL_CAPACITY 16
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" { // Prevent name mangling
|
||||
#endif // __cplusplus
|
||||
|
||||
typedef enum {
|
||||
SOCK_ADDR_INVALID,
|
||||
SOCK_IPV4,
|
||||
SOCK_IPV6
|
||||
SOCK_ADDR_INVALID = 0,
|
||||
SOCK_IPV4 = AF_INET,
|
||||
SOCK_IPV6 = AF_INET6
|
||||
} SockAddrType;
|
||||
|
||||
typedef struct {
|
||||
@@ -38,16 +201,23 @@ typedef struct {
|
||||
socklen_t len;
|
||||
} SockAddr;
|
||||
|
||||
typedef struct {
|
||||
SockAddr *items; // Dynamic array of SockAddr
|
||||
size_t count;
|
||||
size_t capacity;
|
||||
} SockAddrList;
|
||||
|
||||
typedef enum {
|
||||
SOCK_TCP,
|
||||
SOCK_UDP
|
||||
SOCK_TYPE_INVALID = 0,
|
||||
SOCK_TCP = SOCK_STREAM,
|
||||
SOCK_UDP = SOCK_DGRAM
|
||||
} SockType;
|
||||
|
||||
typedef struct {
|
||||
SockType type; // Socket type
|
||||
SockAddr addr; // Socket address
|
||||
int fd; // File descriptor
|
||||
char *last_error; // Last error about this socket
|
||||
int last_errno; // Last error about this socket
|
||||
} Sock;
|
||||
|
||||
typedef void (*SockThreadCallback)(Sock *sock, void *user_data);
|
||||
@@ -64,11 +234,17 @@ Sock *sock_create(SockAddrType domain, SockType type);
|
||||
// Create a SockAddr structure from primitives
|
||||
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
|
||||
bool sock_bind(Sock *sock, SockAddr addr);
|
||||
|
||||
// Make the socket listen to incoming connections
|
||||
bool sock_listen(Sock *sock, int backlog);
|
||||
bool sock_listen(Sock *sock);
|
||||
|
||||
// Accept connections from a socket
|
||||
Sock *sock_accept(Sock *sock);
|
||||
@@ -81,9 +257,11 @@ bool sock_connect(Sock *sock, SockAddr addr);
|
||||
|
||||
// Send data through a socket
|
||||
ssize_t sock_send(Sock *sock, const void *buf, size_t size);
|
||||
ssize_t sock_send_all(Sock *sock, const void *buf, size_t size);
|
||||
|
||||
// Receive data from a socket
|
||||
ssize_t sock_recv(Sock *sock, void *buf, size_t size);
|
||||
ssize_t sock_recv_all(Sock *sock, void *buf, size_t size);
|
||||
|
||||
// Send data through a socket in connectionless mode
|
||||
ssize_t sock_sendto(Sock *sock, const void *buf, size_t size, SockAddr addr);
|
||||
@@ -99,6 +277,7 @@ void sock_log_error(const Sock *sock);
|
||||
|
||||
// Private functions
|
||||
void *sock__accept_thread(void *data);
|
||||
void sock__convert_addr(SockAddr *addr);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
@@ -121,29 +300,7 @@ Sock *sock_create(SockAddrType domain, SockType type)
|
||||
memset(sock, 0, sizeof(*sock));
|
||||
|
||||
sock->type = type;
|
||||
|
||||
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);
|
||||
sock->fd = socket(domain, type, 0);
|
||||
if (sock->fd < 0) {
|
||||
free(sock);
|
||||
return NULL;
|
||||
@@ -152,6 +309,7 @@ Sock *sock_create(SockAddrType domain, SockType type)
|
||||
int enable = 1;
|
||||
if (setsockopt(sock->fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
&enable, sizeof(enable)) < 0) {
|
||||
close(sock->fd);
|
||||
free(sock);
|
||||
return NULL;
|
||||
}
|
||||
@@ -168,46 +326,121 @@ SockAddr sock_addr(const char *addr, int port)
|
||||
return sa;
|
||||
}
|
||||
|
||||
sa.port = port;
|
||||
|
||||
if (inet_pton(AF_INET, addr, &sa.ipv4.sin_addr) == 1) {
|
||||
sa.type = SOCK_IPV4;
|
||||
sa.ipv4.sin_family = AF_INET;
|
||||
sa.ipv4.sin_port = htons(port);
|
||||
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) {
|
||||
sa.type = SOCK_IPV6;
|
||||
sa.ipv6.sin6_family = AF_INET6;
|
||||
sa.ipv6.sin6_port = htons(port);
|
||||
sa.len = sizeof(sa.ipv6);
|
||||
strncpy(sa.str, addr, sizeof(sa.str));
|
||||
} 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;
|
||||
snprintf(sa.str, sizeof(sa.str), "%s", addr);
|
||||
}
|
||||
|
||||
if (res->ai_family == AF_INET) {
|
||||
return sa;
|
||||
}
|
||||
|
||||
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 *service = NULL;
|
||||
char port_str[16];
|
||||
if (port > 0) {
|
||||
snprintf(port_str, sizeof(port_str), "%d", port);
|
||||
service = port_str;
|
||||
}
|
||||
|
||||
struct addrinfo *res;
|
||||
if (getaddrinfo(addr, service, &hints, &res) != 0) {
|
||||
sock_addr_list_free(&list);
|
||||
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.ipv4 = *(struct sockaddr_in*)res->ai_addr;
|
||||
sa.ipv4.sin_port = htons(port);
|
||||
sa.port = ntohs(sin->sin_port);
|
||||
sa.ipv4 = *sin;
|
||||
sa.len = sizeof(sa.ipv4);
|
||||
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.ipv6 = *(struct sockaddr_in6*)res->ai_addr;
|
||||
sa.ipv6.sin6_port = htons(port);
|
||||
sa.port = ntohs(sin6->sin6_port);
|
||||
sa.ipv6 = *sin6;
|
||||
sa.len = sizeof(sa.ipv6);
|
||||
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);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void sock_addr_list_free(SockAddrList *list)
|
||||
{
|
||||
if (list == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
sa.port = port;
|
||||
|
||||
return sa;
|
||||
free(list->items);
|
||||
list->count = 0;
|
||||
list->capacity = 0;
|
||||
}
|
||||
|
||||
bool sock_bind(Sock *sock, SockAddr addr)
|
||||
@@ -217,7 +450,7 @@ bool sock_bind(Sock *sock, SockAddr addr)
|
||||
}
|
||||
|
||||
if (bind(sock->fd, &addr.sockaddr, addr.len) < 0) {
|
||||
sock->last_error = strerror(errno);
|
||||
sock->last_errno = errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -226,14 +459,14 @@ bool sock_bind(Sock *sock, SockAddr addr)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool sock_listen(Sock *sock, int backlog)
|
||||
bool sock_listen(Sock *sock)
|
||||
{
|
||||
if (sock == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (listen(sock->fd, backlog) < 0) {
|
||||
sock->last_error = strerror(errno);
|
||||
if (listen(sock->fd, SOMAXCONN) < 0) {
|
||||
sock->last_errno = errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -246,47 +479,30 @@ Sock *sock_accept(Sock *sock)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fd = accept(sock->fd, &sock->addr.sockaddr, &sock->addr.len);
|
||||
if (fd < 0) {
|
||||
sock->last_error = strerror(errno);
|
||||
if (sock->type != SOCK_TCP) {
|
||||
sock->last_errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Sock *res = (Sock*)malloc(sizeof(*res));
|
||||
if (res == NULL) {
|
||||
sock->last_error = strerror(errno);
|
||||
sock->last_errno = errno;
|
||||
return NULL;
|
||||
}
|
||||
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->fd = fd;
|
||||
|
||||
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;
|
||||
}
|
||||
sock__convert_addr(&res->addr);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -297,6 +513,11 @@ bool sock_async_accept(Sock *sock, SockThreadCallback fn, void *user_data)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sock->type != SOCK_TCP) {
|
||||
sock->last_errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
Sock *client = sock_accept(sock);
|
||||
if (client == NULL) {
|
||||
return false;
|
||||
@@ -306,7 +527,7 @@ bool sock_async_accept(Sock *sock, SockThreadCallback fn, void *user_data)
|
||||
(SockThreadData*)malloc(sizeof(*thread_data));
|
||||
if (thread_data == NULL) {
|
||||
sock_close(client);
|
||||
sock->last_error = strerror(errno);
|
||||
sock->last_errno = errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -318,7 +539,7 @@ bool sock_async_accept(Sock *sock, SockThreadCallback fn, void *user_data)
|
||||
if (pthread_create(&thread, NULL, sock__accept_thread, thread_data) != 0) {
|
||||
free(thread_data);
|
||||
sock_close(client);
|
||||
sock->last_error = strerror(errno);
|
||||
sock->last_errno = errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -333,7 +554,13 @@ bool sock_connect(Sock *sock, SockAddr addr)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (sock->type != SOCK_TCP) {
|
||||
sock->last_errno = EINVAL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (connect(sock->fd, &addr.sockaddr, addr.len) < 0) {
|
||||
sock->last_errno = errno;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -344,70 +571,164 @@ bool sock_connect(Sock *sock, SockAddr addr)
|
||||
|
||||
ssize_t sock_send(Sock *sock, const void *buf, size_t size)
|
||||
{
|
||||
if (sock == NULL || buf == NULL) {
|
||||
if (sock == NULL || buf == NULL || sock->type != SOCK_TCP) {
|
||||
if (sock != NULL) {
|
||||
sock->last_errno = EINVAL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return send(sock->fd, buf, size, 0);
|
||||
while (true) {
|
||||
ssize_t n = send(sock->fd, buf, size, MSG_NOSIGNAL);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
sock->last_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t sock_send_all(Sock *sock, const void *buf, size_t size)
|
||||
{
|
||||
if (sock == NULL || buf == NULL || sock->type != SOCK_TCP) {
|
||||
if (sock != NULL) {
|
||||
sock->last_errno = EINVAL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t *ptr = (uint8_t*)buf;
|
||||
size_t remaining = size;
|
||||
|
||||
while (remaining > 0) {
|
||||
ssize_t n = sock_send(sock, ptr, remaining);
|
||||
if (n < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ptr += n;
|
||||
remaining -= n;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
ssize_t sock_recv(Sock *sock, void *buf, size_t size)
|
||||
{
|
||||
if (sock == NULL || buf == NULL) {
|
||||
if (sock == NULL || buf == NULL || sock->type != SOCK_TCP) {
|
||||
if (sock != NULL) {
|
||||
sock->last_errno = EINVAL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return recv(sock->fd, buf, size, 0);
|
||||
while (true) {
|
||||
ssize_t n = recv(sock->fd, buf, size, 0);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
sock->last_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t sock_recv_all(Sock *sock, void *buf, size_t size)
|
||||
{
|
||||
if (sock == NULL || buf == NULL || sock->type != SOCK_TCP) {
|
||||
if (sock != NULL) {
|
||||
sock->last_errno = EINVAL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t *ptr = (uint8_t*)buf;
|
||||
size_t remaining = size;
|
||||
ssize_t total = 0;
|
||||
|
||||
while (remaining > 0) {
|
||||
ssize_t n = sock_recv(sock, ptr, remaining);
|
||||
if (n < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
return total;
|
||||
}
|
||||
ptr += n;
|
||||
remaining -= n;
|
||||
total += n;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
ssize_t sock_sendto(Sock *sock, const void *buf, size_t size, SockAddr addr)
|
||||
{
|
||||
if (sock == NULL || buf == NULL) {
|
||||
if (sock == NULL || buf == NULL || sock->type != SOCK_UDP) {
|
||||
if (sock != NULL) {
|
||||
sock->last_errno = EINVAL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sendto(sock->fd, buf, size, 0, &addr.sockaddr, addr.len);
|
||||
while (true) {
|
||||
ssize_t n = sendto(sock->fd, buf, size, MSG_NOSIGNAL, &addr.sockaddr,
|
||||
addr.len);
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
sock->last_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t sock_recvfrom(Sock *sock, void *buf, size_t size, SockAddr *addr)
|
||||
{
|
||||
if (sock == NULL || buf == NULL) {
|
||||
if (sock == NULL || buf == NULL || sock->type != SOCK_UDP) {
|
||||
if (sock != NULL) {
|
||||
sock->last_errno = EINVAL;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_storage sa_storage;
|
||||
struct sockaddr *sa = NULL;
|
||||
socklen_t *len = NULL;
|
||||
socklen_t sa_len = 0;
|
||||
socklen_t *len_ptr = NULL;
|
||||
|
||||
if (addr != NULL) {
|
||||
sa = &addr->sockaddr;
|
||||
len = &sock->addr.len;
|
||||
sa = (struct sockaddr*)&sa_storage;
|
||||
sa_len = sizeof(sa_storage);
|
||||
len_ptr = &sa_len;
|
||||
}
|
||||
|
||||
ssize_t res = recvfrom(sock->fd, buf, size, 0, sa, len);
|
||||
if (res < 0 || addr == NULL) {
|
||||
return res;
|
||||
ssize_t res = 0;
|
||||
while (true) {
|
||||
res = recvfrom(sock->fd, buf, size, 0, sa, len_ptr);
|
||||
if (res < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
sock->last_errno = errno;
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
if (addr != NULL) {
|
||||
memset(addr, 0, sizeof(*addr));
|
||||
memcpy(&addr->sockaddr, sa, sa_len);
|
||||
addr->len = sa_len;
|
||||
sock__convert_addr(addr);
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -419,6 +740,15 @@ void sock_close(Sock *sock)
|
||||
return;
|
||||
}
|
||||
|
||||
shutdown(sock->fd, SHUT_WR);
|
||||
uint8_t buffer[1024];
|
||||
while (true) {
|
||||
ssize_t n = sock_recv(sock, buffer, sizeof(buffer));
|
||||
if (n <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
close(sock->fd);
|
||||
free(sock);
|
||||
}
|
||||
@@ -431,13 +761,7 @@ void sock_log_error(const Sock *sock)
|
||||
return;
|
||||
}
|
||||
|
||||
if (sock->last_error == NULL) {
|
||||
fprintf(stderr, "SOCK ERROR: socket is empty. errno says: %s\n",
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "SOCK ERROR: %s\n", sock->last_error);
|
||||
fprintf(stderr, "SOCK ERROR: %s\n", strerror(sock->last_errno));
|
||||
}
|
||||
|
||||
void *sock__accept_thread(void *data)
|
||||
@@ -459,6 +783,35 @@ void *sock__accept_thread(void *data)
|
||||
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
|
||||
}
|
||||
@@ -469,6 +822,16 @@ void *sock__accept_thread(void *data)
|
||||
/*
|
||||
Revision history:
|
||||
|
||||
1.7.5 (2025-12-13) Return false instead of NULL when returning a bool;
|
||||
Fixed memory leak in sock_dns()
|
||||
1.7.4 (2025-12-13) Ignore signals in send and sendto
|
||||
1.7.3 (2025-09-20) Changed sock_send_all() signature; drain buffers on
|
||||
sock_close() to prevent data loss
|
||||
1.7.2 (2025-09-17) New functions sock_recv_all() and sock_send_all();
|
||||
Improved error reporting.
|
||||
1.7.1 (2025-09-07) Pass NULL to getaddrinfo if port is 0 in sock_dns()
|
||||
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.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
|
||||
|
||||
Reference in New Issue
Block a user