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