Skip to content

Commit c8ee90f

Browse files
authored
Merge pull request #169 from sgryphon/feature/add-v6-to-ipaddress
Add IPv6 support to IPAddress
2 parents ee040ed + bcc5233 commit c8ee90f

10 files changed

+998
-27
lines changed

Diff for: api/IPAddress.cpp

+220-17
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,88 @@
2222

2323
using namespace arduino;
2424

25-
IPAddress::IPAddress()
25+
IPAddress::IPAddress() : IPAddress(IPv4) {}
26+
27+
IPAddress::IPAddress(IPType ip_type)
2628
{
27-
_address.dword = 0;
29+
_type = ip_type;
30+
memset(_address.bytes, 0, sizeof(_address.bytes));
2831
}
2932

3033
IPAddress::IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet)
3134
{
32-
_address.bytes[0] = first_octet;
33-
_address.bytes[1] = second_octet;
34-
_address.bytes[2] = third_octet;
35-
_address.bytes[3] = fourth_octet;
35+
_type = IPv4;
36+
memset(_address.bytes, 0, sizeof(_address.bytes));
37+
_address.bytes[IPADDRESS_V4_BYTES_INDEX] = first_octet;
38+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 1] = second_octet;
39+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 2] = third_octet;
40+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = fourth_octet;
41+
}
42+
43+
IPAddress::IPAddress(uint8_t o1, uint8_t o2, uint8_t o3, uint8_t o4, uint8_t o5, uint8_t o6, uint8_t o7, uint8_t o8, uint8_t o9, uint8_t o10, uint8_t o11, uint8_t o12, uint8_t o13, uint8_t o14, uint8_t o15, uint8_t o16) {
44+
_type = IPv6;
45+
_address.bytes[0] = o1;
46+
_address.bytes[1] = o2;
47+
_address.bytes[2] = o3;
48+
_address.bytes[3] = o4;
49+
_address.bytes[4] = o5;
50+
_address.bytes[5] = o6;
51+
_address.bytes[6] = o7;
52+
_address.bytes[7] = o8;
53+
_address.bytes[8] = o9;
54+
_address.bytes[9] = o10;
55+
_address.bytes[10] = o11;
56+
_address.bytes[11] = o12;
57+
_address.bytes[12] = o13;
58+
_address.bytes[13] = o14;
59+
_address.bytes[14] = o15;
60+
_address.bytes[15] = o16;
3661
}
3762

3863
IPAddress::IPAddress(uint32_t address)
3964
{
40-
_address.dword = address;
65+
// IPv4 only
66+
_type = IPv4;
67+
memset(_address.bytes, 0, sizeof(_address.bytes));
68+
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
69+
70+
// NOTE on conversion/comparison and uint32_t:
71+
// These conversions are host platform dependent.
72+
// There is a defined integer representation of IPv4 addresses,
73+
// based on network byte order (will be the value on big endian systems),
74+
// e.g. http://2398766798 is the same as http://142.250.70.206,
75+
// However on little endian systems the octets 0x83, 0xFA, 0x46, 0xCE,
76+
// in that order, will form the integer (uint32_t) 3460758158 .
4177
}
4278

43-
IPAddress::IPAddress(const uint8_t *address)
79+
IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {}
80+
81+
IPAddress::IPAddress(IPType ip_type, const uint8_t *address)
4482
{
45-
memcpy(_address.bytes, address, sizeof(_address.bytes));
83+
_type = ip_type;
84+
if (ip_type == IPv4) {
85+
memset(_address.bytes, 0, sizeof(_address.bytes));
86+
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
87+
} else {
88+
memcpy(_address.bytes, address, sizeof(_address.bytes));
89+
}
4690
}
4791

48-
bool IPAddress::fromString(const char *address)
92+
bool IPAddress::fromString(const char *address) {
93+
if (!fromString4(address)) {
94+
return fromString6(address);
95+
}
96+
return true;
97+
}
98+
99+
bool IPAddress::fromString4(const char *address)
49100
{
50101
// TODO: add support for "a", "a.b", "a.b.c" formats
51102

52103
int16_t acc = -1; // Accumulator
53104
uint8_t dots = 0;
54105

106+
memset(_address.bytes, 0, sizeof(_address.bytes));
55107
while (*address)
56108
{
57109
char c = *address++;
@@ -73,7 +125,7 @@ bool IPAddress::fromString(const char *address)
73125
/* No value between dots, e.g. '1..' */
74126
return false;
75127
}
76-
_address.bytes[dots++] = acc;
128+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + dots++] = acc;
77129
acc = -1;
78130
}
79131
else
@@ -91,37 +143,188 @@ bool IPAddress::fromString(const char *address)
91143
/* No value between dots, e.g. '1..' */
92144
return false;
93145
}
94-
_address.bytes[3] = acc;
146+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = acc;
147+
_type = IPv4;
148+
return true;
149+
}
150+
151+
bool IPAddress::fromString6(const char *address) {
152+
uint32_t acc = 0; // Accumulator
153+
int colons = 0, double_colons = -1;
154+
155+
while (*address)
156+
{
157+
char c = tolower(*address++);
158+
if (isalnum(c) && c <= 'f') {
159+
if (c >= 'a')
160+
c -= 'a' - '0' - 10;
161+
acc = acc * 16 + (c - '0');
162+
if (acc > 0xffff)
163+
// Value out of range
164+
return false;
165+
}
166+
else if (c == ':') {
167+
if (*address == ':') {
168+
if (double_colons >= 0) {
169+
// :: allowed once
170+
return false;
171+
}
172+
if (*address != '\0' && *(address + 1) == ':') {
173+
// ::: not allowed
174+
return false;
175+
}
176+
// remember location
177+
double_colons = colons + !!acc;
178+
address++;
179+
} else if (*address == '\0') {
180+
// can't end with a single colon
181+
return false;
182+
}
183+
if (colons == 7)
184+
// too many separators
185+
return false;
186+
_address.bytes[colons * 2] = acc >> 8;
187+
_address.bytes[colons * 2 + 1] = acc & 0xff;
188+
colons++;
189+
acc = 0;
190+
}
191+
else
192+
// Invalid char
193+
return false;
194+
}
195+
196+
if (double_colons == -1 && colons != 7) {
197+
// Too few separators
198+
return false;
199+
}
200+
if (double_colons > -1 && colons > 6) {
201+
// Too many segments (double colon must be at least one zero field)
202+
return false;
203+
}
204+
_address.bytes[colons * 2] = acc >> 8;
205+
_address.bytes[colons * 2 + 1] = acc & 0xff;
206+
colons++;
207+
208+
if (double_colons != -1) {
209+
for (int i = colons * 2 - double_colons * 2 - 1; i >= 0; i--)
210+
_address.bytes[16 - colons * 2 + double_colons * 2 + i] = _address.bytes[double_colons * 2 + i];
211+
for (int i = double_colons * 2; i < 16 - colons * 2 + double_colons * 2; i++)
212+
_address.bytes[i] = 0;
213+
}
214+
215+
_type = IPv6;
95216
return true;
96217
}
97218

98219
IPAddress& IPAddress::operator=(const uint8_t *address)
99220
{
100-
memcpy(_address.bytes, address, sizeof(_address.bytes));
221+
// IPv4 only conversion from byte pointer
222+
_type = IPv4;
223+
memset(_address.bytes, 0, sizeof(_address.bytes));
224+
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
101225
return *this;
102226
}
103227

104228
IPAddress& IPAddress::operator=(uint32_t address)
105229
{
106-
_address.dword = address;
230+
// IPv4 conversion
231+
// See note on conversion/comparison and uint32_t
232+
_type = IPv4;
233+
memset(_address.bytes, 0, sizeof(_address.bytes));
234+
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
107235
return *this;
108236
}
109237

238+
bool IPAddress::operator==(const IPAddress& addr) const {
239+
return (addr._type == _type)
240+
&& (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
241+
}
242+
110243
bool IPAddress::operator==(const uint8_t* addr) const
111244
{
112-
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
245+
// IPv4 only comparison to byte pointer
246+
// Can't support IPv6 as we know our type, but not the length of the pointer
247+
return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
248+
}
249+
250+
uint8_t IPAddress::operator[](int index) const {
251+
if (_type == IPv4) {
252+
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
253+
}
254+
return _address.bytes[index];
255+
}
256+
257+
uint8_t& IPAddress::operator[](int index) {
258+
if (_type == IPv4) {
259+
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
260+
}
261+
return _address.bytes[index];
113262
}
114263

115264
size_t IPAddress::printTo(Print& p) const
116265
{
117266
size_t n = 0;
267+
268+
if (_type == IPv6) {
269+
// IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
270+
int8_t longest_start = -1;
271+
int8_t longest_length = 1;
272+
int8_t current_start = -1;
273+
int8_t current_length = 0;
274+
for (int8_t f = 0; f < 8; f++) {
275+
if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
276+
if (current_start == -1) {
277+
current_start = f;
278+
current_length = 1;
279+
} else {
280+
current_length++;
281+
}
282+
if (current_length > longest_length) {
283+
longest_start = current_start;
284+
longest_length = current_length;
285+
}
286+
} else {
287+
current_start = -1;
288+
}
289+
}
290+
for (int f = 0; f < 8; f++) {
291+
if (f < longest_start || f >= longest_start + longest_length) {
292+
uint8_t c1 = _address.bytes[f * 2] >> 4;
293+
uint8_t c2 = _address.bytes[f * 2] & 0xf;
294+
uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
295+
uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
296+
if (c1 > 0) {
297+
n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
298+
}
299+
if (c1 > 0 || c2 > 0) {
300+
n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
301+
}
302+
if (c1 > 0 || c2 > 0 || c3 > 0) {
303+
n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
304+
}
305+
n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
306+
if (f < 7) {
307+
n += p.print(':');
308+
}
309+
} else if (f == longest_start) {
310+
if (longest_start == 0) {
311+
n += p.print(':');
312+
}
313+
n += p.print(':');
314+
}
315+
}
316+
return n;
317+
}
318+
319+
// IPv4
118320
for (int i =0; i < 3; i++)
119321
{
120-
n += p.print(_address.bytes[i], DEC);
322+
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
121323
n += p.print('.');
122324
}
123-
n += p.print(_address.bytes[3], DEC);
325+
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
124326
return n;
125327
}
126328

329+
const IPAddress arduino::IN6ADDR_ANY(IPv6);
127330
const IPAddress arduino::INADDR_NONE(0,0,0,0);

0 commit comments

Comments
 (0)