ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_net_curl.c File Reference

libcurl host backend registered through the mdl_net vtable seam. More...

#include "mdl_net_curl.h"
#include <curl/curl.h>
#include <limits.h>
#include <string.h>
#include "mdl_net.h"
#include "mdl_net_curl_internal.h"
#include "mdl_net_internal.h"
#include "mdl_url_guard.h"
#include "ra8_attributes.h"
Include dependency graph for mdl_net_curl.c:

Go to the source code of this file.

Data Structures

struct  mdl_req_headers_t
 Fixed, stable request-header list retained by the backend context. More...
struct  mdl_curl_ctx_t
 Private state of the libcurl backend (the vtable's ctx). More...
struct  hdr_sink_t
 Response-header capture sink: keeps the final response's Retry-After. More...

Enumerations

enum  mdl_curl_limits_t : uint32_t {
  k_curl_max_redirects = 5 ,
  k_http_not_modified = 304 ,
  k_http_client_err_min = 400 ,
  k_http_not_found = 404 ,
  k_http_too_many_req = 429 ,
  k_http_server_err_min = 500 ,
  k_http_unavailable = 503 ,
  k_connect_timeout_ms = 15000 ,
  k_low_speed_bytes = 64 ,
  k_low_speed_secs = 30 ,
  k_origin_host_max = 256 ,
  k_request_header_max = 256 ,
  k_request_header_count = 2
}
 Backend tunables. More...

Functions

static char internal_ascii_lower (char c)
 Lower-case an ASCII byte (locale-independent).
static bool internal_header_is (const char *line, size_t line_len, const char *prefix)
 True when line begins with prefix, ASCII case-insensitively.
static void internal_header_value (const char *line, size_t line_len, char *out, size_t cap)
 Copy a header value (after the colon), trimmed of CR/LF/space, bounded.
static size_t internal_on_header (char *buffer, size_t size, size_t nitems, void *user)
 Capture selected headers from the final libcurl response.
size_t priv_mdl_net_curl_buf_write (char *data, size_t size, size_t nmemb, void *user)
 libcurl write callback: append into a bounded buffer, abort on cap.
size_t priv_mdl_net_curl_body_write (char *data, size_t size, size_t nmemb, void *user)
 Adapt one libcurl body chunk to an injected bounded sink.
static bool internal_redirect_host_ok (mdl_curl_ctx_t *net)
 Verify that libcurl's effective redirect host is permitted.
static int internal_on_prereq (void *clientp, char *conn_primary_ip, char *conn_local_ip, int conn_primary_port, int conn_local_port)
 Refuse non-public peers and forbidden cross-host redirects before transfer.
static bool internal_ok_code (CURLcode code)
 True if code is CURLE_OK (setopt success).
static ra8_err_t internal_apply_security_opts (CURL *curl, mdl_curl_ctx_t *net)
 Apply the security-critical, life-of-handle options (all checked).
static ra8_err_t internal_apply_behavior_opts (CURL *curl, const mdl_curl_ctx_t *net)
 Apply the behavioural, life-of-handle options (all checked).
static bool internal_append_req_header (mdl_req_headers_t *headers, const char *name, const char *value)
 Append one bounded header to caller-owned stable list storage.
static bool internal_build_req_headers (const mdl_net_req_t *req, mdl_req_headers_t *headers)
 Build conditional request headers (If-None-Match, If-Modified-Since).
static bool internal_apply_req (mdl_curl_ctx_t *net, const char *url, const mdl_net_req_t *req)
 Apply the per-request options shared by both fetch paths.
static void internal_release_req_headers (CURL *curl, const mdl_req_headers_t *headers)
 Detach request headers; their backing remains valid in backend state.
ra8_err_t priv_mdl_net_curl_classify (CURLcode code, bool overflow, long status)
 Map a completed libcurl transfer to an ra8_err_t (pure classifier).
static ra8_err_t internal_finish_transfer (CURL *curl, CURLcode code, bool overflow, const hdr_sink_t *hdr, mdl_net_resp_t *resp)
 Read status and captured headers, fill resp, and classify the result.
static ra8_err_t internal_curl_get_buf (void *ctx, const char *url, const mdl_net_req_t *req, char *buf, size_t cap, size_t *out_len, mdl_net_resp_t *resp)
 Vtable method: GET url into a caller buffer.
static ra8_err_t internal_curl_get_body (void *ctx, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, size_t *out_len, mdl_net_resp_t *resp)
 Vtable method: GET url through a caller-owned body sink.
static void internal_curl_destroy (void *ctx)
 Release the libcurl handle and clear this backend state.
ra8_err_t mdl_net_curl_init (mdl_net_iface_t *net, mdl_net_curl_storage_t *storage, const mdl_net_policy_t *policy)
 Initialise a libcurl-backed host network interface in caller storage.

Variables

static const mdl_net_vtable_t s_curl_vtable
 The libcurl backend's immutable method table.

Detailed Description

libcurl host backend registered through the mdl_net vtable seam.

Host-only implementation of the mdl_net_iface_t interface from mdl_net.h. It builds a { vtable, ctx } handle whose four methods forward to one reused libcurl easy handle; callers reach them only through the dispatchers, never by name. libcurl handles TLS, redirects, gzip and the connection pool – exactly the parts we do not want to hand-roll before the on-device NetX/Mbed TLS stack exists.

The URL that reaches curl_easy_perform is attacker-influenced by design, so the handle is hardened rather than left on libcurl's inherited defaults: the transport is pinned to http/https, redirects are refused when they change host or resolve to non-public address space (the SSRF guard, checked on the resolved peer via CURLOPT_PREREQFUNCTION), TLS verification is asserted explicitly, .netrc and proxy-env are disabled, and every response is size- and time-bounded. Every curl_easy_setopt of a security-relevant option is checked; a failure fails handle creation rather than proceeding unhardened.

Definition in file mdl_net_curl.c.

Enumeration Type Documentation

◆ mdl_curl_limits_t

enum mdl_curl_limits_t : uint32_t

Backend tunables.

Enumerator
k_curl_max_redirects 

Redirect hops to follow.

k_http_not_modified 

Conditional GET reused the held entity.

k_http_client_err_min 

First HTTP status treated as an error.

k_http_not_found 

Absent resource (client error).

k_http_too_many_req 

Too Many Requests (throttle).

k_http_server_err_min 

First HTTP status that is a server error.

k_http_unavailable 

Service Unavailable (throttle).

k_connect_timeout_ms 

TCP/TLS connect budget, ms.

k_low_speed_bytes 

Below this many B/s...

k_low_speed_secs 

...for this long, abort a stalled xfer.

k_origin_host_max 

Stored origin-host buffer bytes.

k_request_header_max 

Bytes in one conditional-request header.

k_request_header_count 

ETag and Last-Modified header slots.

Definition at line 37 of file mdl_net_curl.c.

Function Documentation

◆ internal_append_req_header()

bool internal_append_req_header ( mdl_req_headers_t * headers,
const char * name,
const char * value )
static

Append one bounded header to caller-owned stable list storage.

Formats name: value into the next fixed buffer, then links the corresponding embedded curl-list node. No allocation or ownership transfer occurs, so the storage remains valid through curl_easy_perform.

Parameters
[in,out]headersFixed header buffers and embedded list nodes.
[in]nameNUL-terminated HTTP header name.
[in]valueNUL-terminated HTTP header value.
Returns
Whether the complete header was appended.
Return values
trueA node was linked and the count advanced.
falseThe table was full or the formatted header did not fit.
Precondition
All arguments are non-NULL and the header state was initialised.
headers remains alive until the request header is detached.
Postcondition
On true, headers->count increases by one and the list remains terminated.
On false, headers->count and the linked prefix are unchanged.
Note
Header syntax and value policy are supplied by the internal caller.
Since
0.1.0

Definition at line 484 of file mdl_net_curl.c.

References mdl_req_headers_t::count, mdl_req_headers_t::head, k_request_header_count, mdl_req_headers_t::nodes, and mdl_req_headers_t::values.

Referenced by internal_build_req_headers().

◆ internal_apply_behavior_opts()

ra8_err_t internal_apply_behavior_opts ( CURL * curl,
const mdl_curl_ctx_t * net )
static

Apply the behavioural, life-of-handle options (all checked).

Enables transparent content decoding, imports validated caller-owned cookie bytes, installs the response-header callback, and enforces connect and low-speed time bounds. Any failed libcurl option rejects the handle.

Parameters
[in,out]curlEasy handle being configured.
[in]netBackend policy containing the optional cookie bytes.
Returns
Canonical validation or libcurl option status.
Return values
k_ra8_okAll options and cookies were applied.
otherCookie validation or a libcurl option failed.
Precondition
curl is a valid easy handle.
net is NULL or remains readable for this call.
Postcondition
On true, the handle has the complete required behavioural policy.
On false, the caller will destroy rather than use the partial handle.
Note
The security-critical transport options are applied separately.
Since
0.1.0

Definition at line 450 of file mdl_net_curl.c.

References mdl_curl_ctx_t::cookies, internal_ok_code(), internal_on_header(), k_connect_timeout_ms, k_low_speed_bytes, k_low_speed_secs, k_ra8_fail, priv_mdl_net_curl_apply_cookies(), and RA8_INTERNAL.

Referenced by mdl_net_curl_init().

◆ internal_apply_req()

bool internal_apply_req ( mdl_curl_ctx_t * net,
const char * url,
const mdl_net_req_t * req )
static

Apply the per-request options shared by both fetch paths.

Validates and stores the origin host, then sets URL, timeout, referer, and User-Agent.

Parameters
[in,out]netInitialised backend state.
[in]urlAbsolute request URL.
[in]reqRequest metadata and timeout.
Returns
Whether validation and all required options succeeded.
Return values
trueThe handle is ready for this request.
falseHost extraction or a libcurl option failed.
Precondition
net, url, and req are non-NULL.
Request strings, when present, are NUL-terminated.
Postcondition
On true, origin_host identifies url for redirect enforcement.
On host failure, origin_host is empty.
Note
The caller separately attaches response sinks and conditional headers.
Since
0.1.0

Definition at line 559 of file mdl_net_curl.c.

References mdl_curl_ctx_t::curl, internal_ok_code(), mdl_url_host(), mdl_curl_ctx_t::origin_host, mdl_net_req_t::referer, mdl_net_req_t::timeout_ms, and mdl_net_req_t::user_agent.

Referenced by internal_curl_get_body(), and internal_curl_get_buf().

◆ internal_apply_security_opts()

ra8_err_t internal_apply_security_opts ( CURL * curl,
mdl_curl_ctx_t * net )
static

Apply the security-critical, life-of-handle options (all checked).

Configures proxy policy, TLS verification, protocol limits, redirects, and peer checks.

Parameters
[in,out]curlEasy handle being hardened.
[in]netBackend security policy and callback state.
Returns
Canonical policy or option status.
Return values
k_ra8_okEvery required option succeeded.
k_ra8_err_not_supportedCustom CA bytes cannot be bound by this libcurl build.
otherProxy policy is unsafe, custom input is invalid, or an option failed.
Precondition
curl and net are non-NULL.
Policy strings, when present, are NUL-terminated.
Postcondition
On true, the handle enforces the documented transport policy.
On false, the caller destroys the partial handle.
Note
A proxy without the private-address escape hatch is rejected fail-closed.
Since
0.1.0

Definition at line 387 of file mdl_net_curl.c.

References mdl_curl_ctx_t::allow_private, mdl_curl_ctx_t::ca_pem, internal_ok_code(), internal_on_prereq(), k_curl_max_redirects, k_ra8_fail, k_ra8_ok, priv_mdl_net_curl_apply_ca_blob(), mdl_curl_ctx_t::proxy, RA8_INTERNAL, and mdl_curl_ctx_t::socks5.

Referenced by mdl_net_curl_init().

◆ internal_ascii_lower()

char internal_ascii_lower ( char c)
static

Lower-case an ASCII byte (locale-independent).

Maps uppercase ASCII letters and preserves all other bytes.

Parameters
[in]cCharacter to map.
Returns
Lower-case equivalent.
Return values
otherMapped or unchanged character.
Precondition
c is representable as char.
Locale-specific folding is not required.
Postcondition
No state is modified.
Non-uppercase input is unchanged.
Note
Thread-safe: pure arithmetic.
Since
0.1.0

Definition at line 105 of file mdl_net_curl.c.

References RA8_INTERNAL.

Referenced by internal_header_is().

◆ internal_build_req_headers()

bool internal_build_req_headers ( const mdl_net_req_t * req,
mdl_req_headers_t * headers )
static

Build conditional request headers (If-None-Match, If-Modified-Since).

Resets headers, then appends each non-empty validator supplied by req. A NULL request produces an empty, valid header list.

Parameters
[in]reqOptional request carrying entity validators.
[out]headersFixed storage receiving the embedded curl list.
Returns
Whether every requested validator fit.
Return values
trueThe complete conditional-header list was built.
falseA requested header exceeded a fixed bound.
Precondition
headers points to writable mdl_req_headers_t storage.
Validator strings in req, when present, are NUL-terminated.
Postcondition
headers is initialised even when no validator is requested.
On true, the list order is ETag followed by modification date.
Note
The returned list borrows only storage embedded in headers.
Since
0.1.0

Definition at line 524 of file mdl_net_curl.c.

References mdl_net_req_t::if_modified_since, mdl_net_req_t::if_none_match, internal_append_req_header(), and RA8_INTERNAL.

Referenced by internal_curl_get_body(), and internal_curl_get_buf().

◆ internal_curl_destroy()

void internal_curl_destroy ( void * ctx)
static

Release the libcurl handle and clear this backend state.

Cleans up the easy handle when present, zeroes caller-owned storage, and releases global curl state.

Parameters
[in,out]ctxOptional mdl_curl_ctx_t backend state.
Returns
Nothing.
Precondition
ctx is NULL or points to storage initialised by this backend.
No transfer is active on the easy handle.
Postcondition
A non-NULL context contains only zero bytes.
Backend-owned libcurl resources are released.
Note
Not thread-safe with concurrent operations on the same backend.
Since
0.1.0

Definition at line 800 of file mdl_net_curl.c.

References mdl_curl_ctx_t::curl, and RA8_INTERNAL.

◆ internal_curl_get_body()

ra8_err_t internal_curl_get_body ( void * ctx,
const char * url,
const mdl_net_req_t * req,
mdl_net_body_sink_t * sink,
size_t * out_len,
mdl_net_resp_t * resp )
static

Vtable method: GET url through a caller-owned body sink.

Applies request policy, streams bounded chunks through the injected callback, and never opens or names a filesystem object.

Parameters
[in]ctxInitialised curl backend state.
[in]urlAllowed absolute HTTP(S) URL.
[in]reqRequest metadata.
[in,out]sinkReset caller-owned body destination.
[out]out_lenOptional committed body length.
[out]respOptional response metadata.
Returns
Canonical transfer result.
Return values
k_ra8_okA complete response body was accepted.
otherValidation, sink, option, transport, HTTP, or size failure.
Precondition
Required pointers are non-NULL and url is NUL-terminated.
ctx owns an idle easy handle.
Postcondition
On success out_len receives the accepted body extent when requested.
Request headers are detached before return after attachment.
Note
Not thread-safe: reuses backend request storage.
Since
0.1.0

Definition at line 740 of file mdl_net_curl.c.

References mdl_curl_ctx_t::curl, mdl_req_headers_t::head, internal_apply_req(), internal_build_req_headers(), internal_finish_transfer(), internal_ok_code(), internal_release_req_headers(), k_ra8_err_invalid_arg, k_ra8_err_invalid_size, k_ra8_fail, k_ra8_ok, mdl_curl_ctx_t::max_bytes, mdl_url_scheme_allowed(), mdl_net_curl_body_state_t::overflow, priv_mdl_net_curl_body_write(), RA8_INTERNAL, mdl_curl_ctx_t::request_headers, mdl_net_curl_body_state_t::sink_error, and mdl_net_curl_body_state_t::written.

◆ internal_curl_get_buf()

ra8_err_t internal_curl_get_buf ( void * ctx,
const char * url,
const mdl_net_req_t * req,
char * buf,
size_t cap,
size_t * out_len,
mdl_net_resp_t * resp )
static

Vtable method: GET url into a caller buffer.

Applies bounded request state, performs the transfer, detaches headers, and terminates when room remains.

Parameters
[in]ctxInitialised curl backend state.
[in]urlAllowed absolute HTTP(S) URL.
[in]reqRequest metadata.
[out]bufBody destination.
[in]capDestination capacity.
[out]out_lenOptional body length.
[out]respOptional response metadata.
Returns
Canonical transfer result.
Return values
k_ra8_okThe complete response fit.
otherValidation, option, transport, HTTP, or size failure.
Precondition
Required pointers are non-NULL and buf is writable for cap bytes.
ctx owns an idle easy handle.
Postcondition
Request headers are detached before return after attachment.
On success, out_len is written when non-NULL.
Note
Not thread-safe: reuses backend request storage.
Since
0.1.0

Definition at line 675 of file mdl_net_curl.c.

References mdl_curl_ctx_t::curl, mdl_req_headers_t::head, internal_apply_req(), internal_build_req_headers(), internal_finish_transfer(), internal_ok_code(), internal_release_req_headers(), k_ra8_err_invalid_arg, k_ra8_err_invalid_size, k_ra8_fail, k_ra8_ok, buf_sink_t::len, mdl_url_scheme_allowed(), buf_sink_t::overflow, priv_mdl_net_curl_buf_write(), RA8_INTERNAL, and mdl_curl_ctx_t::request_headers.

◆ internal_finish_transfer()

ra8_err_t internal_finish_transfer ( CURL * curl,
CURLcode code,
bool overflow,
const hdr_sink_t * hdr,
mdl_net_resp_t * resp )
static

Read status and captured headers, fill resp, and classify the result.

Retrieves the HTTP response code, copies bounded metadata, and maps transport status.

Parameters
[in]curlEasy handle for the completed request.
[in]codeResult from curl_easy_perform.
[in]overflowWhether the body sink exceeded its cap.
[in]hdrCaptured final-response headers.
[out]respOptional public response metadata.
Returns
Canonical network result.
Return values
k_ra8_okTransfer and HTTP classification succeeded.
otherCanonical transport, size, throttle, or HTTP error.
Precondition
curl and hdr are non-NULL and the transfer has completed.
resp is NULL or points to writable response storage.
Postcondition
A non-NULL resp contains the final status and bounded headers.
Curl and captured header state remain owned by the caller.
Note
Thread safety follows ownership of the easy handle.
Since
0.1.0

Definition at line 633 of file mdl_net_curl.c.

References hdr_sink_t::content_type, mdl_net_resp_t::content_type, hdr_sink_t::etag, mdl_net_resp_t::etag, hdr_sink_t::last_modified, mdl_net_resp_t::last_modified, priv_mdl_net_curl_classify(), RA8_INTERNAL, hdr_sink_t::retry_after, mdl_net_resp_t::retry_after, and mdl_net_resp_t::status.

Referenced by internal_curl_get_body(), and internal_curl_get_buf().

◆ internal_header_is()

bool internal_header_is ( const char * line,
size_t line_len,
const char * prefix )
static

True when line begins with prefix, ASCII case-insensitively.

Compares a bounded header line against a lower-case literal prefix.

Parameters
[in]lineHeader bytes, not necessarily NUL-terminated.
[in]line_lenReadable line length.
[in]prefixNUL-terminated lower-case prefix.
Returns
Whether the complete prefix matched.
Return values
trueThe bounded line starts with prefix.
falseA byte differs or the line ends first.
Precondition
line is readable for line_len bytes and prefix is non-NULL.
prefix is lower-case ASCII.
Postcondition
Inputs are unchanged.
No state is modified.
Note
Thread-safe: reads only arguments.
Since
0.1.0

Definition at line 129 of file mdl_net_curl.c.

References internal_ascii_lower(), and RA8_INTERNAL.

Referenced by internal_on_header().

◆ internal_header_value()

void internal_header_value ( const char * line,
size_t line_len,
char * out,
size_t cap )
static

Copy a header value (after the colon), trimmed of CR/LF/space, bounded.

Finds the first colon, trims surrounding HTTP whitespace, and copies with NUL termination.

Parameters
[in]lineHeader bytes.
[in]line_lenReadable byte count.
[out]outDestination string.
[in]capDestination capacity.
Returns
Nothing.
Precondition
line and out are non-NULL; cap is non-zero.
line is readable for line_len bytes.
Postcondition
out is NUL-terminated and contains at most cap - 1 bytes.
line is unchanged.
Note
Longer values are deliberately truncated.
Since
0.1.0

Definition at line 156 of file mdl_net_curl.c.

Referenced by internal_on_header().

◆ internal_ok_code()

bool internal_ok_code ( CURLcode code)
static

True if code is CURLE_OK (setopt success).

Normalises libcurl option results for checked boolean chains.

Parameters
[in]codelibcurl result code.
Returns
Whether the operation succeeded.
Return values
truecode equals CURLE_OK.
falseAny libcurl error was reported.
Precondition
code came from a libcurl operation.
Exact success classification is intended.
Postcondition
No state is modified.
Input is unchanged.
Note
Thread-safe: pure comparison.
Since
0.1.0

Definition at line 364 of file mdl_net_curl.c.

References RA8_INTERNAL.

Referenced by internal_apply_behavior_opts(), internal_apply_req(), internal_apply_security_opts(), internal_curl_get_body(), and internal_curl_get_buf().

◆ internal_on_header()

size_t internal_on_header ( char * buffer,
size_t size,
size_t nitems,
void * user )
static

Capture selected headers from the final libcurl response.

Clears redirect-hop metadata on a status line and latches bounded validator fields.

Parameters
[in]bufferHeader bytes supplied by libcurl.
[in]sizeElement size.
[in]nitemsElement count.
[in,out]userOptional hdr_sink_t callback state.
Returns
Number of elements consumed, or zero on multiplication overflow.
Return values
0The byte count overflowed.
othernitems when accepted.
Precondition
buffer is readable for size * nitems bytes when representable.
user is NULL or points to writable header sink storage.
Postcondition
Recognised headers update only their bounded sink field.
A new status line clears metadata from the preceding redirect hop.
Note
Signature is fixed by libcurl's callback ABI.
Since
0.1.0

Definition at line 200 of file mdl_net_curl.c.

References hdr_sink_t::content_type, hdr_sink_t::etag, internal_header_is(), internal_header_value(), hdr_sink_t::last_modified, RA8_INTERNAL, and hdr_sink_t::retry_after.

Referenced by internal_apply_behavior_opts().

◆ internal_on_prereq()

int internal_on_prereq ( void * clientp,
char * conn_primary_ip,
char * conn_local_ip,
int conn_primary_port,
int conn_local_port )
static

Refuse non-public peers and forbidden cross-host redirects before transfer.

Classifies libcurl's resolved primary address and then applies redirect-host policy.

Parameters
[in]clientpInitialised mdl_curl_ctx_t callback state.
[in]conn_primary_ipNUL-terminated resolved peer address.
[in]conn_local_ipLocal address supplied by libcurl but unused.
[in]conn_primary_portResolved peer port supplied by libcurl.
[in]conn_local_portLocal port supplied by libcurl.
Returns
libcurl prerequisite decision.
Return values
CURL_PREREQFUNC_OKThe peer and redirect host are permitted.
CURL_PREREQFUNC_ABORTState or policy validation failed.
Precondition
Address pointers follow libcurl's prerequisite callback contract.
clientp is NULL or points to backend state.
Postcondition
No connection data is modified.
A decision that cannot be made is denied.
Note
Parameter types are fixed by libcurl's ABI.
Since
0.1.0

Definition at line 325 of file mdl_net_curl.c.

References mdl_curl_ctx_t::allow_private, internal_redirect_host_ok(), mdl_addr_is_fetchable(), mdl_classify_ip(), and RA8_INTERNAL.

Referenced by internal_apply_security_opts().

◆ internal_redirect_host_ok()

bool internal_redirect_host_ok ( mdl_curl_ctx_t * net)
static

Verify that libcurl's effective redirect host is permitted.

Allows configured cross-host redirects; otherwise compares the effective URL host to origin.

Parameters
[in]netInitialised curl backend state.
Returns
Whether the effective host satisfies redirect policy.
Return values
trueCross-host policy allows it or the host matches.
falseThe effective URL cannot be classified or changes host.
Precondition
net and its easy handle are non-NULL.
origin_host was populated for the request when policy requires it.
Postcondition
Backend state is unchanged.
No network operation is initiated.
Note
Failure to classify is fail-closed.
Since
0.1.0

Definition at line 288 of file mdl_net_curl.c.

References mdl_curl_ctx_t::allow_cross_host, mdl_curl_ctx_t::curl, k_origin_host_max, mdl_url_host(), mdl_curl_ctx_t::origin_host, RA8_INTERNAL, and strcmp().

Referenced by internal_on_prereq().

◆ internal_release_req_headers()

void internal_release_req_headers ( CURL * curl,
const mdl_req_headers_t * headers )
static

Detach request headers; their backing remains valid in backend state.

Clears CURLOPT_HTTPHEADER only when an embedded list was attached. The fixed nodes and strings are caller-owned and therefore are not freed.

Parameters
[in,out]curlEasy handle from which to detach the request list.
[in]headersFixed list state used for the completed request.
Returns
Nothing.
Precondition
curl and headers are non-NULL.
The request using headers has completed or failed before transfer.
Postcondition
A non-empty list is no longer associated with curl.
headers and all embedded backing bytes remain unchanged.
Note
A libcurl detach error is intentionally ignored during cleanup.
Since
0.1.0

Definition at line 592 of file mdl_net_curl.c.

References mdl_req_headers_t::head, and RA8_INTERNAL.

Referenced by internal_curl_get_body(), and internal_curl_get_buf().

◆ mdl_net_curl_init()

ra8_err_t mdl_net_curl_init ( mdl_net_iface_t * net,
mdl_net_curl_storage_t * storage,
const mdl_net_policy_t * policy )
nodiscard

Initialise a libcurl-backed host network interface in caller storage.

The returned handle wires the libcurl backend's method table to a private libcurl easy handle hardened per policy (scheme allowlist, SSRF and cross-host redirect refusal, TLS verification, size and time bounds). One easy handle is reused across requests so the connection pool and cookie jar persist (unlike the Kotlin per-request client). This is the Dependency Injection seam: production wires this factory; tests wire a fake with the same mdl_net_iface_t shape.

Parameters
[out]netCaller-owned interface populated on success.
[in,out]storageCaller-owned private storage retained until destroy.
[in]policySession security policy, or NULL for the safe defaults.
Returns
Canonical initialisation status.
Return values
k_ra8_okA ready interface; release resources with mdl_net_destroy.
k_ra8_err_invalid_argA required object or credential byte view is invalid, or a cookie row is unsafe.
k_ra8_err_invalid_sizeOne cookie row exceeds the bounded importer.
k_ra8_err_not_supportedThis libcurl/TLS build cannot consume a caller-owned CA blob.
k_ra8_failGlobal/easy init or option hardening failed.
Precondition
libcurl is available at link time.
policy, when non-NULL, describes the intended escape hatches.
Credential bytes referenced by policy remain readable until mdl_net_destroy because custom CA data is bound with CURL_BLOB_NOCOPY.
Postcondition
On success net owns a hardened libcurl easy handle while caller storage remains valid.
On failure net and storage contain only zero bytes.
Note
Not thread-safe: one interface per worker.
Since
0.1.0

Definition at line 819 of file mdl_net_curl.c.

References mdl_curl_ctx_t::allow_cross_host, mdl_net_policy_t::allow_cross_host_redirect, mdl_curl_ctx_t::allow_private, mdl_net_policy_t::allow_private_hosts, mdl_net_curl_storage::bytes, mdl_curl_ctx_t::ca_pem, mdl_net_policy_t::ca_pem, mdl_curl_ctx_t::cookies, mdl_net_policy_t::cookies, mdl_curl_ctx_t::curl, internal_apply_behavior_opts(), internal_apply_security_opts(), k_ra8_err_invalid_arg, k_ra8_fail, k_ra8_ok, mdl_curl_ctx_t::max_bytes, mdl_net_policy_t::max_response_bytes, mdl_curl_ctx_t::proxy, mdl_net_policy_t::proxy, s_curl_vtable, mdl_curl_ctx_t::socks5, and mdl_net_policy_t::socks5.

Referenced by internal_curl_open().

◆ priv_mdl_net_curl_body_write()

size_t priv_mdl_net_curl_body_write ( char * data,
size_t size,
size_t nmemb,
void * user )

Adapt one libcurl body chunk to an injected bounded sink.

Validates multiplication and configured capacity before dispatching one complete chunk to the injected writer.

Parameters
[in]dataBytes supplied by libcurl.
[in]sizeElement size.
[in]nmembElement count.
[in,out]usermdl_net_curl_body_state_t state.
Returns
Exact byte count accepted, or zero to abort libcurl.
Return values
0Overflow, invalid state, or sink failure aborted the transfer.
otherExact size * nmemb bytes accepted by the sink.
Precondition
Representable nonempty input is readable for size * nmemb bytes.
user is NULL or points to mutable callback state.
Postcondition
Overflow or sink failure is latched before returning zero.
Success advances the cumulative written extent exactly once.
Note
Signature is fixed by libcurl's callback ABI.
Since
0.1.0

Definition at line 247 of file mdl_net_curl.c.

References mdl_net_curl_body_state_t::cap, mdl_net_body_sink_t::ctx, k_ra8_err_invalid_state, k_ra8_ok, mdl_size_exceeds(), mdl_net_curl_body_state_t::overflow, RA8_PRIV, mdl_net_curl_body_state_t::sink, mdl_net_curl_body_state_t::sink_error, mdl_net_body_sink_t::write, and mdl_net_curl_body_state_t::written.

Referenced by internal_curl_get_body().

◆ priv_mdl_net_curl_buf_write()

size_t priv_mdl_net_curl_buf_write ( char * data,
size_t size,
size_t nmemb,
void * user )

libcurl write callback: append into a bounded buffer, abort on cap.

The registered CURLOPT_WRITEFUNCTION for a page fetch. Appends size * nmemb bytes into the buf_sink_t user unless doing so would exceed its capacity, in which case it latches overflow and returns 0 to abort the transfer (libcurl's contract for a short write).

Parameters
[in]dataBytes libcurl delivered (not NUL-terminated).
[in]sizeSize of each member (libcurl passes 1).
[in]nmembNumber of members.
[in,out]userThe buf_sink_t receiving the bytes, or NULL.
Returns
The number of members consumed, or 0 to abort.
Return values
nmembAll bytes were appended.
0user is NULL, or the append would exceed cap (overflow).
Precondition
user, when non-NULL, is a buf_sink_t with a valid buf/cap.
The caller treats a 0 return as a deliberate transfer abort.
Postcondition
On success sink->len advanced by size * nmemb.
On overflow sink->overflow is true and sink->buf is unchanged.
Note
Thread-safe: writes only the caller-provided sink.
MC/DC:
The two guards – user == NULL and (len + bytes) > cap – are single conditions, not compound decisions. Vectors: NULL sink, an appending write, and an over-cap write that latches overflow.
Since
0.1.0

Definition at line 228 of file mdl_net_curl.c.

References buf_sink_t::buf, buf_sink_t::cap, buf_sink_t::len, memcpy(), buf_sink_t::overflow, and RA8_PRIV.

Referenced by internal_curl_get_buf().

◆ priv_mdl_net_curl_classify()

ra8_err_t priv_mdl_net_curl_classify ( CURLcode code,
bool overflow,
long status )

Map a completed libcurl transfer to an ra8_err_t (pure classifier).

The backend's decision table, factored out of the getinfo wrapper so it is a pure function of the finished transfer's three observable outcomes and can be unit-tested for every status class without a network. Precedence is overflow, then timeout, then any other transport error, then the HTTP status class. Mapping distinct status classes to distinct codes is what lets the politeness governor tell a throttle (back off) from an absent page (skip) from a server error (retry later).

Parameters
[in]codelibcurl completion code from curl_easy_perform.
[in]overflowWhether a sink latched a size-cap overflow.
[in]statusHTTP response status (from CURLINFO_RESPONSE_CODE).
Returns
The classified transfer result.
Return values
k_ra8_err_no_memoverflow is true.
k_ra8_err_timeoutcode is CURLE_OPERATION_TIMEDOUT.
k_ra8_err_busyOK code and status is 429 or 503 (throttle).
k_ra8_err_not_foundOK code and status is 404 or another 4xx.
k_ra8_failAny other non-OK code, or status >= 500.
k_ra8_okOK code and status < 400.
Precondition
status is a meaningful HTTP status only when code == CURLE_OK.
The caller has finished the transfer before classifying it.
Postcondition
No state is modified.
Note
Thread-safe: depends only on its arguments.
MC/DC:
The status tests (status == 429 || status == 503, then status >= 500, then status >= 400) gate independent ifs reached only when code == CURLE_OK. The throttle test is a two-condition OR: vectors 429 and 503 each vary one condition true while a non-throttle status (e.g. 200/404/500) holds both false, and the 500/404/200 vectors exercise the remaining relational branches. Combined with the overflow and timeout vectors, every class the function distinguishes has a vector.
Since
0.1.0
Postcondition
Documented outputs and the return value describe the same outcome.

Definition at line 601 of file mdl_net_curl.c.

References k_ra8_err_no_mem, k_ra8_err_timeout, k_ra8_fail, priv_mdl_net_classify_http(), and RA8_PRIV.

Referenced by internal_finish_transfer().

Variable Documentation

◆ s_curl_vtable

const mdl_net_vtable_t s_curl_vtable
static
Initial value:
= {
}
static ra8_err_t internal_curl_get_body(void *ctx, const char *url, const mdl_net_req_t *req, mdl_net_body_sink_t *sink, size_t *out_len, mdl_net_resp_t *resp)
Vtable method: GET url through a caller-owned body sink.
static void internal_curl_destroy(void *ctx)
Release the libcurl handle and clear this backend state.
static ra8_err_t internal_curl_get_buf(void *ctx, const char *url, const mdl_net_req_t *req, char *buf, size_t cap, size_t *out_len, mdl_net_resp_t *resp)
Vtable method: GET url into a caller buffer.

The libcurl backend's immutable method table.

Definition at line 813 of file mdl_net_curl.c.

Referenced by mdl_net_curl_init().