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:

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,
    // then we will race the protocols TCP is the only protocol
    // compatible with this requirement
    ct_transport_properties_set_preserve_msg_boundaries(
        transport_properties, PROHIBIT);

    // Create preconnection
    // No local endpoint, so will bind to wildcard 
    ct_preconnection_t* preconnection = ct_preconnection_new(
        NULL, 0, &remote_endpoint, 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_preserve_msg_boundaries(
        listener_props, PROHIBIT); // Force TCP

    ct_local_endpoint_t* local_endpoint = ct_local_endpoint_new();
    ct_local_endpoint_with_port(local_endpoint, 1234);

    // Create preconnection
    ct_preconnection_t* preconnection = ct_preconnection_new(
        &local_endpoint, 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, but libglib2.0-dev must be installed separately:

sudo apt-get install libglib2.0-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