IPAddress.Parse() using port on IPv4

22.9k views Asked by At

I'm trying to parse a string containing an IP address and a port using IPAddress.Parse. This works well with IPv6 addresses but not with IPv4 addresses. Can somone explain why this happens?

The code I'm using is:

IPAddress.Parse("[::1]:5"); //Valid
IPAddress.Parse("127.0.0.1:5"); //null
7

There are 7 answers

0
David Yenglin On

IPAddress.Parse is meant to take A string that contains an IP address in dotted-quad notation for IPv4 and in colon-hexadecimal notation for IPv6. So your first example works for IPv6 and your second example fails because it doesnt support a port for IPv4. Link http://msdn.microsoft.com/en-us/library/system.net.ipaddress.parse.aspx

2
Alex J On

This happens because the port is not part of the IP address. It belongs to TCP/UDP, and you'll have to strip it out first. The Uri class might be helpful for this.

3
Tedd Hansen On

IPAddress is not IP+Port. You want IPEndPoint.

Example from http://www.java2s.com/Code/CSharp/Network/ParseHostString.htm

public static void ParseHostString(string hostString, ref string hostName, ref int port)
{
   hostName = hostString;
   if (hostString.Contains(":"))
   {
      string[] hostParts = hostString.Split(':');

      if (hostParts.Length == 2)
      {
         hostName = hostParts[0];
         int.TryParse(hostParts[1], out port);
      }
   }
}

Edit: Ok, I'll admit that wasn't the most elegant solution. Try this one I wrote (just for you) instead:

// You need to include some usings:
using System.Text.RegularExpressions;
using System.Net;

// Then this code (static is not required):
private static Regex hostPortMatch = new Regex(@"^(?<ip>(?:\[[\da-fA-F:]+\])|(?:\d{1,3}\.){3}\d{1,3})(?::(?<port>\d+))?$", System.Text.RegularExpressions.RegexOptions.Compiled);
public static IPEndPoint ParseHostPort(string hostPort)
{
   Match match = hostPortMatch.Match(hostPort);
   if (!match.Success)
      return null;

   return new IPEndPoint(IPAddress.Parse(match.Groups["ip"].Value), int.Parse(match.Groups["port"].Value));
}

Note that this one ONLY accepts IP address, not hostname. If you want to support hostname you'll either have to resolve it to IP or not use IPAddress/IPEndPoint.

2
abatishchev On
Uri url;
IPAddress ip;
if (Uri.TryCreate(String.Format("http://{0}", "127.0.0.1:5"), UriKind.Absolute, out url) &&
   IPAddress.TryParse(url.Host, out ip))
{
    IPEndPoint endPoint = new IPEndPoint(ip, url.Port);
}
0
Loudenvier On

To add my two cents... Since Microsoft itself implemented TryParse in NET Core 3.0 I've opted to stop using my custom IP+Port parser and kindly borrowed their code with some adaptations:

public static class IPEndPointParserExtension
{
    public static bool TryParseAsIPEndPoint(this string s, out IPEndPoint result) {
#if NETCOREAPP3_0_OR_GREATER
        return IPEndPoint.TryParse(s, out result);
#else
        int addressLength = s.Length;  // If there's no port then send the entire string to the address parser
        int lastColonPos = s.LastIndexOf(':');
        // Look to see if this is an IPv6 address with a port.
        if (lastColonPos > 0) {
            if (s[lastColonPos - 1] == ']') 
                addressLength = lastColonPos;
            // Look to see if this is IPv4 with a port (IPv6 will have another colon)
            else if (s.Substring(0, lastColonPos).LastIndexOf(':') == -1) 
                addressLength = lastColonPos;
        }
        if (IPAddress.TryParse(s.Substring(0, addressLength), out IPAddress address)) {
            long port = 0;
            if (addressLength == s.Length ||
                (long.TryParse(s.Substring(addressLength + 1), out port) 
                    && port <= IPEndPoint.MaxPort)) {
                result = new IPEndPoint(address, (int)port);
                return true;
            }
        }
        result = null;
        return false;
#endif
    }
    public static IPEndPoint AsIPEndPoint(this string s) => 
        s.TryParseAsIPEndPoint(out var endpoint) 
            ? endpoint 
            : throw new FormatException($"'{s}' is not a valid IP Endpoint");
   
}

My changes were to basically exchange Span<char> for string and make it an extension method of the class String itself. I've also conditionally compile to use Microsoft's implementation if it is available (NET Core 3.0 or greater).

The following nUnit tests show how to use the code:

[Test]
public void CanParseIpv4WithPort() {
    var sIp = "192.168.0.233:8080";
    if (sIp.TryParseAsIPEndPoint(out var endpoint)) {
        var expected = new IPEndPoint(new IPAddress(new byte[] { 192, 168, 0, 233 }), 8080);
        Assert.AreEqual(expected, endpoint);
    } else 
        Assert.Fail($"Failed to parse {sIp}");
}
[Test]
public void CanParseIpv6WithPort() {
    var sIp = "[2001:db8:85a3:8d3:1319:8a2e:370:7348]:443";
    if (sIp.TryParseAsIPEndPoint(out var endpoint)) {
        var expected = new IPEndPoint(IPAddress.Parse("2001:db8:85a3:8d3:1319:8a2e:370:7348"), 443);
        Assert.AreEqual(expected, endpoint);
    } else
        Assert.Fail($"Failed to parse {sIp}");
}

You can also use AsIpEndPoint which will throw an exception if it fails to parse the IP address and port (port is optional):

var ep = "127.0.0.1:9000".AsIPEndPoint();
0
0xced On

As Tedd Hansen pointed out, what you are trying to parse is not an IP address but an IP endpoint (IP address + port). And since .NET Core 3.0, you can use IPEndPoint.TryParse to parse a string as an IPEndPoint:

if (IPEndPoint.TryParse("127.0.0.1:5", out IPEndPoint endpoint))
{
    // parsed successfully, you can use the "endpoint" variable
    Console.WriteLine(endpoint.Address.ToString()); // writes "127.0.0.1"
    Console.WriteLine(endpoint.Port.ToString()); // writes "5"
}
else
{
    // failed to parse
}
0
Yuriy Gavrishov On

If you work on older versions of .net you can take IPEndPoint.Parse implementation from open source: https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Primitives/src/System/Net/IPEndPoint.cs