Skip to content

Commit 66e0134

Browse files
committed
Update IPAddress implementation to support V6
1 parent 682b4b0 commit 66e0134

File tree

1 file changed

+244
-21
lines changed

1 file changed

+244
-21
lines changed

cores/esp32/IPAddress.cpp

Lines changed: 244 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,77 +21,223 @@
2121
#include <IPAddress.h>
2222
#include <Print.h>
2323

24-
IPAddress::IPAddress()
24+
IPAddress::IPAddress() : IPAddress(IPv4) {}
25+
26+
IPAddress::IPAddress(IPType ip_type)
2527
{
26-
_address.dword = 0;
28+
_type = ip_type;
29+
memset(_address.bytes, 0, sizeof(_address.bytes));
2730
}
2831

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

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

42-
IPAddress::IPAddress(const uint8_t *address)
78+
IPAddress::IPAddress(const uint8_t *address) : IPAddress(IPv4, address) {}
79+
80+
IPAddress::IPAddress(IPType ip_type, const uint8_t *address)
4381
{
44-
memcpy(_address.bytes, address, sizeof(_address.bytes));
82+
_type = ip_type;
83+
if (ip_type == IPv4) {
84+
memset(_address.bytes, 0, sizeof(_address.bytes));
85+
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
86+
} else {
87+
memcpy(_address.bytes, address, sizeof(_address.bytes));
88+
}
4589
}
4690

4791
IPAddress& IPAddress::operator=(const uint8_t *address)
4892
{
49-
memcpy(_address.bytes, address, sizeof(_address.bytes));
93+
// IPv4 only conversion from byte pointer
94+
_type = IPv4;
95+
memset(_address.bytes, 0, sizeof(_address.bytes));
96+
memcpy(&_address.bytes[IPADDRESS_V4_BYTES_INDEX], address, sizeof(uint32_t));
5097
return *this;
5198
}
5299

53100
IPAddress& IPAddress::operator=(uint32_t address)
54101
{
55-
_address.dword = address;
102+
// IPv4 conversion
103+
// See note on conversion/comparison and uint32_t
104+
_type = IPv4;
105+
memset(_address.bytes, 0, sizeof(_address.bytes));
106+
_address.dword[IPADDRESS_V4_DWORD_INDEX] = address;
56107
return *this;
57108
}
58109

59110
bool IPAddress::operator==(const uint8_t* addr) const
60111
{
61-
return memcmp(addr, _address.bytes, sizeof(_address.bytes)) == 0;
112+
return (addr._type == _type)
113+
&& (memcmp(addr._address.bytes, _address.bytes, sizeof(_address.bytes)) == 0);
114+
}
115+
116+
bool IPAddress::operator==(const uint8_t* addr) const
117+
{
118+
// IPv4 only comparison to byte pointer
119+
// Can't support IPv6 as we know our type, but not the length of the pointer
120+
return _type == IPv4 && memcmp(addr, &_address.bytes[IPADDRESS_V4_BYTES_INDEX], sizeof(uint32_t)) == 0;
121+
}
122+
123+
uint8_t IPAddress::operator[](int index) const {
124+
if (_type == IPv4) {
125+
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
126+
}
127+
return _address.bytes[index];
128+
}
129+
130+
uint8_t& IPAddress::operator[](int index) {
131+
if (_type == IPv4) {
132+
return _address.bytes[IPADDRESS_V4_BYTES_INDEX + index];
133+
}
134+
return _address.bytes[index];
62135
}
63136

64137
size_t IPAddress::printTo(Print& p) const
65138
{
66139
size_t n = 0;
67-
for(int i = 0; i < 3; i++) {
68-
n += p.print(_address.bytes[i], DEC);
140+
141+
if (_type == IPv6) {
142+
// IPv6 IETF canonical format: compress left-most longest run of two or more zero fields, lower case
143+
int8_t longest_start = -1;
144+
int8_t longest_length = 1;
145+
int8_t current_start = -1;
146+
int8_t current_length = 0;
147+
for (int8_t f = 0; f < 8; f++) {
148+
if (_address.bytes[f * 2] == 0 && _address.bytes[f * 2 + 1] == 0) {
149+
if (current_start == -1) {
150+
current_start = f;
151+
current_length = 1;
152+
} else {
153+
current_length++;
154+
}
155+
if (current_length > longest_length) {
156+
longest_start = current_start;
157+
longest_length = current_length;
158+
}
159+
} else {
160+
current_start = -1;
161+
}
162+
}
163+
for (int f = 0; f < 8; f++) {
164+
if (f < longest_start || f >= longest_start + longest_length) {
165+
uint8_t c1 = _address.bytes[f * 2] >> 4;
166+
uint8_t c2 = _address.bytes[f * 2] & 0xf;
167+
uint8_t c3 = _address.bytes[f * 2 + 1] >> 4;
168+
uint8_t c4 = _address.bytes[f * 2 + 1] & 0xf;
169+
if (c1 > 0) {
170+
n += p.print((char)(c1 < 10 ? '0' + c1 : 'a' + c1 - 10));
171+
}
172+
if (c1 > 0 || c2 > 0) {
173+
n += p.print((char)(c2 < 10 ? '0' + c2 : 'a' + c2 - 10));
174+
}
175+
if (c1 > 0 || c2 > 0 || c3 > 0) {
176+
n += p.print((char)(c3 < 10 ? '0' + c3 : 'a' + c3 - 10));
177+
}
178+
n += p.print((char)(c4 < 10 ? '0' + c4 : 'a' + c4 - 10));
179+
if (f < 7) {
180+
n += p.print(':');
181+
}
182+
} else if (f == longest_start) {
183+
if (longest_start == 0) {
184+
n += p.print(':');
185+
}
186+
n += p.print(':');
187+
}
188+
}
189+
return n;
190+
}
191+
192+
// IPv4
193+
for (int i =0; i < 3; i++)
194+
{
195+
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + i], DEC);
69196
n += p.print('.');
70197
}
71-
n += p.print(_address.bytes[3], DEC);
198+
n += p.print(_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3], DEC);
72199
return n;
73200
}
74201

75202
String IPAddress::toString() const
76203
{
204+
if (_type == IPv6)
205+
{
206+
StreamString s;
207+
s.reserve(40);
208+
printTo(s);
209+
return s;
210+
}
211+
212+
// IPv4
77213
char szRet[16];
78214
sprintf(szRet,"%u.%u.%u.%u", _address.bytes[0], _address.bytes[1], _address.bytes[2], _address.bytes[3]);
79215
return String(szRet);
80216
}
81217

82218
bool IPAddress::fromString(const char *address)
219+
{
220+
if (!fromString4(address))
221+
{
222+
return fromString6(address);
223+
}
224+
return true;
225+
}
226+
227+
bool IPAddress::fromString4(const char *address)
83228
{
84229
// TODO: add support for "a", "a.b", "a.b.c" formats
85230

86-
uint16_t acc = 0; // Accumulator
231+
int16_t acc = -1; // Accumulator
87232
uint8_t dots = 0;
88233

234+
memset(_address.bytes, 0, sizeof(_address.bytes));
89235
while (*address)
90236
{
91237
char c = *address++;
92238
if (c >= '0' && c <= '9')
93239
{
94-
acc = acc * 10 + (c - '0');
240+
acc = (acc < 0) ? (c - '0') : acc * 10 + (c - '0');
95241
if (acc > 255) {
96242
// Value out of [0..255] range
97243
return false;
@@ -100,11 +246,15 @@ bool IPAddress::fromString(const char *address)
100246
else if (c == '.')
101247
{
102248
if (dots == 3) {
103-
// Too much dots (there must be 3 dots)
249+
// Too many dots (there must be 3 dots)
104250
return false;
105251
}
106-
_address.bytes[dots++] = acc;
107-
acc = 0;
252+
if (acc < 0) {
253+
/* No value between dots, e.g. '1..' */
254+
return false;
255+
}
256+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + dots++] = acc;
257+
acc = -1;
108258
}
109259
else
110260
{
@@ -117,7 +267,80 @@ bool IPAddress::fromString(const char *address)
117267
// Too few dots (there must be 3 dots)
118268
return false;
119269
}
120-
_address.bytes[3] = acc;
270+
if (acc < 0) {
271+
/* No value between dots, e.g. '1..' */
272+
return false;
273+
}
274+
_address.bytes[IPADDRESS_V4_BYTES_INDEX + 3] = acc;
275+
_type = IPv4;
276+
return true;
277+
}
278+
279+
bool IPAddress::fromString6(const char *address) {
280+
uint32_t acc = 0; // Accumulator
281+
int colons = 0, double_colons = -1;
282+
283+
while (*address)
284+
{
285+
char c = tolower(*address++);
286+
if (isalnum(c) && c <= 'f') {
287+
if (c >= 'a')
288+
c -= 'a' - '0' - 10;
289+
acc = acc * 16 + (c - '0');
290+
if (acc > 0xffff)
291+
// Value out of range
292+
return false;
293+
}
294+
else if (c == ':') {
295+
if (*address == ':') {
296+
if (double_colons >= 0) {
297+
// :: allowed once
298+
return false;
299+
}
300+
if (*address != '\0' && *(address + 1) == ':') {
301+
// ::: not allowed
302+
return false;
303+
}
304+
// remember location
305+
double_colons = colons + !!acc;
306+
address++;
307+
} else if (*address == '\0') {
308+
// can't end with a single colon
309+
return false;
310+
}
311+
if (colons == 7)
312+
// too many separators
313+
return false;
314+
_address.bytes[colons * 2] = acc >> 8;
315+
_address.bytes[colons * 2 + 1] = acc & 0xff;
316+
colons++;
317+
acc = 0;
318+
}
319+
else
320+
// Invalid char
321+
return false;
322+
}
323+
324+
if (double_colons == -1 && colons != 7) {
325+
// Too few separators
326+
return false;
327+
}
328+
if (double_colons > -1 && colons > 6) {
329+
// Too many segments (double colon must be at least one zero field)
330+
return false;
331+
}
332+
_address.bytes[colons * 2] = acc >> 8;
333+
_address.bytes[colons * 2 + 1] = acc & 0xff;
334+
colons++;
335+
336+
if (double_colons != -1) {
337+
for (int i = colons * 2 - double_colons * 2 - 1; i >= 0; i--)
338+
_address.bytes[16 - colons * 2 + double_colons * 2 + i] = _address.bytes[double_colons * 2 + i];
339+
for (int i = double_colons * 2; i < 16 - colons * 2 + double_colons * 2; i++)
340+
_address.bytes[i] = 0;
341+
}
342+
343+
_type = IPv6;
121344
return true;
122345
}
123346

0 commit comments

Comments
 (0)