# 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 ```sh 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 ```c #define QPQ_OK 0 #define QPQ_ERROR 1 #define QPQ_AUTH_FAILED 2 #define QPQ_TIMEOUT 3 #define QPQ_NOT_CONNECTED 4 ``` ### Functions ```c // 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) ```c #include #include // 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: ```sh 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