212 lines
8.7 KiB
C
212 lines
8.7 KiB
C
#ifndef TCP_INCLUDED
|
|
#define TCP_INCLUDED
|
|
|
|
#include "byte_queue.h"
|
|
|
|
// Abstraction over TCP and TLS sockets.
|
|
//
|
|
// It works by creating a pool of TCP connections. Connections can be added
|
|
// to the pool by connecting to other processes via the tcp_connect() function,
|
|
// or by adding them automatically as they arrive from other peers, if the
|
|
// pool is configured in listening mode. This allows the same abstraction to
|
|
// work for servers, clients, and nodes in a larger network that behave both
|
|
// as clients and servers.
|
|
//
|
|
// It features:
|
|
// - Cross-platform (Windows and Linux)
|
|
// - All I/O is multiplexed, which means slow connections will not stall faster ones.
|
|
// - Input and output buffering
|
|
// - Encryption via TLS (OpenSSL on Linux and SChannel on Windows)
|
|
|
|
// The TCP structure holds the state of a single instance. It is dynamically
|
|
// allocated internally so the caller doesn't need to read its contents.
|
|
typedef struct TCP TCP;
|
|
|
|
// Create an instance of the TCP subsystem. The max_conns argument is the
|
|
// maximum number of TCP connection this instance will be able to manage.
|
|
TCP *tcp_init(int max_conns);
|
|
|
|
// Free a TCP subsystem instance. Any resources provided by the subsystem
|
|
// will be forcefully released too.
|
|
void tcp_free(TCP *tcp);
|
|
|
|
// Enable a listening interface for this TCP pool. Connections accepted via
|
|
// this interface will be plaintext.
|
|
int tcp_listen_tcp(TCP *tcp, Address addr);
|
|
|
|
// Enable a listening interface for this TCP pool. Connections accepted via
|
|
// this interface will be encrypted. A single TCP pool may be configured for
|
|
// plaintext and encrypted connections at the same time. From the user's
|
|
// perspective, the interface from which a connections was accepted is totally
|
|
// transparent.
|
|
// The cert_file and key_file parameters refer to the certificate file and
|
|
// associated private key file to use for encryption, both in PEM format.
|
|
int tcp_listen_tls(TCP *tcp, Address addr, string cert_file, string key_file);
|
|
|
|
// If the TCP pool is configured in TLS mode (tcp_listen_tls was called), this
|
|
// function can be used to add an additional certificate. Connecting sockets
|
|
// will be able to pick the right certificate by expressing the domain name they
|
|
// are expecting to talk to.
|
|
int tcp_add_cert(TCP *tcp, string domain, string cert_file, string key_file);
|
|
|
|
// Handle structure representing a TCP connection of the TCP pool. The contents
|
|
// should not be interpreted by users.
|
|
typedef struct {
|
|
TCP *tcp;
|
|
int idx;
|
|
int gen;
|
|
} TCP_Handle;
|
|
|
|
// Add a connection to the TCP pool by establishing one towards the specified
|
|
// peer. The addrs array (of size num_addrs) contains the list of IP addresses
|
|
// for the host. The TCP pool will try each address one by one until a connection
|
|
// is established. If the secure argument is true, the connection will be
|
|
// encrypted.
|
|
int tcp_connect(TCP *tcp, bool secure, Address *addrs, int num_addrs, TCP_Handle *handle);
|
|
|
|
// Forward-declare poll item type. The user must include poll.h (Linux) or
|
|
// winsock2.h (Windows) to get this definition (and the definition of poll()
|
|
// and WSAPoll()).
|
|
struct pollfd;
|
|
|
|
// Initialize an array of pollfd structures with all the descriptor the pool
|
|
// needs to monitor with the associated events. The array is such that the caller
|
|
// can then call poll() on it to block execution of the process while the TCP
|
|
// pool has no work to be done. The number of items written to the array is
|
|
// returned.
|
|
// The ptrs array is some state set by the TCP pool to associate metadata to
|
|
// each descriptor for internal book-keping.
|
|
int tcp_register_events(TCP *tcp, void **ptrs, struct pollfd *pfds, int cap);
|
|
|
|
// After poll() is called and revents flags are set on the array initialized by
|
|
// tcp_register_events, this function can be called to go over the triggered
|
|
// events and update the internal state of the TCP pool. The ptrs array should
|
|
// be passed in as it was initialized by the tcp_register_events as-is.
|
|
void tcp_process_events(TCP *tcp, void **ptrs, struct pollfd *pfds, int num);
|
|
|
|
// Flags for the "flags" field in TCP_Event.
|
|
enum {
|
|
TCP_EVENT_NEW = 1<<0,
|
|
TCP_EVENT_HUP = 1<<1,
|
|
TCP_EVENT_DATA = 1<<2,
|
|
};
|
|
|
|
// See tcp_next_event.
|
|
typedef struct {
|
|
int flags;
|
|
TCP_Handle handle;
|
|
} TCP_Event;
|
|
|
|
// After tcp_process_events is called, some new events may be available for the
|
|
// user. This function returns the next event in the TCP pool.
|
|
//
|
|
// If an event is available, true is returned and the event structure is
|
|
// initialized with the handle to the connection and flags that identify the
|
|
// events that triggered associated to that handle. The events are:
|
|
// TCP_EVENT_NEW: This connection was just established. It's the first time the
|
|
// user's code sees it.
|
|
// TCP_EVENT_HUP: The peer disconnected and therefore the user should close
|
|
// the connection associated to it.
|
|
// TCP_EVENT_DATA: Some bytes were buffered for this connection.
|
|
// (It's possible that this event to triggered with 0 new bytes,
|
|
// for instance if the user called tcp_mark_ready)
|
|
// Any of these events may happen at the same time. They are not exclusive.
|
|
//
|
|
// If no event is available, false is returned.
|
|
//
|
|
// The general way one would use is function is by doing:
|
|
// tcp_process_events(...)
|
|
// for (TCP_Event event; tcp_next_event(tcp, &event); ) {
|
|
// if (event.flags & TCP_EVENT_NEW) {
|
|
// // ...
|
|
// }
|
|
//
|
|
// if (event.flags & TCP_EVENT_DATA) {
|
|
// // ...
|
|
// }
|
|
//
|
|
// if (event.flags & TCP_EVENT_HUP) {
|
|
// tcp_close(event.handle);
|
|
// }
|
|
// }
|
|
//
|
|
// Note that the handle returned by the TCP_EVENT_NEW event
|
|
// (and all subsequent events) will be valid until the user
|
|
// calls tcp_close() on it.
|
|
bool tcp_next_event(TCP *tcp, TCP_Event *event);
|
|
|
|
// Start a read operation into the TCP connection's input buffer.
|
|
//
|
|
// This function returns a slice of the input buffer. The user
|
|
// may inspect the contents and decide to consume some bytes from
|
|
// the buffer by calling tcp_read_ack(handle, num) with the number
|
|
// of bytes. Reading the input buffer with this function locks the
|
|
// buffer not allowing new bytes to be buffered. For this reason
|
|
// tcp_read_ack(handle, 0) must be called even if no bytes were
|
|
// consumed.
|
|
//
|
|
// Note that returned bytes are plaintext regardless of whether
|
|
// the connection was accepted via the plaintext or encrypted
|
|
// listening interface.
|
|
string tcp_read_buf(TCP_Handle handle);
|
|
|
|
// Complete a read operation into the TCP connection's input buffer.
|
|
void tcp_read_ack(TCP_Handle handle, int num);
|
|
|
|
// Start a write operation into the TCP connection's output buffer.
|
|
//
|
|
// This function is specular to tcp_read_buf except the user must
|
|
// write into the returned slice instead of reading from it.
|
|
string tcp_write_buf(TCP_Handle handle);
|
|
|
|
// Complete a write operation into the TCP connection's output buffer.
|
|
// The num argument is the number of bytes written into the slice by
|
|
// the user.
|
|
void tcp_write_ack(TCP_Handle handle, int num);
|
|
|
|
// See tcp_write_off
|
|
typedef ByteQueueOffset TCP_Offset;
|
|
|
|
// Returns the offset of the next byte that would be written into the
|
|
// output buffer.
|
|
//
|
|
// This offset is such that removing previous data from the output
|
|
// buffer will not invalidate such offset. It's useful to calcuate
|
|
// the number of bytes between to offsets of apply operations on
|
|
// bytes since a given offset on the buffer.
|
|
TCP_Offset tcp_write_off(TCP_Handle handle);
|
|
|
|
// Writes bytes into the TCP connections' output buffer. It's just
|
|
// a shorthand for tcp_write_buf/tcp_write_ack.
|
|
void tcp_write(TCP_Handle handle, string data);
|
|
|
|
// Writes bytes at the specified offset of the output buffer. Note
|
|
// that this only overwrites bytes in the buffer and does not grow
|
|
// its size, therefore the user must have already inserted some values
|
|
// after that offset. Also, the region referred by the offset must
|
|
// still be into the buffer and not be read out.
|
|
void tcp_patch(TCP_Handle handle, TCP_Offset offset, string data);
|
|
|
|
// Removes all bytes in the TCP connection's output buffer from the
|
|
// specified offset onwards.
|
|
void tcp_clear_from_offset(TCP_Handle handle, TCP_Offset offset);
|
|
|
|
// Close a TCP connection. Previously buffered output bytes will be
|
|
// sent out asynchronously.
|
|
void tcp_close(TCP_Handle handle);
|
|
|
|
// Associate an opaque pointer value to this connection. The tcp_get_user_ptr
|
|
// can be used to retrieve the pointer at any time.
|
|
void tcp_set_user_ptr(TCP_Handle handle, void *user_ptr);
|
|
|
|
// Retrieve the user pointer associated to a TCP connection. If no user
|
|
// pointer was previously set, NULL is returned.
|
|
void *tcp_get_user_ptr(TCP_Handle handle);
|
|
|
|
// Mark the TCP connection as "ready" causing it to be returned once more
|
|
// by the tcp_next_event() function with the TCP_EVENT_DATA flag set, even
|
|
// if no more data was buffered.
|
|
void tcp_mark_ready(TCP_Handle handle);
|
|
|
|
#endif // TCP_INCLUDED
|