CTaps 0.3.0
A C implementation of the Transport Services API (RFC 9621 - 9623)
Loading...
Searching...
No Matches
CTaps

A C implementation of IETF Transport Services (TAPS) with support for QUIC, TCP and UDP.

CTaps provides an asynchronous, callback-based interface for network connections. It supports multistreaming, connection migration (as a proof of concept), session resumption, candidate gathering and candidate racing. Internally it uses Picoquic and libuv.

Transport Services is described in:

CTaps documentation is available on GitHub pages, with the most useful overview being the topics page

Core Structures

CTaps implements several key abstractions from RFC 9622. Among these, the most central to communication are:

RFC 9622 Concept CTaps Equivalent Description
Preconnection ct_preconnection_t Configuration object for setting up Connections before establishment.
Connection ct_connection_t Active connection created from a Preconnection.
Listener ct_listener_t Listener for receiving incoming Connections created from a Preconnection.
Message ct_message_t A single message delivered by one of the underlying protocols.
Local Endpoint ct_local_endpoint_t A generic or resolved local address to communicate from.
Remote Endpoint ct_remote_endpoint_t A generic or resolved remote address to communicate with.

Several other abstractions exist to set up the underlying Connection.

Examples

Example CTaps client

#include <arpa/inet.h>
#include <ctaps.h>
#include <stdio.h>
#include <string.h>
void close_on_message_received(ct_connection_t* connection,
ct_message_t* received_message,
ct_message_context_t* message_context) {
);
printf("Received message: %s on port %d\n",
ct_message_get_content(received_message), port);
ct_connection_close(connection);
}
void send_message_and_receive(ct_connection_t* connection) {
ct_message_t* message =
ct_message_new_with_content("ping", strlen("ping") + 1);
// CTaps takes a deep copy of the passed content,
// so the message can be freed after this returns
ct_send_message(connection, message);
ct_message_free(message);
ct_receive_callbacks_t receive_message_request = {
.receive_callback = close_on_message_received,
};
ct_receive_message(connection, &receive_message_request);
}
void free_on_connection_closed(ct_connection_t* connection) {
ct_connection_free(connection);
}
int main() {
ct_initialize(); // Init global state
// Create remote endpoint (where we will try to connect to)
ct_remote_endpoint_with_ipv4(remote_endpoint, inet_addr("127.0.0.1"));
ct_remote_endpoint_with_port(remote_endpoint, 1234); // example port
// Create transport properties
ct_transport_properties_t* transport_properties =
// selection properties decide which protocol(s) will be used,
// if multiple are compatible with our requirements,
// TCP is preferred with this requirement
transport_properties, AVOID);
const ct_remote_endpoint_t* remotes[] = {remote_endpoint};
// Create preconnection
// No local endpoint, so will bind to wildcard
NULL, 0, remotes, 1, transport_properties, NULL);
ct_connection_callbacks_t connection_callbacks = {
.ready = send_message_and_receive,
.closed = free_on_connection_closed
};
// Gather potential endpoints and start racing, when event loop starts
preconnection,
&connection_callbacks);
if (rc < 0) {
perror("Error in initiating connection\n");
return rc;
}
// Block until all connections close (or no connection can be established)
// Cleanup
ct_preconnection_free(preconnection);
ct_transport_properties_free(transport_properties);
ct_remote_endpoint_free(remote_endpoint);
return 0;
}
const ct_local_endpoint_t * ct_message_context_get_local_endpoint(const ct_message_context_t *message_context)
Get the local endpoint from a message context.
void ct_connection_close(ct_connection_t *connection)
Close a connection gracefully.
int ct_receive_message(ct_connection_t *connection, const ct_receive_callbacks_t *receive_callbacks)
Register callbacks to receive messages on a connection.
int ct_send_message(ct_connection_t *connection, const ct_message_t *message)
Send a message over a connection with default properties.
void ct_connection_free(ct_connection_t *connection)
Free resources in a connection.
int ct_start_event_loop(void)
Start the CTaps event loop (blocking operation).
int ct_close(void)
Close and cleanup the CTaps library.
int ct_initialize(void)
Initialize the CTaps library.
uint16_t ct_local_endpoint_get_resolved_port(const ct_local_endpoint_t *local_endpoint)
Get the resolved port for a local endpoint after binding.
const char * ct_message_get_content(const ct_message_t *message)
Get the content buffer of a message.
ct_message_t * ct_message_new_with_content(const char *content, size_t length)
Allocate a new message with content.
void ct_message_free(ct_message_t *message)
Free all resources in a message including the structure.
ct_preconnection_t * ct_preconnection_new(const ct_local_endpoint_t *const *local_endpoints, size_t num_local_endpoints, const ct_remote_endpoint_t *const *remote_endpoints, size_t num_remote_endpoints, const ct_transport_properties_t *transport_properties, const ct_security_parameters_t *security_parameters)
Create a new preconnection with transport properties and endpoints.
void ct_preconnection_free(ct_preconnection_t *preconnection)
Free a preconnection object.
int ct_preconnection_initiate(const ct_preconnection_t *preconnection, const ct_connection_callbacks_t *connection_callbacks)
Initiate a connection.
int ct_remote_endpoint_with_ipv4(ct_remote_endpoint_t *remote_endpoint, in_addr_t ipv4_addr)
Set the IPv4 address for a remote endpoint.
ct_remote_endpoint_t * ct_remote_endpoint_new(void)
Create a new heap-allocated remote endpoint.
void ct_remote_endpoint_free(ct_remote_endpoint_t *remote_endpoint)
Free all resources in a remote endpoint including the structure itself.
void ct_remote_endpoint_with_port(ct_remote_endpoint_t *remote_endpoint, uint16_t port)
Set the port number for a remote endpoint.
void ct_transport_properties_set_preserve_msg_boundaries(ct_transport_properties_t *transport_props, ct_selection_preference_enum_t val)
@ AVOID
Prefer protocols without this property if possible.
Definition ctaps.h:190
void ct_transport_properties_free(ct_transport_properties_t *props)
Free a transport properties object.
ct_transport_properties_t * ct_transport_properties_new(void)
Create a new transport properties object with default values.
Callback functions for connection lifecycle events.
Definition ctaps.h:1152
void(* ready)(ct_connection_t *connection)
Called when connection is established and ready for data transfer.
Definition ctaps.h:1166
Opaque handle representing a connection.
Opaque handle representing message metadata to pass to sending protocol.
Opaque handle representing a single message to be sent or received.
Opaque handle representing a preconnection.
Callback functions for receiving messages on a connection.
Definition ctaps.h:1111
void(* receive_callback)(ct_connection_t *connection, ct_message_t *received_message, ct_message_context_t *ctx)
Called when a complete message is received.
Definition ctaps.h:1117
Opaque handle representing a remote endpoint (generic or resolved to specific ip address and port).
Opaque handle representing transport properties used for selecting and configuring protocols.
Example CTaps server

#include <ctaps.h>
#include <stdio.h>
#include <string.h>
void close_on_message_received(ct_connection_t* connection,
ct_message_t* received_message,
ct_message_context_t* message_context) {
);
printf("Received message: %s on port %d\n",
ct_message_get_content(received_message), port);
ct_connection_close(connection);
}
void on_connection_received_receive_message(ct_listener_t* listener,
ct_connection_t* new_connection) {
printf("Listener received new connection\n");
ct_receive_callbacks_t receive_message_request = {
.receive_callback = close_on_message_received,
};
ct_receive_message(new_connection, &receive_message_request);
// Stop accepting new connections after the first one is received
ct_listener_close(listener);
}
void free_on_listener_closed(ct_listener_t* listener) {
ct_listener_free(listener);
}
void free_on_connection_closed(ct_connection_t* connection) {
ct_connection_free(connection);
}
int main() {
ct_initialize(); // Init logging and event loop
// Create transport properties
listener_props, AVOID); // Prefer TCP
ct_local_endpoint_with_port(local_endpoint, 1234);
const ct_local_endpoint_t* locals[] = {local_endpoint};
// Create preconnection
locals, 1, NULL, 0, listener_props, NULL);
ct_listener_callbacks_t listener_callbacks = {
.connection_received = on_connection_received_receive_message,
.listener_closed = free_on_listener_closed
};
ct_connection_callbacks_t connection_callbacks = {
.closed = free_on_connection_closed
};
preconnection, &listener_callbacks, &connection_callbacks);
if (rc < 0) {
perror("Sync error in establishing listener\n");
return -1;
}
// Cleanup
ct_preconnection_free(preconnection);
ct_local_endpoint_free(local_endpoint);
return 0;
}
void ct_listener_close(ct_listener_t *listener)
Close a listener and stop accepting new connections.
void ct_listener_free(ct_listener_t *listener)
Free resources in a listener.
void ct_local_endpoint_with_port(ct_local_endpoint_t *local_endpoint, uint16_t port)
Set the port number for a local endpoint.
void ct_local_endpoint_free(ct_local_endpoint_t *local_endpoint)
Free all resources in a local endpoint including the structure itself.
ct_local_endpoint_t * ct_local_endpoint_new(void)
Create a new heap-allocated local endpoint.
void ct_set_log_level(ct_log_level_enum_t level)
Set the minimum logging level for CTaps.
@ CT_LOG_INFO
Informational messages (default)
Definition ctaps.h:128
int ct_preconnection_listen(const ct_preconnection_t *preconnection, const ct_listener_callbacks_t *listener_callbacks, const ct_connection_callbacks_t *connection_callbacks)
Start listening for incoming connections using the configured Preconnection.
void ct_transport_properties_set_multistreaming(ct_transport_properties_t *transport_props, ct_selection_preference_enum_t val)
void(* closed)(ct_connection_t *connection)
Called when connection is closed and can be safely freed.
Definition ctaps.h:1169
Callback functions for listener events.
Definition ctaps.h:1205
void(* connection_received)(ct_listener_t *listener, ct_connection_t *new_conn)
Called when a new connection is received.
Definition ctaps.h:1216
Opaque handle representing a listener.
Opaque handle representing a local endpoint (generic or resolved to specific ip address and port).

Additional examples can be found in the CTaps example project.

Project Structure

ctaps/
├── benchmark/ # Code used to compare CTaps to native benchmarks
├── include/ # Public API headers
│ └── ctaps.h # Public interface
├── examples/ # Example client and server from this README
├── src/ # Implementation
│ ├── connection/ # (pre)connection abstractions
│ ├── protocol/ # Protocol interfaces (TCP, UDP, QUIC) and setup
│ ├── candidate_gathering/ # Protocol/endpoint selection and racing
│ └── ...
└── test/ # Test suite (googletest)

Building

Most dependencies are installed automatically by CMake via FetchContent.

Some system-level dependencies must be installed separately:

sudo apt-get install pkg-config libglib2.0-dev libssl-dev
cmake . -B out/Debug
cmake --build out/Debug --target all

Note that the migration tests require CAP_NET_ADMIN and are by default not built. They can be built by setting the CTAPS_ENABLE_MIGRATION_TESTS option in CMake:

cmake . -B out/Debug -DCTAPS_ENABLE_MIGRATION_TESTS=ON
cmake --build out/Debug --target all

Running Tests

cd out/Debug/test && ctest

Including in your project

See the CTaps example project for an example on how to fetch CTaps as a dependency using CMake.

Platform Support

CTaps is supported on Linux only.

Thesis

CTaps was developed as part of a master's thesis at UiO, the final thesis will be added here when published.

License

This project is licensed under the MIT license.