问题
This code throws an ArgumentOutOfRangeException on the last line
var initAddress = IPAddress.Parse("1.65.128.190");
var ipv6Address = initAddress.MapToIPv6();
Assert.IsTrue(ipv6Address.IsIPv4MappedToIPv6);
var ipv4Address = ipv6Address.MapToIPv4();
Can anyone explain why MapToIPv6() and MapToIPv4() are not round trip compatible?
edit: The exception originates from the IPAddress constructor, called by MapToIPv4().
Also, when the first line is
var initAddress = IPAddress.Parse("1.65.128.90");
no exceptions are thrown anymore
edit2: as @Luaan reproduced this, I added the tag [bug-reporting]. Also added [bcl]. Let's see if any MS personnel tracks those tags :)
edit3: reported at Connect https://connect.microsoft.com/VisualStudio/feedback/details/871964
回答1:
Ok, I've actually verified this, so let me post this as an answer.
The IPAddress
class has an error when mapping the address back to IPv4.
According to the .NET reference code, it does this:
long address =
(((m_Numbers[6] & 0x0000FF00) >> 8) | ((m_Numbers[6] & 0x000000FF) << 8)) |
((((m_Numbers[7] & 0x0000FF00) >> 8) | ((m_Numbers[7] & 0x000000FF) << 8)) << 16);
The problem should be quite obvious to anyone doing bitwise operations in .NET - the numbers are all int
s. So shifting the second ushort
(m_Numbers[7]
) will give a negative value, because the most significant bit is 1
. This means that all IPv4 addresses that end with a byte higher than 127
will cause an error when mapping back from IPv6.
The simple fix would be this:
long address =
(((m_Numbers[6] & 0x0000FF00) >> 8) | ((m_Numbers[6] & 0x000000FF) << 8))
|
(
(uint)(((m_Numbers[7] & 0x0000FF00) >> 8) | ((m_Numbers[7] & 0x000000FF) << 8))
<< 16
);
Just by casting the int
to an uint
before doing the bitshift does the trick.
Bitwise operations can be quite tricky when you factor in signed types. I guess the code was copied from a C++ library or something, where this issue wouldn't manifest.
来源:https://stackoverflow.com/questions/23608829/why-does-ipaddress-maptoipv4-throw-argumentoutofrangeexception