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