net.ip: Parse embedded Ipv4. Fix #64

This commit is contained in:
Eugen Wissner 2018-10-03 20:49:14 +02:00
parent 772e87739c
commit 9364112690

View File

@ -490,8 +490,8 @@ struct Address6
assert(address6("::14") > address6("::1")); assert(address6("::14") > address6("::1"));
assert(address6("::1") < address6("::14")); assert(address6("::1") < address6("::14"));
assert(address6("::1") == address6("::1")); assert(address6("::1") == address6("::1"));
assert(address6("::1%1") < address6("::1%2")); assert(address6("fe80::1%1") < address6("fe80::1%2"));
assert(address6("::1%2") > address6("::1%1")); assert(address6("fe80::1%2") > address6("fe80::1%1"));
} }
/** /**
@ -716,20 +716,17 @@ private void write2Bytes(R)(ref R range, ubyte[] address)
* is specified (i.e. first character after `%` is not a digit), the parser * is specified (i.e. first character after `%` is not a digit), the parser
* tries to convert it to the ID of that interface. If the interface with the * tries to convert it to the ID of that interface. If the interface with the
* given name can't be found, the parser doesn't fail, but just ignores the * given name can't be found, the parser doesn't fail, but just ignores the
* invalid interface name. * invalid interface name, scope ID is `0` then.
* *
* If an ID is given (i.e. first character after `%` is a digit), * If an ID is given (i.e. first character after `%` is a digit),
* $(D_PSYMBOL address6) just stores it in $(D_PSYMBOL Address6.scopeID) without * $(D_PSYMBOL address6) just stores it in $(D_PSYMBOL Address6.scopeID) without
* checking whether an interface with this ID really exists. If the ID is * checking whether an interface with this ID really exists. If the ID is
* invalid (if it is too long or contains non decimal characters), parsing * invalid (if it is too long or contains non decimal characters), parsing
* and nothing is returned. * fails and nothing is returned.
* *
* If neither an ID nor a name is given, $(D_PSYMBOL Address6.scopeID) is set * If neither an ID nor a name is given, $(D_PSYMBOL Address6.scopeID) is set
* to `0`. * to `0`.
* *
* The parser doesn't support notation with an embedded IPv4 address (e.g.
* ::1.2.3.4).
*
* Params: * Params:
* R = Input range type. * R = Input range type.
* range = Stringish range containing the address. * range = Stringish range containing the address.
@ -746,7 +743,6 @@ if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
} }
Address6 result; Address6 result;
ubyte[12] tail; ubyte[12] tail;
size_t i;
size_t j; size_t j;
// An address begins with a number, not ':'. But there is a special case // An address begins with a number, not ':'. But there is a special case
@ -764,10 +760,27 @@ if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
// Parse the address before '::'. // Parse the address before '::'.
// This loop parses the whole address if it doesn't contain '::'. // This loop parses the whole address if it doesn't contain '::'.
for (; i < 13; i += 2) static foreach (i; 0 .. 7)
{ {
write2Bytes(range, result.address[i .. $]); { // To make "state" definition local
if (range.empty || range.front != ':') static if (i == 6) // Can be embedded IPv4
{
auto state = range.save();
}
write2Bytes(range, result.address[i * 2 .. $]);
if (range.empty)
{
return typeof(return)();
}
static if (i == 6)
{
if (range.front == '.')
{
swap(range, state);
goto ParseIPv4;
}
}
if (range.front != ':')
{ {
return typeof(return)(); return typeof(return)();
} }
@ -782,6 +795,7 @@ if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
goto ParseTail; goto ParseTail;
} }
} }
}
write2Bytes(range, result.address[14 .. $]); write2Bytes(range, result.address[14 .. $]);
if (range.empty) if (range.empty)
@ -810,6 +824,9 @@ ParseTail: // after ::
{ {
return typeof(return)(); return typeof(return)();
} }
{ // To make "state" definition local
auto state = range.save();
write2Bytes(range, tail[j .. $]); write2Bytes(range, tail[j .. $]);
if (range.empty) if (range.empty)
{ {
@ -819,18 +836,26 @@ ParseTail: // after ::
{ {
goto ParseIface; goto ParseIface;
} }
else if (range.front == '.')
{
swap(range, state);
goto ParseIPv4;
}
else if (range.front != ':') else if (range.front != ':')
{ {
return typeof(return)(); return typeof(return)();
} }
range.popFront(); range.popFront();
}
for (i = 2, j = 2; i <= 11; i += 2, j += 2, range.popFront()) j = 2;
for (size_t i = 2; i <= 11; i += 2, j += 2, range.popFront())
{ {
if (range.empty || range.front == ':') if (range.empty || range.front == ':')
{ {
return typeof(return)(); return typeof(return)();
} }
auto state = range.save();
write2Bytes(range, tail[j .. $]); write2Bytes(range, tail[j .. $]);
if (range.empty) if (range.empty)
@ -841,12 +866,45 @@ ParseTail: // after ::
{ {
goto ParseIface; goto ParseIface;
} }
else if (range.front == '.')
{
swap(range, state);
goto ParseIPv4;
}
else if (range.front != ':') else if (range.front != ':')
{ {
return typeof(return)(); return typeof(return)();
} }
} }
ParseIPv4:
// We know there is a number followed by '.'. We have to ensure this number
// is an octet
tail[j] = readIntegral!ubyte(range);
static foreach (i; 1 .. 4)
{
if (range.empty || range.front != '.')
{
return typeof(return)();
}
range.popFront();
if (range.empty)
{
return typeof(return)();
}
tail[j + i] = readIntegral!ubyte(range);
}
j += 2;
if (range.empty)
{
goto CopyTail;
}
else if (range.front != '%')
{
return typeof(return)();
}
ParseIface: // Scope name or ID ParseIface: // Scope name or ID
range.popFront(); range.popFront();
if (range.empty) if (range.empty)
@ -907,7 +965,31 @@ CopyTail:
assert(address6(":a").isNothing); assert(address6(":a").isNothing);
assert(address6("a:").isNothing); assert(address6("a:").isNothing);
assert(address6("1:2:3:4::6:").isNothing); assert(address6("1:2:3:4::6:").isNothing);
assert(address6("1:2:3:4::6:7:8%").isNothing); assert(address6("fe80:2:3:4::6:7:8%").isNothing);
}
// Parses embedded IPv4 address
@nogc nothrow @safe unittest
{
{
ubyte[16] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4];
auto actual = address6("0:0:0:0:0:0:1.2.3.4");
assert(actual.address == expected);
}
{
ubyte[16] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4];
auto actual = address6("::1.2.3.4");
assert(actual.address == expected);
}
{
ubyte[16] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 1, 2, 3, 4];
auto actual = address6("::5:6:1.2.3.4");
assert(actual.address == expected);
}
assert(address6("0:0:0:0:0:0:1.2.3.").isNothing);
assert(address6("0:0:0:0:0:0:1.2:3.4").isNothing);
assert(address6("0:0:0:0:0:0:1.2.3.4.").isNothing);
assert(address6("fe80:0:0:0:0:0:1.2.3.4%1").scopeID == 1);
} }
/** /**