Files
cHTTP/src/socket.h
T

344 lines
10 KiB
C

// This file (and its relative .c file) implements an asynchronous TCP/TLS
// server and client abstraction.
//
// It introduces the concept of a "socket manager", which is a pool of
// connection sockets and a listener socket. The listener is managed
// internally, which means the manager automatically accepts sockets
// from it and adds them to the pool.
//
// If the listener is configured using the function:
//
// socket_manager_listen_tcp
//
// the resulting connections will not use TLS. If instead the listener
// is configured using:
//
// socket_manager_listen_tls
//
// the listener will use TLS. Note that both functions can be used on
// the same manager to allow both plaintext and encrypted connections.
// Users may enable zero listeners, in which case only outgoing
// connections are allowed (more on this later).
//
// Once the manager is set up, one can wait for events by following
// this pattern:
//
// struct pollfd polled[...];
// int num_polled = socket_manager_register_events(sm, polled, max_polled);
// poll(polled, num_polled, -1);
//
// #define MAX_EVENTS ...
// SocketEvent events[MAX_EVENTS];
// int num_events = socket_manager_translate_events(sm, events, MAX_EVENTS, polled, num_polled);
// for (int i = 0; i < num_events; i++) {
// ... Here call socket_recv, socket_send, socket_close, ...
// }
//
// Note that from the user's perspective, there is no difference
// between connections that use plain TCP and those that use TCP/TLS.
//
// Users can also establish outgoing connections by calling the
// function:
//
// socket_connect
//
// Which allows the creation of a connection towards an host given
// its domain, IPv4, IPv6, or an array of them. This can be done both
// for TCP and TCP/TLS connection. Note that users that only intend
// to establish outgoing connection may omit the configuration of
// listeners entirely.
#ifdef _WIN32
#define NATIVE_SOCKET SOCKET
#define NATIVE_SOCKET_INVALID INVALID_SOCKET
#define CLOSE_NATIVE_SOCKET closesocket
#else
#define NATIVE_SOCKET int
#define NATIVE_SOCKET_INVALID -1
#define CLOSE_NATIVE_SOCKET close
#endif
typedef uint32_t SocketHandle;
#define SOCKET_HANDLE_INVALID ((SocketHandle) 0)
typedef uint16_t Port;
typedef enum {
SOCKET_EVENT_READY,
SOCKET_EVENT_DISCONNECT,
} SocketEventType;
typedef struct {
SocketEventType type;
SocketHandle handle;
void* user;
} SocketEvent;
// Internal use only
typedef enum {
// The Socket struct is unused
SOCKET_STATE_FREE,
// The state associated to a socket created
// by a connect operation that hasn't been
// processed yet.
SOCKET_STATE_PENDING,
// A connect() operation was started but is
// still pending.
SOCKET_STATE_CONNECTING,
// Outgoing connection was established, but
// a TLS handshake may need to be performed.
SOCKET_STATE_CONNECTED,
// Incoming connection was established, but
// a TLS handshake may need to be performed.
SOCKET_STATE_ACCEPTED,
// The connection was esablished, but the user
// wants to perform a read or write operation that
// would block.
SOCKET_STATE_ESTABLISHED_WAIT,
// The connection was established and it's possible
// to perform read or write operations on it without
// blocking.
SOCKET_STATE_ESTABLISHED_READY,
// The socket was marked to be closed.
SOCKET_STATE_SHUTDOWN,
// The current socket is was closed. The only
// valid thing to do here is free its resources.
SOCKET_STATE_DIED,
} SocketState;
typedef struct {
int refs;
char data[];
} RegisteredName;
// Internal use only
typedef struct {
union {
HTTP_IPv4 ipv4;
HTTP_IPv6 ipv6;
};
bool is_ipv4;
Port port;
#ifdef HTTPS_ENABLED
// When connecting to a peer using TLS, if the address
// was resolved from a registered name, that name is
// used to request the correct certificate once the TCP
// handshake is established, and therefore need to
// store it somewhere until that happens.
RegisteredName *name;
#endif
} AddressAndPort;
// Internal use only
typedef struct {
SocketState state;
// OS-specific socket type
NATIVE_SOCKET sock;
// Native socket events that need to be monitored
int events;
// If this is set, the raw socket handle shouldn't be monitored
bool silent;
// Generation counter to invalidate any SocketHandle
// referring to this socket when it is freed.
// Note that this counter may wrap but always skips
// the 0 value to ensure the 0 SocketHandle is always
// invalid.
uint16_t gen;
// User-provided context pointer
void *user;
// A single connect operation may involve
// trying to establish a connection towards
// one of a set of addresses.
int num_addr;
int next_addr;
union {
AddressAndPort addr; // When num_addr=1
AddressAndPort *addrs; // Dynamically allocated when num_addr>1
};
#ifdef HTTPS_ENABLED
ClientSecureContext *client_secure_context;
ServerSecureContext *server_secure_context;
SSL *ssl;
bool dont_verify_cert;
#endif
} Socket;
// Glorified array of sockets. This structure
// is private to the .c file associated to this
// header.
typedef struct {
// TCP listener sockets. The first is intended
// for plaintext, while the second is for TLS.
// The socket manager will accept and add new
// sockets to the pool automatically. Note that
// either may be unset. If both are unset, users
// can only create outgoing connections.
NATIVE_SOCKET plain_sock;
NATIVE_SOCKET secure_sock;
// Handles for the self-pipe trick necessary for
// other threads to wake up sockets blocked on
// poll().
NATIVE_SOCKET wait_sock;
NATIVE_SOCKET signal_sock;
// TLS contexts. One is used for outgoing connections
// (the client context) and one for incoming
// connections (server). If the secure_sock is
// set, the server context is initialized. If at
// least one connect was performed using TLS
// (and the flag is set), the client context is
// initialized.
bool at_least_one_secure_connect;
ClientSecureContext client_secure_context;
ServerSecureContext server_secure_context;
// If the socket manager needed to initialize some
// global state for its initialization, this flag
// will be set so that it will remember to cleanup
// that state during deinitialization.
bool global_cleanup;
// Array of sockets. Structs with state FREE
// are unused.
int num_used;
int max_used;
Socket *sockets;
} SocketManager;
// Instanciate a socket manager. Returns 0 on
// success and -1 on error.
int socket_manager_init(SocketManager *sm, Socket *socks,
int num_socks);
// Deinitialize a socket manager
void socket_manager_free(SocketManager *sm);
// Configure the socket manager to listen on
// the specified interface for TCP connections.
// Incoming connections will be automatically
// added to the internal pool. This function
// can only be used once per manager.
// Returns 0 on success, -1 on error.
int socket_manager_listen_tcp(SocketManager *sm,
HTTP_String addr, Port port, int backlog,
bool reuse_addr);
// Same as the previous function, but incoming
// connections will be interpreted as TLS. You
// can only call this function once per manager,
// but you can call this and the plaintext variant
// on the same manager to accept both plaintext
// and secure connections.
// Returns 0 on success, -1 on error.
int socket_manager_listen_tls(SocketManager *sm,
HTTP_String addr, Port port, int backlog,
bool reuse_addr, HTTP_String cert_file,
HTTP_String key_file);
// If the socket manager was configures to accept
// TLS connections, this adds additional certificates
// the client can use to verify the server's
// authenticity.
// Returns 0 on success, -1 on error.
int socket_manager_add_certificate(SocketManager *sm,
HTTP_String domain, HTTP_String cert_file, HTTP_String key_file);
// When a thread is blocked on a poll() call for
// descriptors associated to this socket manager,
// other threads can call this function to wake
// up that blocked thread.
// Returns 0 on success, -1 on error.
int socket_manager_wakeup(SocketManager *sm);
typedef struct {
void **ptrs;
struct pollfd *polled;
int num_polled;
} EventRegister;
// Resets the event register with the list of descriptors
// the socket manager wants monitored.
void socket_manager_register_events(SocketManager *sm,
EventRegister *reg);
// After poll() is called on the previously registered
// pollfd array and the revents fields are set, this
// function processes those events to produce higher-level
// socket events. Returns the number of socket events
// written to the output array, or -1 on error.
//
// The maximum number of events this will write
// to the events array is equal to the numero of
// socket structs provided to the socket manager
// via the init function.
int socket_manager_translate_events(SocketManager *sm,
SocketEvent *events, EventRegister reg);
typedef enum {
CONNECT_TARGET_NAME,
CONNECT_TARGET_IPV4,
CONNECT_TARGET_IPV6,
} ConnectTargetType;
typedef struct {
ConnectTargetType type;
Port port;
union {
HTTP_IPv4 ipv4;
HTTP_IPv6 ipv6;
HTTP_String name;
};
} ConnectTarget;
// Connect to one of the given targets. The socket
// manager will try to connecting to addresses until
// one succedes. If secure=true, the socket uses TLS.
// Returns 0 on success, -1 on error.
int socket_connect(SocketManager *sm, int num_targets,
ConnectTarget *targets, bool secure, bool dont_verify_cert,
void *user);
int socket_recv(SocketManager *sm, SocketHandle handle,
char *dst, int max);
int socket_send(SocketManager *sm, SocketHandle handle,
char *src, int len);
void socket_close(SocketManager *sm, SocketHandle handle);
// Returns -1 on error, 0 if the socket was accepted
// from the plaintext listener, or 1 if it was accepted
// by the secure listener.
bool socket_is_secure(SocketManager *sm, SocketHandle handle);
// Set the user pointer of a socket
void socket_set_user(SocketManager *sm, SocketHandle handle, void *user);
// Returns true iff the socket is ready for reading or
// writing.
bool socket_ready(SocketManager *sm, SocketHandle handle);
// When a socket is marked as silent it will not generate events
void socket_silent(SocketManager *sm, SocketHandle handle, bool value);