Skip to content

Instantly share code, notes, and snippets.

@a-luna
Last active August 12, 2024 14:01
Show Gist options
  • Save a-luna/bd93686ace9f6f22acf0a7032fc41777 to your computer and use it in GitHub Desktop.
Save a-luna/bd93686ace9f6f22acf0a7032fc41777 to your computer and use it in GitHub Desktop.
Helpful C# Network/IP functions. Parse IPs from any arbitrary block of text, retrieve private/public IP addresses and check if IP exists in any range defined by an address in CIDR notation.
namespace NetworkUtilitiesTest
{
using AaronLuna.Common.Network;
using AaronLuna.Common.Extensions;
using System.Threading.Tasks;
class Program
{
// async Main is a C# 7.1 feature, change your project settings to the
// new version if this is flagged as an error
static async Task Main()
{
// Usage examples for classes in this gist:
//
// NetworkUtilities
// bool IpAddressIsInRange(string checkIp, string cidrIp)
// bool IpAddressIsInRange(IPAddress checkIp, string cidrIp)
// List<IPAddress> ParseIPv4Addresses(string input)
// IPAddress GetLocalIPv4Address(string localNetworkCidrIp)
// List<IPAddress> GetLocalIPv4AddressList()
// Task<IPAddress> GetPublicIPv4AddressAsync()
//
// IpAddressExtensions
// bool IsInRange(this IPAddress ip, string cidrIp)
// bool IsPrivateAddress(this IPAddress ip)
// bool IsPublicAddress(this IPAddress ip)
var ip1 = "192.168.2.4";
var cidrIp1 = "192.168.2.0/24";
var isInRange1 = NetworkUtilities.IpAddressIsInRange(ip1, cidrIp1); // isInRange = true
var textToParse = "10.0.2.2 is a private (internal) address and 75.94.26.74 is a public (external) addresss";
var parse2Ips = NetworkUtilities.ParseIPv4Addresses(textToParse); // parse2Ips = {"10.0.2.2", "75.94.26.74"}
var isInRange2 = parse2Ips[1].IsPrivateAddress(); // isInRange2 = false
var myLocalIp = NetworkUtilities.GetLocalIPv4Address(cidrIp1); // myLocalIp = "192.168.3.5"
var myPublicIp = await NetworkUtilities.GetPublicIPv4AddressAsync(); // myPublicIp = "75.94.26.74"
var testLocalTrue = myLocalIp.IsPublicAddress(); // testLocalTrue = true
var testLocalFalse = myLocalIp.IsPrivateAddress(); // testLocalFalse = false'
var testPublic = myPublicIp.IsPublicAddress(); // testPublic = true;
}
}
}
namespace AaronLuna.Common.Network
{
using System;
using System.Linq;
using System.Net;
public static partial class NetworkUtilities
{
// This method is used multiple times by the other methods in this class.
// The input string must contain ONLY a single IPv4 address in a.b.c.d format.
// If the format of the input is unknown or you are attempting to parse
// an IP address from an arbitrary block of text, use ParseIPv4Addresses().
public static IPAddress ParseSingleIPv4Address(string input)
{
if (string.IsNullOrEmpty(input))
{
throw new ArgumentException("Input string must not be null", input);
}
var addressBytesSplit = input.Trim().Split('.').ToList();
if (addressBytesSplit.Count != 4)
{
throw new ArgumentException("Input string was not in valid IPV4 format \"a.b.c.d\"", input);
}
var addressBytes = new byte[4];
foreach (var i in Enumerable.Range(0, addressBytesSplit.Count))
{
if (!int.TryParse(addressBytesSplit[i], out var parsedInt))
{
throw new FormatException($"Unable to parse integer from {addressBytesSplit[i]}");
}
if (0 > parsedInt || parsedInt > 255)
{
throw new ArgumentOutOfRangeException($"{parsedInt} not within required IP address range [0,255]");
}
addressBytes[i] = (byte)parsedInt;
}
return new IPAddress(addressBytes);
}
}
}
namespace AaronLuna.Common.Network
{
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
public static partial class NetworkUtilities
{
// THis method can be used to parse all IP addresses from an arbitrary block of text, if you
// know that your input is a single IPv4 address in a.b.c.d format, use ParseSingleIPv4Address()
public static List<IPAddress> ParseIPv4Addresses(string input)
{
const string ipV4Pattern =
@"(?:(?:1\d\d|2[0-5][0-5]|2[0-4]\d|0?[1-9]\d|0?0?\d)\.){3}(?:1\d\d|2[0-5][0-5]|2[0-4]\d|0?[1-9]\d|0?0?\d)";
if (string.IsNullOrEmpty(input))
{
throw new ArgumentException("Input string must not be null", input);
}
var ips = new List<IPAddress>();
try
{
var regex = new Regex(ipV4Pattern);
foreach (Match match in regex.Matches(input))
{
var ip = ParseSingleIPv4Address(match.Value);
ips.Add(ip);
}
}
catch (RegexMatchTimeoutException ex)
{
// Handle exeption
}
return ips;
}
}
}
namespace AaronLuna.Common.Network
{
using System;
using System.Net;
public static partial class NetworkUtilities
{
// true if ipAddress falls inside the range defined by cidrIp, example:
// bool result = IsInCidrRange("192.168.2.3", "192.168.2.0/24"); // result = true
public static bool IpAddressIsInRange(string checkIp, string cidrIp)
{
if (string.IsNullOrEmpty(checkIp))
{
throw new ArgumentException("Input string must not be null", checkIp);
}
var ipAddress = ParseIPv4Addresses(checkIp)[0];
return IpAddressIsInRange(ipAddress, cidrIp);
}
public static bool IpAddressIsInRange(IPAddress checkIp, string cidrIp)
{
if (string.IsNullOrEmpty(cidrIp))
{
throw new ArgumentException("Input string must not be null", cidrIp);
}
var cidrAddress = ParseIPv4Addresses(cidrIp)[0];
var parts = cidrIp.Split('/');
if (parts.Length != 2)
{
throw new FormatException($"cidrMask was not in the correct format:\nExpected: a.b.c.d/n\nActual: {cidrIp}");
}
if (!Int32.TryParse(parts[1], out var netmaskBitCount))
{
throw new FormatException($"Unable to parse netmask bit count from {cidrIp}");
}
if (0 > netmaskBitCount || netmaskBitCount > 32)
{
throw new ArgumentOutOfRangeException($"Netmask bit count value of {netmaskBitCount} is invalid, must be in range 0-32");
}
var ipAddressBytes = BitConverter.ToInt32(checkIp.GetAddressBytes(), 0);
var cidrAddressBytes = BitConverter.ToInt32(cidrAddress.GetAddressBytes(), 0);
var cidrMaskBytes = IPAddress.HostToNetworkOrder(-1 << (32 - netmaskBitCount));
var ipIsInRange = (ipAddressBytes & cidrMaskBytes) == (cidrAddressBytes & cidrMaskBytes);
return ipIsInRange;
}
}
}
namespace AaronLuna.Common.Network
{
using System.Net;
public static partial class NetworkUtilities
{
public const string CidrPrivateAddressBlockA = "10.0.0.0/8";
public const string CidrPrivateAddressBlockB = "172.16.0.0/12";
public const string CidrPrivateAddressBlockC = "192.168.0.0/16";
public static bool IpAddressIsInPrivateAddressSpace(string ipv4string)
{
var checkIp = ParseIPv4Addresses(ipv4string)[0];
return IpAddressIsInPrivateAddressSpace(checkIp);
}
public static bool IpAddressIsInPrivateAddressSpace(IPAddress ipAddress)
{
var inPrivateBlockA = IpAddressIsInCidrRange(ipAddress, CidrPrivateAddressBlockA);
var inPrivateBlockB = IpAddressIsInCidrRange(ipAddress, CidrPrivateAddressBlockB);
var inPrivateBlockC = IpAddressIsInCidrRange(ipAddress, CidrPrivateAddressBlockC);
return inPrivateBlockA || inPrivateBlockB || inPrivateBlockC;
}
}
}
namespace AaronLuna.Common.Network
{
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
public static partial class NetworkUtilities
{
// This method first attempts to retrieve the local IP address by connecting
// to Google's Public DNS service. If there is no internet connection, the
// address range defined by localNetworkCidrIp is used to determine which
// IP is the correct local address. The CIDR IP must be in the correct
// format: a.b.c.d/n. For example, if your LAN is setup to allow 254 hosts
// and the router's address is 192.168.2.1, the correct value for
// localNetworkCidrIp would be "192.168.2.0/24"
public static IPAddress GetLocalIPv4Address(string localNetworkCidrIp)
{
var acquiredIp = true;
var localIp = IPAddress.None;
try
{
localIp = GetLocalIPv4AddressRequiresInternet();
}
catch (SocketException ex)
{
acquiredIp = false;
}
if (acquiredIp)
{
return localIp;
}
return GetLocalIPv4AddressWithoutInternet(localNetworkCidrIp);
}
static IPAddress GetLocalIPv4AddressRequiresInternet()
{
var localIp = IPAddress.None;
try
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, 0))
{
// Connect socket to Google's Public DNS service
if (!(socket.LocalEndPoint is IPEndPoint endPoint))
{
throw new InvalidOperationException($"Error occurred casting {socket.LocalEndPoint} to IPEndPoint");
}
localIp = endPoint.Address;
}
}
catch (SocketException ex)
{
// Handle exception
}
return localIp;
}
static IPAddress GetLocalIPv4AddressWithoutInternet(string localNetworkCidrIp)
{
var localIps = GetLocalIPv4AddressList();
if (localIps.Count == 1)
{
return localIps[0];
}
foreach (var ip in localIps)
{
if (IpAddressIsInRange(ip, localNetworkCidrIp))
{
return ip;
}
}
return IPAddress.None;
}
public static List<IPAddress> GetLocalIPv4AddressList()
{
var localIps = new List<IPAddress>();
foreach (var nic in NetworkInterface.GetAllNetworkInterfaces())
{
var ips =
nic.GetIPProperties().UnicastAddresses
.Select(uni => uni.Address)
.Where(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToList();
localIps.AddRange(ips);
}
return localIps;
}
}
}
namespace AaronLuna.Common.Network
{
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
public static partial class NetworkUtilities
{
public static async Task<IPAddress> GetPublicIPv4AddressAsync()
{
var urlContent = await GetUrlContentAsStringAsync("http://ipv4.icanhazip.com/").ConfigureAwait(false);
return ParseSingleIPv4Address(urlContent);
}
static async Task<string> GetUrlContentAsStringAsync(string url)
{
var urlContent = string.Empty;
try
{
using (var httpClient = new HttpClient())
using (var httpResonse = await httpClient.GetAsync(url).ConfigureAwait(false))
{
urlContent = await httpResonse.Content.ReadAsStringAsync().ConfigureAwait(false);
}
}
catch (HttpRequestException ex)
{
// Handle exception
}
catch(InvalidOperationException ex)
{
// Handle exception
}
return urlContent;
}
}
}
namespace AaronLuna.Common.Extensions
{
using System.Net;
using Network;
public static class IpAddressExtensions
{
public static bool IsInRange(this IPAddress ip, string cidrIp)
{
return NetworkUtilities.IpAddressIsInRange(ip, cidrIp);
}
public static bool IsPrivateAddress(this IPAddress ip)
{
return NetworkUtilities.IpAddressIsInPrivateAddressSpace(ip);
}
public static bool IsPublicAddress(this IPAddress ip)
{
return !NetworkUtilities.IpAddressIsInPrivateAddressSpace(ip);
}
public static string ConvertToBinaryString(this IPAddress ip)
{
return NetworkUtilities.ConvertIpAddressToBinary(ip);
}
}
}
@KeithVinson
Copy link

Hey, thanks for the class NetworkUtilities. I noticed that in the files you posted there are two missing methods IpAddressIsInCidrRange and ConvertIpAddressToBinary. Just thought you might want to know...

KV

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment