diff options
| author | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-02 20:50:24 +0100 |
|---|---|---|
| committer | Dirk Engling <erdgeist@erdgeist.org> | 2026-11-02 21:14:02 +0100 |
| commit | ad541c67761b8aa6da1566802c347cac4018d0c2 (patch) | |
| tree | a9166fa9d349911d69a6948e53b05344dcc348a0 /src/ot_ip.c | |
| parent | c679ff8a3b1418a5960cc49ba6458a0007df54fa (diff) | |
Move the build sources into src/
Diffstat (limited to 'src/ot_ip.c')
| -rw-r--r-- | src/ot_ip.c | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/ot_ip.c b/src/ot_ip.c new file mode 100644 index 0000000..9c76290 --- /dev/null +++ b/src/ot_ip.c | |||
| @@ -0,0 +1,199 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. */ | ||
| 3 | |||
| 4 | /* System */ | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <string.h> | ||
| 7 | |||
| 8 | /* Opentracker */ | ||
| 9 | #include "ot_hex.h" | ||
| 10 | #include "ot_ip.h" | ||
| 11 | |||
| 12 | const char ip_v4mapped_prefix[12] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}; | ||
| 13 | |||
| 14 | /* The dotted quad is only ever reached through the v4 mapped form, so both | ||
| 15 | halves of it stay in here */ | ||
| 16 | |||
| 17 | static size_t ip4_format(char *dest, const uint8_t *ip) { | ||
| 18 | char *d = dest; | ||
| 19 | int i; | ||
| 20 | |||
| 21 | for (i = 0; i < 4; ++i) { | ||
| 22 | unsigned int octet = ip[i]; | ||
| 23 | |||
| 24 | if (i) | ||
| 25 | *d++ = '.'; | ||
| 26 | if (octet >= 100) | ||
| 27 | *d++ = (char)('0' + octet / 100); | ||
| 28 | if (octet >= 10) | ||
| 29 | *d++ = (char)('0' + (octet / 10) % 10); | ||
| 30 | *d++ = (char)('0' + octet % 10); | ||
| 31 | } | ||
| 32 | |||
| 33 | return (size_t)(d - dest); | ||
| 34 | } | ||
| 35 | |||
| 36 | static size_t ip4_scan(const char *src, uint8_t ip[4]) { | ||
| 37 | uint8_t octets[4]; | ||
| 38 | const char *s = src; | ||
| 39 | int i; | ||
| 40 | |||
| 41 | for (i = 0; i < 4; ++i) { | ||
| 42 | unsigned int value = 0; | ||
| 43 | int digits = 0; | ||
| 44 | |||
| 45 | if (i && *s++ != '.') | ||
| 46 | return 0; | ||
| 47 | |||
| 48 | /* Leading zeros are tolerated, a value beyond one octet is not */ | ||
| 49 | while (*s >= '0' && *s <= '9' && digits < 4) { | ||
| 50 | value = value * 10 + (unsigned int)(*s++ - '0'); | ||
| 51 | ++digits; | ||
| 52 | } | ||
| 53 | if (!digits || value > 255) | ||
| 54 | return 0; | ||
| 55 | |||
| 56 | octets[i] = (uint8_t)value; | ||
| 57 | } | ||
| 58 | |||
| 59 | memcpy(ip, octets, sizeof(octets)); | ||
| 60 | return (size_t)(s - src); | ||
| 61 | } | ||
| 62 | |||
| 63 | static unsigned int group_at(const char *ip, int n) { return ((unsigned int)(uint8_t)ip[2 * n] << 8) | (uint8_t)ip[2 * n + 1]; } | ||
| 64 | |||
| 65 | size_t ip_format(char *dest, const ot_ip6 ip) { | ||
| 66 | static const char hexdigit[] = "0123456789abcdef"; | ||
| 67 | char *d = dest; | ||
| 68 | int best_start = 0, best_len = 0, i = 0; | ||
| 69 | |||
| 70 | if (ip_is_v4mapped(ip)) | ||
| 71 | return ip4_format(dest, (const uint8_t *)ip + sizeof(ip_v4mapped_prefix)); | ||
| 72 | |||
| 73 | /* RFC 5952 4.2: the longest run of zero groups becomes "::", never a run | ||
| 74 | of one, and the first of two equal runs wins */ | ||
| 75 | while (i < 8) { | ||
| 76 | int j = i; | ||
| 77 | |||
| 78 | if (group_at(ip, i)) { | ||
| 79 | ++i; | ||
| 80 | continue; | ||
| 81 | } | ||
| 82 | while (j < 8 && !group_at(ip, j)) | ||
| 83 | ++j; | ||
| 84 | if (j - i > best_len) { | ||
| 85 | best_len = j - i; | ||
| 86 | best_start = i; | ||
| 87 | } | ||
| 88 | i = j; | ||
| 89 | } | ||
| 90 | if (best_len < 2) | ||
| 91 | best_len = 0; | ||
| 92 | |||
| 93 | for (i = 0; i < 8; ++i) { | ||
| 94 | unsigned int group; | ||
| 95 | int shift = 12; | ||
| 96 | |||
| 97 | if (best_len && i == best_start) { | ||
| 98 | *d++ = ':'; | ||
| 99 | if (i + best_len == 8) | ||
| 100 | *d++ = ':'; /* nothing follows, so close the run */ | ||
| 101 | i += best_len - 1; | ||
| 102 | continue; | ||
| 103 | } | ||
| 104 | |||
| 105 | if (i) | ||
| 106 | *d++ = ':'; | ||
| 107 | |||
| 108 | group = group_at(ip, i); | ||
| 109 | while (shift && !(group >> shift)) | ||
| 110 | shift -= 4; /* RFC 5952 4.1: no leading zeros */ | ||
| 111 | for (;; shift -= 4) { | ||
| 112 | *d++ = hexdigit[(group >> shift) & 15]; | ||
| 113 | if (!shift) | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | } | ||
| 117 | |||
| 118 | return (size_t)(d - dest); | ||
| 119 | } | ||
| 120 | |||
| 121 | size_t ip_parse(const char *src, ot_ip6 ip) { | ||
| 122 | uint8_t prefix[16], suffix[16]; | ||
| 123 | size_t prefixlen = 0, suffixlen = 0; | ||
| 124 | const char *s = src; | ||
| 125 | int have_gap = 0; | ||
| 126 | int need_colon = 0; | ||
| 127 | |||
| 128 | /* A plain IPv4 address is an address too, stored v4 mapped */ | ||
| 129 | { | ||
| 130 | size_t consumed = ip4_scan(src, prefix); | ||
| 131 | if (consumed) { | ||
| 132 | memcpy(ip, ip_v4mapped_prefix, sizeof(ip_v4mapped_prefix)); | ||
| 133 | memcpy(ip + sizeof(ip_v4mapped_prefix), prefix, 4); | ||
| 134 | return consumed; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | for (;;) { | ||
| 139 | unsigned int value = 0; | ||
| 140 | int digits = 0, nibble; | ||
| 141 | uint8_t *into = have_gap ? suffix : prefix; | ||
| 142 | size_t *fill = have_gap ? &suffixlen : &prefixlen; | ||
| 143 | |||
| 144 | /* RFC 4291 2.2: one run of zero groups may be written "::" */ | ||
| 145 | if (s[0] == ':' && s[1] == ':') { | ||
| 146 | if (have_gap) | ||
| 147 | return 0; | ||
| 148 | have_gap = 1; | ||
| 149 | need_colon = 0; | ||
| 150 | s += 2; | ||
| 151 | continue; | ||
| 152 | } | ||
| 153 | |||
| 154 | if (need_colon) { | ||
| 155 | if (*s != ':') | ||
| 156 | break; | ||
| 157 | ++s; | ||
| 158 | } | ||
| 159 | |||
| 160 | /* RFC 4291 2.2 form 3: the final two groups may be a dotted quad */ | ||
| 161 | if (prefixlen + suffixlen + 4 <= 16) { | ||
| 162 | size_t consumed = ip4_scan(s, into + *fill); | ||
| 163 | if (consumed) { | ||
| 164 | *fill += 4; | ||
| 165 | s += consumed; | ||
| 166 | break; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | while (digits < 4 && (nibble = hex_value(*s)) >= 0) { | ||
| 171 | value = value * 16 + (unsigned int)nibble; | ||
| 172 | ++digits; | ||
| 173 | ++s; | ||
| 174 | } | ||
| 175 | if (!digits) { | ||
| 176 | if (need_colon) | ||
| 177 | --s; /* leave the separator we ate for the caller */ | ||
| 178 | break; | ||
| 179 | } | ||
| 180 | |||
| 181 | if (prefixlen + suffixlen + 2 > 16) | ||
| 182 | return 0; | ||
| 183 | |||
| 184 | into[(*fill)++] = (uint8_t)(value >> 8); | ||
| 185 | into[(*fill)++] = (uint8_t)value; | ||
| 186 | need_colon = 1; | ||
| 187 | } | ||
| 188 | |||
| 189 | if (!prefixlen && !suffixlen && !have_gap) | ||
| 190 | return 0; | ||
| 191 | if (!have_gap && prefixlen + suffixlen != 16) | ||
| 192 | return 0; /* without "::" every group has to be spelled out */ | ||
| 193 | |||
| 194 | memset(ip, 0, 16); | ||
| 195 | memcpy(ip, prefix, prefixlen); | ||
| 196 | memcpy(ip + 16 - suffixlen, suffix, suffixlen); | ||
| 197 | |||
| 198 | return (size_t)(s - src); | ||
| 199 | } | ||
