Why does IPAddress.MapToIPv4() throw ArgumentOutOfRangeException?
Asked Answered
A

1

14

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

Assemble answered 12/5, 2014 at 12:17 Comment(3)
Interesting. There's a lot of IPv4 addresses for which this works just fine. In fact, based on some ad-hoc testing, it seems that the last bit is to blame - perhaps there's an error somewhere in the code that so liberally uses long, ulong and int casts with positive and negative numbers. Why do you want IPv6 mapped IPv4 addresses anyway, though? Are you working with linux servers? I thought Windows doesn't really handle those (since IPv4 and IPv6 are separate drivers on the networking stack - you need two separate sockets to handle both IPv4 and IPv6).Henn
Well, I don't really need it, but I considered it a small win when comparing addresses. I guess I'm lucky the tests of my code actually had an address like this, to steer me away from my premature (unprofiled) optimization...Assemble
@PatrickHuizinga Ah, I've been posting the bug report as well. Anyway, I upped yours :)Henn
H
18

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 ints. 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.

Henn answered 12/5, 2014 at 12:42 Comment(3)
Are you saying this is essentially a bug in the .NET source? - Should Microsoft be notified of this?Remittee
@series0ne Yup, it's now been reported - connect.microsoft.com/VisualStudio/feedback/details/871964/…Henn
Just for everyone's reference, I hit this on a fully patched server running .NET Framework 4.5.2, so I suppose the fix won't be until 4.6Jillene

© 2022 - 2024 — McMap. All rights reserved.