ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_url_guard.c
Go to the documentation of this file.
1
9#include "mdl_url_guard.h"
10
11#include <arpa/inet.h>
12#include <string.h>
13
14#include "ra8_attributes.h"
15
17typedef enum : uint8_t {
18 k_v4_o0 = 0,
19 k_v4_o1 = 1,
21
39
41typedef enum : uint8_t {
46 k_v6_last = 15,
48
50typedef enum : uint16_t {
51 k_v6_byte_ff = 0xFF,
54 k_v6_ll_lead = 0xFE,
55 k_v6_ll_mask = 0xC0,
59
61static const char* const s_scheme_http = "http://";
62static const char* const s_scheme_https = "https://";
63
79RA8_INTERNAL static bool internal_starts_with_ci(const char* s, const char* prefix)
80{
81 size_t i = 0U;
82 while (prefix[i] != '\0') {
83 const char a = s[i];
84 if (a == '\0') {
85 return false;
86 }
87 const char la = (char)(((a >= 'A') && (a <= 'Z')) ? (a + ('a' - 'A')) : a);
88 if (la != prefix[i]) {
89 return false;
90 }
91 ++i;
92 }
93 return true;
94}
95
96bool mdl_url_scheme_allowed(const char* url)
97{
98 if ((url == nullptr) || (url[0] == '\0')) {
99 return false;
100 }
103}
104
123{
124 const unsigned o0 = o[k_v4_o0];
125 const unsigned o1 = o[k_v4_o1];
126 if (o0 == (unsigned)k_v4_zero_net) {
127 return k_mdl_addr_unknown;
128 }
129 if (o0 == (unsigned)k_v4_loopback_net) {
130 return k_mdl_addr_loopback;
131 }
132 if (o0 >= (unsigned)k_v4_multicast_min) {
133 return k_mdl_addr_unknown;
134 }
135 if (o0 == (unsigned)k_v4_linklocal) {
136 return (o1 == (unsigned)k_v4_linklocal_2) ? k_mdl_addr_linklocal : k_mdl_addr_public;
137 }
138 const bool priv_a = (o0 == (unsigned)k_v4_private_a);
139 const bool priv_b = (o0 == (unsigned)k_v4_private_b) && (o1 >= (unsigned)k_v4_private_b_lo) &&
140 (o1 <= (unsigned)k_v4_private_b_hi);
141 const bool priv_c = (o0 == (unsigned)k_v4_private_c) && (o1 == (unsigned)k_v4_private_c_2);
142 const bool cgnat = (o0 == (unsigned)k_v4_cgnat) && (o1 >= (unsigned)k_v4_cgnat_lo) &&
143 (o1 <= (unsigned)k_v4_cgnat_hi);
144 return (priv_a || priv_b || priv_c || cgnat) ? k_mdl_addr_private : k_mdl_addr_public;
145}
146
161RA8_INTERNAL static bool internal_is_v4_mapped(const unsigned char* b)
162{
163 for (size_t i = 0U; i < (size_t)k_v6_mapped_ff_a; ++i) {
164 if (b[i] != 0U) {
165 return false;
166 }
167 }
168 return (b[k_v6_mapped_ff_a] == (unsigned char)k_v6_byte_ff) &&
169 (b[k_v6_mapped_ff_b] == (unsigned char)k_v6_byte_ff);
170}
171
186RA8_INTERNAL static bool internal_is_v6_loopback(const unsigned char* b)
187{
188 for (size_t i = 0U; i < (size_t)k_v6_last; ++i) {
189 if (b[i] != 0U) {
190 return false;
191 }
192 }
193 return b[k_v6_last] == (unsigned char)k_v6_loopback_last;
194}
195
214{
215 if (internal_is_v4_mapped(b)) {
216 return internal_classify_v4(b + (size_t)k_v6_mapped_v4);
217 }
219 return k_mdl_addr_loopback;
220 }
221 if (b[k_v4_o0] == (unsigned char)k_v6_byte_ff) {
222 return k_mdl_addr_unknown; /* multicast */
223 }
224 if ((b[k_v4_o0] == (unsigned char)k_v6_ll_lead) &&
225 ((b[k_v4_o1] & (unsigned char)k_v6_ll_mask) == (unsigned char)k_v6_ll_value)) {
227 }
228 if ((b[k_v4_o0] & (unsigned char)k_v6_ula_mask) == (unsigned char)k_v6_ula_value) {
229 return k_mdl_addr_private;
230 }
231 return k_mdl_addr_public;
232}
233
235{
236 if (ip == nullptr) {
237 return k_mdl_addr_unknown;
238 }
239 unsigned char v4[sizeof(struct in_addr)] = {};
240 if (inet_pton(AF_INET, ip, v4) == 1) {
241 return internal_classify_v4(v4);
242 }
243 unsigned char v6[sizeof(struct in6_addr)] = {};
244 if (inet_pton(AF_INET6, ip, v6) == 1) {
245 return internal_classify_v6(v6);
246 }
247 return k_mdl_addr_unknown;
248}
249
250bool mdl_addr_is_fetchable(mdl_addr_class_t cls, bool allow_private)
251{
252 if (cls == k_mdl_addr_public) {
253 return true;
254 }
255 if (cls == k_mdl_addr_unknown) {
256 return false;
257 }
258 return allow_private;
259}
260
261bool mdl_size_exceeds(uint64_t have, uint64_t add, uint64_t cap)
262{
263 if (cap == 0U) {
264 return false;
265 }
266 if (have > cap) {
267 return true;
268 }
269 return add > (cap - have);
270}
271
272bool mdl_url_host(const char* url, char* out, size_t cap)
273{
274 if ((out == nullptr) || (cap == 0U)) {
275 return false;
276 }
277 out[0] = '\0';
278 if (url == nullptr) {
279 return false;
280 }
281 const char* sep = strstr(url, "://");
282 if (sep == nullptr) {
283 return false;
284 }
285 const char* host = sep + (sizeof("://") - 1U);
286 const char* at = strchr(host, '@');
287 const char* path = strpbrk(host, "/?#");
288 if (at != nullptr) {
289 const bool at_in_authority = (path == nullptr) || (at < path);
290 if (at_in_authority) {
291 host = at + 1U;
292 }
293 }
294 size_t n = 0U;
295 /* Keep the port: robots.txt is scoped per (scheme, host, port) authority, and
296 * a redirect to a different port is a different origin. Only userinfo is
297 * dropped (handled above). */
298 while ((host[n] != '\0') && (host[n] != '/') && (host[n] != '?') && (host[n] != '#')) {
299 if ((n + 1U) >= cap) {
300 out[0] = '\0';
301 return false;
302 }
303 const char c = host[n];
304 out[n] = (char)(((c >= 'A') && (c <= 'Z')) ? (c + ('a' - 'A')) : c);
305 ++n;
306 }
307 out[n] = '\0';
308 return n > 0U;
309}
310
311bool mdl_url_path(const char* url, char* out, size_t cap)
312{
313 if ((out == nullptr) || (cap == 0U)) {
314 return false;
315 }
316 out[0] = '\0';
317 if (url == nullptr) {
318 return false;
319 }
320 const char* sep = strstr(url, "://");
321 if (sep == nullptr) {
322 return false;
323 }
324 const char* host = sep + (sizeof("://") - 1U);
325 const char* path = strchr(host, '/');
326 if (path == nullptr) {
327 if (cap < 2U) {
328 return false;
329 }
330 out[0] = '/';
331 out[1] = '\0';
332 return true;
333 }
334 size_t n = 0U;
335 while ((path[n] != '\0') && (path[n] != '?') && (path[n] != '#')) {
336 if ((n + 1U) >= cap) {
337 out[0] = '\0';
338 return false;
339 }
340 out[n] = path[n];
341 ++n;
342 }
343 out[n] = '\0';
344 return true;
345}
static mdl_addr_class_t internal_classify_v6(const unsigned char *b)
Classify the sixteen bytes of an IPv6 address.
bool mdl_url_host(const char *url, char *out, size_t cap)
Extract the authority (host and optional port) from an http(s) URL.
bool mdl_url_scheme_allowed(const char *url)
True when url uses an allowlisted scheme (http:// or https://).
bool mdl_addr_is_fetchable(mdl_addr_class_t cls, bool allow_private)
Decide whether an address class is fetchable under the active policy.
mdl_v4_range_t
IPv4 range boundaries that mark non-public address space.
@ k_v4_linklocal
169.254.0.0/16 first octet.
@ k_v4_private_c
192.168.0.0/16 first octet.
@ k_v4_private_a
10.0.0.0/8.
@ k_v4_private_c_2
192.168 second octet.
@ k_v4_linklocal_2
169.254 second octet.
@ k_v4_private_b
172.16.0.0/12 first octet.
@ k_v4_private_b_lo
172.16 low second octet (inclusive).
@ k_v4_cgnat_lo
100.64 low second octet (inclusive).
@ k_v4_multicast_min
224.0.0.0/4 and up: multicast/reserved.
@ k_v4_private_b_hi
172.31 high second octet (inclusive).
@ k_v4_cgnat_hi
100.127 high second octet (inclusive).
@ k_v4_zero_net
0.0.0.0/8 "this network" (unspecified).
@ k_v4_cgnat
100.64.0.0/10 (RFC6598) first octet.
@ k_v4_loopback_net
127.0.0.0/8 loopback.
static mdl_addr_class_t internal_classify_v4(const unsigned char *o)
Classify the four octets of an IPv4 address.
mdl_v4_index_t
IPv4 address byte layout used while classifying.
@ k_v4_o0
First octet index.
@ k_v4_o1
Second octet index.
static const char *const s_scheme_https
static const char *const s_scheme_http
Scheme prefixes accepted by mdl_url_scheme_allowed.
bool mdl_size_exceeds(uint64_t have, uint64_t add, uint64_t cap)
True when appending add bytes to have would exceed cap.
static bool internal_is_v4_mapped(const unsigned char *b)
True if the 16 IPv6 bytes carry an IPv4-mapped ffff:a.b.c.d.
mdl_addr_class_t mdl_classify_ip(const char *ip)
Classify a resolved peer IP string into a mdl_addr_class_t.
mdl_v6_index_t
IPv6 address byte layout and the bytes classification inspects.
@ k_v6_mapped_v4
Offset of the embedded IPv4 address.
@ k_v6_mapped_ff_b
Second 0xFF byte of the mapped prefix.
@ k_v6_mapped_ff_a
First 0xFF byte of an IPv4-mapped prefix.
@ k_v6_bytes
Total bytes in an IPv6 address.
@ k_v6_last
Index of the final byte (for ::1).
static bool internal_is_v6_loopback(const unsigned char *b)
True if the 16 IPv6 bytes are the loopback address ::1.
static bool internal_starts_with_ci(const char *s, const char *prefix)
Case-insensitive test that s begins with prefix.
bool mdl_url_path(const char *url, char *out, size_t cap)
Extract the path (with leading /, without query/fragment) from a URL.
mdl_v6_prefix_t
IPv6 prefix byte values that mark non-public address space.
@ k_v6_ula_mask
Mask isolating the fc00::/7 prefix.
@ k_v6_ll_value
fe80::/10 masked value.
@ k_v6_ll_lead
fe80::/10 lead byte.
@ k_v6_ll_mask
Mask isolating the /10 boundary.
@ k_v6_loopback_last
Final byte of ::1.
@ k_v6_ula_value
fc00::/7 unique-local value.
@ k_v6_byte_ff
Multicast lead byte / mapped filler.
Pure URL / address safety predicates for the libcurl backend.
mdl_addr_class_t
Reachability class of a resolved peer address.
@ k_mdl_addr_public
Routable public unicast address.
@ k_mdl_addr_unknown
Unparseable, unspecified, or reserved.
@ k_mdl_addr_loopback
127.0.0.0/8 or ::1.
@ k_mdl_addr_private
RFC1918, RFC6598 (CGNAT), or fc00::/7.
@ k_mdl_addr_linklocal
169.254.0.0/16 or fe80::/10.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
char * strstr(const char *haystack, const char *needle)
Locate substring in string.
char * strchr(const char *s, int c)
Locate first occurrence of character in string.