Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_018Ae69wJS7sueQMUwNxV3nX
211 lines
4.6 KiB
C
211 lines
4.6 KiB
C
/*
|
|
* Minimal <arpa/inet.h> for the MontaukOS NetSurf port.
|
|
*
|
|
* NetSurf uses inet_pton() only to recognise IP literals in URLs
|
|
* (content/urldb.c, utils/utils.c) -- it never opens a socket with the result.
|
|
* A self-contained parser is therefore enough, and keeps this out of the
|
|
* MontaukOS libc, where a partial implementation would be misleading.
|
|
*
|
|
* static inline throughout: these headers are included by a handful of
|
|
* translation units and NetSurf builds with -Werror, so a non-static
|
|
* definition would either collide at link time or warn when unused.
|
|
*/
|
|
#ifndef _MONTAUK_COMPAT_ARPA_INET_H_
|
|
#define _MONTAUK_COMPAT_ARPA_INET_H_
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
/* Parse a dotted-quad into 4 network-order bytes. Returns 1 on success. */
|
|
static inline int montauk_inet_pton4(const char *src, uint8_t *dst)
|
|
{
|
|
uint32_t octet = 0;
|
|
int digits = 0;
|
|
int seen = 0;
|
|
uint8_t out[4];
|
|
|
|
for (;;) {
|
|
char c = *src++;
|
|
|
|
if (c >= '0' && c <= '9') {
|
|
if (digits == 3)
|
|
return 0;
|
|
/* a leading zero is not accepted (023 is ambiguous) */
|
|
if (digits == 1 && out[seen] == 0)
|
|
return 0;
|
|
octet = octet * 10 + (uint32_t)(c - '0');
|
|
if (octet > 255)
|
|
return 0;
|
|
out[seen] = (uint8_t)octet;
|
|
digits++;
|
|
} else if (c == '.' || c == '\0') {
|
|
if (digits == 0)
|
|
return 0;
|
|
if (c == '\0') {
|
|
if (seen != 3)
|
|
return 0;
|
|
memcpy(dst, out, 4);
|
|
return 1;
|
|
}
|
|
if (seen == 3)
|
|
return 0;
|
|
seen++;
|
|
octet = 0;
|
|
digits = 0;
|
|
out[seen] = 0;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse an IPv6 literal, including "::" compression and a trailing
|
|
* dotted-quad (e.g. ::ffff:192.0.2.1). Returns 1 on success.
|
|
*/
|
|
static inline int montauk_inet_pton6(const char *src, uint8_t *dst)
|
|
{
|
|
uint8_t out[16];
|
|
int head = 0; /* bytes written before "::" */
|
|
int tail = 0; /* bytes buffered after "::" */
|
|
uint8_t tailbuf[16];
|
|
int seen_compress = 0;
|
|
uint8_t *cur = out;
|
|
int *curlen = &head;
|
|
|
|
memset(out, 0, sizeof(out));
|
|
|
|
if (src[0] == ':') {
|
|
if (src[1] != ':')
|
|
return 0;
|
|
src += 2;
|
|
seen_compress = 1;
|
|
cur = tailbuf;
|
|
curlen = &tail;
|
|
if (*src == '\0')
|
|
goto done;
|
|
}
|
|
|
|
for (;;) {
|
|
uint32_t group = 0;
|
|
int digits = 0;
|
|
|
|
/* a dotted-quad may only appear as the final component */
|
|
if (strchr(src, '.') != NULL) {
|
|
const char *p = src;
|
|
int dots = 0;
|
|
while (*p != '\0' && *p != ':') {
|
|
if (*p == '.')
|
|
dots++;
|
|
p++;
|
|
}
|
|
if (dots == 3 && *p == '\0') {
|
|
if (*curlen + 4 > 16)
|
|
return 0;
|
|
if (montauk_inet_pton4(src, cur + *curlen) == 0)
|
|
return 0;
|
|
*curlen += 4;
|
|
break;
|
|
}
|
|
}
|
|
|
|
while (*src != '\0' && *src != ':') {
|
|
char c = *src;
|
|
uint32_t v;
|
|
|
|
if (c >= '0' && c <= '9')
|
|
v = (uint32_t)(c - '0');
|
|
else if (c >= 'a' && c <= 'f')
|
|
v = (uint32_t)(c - 'a' + 10);
|
|
else if (c >= 'A' && c <= 'F')
|
|
v = (uint32_t)(c - 'A' + 10);
|
|
else
|
|
return 0;
|
|
|
|
group = group * 16 + v;
|
|
if (++digits > 4)
|
|
return 0;
|
|
src++;
|
|
}
|
|
|
|
if (digits == 0)
|
|
return 0;
|
|
if (*curlen + 2 > 16)
|
|
return 0;
|
|
|
|
cur[(*curlen)++] = (uint8_t)(group >> 8);
|
|
cur[(*curlen)++] = (uint8_t)(group & 0xff);
|
|
|
|
if (*src == '\0')
|
|
break;
|
|
|
|
src++; /* consume ':' */
|
|
|
|
if (*src == ':') {
|
|
if (seen_compress)
|
|
return 0;
|
|
seen_compress = 1;
|
|
src++;
|
|
cur = tailbuf;
|
|
curlen = &tail;
|
|
if (*src == '\0')
|
|
break;
|
|
} else if (*src == '\0') {
|
|
/* trailing single colon */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (seen_compress) {
|
|
if (head + tail > 15) /* "::" must stand for >= 1 zero group */
|
|
return 0;
|
|
memmove(out + 16 - tail, tailbuf, (size_t)tail);
|
|
memset(out + head, 0, (size_t)(16 - tail - head));
|
|
} else if (head != 16) {
|
|
return 0;
|
|
}
|
|
|
|
memcpy(dst, out, 16);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* inet_aton(). Returns non-zero on success, as BSD does.
|
|
*
|
|
* Only the full dotted-quad form is accepted. Historic inet_aton also takes
|
|
* "a", "a.b", "a.b.c" and octal/hex octets, but content/urldb.c immediately
|
|
* rejects anything without three dots (see the comment at its call site), so
|
|
* the narrower behaviour matches how NetSurf actually uses it -- and refusing
|
|
* octal-looking octets like "1.2.3.04" is the safer reading for a URL host.
|
|
*/
|
|
static inline int inet_aton(const char *cp, struct in_addr *addr)
|
|
{
|
|
uint8_t out[4];
|
|
|
|
if (cp == NULL || addr == NULL)
|
|
return 0;
|
|
if (montauk_inet_pton4(cp, out) != 1)
|
|
return 0;
|
|
|
|
memcpy(&addr->s_addr, out, 4);
|
|
return 1;
|
|
}
|
|
|
|
static inline int inet_pton(int af, const char *src, void *dst)
|
|
{
|
|
if (src == NULL || dst == NULL)
|
|
return -1;
|
|
|
|
if (af == AF_INET)
|
|
return montauk_inet_pton4(src, (uint8_t *) dst);
|
|
if (af == AF_INET6)
|
|
return montauk_inet_pton6(src, (uint8_t *) dst);
|
|
|
|
return -1; /* EAFNOSUPPORT */
|
|
}
|
|
|
|
#endif
|