26 if (c >=
'0' && c <=
'9')
28 if (c >=
'a' && c <=
'f')
30 if (c >=
'A' && c <=
'F')
36bool parse_v4(
const char *s,
size_t len, uint8_t out[4])
41 for (
size_t i = 0; i < len; i++)
46 if (digits == 0 || oct >= 3)
48 out[oct++] = (uint8_t)val;
52 else if (c >=
'0' && c <=
'9')
56 val = (val < 0 ? 0 : val) * 10 + (c -
'0');
64 if (oct != 3 || digits == 0)
66 out[3] = (uint8_t)val;
71bool parse_hextet(
const char *s,
size_t len, uint16_t *out)
73 if (len == 0 || len > 4)
76 for (
size_t i = 0; i < len; i++)
92void assemble_v6(
const uint16_t *head,
int nhead,
const uint16_t *tail,
int ntail, uint8_t out[16])
95 memset(g, 0,
sizeof(g));
96 for (
int k = 0; k < nhead; k++)
98 for (
int k = 0; k < ntail; k++)
99 g[8 - ntail + k] = tail[k];
100 for (
int k = 0; k < 8; k++)
102 out[2 * k] = (uint8_t)(g[k] >> 8);
103 out[2 * k + 1] = (uint8_t)(g[k] & 0xFF);
115bool parse_v6(
const char *s,
size_t len, uint8_t out[16])
121 bool seen_dc =
false;
122 uint16_t *cur = head;
126 if (len >= 1 && s[0] ==
':')
128 if (len < 2 || s[1] !=
':')
145 bool has_dot =
false;
146 while (j < len && s[j] !=
':')
157 if (!parse_v4(s + i, tlen, q) || *ncur > 6 || j != len)
159 cur[(*ncur)++] = (uint16_t)((q[0] << 8) | q[1]);
160 cur[(*ncur)++] = (uint16_t)((q[2] << 8) | q[3]);
165 if (!parse_hextet(s + i, tlen, &hx) || *ncur >= 8)
173 if (i + 1 < len && s[i + 1] ==
':')
194 int total = nhead + ntail;
195 if (seen_dc ? (total > 7) : (total != 8))
198 assemble_v6(head, nhead, tail, ntail, out);
207size_t put_u8(uint8_t v,
char *o)
211 o[n++] = (char)(
'0' + v / 100);
213 o[n++] = (char)(
'0' + (v / 10) % 10);
214 o[n++] = (char)(
'0' + v % 10);
219size_t format_v4(
const uint8_t *b,
char *out,
size_t cap)
223 for (
int k = 0; k < 4; k++)
227 n += put_u8(b[k], tmp + n);
237size_t put_hex16(uint16_t v,
char *o)
239 static const char H[] =
"0123456789abcdef";
247 for (
int k = 0; k < n; k++)
256void longest_zero_run(
const uint16_t g[8],
int *start,
int *len)
262 for (
int k = 0; k < 8; k++)
269 if (cur_len > best_len)
272 best_start = cur_start;
281 *start = (best_len >= 2) ? best_start : -1;
290bool is_v4_mapped_bytes(
const uint8_t *b)
292 for (
int k = 0; k < 10; k++)
295 return b[10] == 0xff && b[11] == 0xff;
301 if (b[0] == 0 && b[1] == 0 && b[2] == 0 && b[3] == 0)
305 if (b[0] == 169 && b[1] == 254)
307 if (b[0] == 10 || (b[0] == 172 && b[1] >= 16 && b[1] <= 31) || (b[0] == 192 && b[1] == 168))
309 if (b[0] >= 224 && b[0] <= 239)
318 for (
int k = 0; k < 16; k++)
327 bool loopback = (b[15] == 1);
328 for (
int k = 0; k < 15 && loopback; k++)
334 if (is_v4_mapped_bytes(b))
335 return classify_v4(b + 12);
338 if (b[0] == 0xfe && (b[1] & 0xc0) == 0x80)
340 if ((b[0] & 0xfe) == 0xfc)
362 else if (s[len] ==
'.')
370 memset(out->
bytes, 0, 16);
373 if (!parse_v6(s, len, out->
bytes))
380 if (!parse_v4(s, len, out->
bytes))
390 if (!ip || !out || cap == 0)
393 return format_v4(ip->
bytes, out, cap);
401 size_t tn = format_v4(ip->
bytes + 12, tail,
sizeof(tail));
402 if (tn == 0 || 7 + tn + 1 > cap)
404 memcpy(out,
"::ffff:", 7);
405 memcpy(out + 7, tail, tn);
411 for (
int k = 0; k < 8; k++)
412 g[k] = (uint16_t)((ip->
bytes[2 * k] << 8) | ip->
bytes[2 * k + 1]);
416 longest_zero_run(g, &zs, &zl);
421 for (
int k = 0; k < 8; k++)
423 if (zs != -1 && k >= zs && k < zs + zl)
431 n += put_hex16(g[k], tmp + n);
433 if (zs != -1 && zs + zl == 8)
453 return classify_v4(ip->
bytes);
455 return classify_v6(ip->
bytes);
470 return memcmp(a->
bytes, b->
bytes, (
size_t)n) == 0;
476 memset(&ip, 0,
sizeof(ip));
489 memcpy(ip.
bytes, bytes, 16);
497 const uint8_t *b = ip->
bytes;
499 return ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
501 return ((uint32_t)b[12] << 24) | ((uint32_t)b[13] << 16) | ((uint32_t)b[14] << 8) | b[15];
510 for (
int i = 0; i < n; i++)
521 if (bits == 0 || prefix_len > bits)
523 int whole = prefix_len / 8;
524 for (
int i = 0; i < whole; i++)
527 int rem = prefix_len % 8;
530 uint8_t mask = (uint8_t)(0xFF << (8 - rem));
531 if ((addr->
bytes[whole] & mask) != (net->
bytes[whole] & mask))
uint32_t det_ip_to_v4_be(const DetIp *ip)
The v4 address as a big-endian (network-order) uint32 (a<<24 | b<<16 | c<<8 | d).
DetIpScope det_ip_classify(const DetIp *ip)
Classify ip into a DetIpScope.
bool det_ip_prefix_match(const DetIp *addr, const DetIp *net, uint8_t prefix_len)
CIDR containment: is addr inside the net / prefix_len block?
bool det_ip_is_unspecified(const DetIp *ip)
True if ip is empty (DetIpFamily::DET_IP_NONE) or the all-zero unspecified address (0....
size_t det_ip_format(const DetIp *ip, char *out, size_t cap)
Format ip into out as its RFC 5952 canonical text.
bool det_ip_is_v4_mapped(const DetIp *ip)
True if ip is an IPv4-mapped IPv6 address (::ffff:a.b.c.d, RFC 4291 §2.5.5.2).
bool det_ip_equal(const DetIp *a, const DetIp *b)
True if a and b are the same family and address.
bool det_ip_parse(const char *s, DetIp *out)
Parse an IPv4 or IPv6 textual address (RFC 4291 §2.2) into out.
DetIp det_ip_from_v6_bytes(const uint8_t bytes[16])
Build a v6 DetIp from 16 address bytes in network (big-endian) order.
DetIp det_ip_from_v4_octets(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
Build a v4 DetIp from four octets (a.b.c.d).
Layer 3 (Network) - a family-tagged IP address (IPv4 or IPv6) with RFC-faithful text parsing,...
#define DET_IP_STR_MAX
Longest text an det_ip_format can produce, including the NUL (RFC 5952 v4-mapped).
@ DET_IP_V4
IPv4 (bytes[0..3])
@ DET_IP_V6
IPv6 (bytes[0..15])
@ DET_IP_NONE
empty / unparsed
DetIpScope
Address scope, in rough order of reachability (used for allow/deny policy + logging).
@ DET_IP_SCOPE_GLOBAL
globally routable unicast
@ DET_IP_SCOPE_LOOPBACK
127.0.0.0/8 / ::1
@ DET_IP_SCOPE_PRIVATE
RFC1918 (10/8, 172.16/12, 192.168/16) / ULA fc00::/7.
@ DET_IP_SCOPE_UNSPECIFIED
0.0.0.0 / ::
@ DET_IP_SCOPE_MULTICAST
224.0.0.0/4 / ff00::/8
@ DET_IP_SCOPE_LINK_LOCAL
169.254.0.0/16 / fe80::/10
A v4 or v6 address in network (big-endian) byte order.
uint8_t bytes[16]
network order; v4 uses the first 4
DetIpFamily family
address family tag