Files
quicproquo/docs/sdk/c-ffi.md
Christian Nennemann cbb76af6b1 docs(sdk): add comprehensive SDK documentation and wire format reference
Covers all official SDKs (Rust, Go, Python, TypeScript, C FFI),
the v2 wire format with method ID tables, authentication flow,
and a build-your-own-SDK guide with implementation checklist.
2026-03-04 20:55:24 +01:00

3.9 KiB

C FFI Bindings

The C FFI layer (crates/quicproquo-ffi/) provides synchronous C-callable functions that wrap the Rust client library. This is the foundation for language bindings in Python (CFFI), Swift, Kotlin/JNI, Java, and Ruby.

Building

cargo build --release -p quicproquo-ffi

This produces:

  • Linux: target/release/libquicproquo_ffi.so
  • macOS: target/release/libquicproquo_ffi.dylib
  • Windows: target/release/quicproquo_ffi.dll

API

Status Codes

#define QPQ_OK             0
#define QPQ_ERROR          1
#define QPQ_AUTH_FAILED    2
#define QPQ_TIMEOUT        3
#define QPQ_NOT_CONNECTED  4

Functions

// Connect to a server. Returns opaque handle or NULL on failure.
QpqHandle* qpq_connect(
    const char* server,       // "host:port"
    const char* ca_cert,      // path to CA certificate PEM
    const char* server_name   // TLS SNI name
);

// Authenticate with OPAQUE. Returns status code.
int qpq_login(
    QpqHandle* handle,
    const char* username,
    const char* password
);

// Send a message to a recipient (by username).
int qpq_send(
    QpqHandle* handle,
    const char* recipient,    // recipient username
    const uint8_t* message,   // message bytes
    size_t message_len
);

// Receive pending messages. Blocks up to timeout_ms.
// On success, *out_json is a JSON array of strings.
int qpq_receive(
    QpqHandle* handle,
    uint32_t timeout_ms,
    char** out_json           // caller must free with qpq_free_string
);

// Disconnect and free the handle.
void qpq_disconnect(QpqHandle* handle);

// Get last error message (valid until next FFI call on this handle).
const char* qpq_last_error(const QpqHandle* handle);

// Free a string returned by qpq_receive.
void qpq_free_string(char* ptr);

Usage Example (C)

#include <stdio.h>
#include <string.h>

// Forward declarations (or include a header).
typedef struct QpqHandle QpqHandle;
extern QpqHandle* qpq_connect(const char*, const char*, const char*);
extern int qpq_login(QpqHandle*, const char*, const char*);
extern int qpq_send(QpqHandle*, const char*, const unsigned char*, size_t);
extern int qpq_receive(QpqHandle*, unsigned int, char**);
extern void qpq_disconnect(QpqHandle*);
extern const char* qpq_last_error(const QpqHandle*);
extern void qpq_free_string(char*);

int main(void) {
    QpqHandle* h = qpq_connect("127.0.0.1:5001", "ca.pem", "localhost");
    if (!h) {
        fprintf(stderr, "connect failed\n");
        return 1;
    }

    int rc = qpq_login(h, "alice", "password123");
    if (rc != 0) {
        fprintf(stderr, "login failed: %s\n", qpq_last_error(h));
        qpq_disconnect(h);
        return 1;
    }

    const char* msg = "hello from C!";
    qpq_send(h, "bob", (const unsigned char*)msg, strlen(msg));

    char* json = NULL;
    rc = qpq_receive(h, 5000, &json);
    if (rc == 0 && json) {
        printf("received: %s\n", json);
        qpq_free_string(json);
    }

    qpq_disconnect(h);
    return 0;
}

Compile with:

gcc -o demo demo.c -L target/release -lquicproquo_ffi

Memory Management

  • qpq_connect returns a heap-allocated handle. The caller must call qpq_disconnect to free it.
  • qpq_receive writes a heap-allocated JSON string to *out_json. The caller must call qpq_free_string to free it.
  • qpq_last_error returns a pointer owned by the handle. Do not free it. It is valid until the next FFI call on the same handle.

Thread Safety

Each QpqHandle owns its own Tokio runtime. Concurrent calls on the same handle are not safe. Create separate handles for concurrent use.

Internals

The FFI layer bridges synchronous C callers to the async Rust client:

C caller  ─── qpq_login() ───►  QpqHandle  ─── runtime.block_on() ───►  async Rust client

Each handle contains:

  • A Tokio runtime for blocking on async operations
  • Server connection parameters
  • Login state and error buffer