Skip to content

ikhovind/CTaps

Repository files navigation

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) {

    uint16_t port = ct_local_endpoint_get_resolved_port(
        ct_message_context_get_local_endpoint(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_t* remote_endpoint = ct_remote_endpoint_new();
    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 =
        ct_transport_properties_new();

    // selection properties decide which protocol(s) will be used,
    // if multiple are compatible with our requirements,
    // TCP is preferred with this requirement
    ct_transport_properties_set_preserve_msg_boundaries(
        transport_properties, AVOID);

    const ct_remote_endpoint_t* remotes[] = {remote_endpoint};
    // Create preconnection
    // No local endpoint, so will bind to wildcard 
    ct_preconnection_t* preconnection = ct_preconnection_new(
        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
    int rc = ct_preconnection_initiate(
        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)
    ct_start_event_loop();

    // Cleanup
    ct_preconnection_free(preconnection);
    ct_transport_properties_free(transport_properties);
    ct_remote_endpoint_free(remote_endpoint);
    ct_close();

    return 0;
}
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) {
    uint16_t port = ct_local_endpoint_get_resolved_port(
        ct_message_context_get_local_endpoint(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
    
    ct_set_log_level(CT_LOG_INFO);

    // Create transport properties
    ct_transport_properties_t* listener_props = ct_transport_properties_new();
    ct_transport_properties_set_multistreaming(
        listener_props, AVOID); // Prefer TCP

    ct_local_endpoint_t* local_endpoint = ct_local_endpoint_new();
    ct_local_endpoint_with_port(local_endpoint, 1234);
    const ct_local_endpoint_t* locals[] = {local_endpoint};

    // Create preconnection
    ct_preconnection_t* preconnection = ct_preconnection_new(
        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 
    };

    int rc = ct_preconnection_listen(
        preconnection, &listener_callbacks, &connection_callbacks);

    if (rc < 0) {
        perror("Sync error in establishing listener\n");
        return -1;
    }

    ct_start_event_loop();

    // Cleanup
    ct_preconnection_free(preconnection);
    ct_transport_properties_free(listener_props);
    ct_local_endpoint_free(local_endpoint);
    ct_close();

    return 0;
}

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.

About

CTaps - A C implementation of Transport Services (TAPS) with support for QUIC, UDP and TCP. Using Picoquic and libuv

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors