ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_session.c
Go to the documentation of this file.
1
12#include "mdl_session.h"
13
14#include <stdio.h>
15#include <string.h>
16
17#include "mdl_stream_internal.h"
18#include "mdl_url_guard.h"
19#include "ra8_attributes.h"
20
22typedef enum : uint32_t {
25
34
36static const char* const s_ua_token = "mdl";
38static const char* const s_ua_version = "0.1.0";
40static const char* const s_ua_url = "https://github.com/bsikar/ra8-firmware";
41
42const char* mdl_session_ua_token(void)
43{
44 return s_ua_token;
45}
46
47bool mdl_session_build_ua(const char* contact, char* out, size_t cap)
48{
49 if ((out == nullptr) || (cap == 0U)) {
50 return false;
51 }
52 const bool have = (contact != nullptr) && (contact[0] != '\0');
53 if (have) {
54 (void)snprintf(out, cap, "%s/%s (+%s; %s)", s_ua_token, s_ua_version, s_ua_url, contact);
55 } else {
56 (void)snprintf(out, cap, "%s/%s (+%s)", s_ua_token, s_ua_version, s_ua_url);
57 }
58 return have;
59}
60
62 mdl_net_iface_t* net,
63 const char* user_agent,
64 ra8_io_stream_t* diagnostic,
65 bool honor_robots)
66{
67 if (session == nullptr) {
68 return;
69 }
70 session->net = net;
71 session->user_agent = user_agent;
72 session->diagnostic = diagnostic;
73 session->honor_robots = honor_robots;
74 session->cache = (mdl_robots_cache_t){};
75}
76
96RA8_INTERNAL static bool internal_url_prefix(const char* url, const char* prefix)
97{
98 size_t i = 0U;
99 while (prefix[i] != '\0') {
100 char c = url[i];
101 if ((c >= 'A') && (c <= 'Z')) {
102 c = (char)(c + ('a' - 'A'));
103 }
104 if (c != prefix[i]) {
105 return false;
106 }
107 ++i;
108 }
109 return true;
110}
111
113RA8_INTERNAL static const char* internal_url_scheme(const char* url)
114{
115 if (internal_url_prefix(url, "https://")) {
116 return "https";
117 }
118 if (internal_url_prefix(url, "http://")) {
119 return "http";
120 }
121 return nullptr;
122}
123
144RA8_INTERNAL static bool internal_robots_target(const char* url, char* out, size_t cap)
145{
146 if ((url == nullptr) || (out == nullptr) || (cap < 2U)) {
147 return false;
148 }
149 const char* sep = strstr(url, "://");
150 if (sep == nullptr) {
151 return false;
152 }
153 const char* authority = sep + 3U;
154 const char* start = strpbrk(authority, "/?#");
155 size_t n = 0U;
156 if ((start == nullptr) || (*start == '#')) {
157 out[0] = '/';
158 out[1] = '\0';
159 return true;
160 }
161 if (*start == '?') {
162 out[n] = '/';
163 ++n;
164 }
165 const char* p = start;
166 while ((*p != '\0') && (*p != '#')) {
167 if ((n + 1U) >= cap) {
168 out[0] = '\0';
169 return false;
170 }
171 out[n] = *p;
172 ++n;
173 ++p;
174 }
175 out[n] = '\0';
176 return true;
177}
178
200internal_session_fetch(void* ctx, const char* robots_url, char* buf, size_t cap, size_t* out_len)
201{
202 mdl_session_t* s = (mdl_session_t*)ctx;
203 const mdl_net_req_t req = {.user_agent = s->user_agent,
204 .referer = nullptr,
205 .timeout_ms = k_robots_timeout_ms};
206 mdl_net_resp_t resp = {};
207 if (mdl_net_get_buf(s->net, robots_url, &req, buf, cap, out_len, &resp) == k_ra8_ok) {
209 }
210 if ((resp.status >= (long)k_http_client_err_min) &&
211 (resp.status <= (long)k_http_client_err_max) && (resp.status != (long)k_http_too_many)) {
213 }
214 if ((resp.status >= (long)k_http_server_err_min) &&
215 (resp.status <= (long)k_http_server_err_max)) {
217 }
219}
220
234RA8_INTERNAL static void
235internal_report_robots_unavailable(mdl_session_t* session, const char* host, const char* path)
236{
238 session->diagnostic,
239 "mdl: robots.txt unavailable, rate-limited, or "
240 "oversized for ");
241 error = priv_mdl_stream_text(error, session->diagnostic, host);
242 error = priv_mdl_stream_text(error, session->diagnostic, "; refusing ");
243 error = priv_mdl_stream_text(error, session->diagnostic, path);
244 (void)priv_mdl_stream_text(error, session->diagnostic, "\n");
245}
246
262 const char* path,
263 const char* host,
264 const char* reason)
265{
266 ra8_err_t error =
267 priv_mdl_stream_text(k_ra8_ok, session->diagnostic, "mdl: robots.txt disallows ");
268 error = priv_mdl_stream_text(error, session->diagnostic, path);
269 error = priv_mdl_stream_text(error, session->diagnostic, " on ");
270 error = priv_mdl_stream_text(error, session->diagnostic, host);
271 error = priv_mdl_stream_text(error, session->diagnostic, " (rule \"");
272 error = priv_mdl_stream_text(error, session->diagnostic, reason);
273 (void)priv_mdl_stream_text(error, session->diagnostic, "\"); skipping\n");
274}
275
276bool mdl_session_url_allowed(mdl_session_t* session, const char* url, uint32_t* crawl_delay_ms)
277{
278 if (crawl_delay_ms != nullptr) {
279 *crawl_delay_ms = 0U;
280 }
281 if ((session == nullptr) || (url == nullptr)) {
282 return false;
283 }
284 if (!session->honor_robots) {
285 return true;
286 }
287 const char* scheme = internal_url_scheme(url);
288 if (scheme == nullptr) {
290 session->diagnostic,
291 "mdl: cannot apply robots.txt to malformed/non-HTTP URL; "
292 "refusing\n");
293 return false;
294 }
295 char host[k_mdl_robots_host_max];
296 if (!mdl_url_host(url, host, sizeof(host))) {
298 session->diagnostic,
299 "mdl: cannot identify URL origin for robots.txt; refusing\n");
300 return false;
301 }
302 char path[k_mdl_robots_path_max];
303 if (!internal_robots_target(url, path, sizeof(path))) {
305 session->diagnostic,
306 "mdl: URL path/query exceeds robots.txt bound; refusing\n");
307 return false;
308 }
309 const mdl_robots_t* rules = mdl_robots_cache_consult(&session->cache,
310 scheme,
311 host,
314 session,
315 session->scratch,
316 sizeof(session->scratch));
317 if (rules == nullptr) {
318 internal_report_robots_unavailable(session, host, path);
319 return false;
320 }
321 if ((crawl_delay_ms != nullptr) && rules->have_crawl_delay) {
322 *crawl_delay_ms = rules->crawl_delay_ms;
323 }
324 const char* reason = mdl_robots_disallow_reason(rules, path);
325 if (reason != nullptr) {
326 internal_report_robots_disallow(session, path, host, reason);
327 return false;
328 }
329 return true;
330}
ra8_err_t mdl_net_get_buf(mdl_net_iface_t *net, const char *url, const mdl_net_req_t *req, char *buf, size_t cap, size_t *out_len, mdl_net_resp_t *resp)
GET url fully into a caller buffer (used for HTML pages).
Definition mdl_net.c:78
@ k_http_client_err_min
First HTTP status treated as an error.
@ k_http_server_err_min
First HTTP status that is a server error.
@ k_mdl_robots_path_max
Max bytes per rule path pattern.
Definition mdl_robots.h:28
mdl_robots_fetch_result_t
Outcome class of a robots.txt fetch, per RFC 9309 convention.
Definition mdl_robots.h:44
@ k_mdl_robots_fetch_absent
Absent / transport error: allow all.
Definition mdl_robots.h:46
@ k_mdl_robots_fetch_denied
5xx status: disallow all.
Definition mdl_robots.h:47
@ k_mdl_robots_fetch_ok
A body was retrieved to parse.
Definition mdl_robots.h:45
@ k_mdl_robots_host_max
Max host string bytes.
Definition mdl_robots.h:34
const char * mdl_robots_disallow_reason(const mdl_robots_t *robots, const char *path)
Return the Disallow pattern that forbids path, or NULL if allowed.
Definition mdl_robots.c:587
const mdl_robots_t * mdl_robots_cache_consult(mdl_robots_cache_t *cache, const char *scheme, const char *host, const char *ua_token, mdl_robots_fetch_fn fetch, void *ctx, char *scratch, size_t scratch_cap)
Return the cached robots rules for host, fetching on first contact.
Definition mdl_robots.c:620
static const char *const s_ua_url
Project URL reported in the User-Agent for a contact channel.
Definition mdl_session.c:40
static mdl_robots_fetch_result_t internal_session_fetch(void *ctx, const char *robots_url, char *buf, size_t cap, size_t *out_len)
robots.txt fetch callback: map an mdl_net GET to a fetch result.
bool mdl_session_url_allowed(mdl_session_t *session, const char *url, uint32_t *crawl_delay_ms)
Decide whether url may be fetched under robots.txt, and its delay.
static bool internal_url_prefix(const char *url, const char *prefix)
ASCII case-insensitive comparison of one URL prefix.
Definition mdl_session.c:96
static const char *const s_ua_version
Tool version reported in the User-Agent.
Definition mdl_session.c:38
static const char * internal_url_scheme(const char *url)
Return the validated lower-case HTTP(S) scheme, or NULL.
mdl_session_net_t
robots.txt fetch tunables.
Definition mdl_session.c:22
@ k_robots_timeout_ms
Per-robots.txt request budget, ms.
Definition mdl_session.c:23
void mdl_session_init(mdl_session_t *session, mdl_net_iface_t *net, const char *user_agent, ra8_io_stream_t *diagnostic, bool honor_robots)
Initialise a session over a network backend.
Definition mdl_session.c:61
static void internal_report_robots_unavailable(mdl_session_t *session, const char *host, const char *path)
Report a failed robots consultation for one origin and path.
static void internal_report_robots_disallow(mdl_session_t *session, const char *path, const char *host, const char *reason)
Report the robots rule that rejected one path.
static bool internal_robots_target(const char *url, char *out, size_t cap)
Extract path plus query, excluding fragment, for RFC 9309 matching.
mdl_session_status_t
HTTP server-error range treated as "disallow all".
Definition mdl_session.c:27
@ k_http_client_err_max
Last 4xx unavailable status.
Definition mdl_session.c:30
@ k_http_server_err_max
Last 5xx status.
Definition mdl_session.c:32
@ k_http_too_many
Rate limiting: do not bypass robots.
Definition mdl_session.c:28
const char * mdl_session_ua_token(void)
The robots.txt product token this tool matches on ("mdl").
Definition mdl_session.c:42
static const char *const s_ua_token
Product token used to match robots.txt User-agent groups.
Definition mdl_session.c:36
bool mdl_session_build_ua(const char *contact, char *out, size_t cap)
Build the truthful default User-Agent into out.
Definition mdl_session.c:47
Session identity (honest User-Agent) and robots.txt gating.
ra8_err_t priv_mdl_stream_text(ra8_err_t prior, ra8_io_stream_t *stream, const char *text)
Append text unless an earlier operation already failed.
Definition mdl_stream.c:16
Private bounded text helpers over the portable byte-stream contract.
Pure URL / address safety predicates for the libcurl backend.
bool mdl_url_host(const char *url, char *out, size_t cap)
Extract the authority (host and optional port) from an http(s) URL.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
@ k_ra8_ok
Success – operation completed with all postconditions satisfied.
Definition ra8_err.h:119
ra8_err_codes_t ra8_err_t
Canonical error-return type used by every ra8-firmware API.
Definition ra8_err.h:546
char * strstr(const char *haystack, const char *needle)
Locate substring in string.
A network backend handle: an immutable method table plus its state.
Per-request session parameters.
Definition mdl_net.h:43
Per-transfer response metadata surfaced to the politeness governor.
Definition mdl_net.h:120
long status
Finished transfer's HTTP status, 0 if none.
Definition mdl_net.h:121
Fixed-size per-host robots.txt cache for one run.
Definition mdl_robots.h:185
Rules and crawl-delay for the group matching our user-agent.
Definition mdl_robots.h:74
bool have_crawl_delay
A Crawl-delay was seen.
Definition mdl_robots.h:78
uint32_t crawl_delay_ms
Crawl-delay in ms.
Definition mdl_robots.h:79
One download session's identity and robots.txt state.
Definition mdl_session.h:39
mdl_robots_cache_t cache
Per-host robots cache.
Definition mdl_session.h:44
mdl_net_iface_t * net
Backend used for robots.txt fetches.
Definition mdl_session.h:40
bool honor_robots
False disables all robots gating.
Definition mdl_session.h:43
const char * user_agent
Full UA header (caller-owned).
Definition mdl_session.h:41
ra8_io_stream_t * diagnostic
Borrowed diagnostic byte sink.
Definition mdl_session.h:42
char scratch[k_mdl_session_scratch]
Fetch scratch.
Definition mdl_session.h:45
Caller-allocated byte-stream handle binding a sink to its context.