Last active
December 27, 2015 08:09
-
-
Save josheinstein/7293743 to your computer and use it in GitHub Desktop.
Classes and extension methods that make it easy to work with blocks of IPv4 addresses in .NET.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Net; | |
namespace Einstein.Net | |
{ | |
/// <summary> | |
/// Compares IP addresses to determine numerically which is greater than the other. | |
/// </summary> | |
public class IPAddressComparer : Comparer<IPAddress> | |
{ | |
#region Constructors | |
/// <summary> | |
/// Initializes a new instance of the <see cref="T:IPAddressComparer"/> class. | |
/// </summary> | |
public IPAddressComparer( ) | |
{ | |
} | |
#endregion | |
#region Properties | |
private static readonly Lazy<IPAddressComparer> _Default = new Lazy<IPAddressComparer>( ); | |
/// <summary> | |
/// The default singleton instance of the comparer. | |
/// </summary> | |
public static new IPAddressComparer Default | |
{ | |
get | |
{ | |
return _Default.Value; | |
} | |
} | |
#endregion | |
#region Methods | |
/// <summary> | |
/// When overridden in a derived class, performs a comparison of two objects of | |
/// the same type and returns a value indicating whether one object is less than, | |
/// equal to, or greater than the other. | |
/// </summary> | |
/// <param name="x">The first object to compare.</param> | |
/// <param name="y">The second object to compare.</param> | |
/// <returns> | |
/// Value | |
/// Condition | |
/// Less than zero | |
/// <paramref name="x"/> is less than <paramref name="y"/>. | |
/// Zero | |
/// <paramref name="x"/> equals <paramref name="y"/>. | |
/// Greater than zero | |
/// <paramref name="x"/> is greater than <paramref name="y"/>. | |
/// </returns> | |
public override int Compare( IPAddress x, IPAddress y ) | |
{ | |
if ( ReferenceEquals( x, y ) ) { | |
return 0; // same instance | |
} | |
if ( ReferenceEquals( x, null ) ) { | |
return -1; // nulls are always less than non-null | |
} | |
if ( ReferenceEquals( y, null ) ) { | |
return 1; // non-null is always greater than null | |
} | |
if ( x.AddressFamily != y.AddressFamily ) { | |
throw new ArgumentException( "IP addresses must be of the same address family." ); | |
} | |
byte[] xBytes = x.GetAddressBytes( ); | |
byte[] yBytes = y.GetAddressBytes( ); | |
if ( xBytes.Length != yBytes.Length ) { | |
throw new ArgumentException( "IP addresses must be of the same length." ); | |
} | |
// compare byte by byte | |
for ( int i = 0 ; i < xBytes.Length ; i++ ) { | |
if ( xBytes[i] < yBytes[i] ) { | |
return -1; // x is less | |
} | |
else if ( xBytes[i] > yBytes[i] ) { | |
return 1; // y is less | |
} | |
} | |
return 0; // equal | |
} | |
#endregion | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Net; | |
using System.Net.Sockets; | |
namespace Einstein.Net | |
{ | |
/// <summary> | |
/// Extension methods for IP addresses. | |
/// </summary> | |
public static class IPAddressExtensions | |
{ | |
/// <summary> | |
/// Determines if the <paramref name="address"/> is between <paramref name="minAddressInclusive"/> and | |
/// <paramref name="maxAddressInclusive"/>. | |
/// </summary> | |
/// <param name="address">The IP address to check.</param> | |
/// <param name="minAddressInclusive">The minimum (inclusive) address of the range.</param> | |
/// <param name="maxAddressInclusive">The maximum (inclusive) address of the range.</param> | |
/// <returns>True if <paramref name="address"/> is between <paramref name="minAddressInclusive"/> and | |
/// <paramref name="maxAddressInclusive"/>, otherwise false.</returns> | |
public static bool Between( this IPAddress address, IPAddress minAddressInclusive, IPAddress maxAddressInclusive ) | |
{ | |
Args.ThrowIfNull( address, "value" ); | |
Args.ThrowIfNull( minAddressInclusive, "minAddressInclusive" ); | |
Args.ThrowIfNull( maxAddressInclusive, "maxAddressInclusive" ); | |
var comparer = IPAddressComparer.Default; | |
int o1 = comparer.Compare( address, minAddressInclusive ); | |
int o2 = comparer.Compare( address, maxAddressInclusive ); | |
return ( o1 >= 0 && o2 <= 0 ) || ( o1 <= 0 && o2 >= 0 ); | |
} | |
/// <summary> | |
/// Increments the specified <paramref name="address"/> by one. | |
/// </summary> | |
/// <remarks> | |
/// This method does not check for overflows so incrementing 255.255.255.255 will | |
/// result in the value 0.0.0.0. | |
/// </remarks> | |
/// <returns>The next IP address.</returns> | |
public static IPAddress Increment( this IPAddress address ) | |
{ | |
Args.ThrowIfNull( address, "address" ); | |
byte[] addressBytes = address.GetAddressBytes( ); | |
// this is going to be slower than an ordinary primitive | |
// increment operation on a 32 bit integer but the problem | |
// is that the ip address layout is different than the | |
// integer layout. for readability and to avoid allocating | |
// additional space, we'll loop backwards across the | |
// address to increment it. | |
for ( int i = addressBytes.Length - 1 ; i >= 0 ; i-- ) { | |
if ( ++addressBytes[i] != 0 ) { | |
break; | |
} | |
} | |
return new IPAddress( addressBytes ); | |
} | |
/// <summary> | |
/// Applies a <paramref name="subnetMask"/> to an <paramref name="address"/> in order to | |
/// obtain the broadcast address for the subnet. | |
/// </summary> | |
/// <param name="address">The address whose broadcast address to determine.</param> | |
/// <param name="subnetMask">An IP address that represents the subnet mask.</param> | |
/// <returns>The broadcast address of the subnet.</returns> | |
public static IPAddress GetBroadcastAddress( this IPAddress address, IPAddress subnetMask ) | |
{ | |
byte[] ipAdressBytes = address.GetAddressBytes( ); | |
byte[] subnetMaskBytes = subnetMask.GetAddressBytes( ); | |
if ( ipAdressBytes.Length != subnetMaskBytes.Length ) { | |
throw new ArgumentException( "Lengths of IP address and subnet mask do not match." ); | |
} | |
byte[] broadcastAddress = new byte[ipAdressBytes.Length]; | |
for ( int i = 0 ; i < broadcastAddress.Length ; i++ ) { | |
broadcastAddress[i] = (byte)( ipAdressBytes[i] | ( subnetMaskBytes[i] ^ 255 ) ); | |
} | |
return new IPAddress( broadcastAddress ); | |
} | |
/// <summary> | |
/// Applies a <paramref name="subnetMask"/> to an <paramref name="address"/> in order to | |
/// obtain the first address that falls within the subnet. | |
/// </summary> | |
/// <param name="address">The address whose network address to determine.</param> | |
/// <param name="subnetMask">An IP address that represents the subnet mask.</param> | |
/// <returns>The first IP address to fall within the subnet.</returns> | |
public static IPAddress GetNetworkAddress( this IPAddress address, IPAddress subnetMask ) | |
{ | |
byte[] ipAdressBytes = address.GetAddressBytes( ); | |
byte[] subnetMaskBytes = subnetMask.GetAddressBytes( ); | |
if ( ipAdressBytes.Length != subnetMaskBytes.Length ) { | |
throw new ArgumentException( "Lengths of IP address and subnet mask do not match." ); | |
} | |
byte[] broadcastAddress = new byte[ipAdressBytes.Length]; | |
for ( int i = 0 ; i < broadcastAddress.Length ; i++ ) { | |
broadcastAddress[i] = (byte)( ipAdressBytes[i] & ( subnetMaskBytes[i] ) ); | |
} | |
return new IPAddress( broadcastAddress ); | |
} | |
/// <summary> | |
/// Determines if two IP addresses are within the same subnet. | |
/// </summary> | |
/// <param name="address2">The an IP address to check.</param> | |
/// <param name="address">An IP address to check.</param> | |
/// <param name="subnetMask">The subnet mask of the network.</param> | |
/// <returns>True if both IPs are in the same subnet, otherwise false.</returns> | |
public static bool IsInSameSubnet( this IPAddress address2, IPAddress address, IPAddress subnetMask ) | |
{ | |
IPAddress network1 = address.GetNetworkAddress( subnetMask ); | |
IPAddress network2 = address2.GetNetworkAddress( subnetMask ); | |
return network1.Equals( network2 ); | |
} | |
/// <summary> | |
/// Returns an IP address formatted as a string in the specified manner. | |
/// </summary> | |
/// <param name="ip">The IP address to format.</param> | |
/// <param name="format"> | |
/// The format specifier, which can be G (general, the default format), P (padded zeroes), or | |
/// N (numeric, as an unsigned decimal number). P and N are only supported for IPv4.</param> | |
/// <param name="formatProvider">The format provider or null to use the default provider.</param> | |
/// <returns>A <see cref="T:String" /> that represents the address in the given format.</returns> | |
public static string ToString( this IPAddress ip, string format, IFormatProvider formatProvider = null ) | |
{ | |
Args.ThrowIfNull( ip, "ip" ); | |
if ( String.IsNullOrWhiteSpace( format ) ) { format = "g"; } | |
format = format.ToLowerInvariant( ); | |
// general | |
if ( format == "g" ) { | |
return String.Format( formatProvider, "{0}", ip ); | |
} | |
if ( ip.AddressFamily == AddressFamily.InterNetwork ) { | |
byte[] bytes = ip.GetAddressBytes( ); | |
// Padded: 001.001.001.001 | |
if ( format == "p" ) { | |
return String.Format( formatProvider, "{0:000}.{1:000}.{2:000}.{3:000}", bytes[0], bytes[1], bytes[2], bytes[3] ); | |
} | |
// Numeric: 325344367 | |
if ( format == "n" ) { | |
return String.Format( formatProvider, "{0:0}", | |
(uint)bytes[0] << 24 | | |
(uint)bytes[1] << 16 | | |
(uint)bytes[2] << 8 | | |
(uint)bytes[3] ); | |
} | |
} | |
throw new ArgumentOutOfRangeException("format", format, "Invalid format string." ); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Globalization; | |
using System.Linq; | |
using System.Net; | |
using System.Net.Sockets; | |
using System.Runtime.Serialization; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
namespace Einstein.Net | |
{ | |
/// <summary> | |
/// A block of IP addresses which can be expressed in CIDR notation. | |
/// </summary> | |
[Serializable] | |
public sealed class IPv4Block : IFormattable | |
{ | |
#region Fields | |
/// <summary> | |
/// A default instance of IPv4Block that represents an empty range. | |
/// </summary> | |
public static readonly IPv4Block Empty = new IPv4Block( ); | |
#endregion | |
#region Constructors | |
/// <summary> | |
/// Initializes a new instance of the <see cref="IPv4Block"/> class. | |
/// </summary> | |
public IPv4Block( ) | |
{ | |
Address = IPAddress.Any; | |
Broadcast = IPAddress.Broadcast; | |
Prefix = 0; | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="T:IPv4Block"/> class. | |
/// </summary> | |
/// <param name="address">The IPv4 address.</param> | |
/// <param name="prefix">The number of significant bits in the routing prefix.</param> | |
public IPv4Block( string address, int prefix ) | |
{ | |
Args.ThrowIfNullOrEmpty( address, "address" ); | |
Args.ThrowIfOutOfRange( prefix, "prefix", 0, 32 ); | |
IPAddress ip; | |
if ( !IPAddress.TryParse( address, out ip ) ) { | |
throw new ArgumentException( "The specified address could not be parsed.", "address" ); | |
} | |
CheckAddressFamily( ip, "address" ); | |
var mask = ConvertSubnetBitsToMask( prefix ); | |
Prefix = prefix; | |
Address = IPAddressExtensions.GetNetworkAddress( ip, mask ); | |
Broadcast = IPAddressExtensions.GetBroadcastAddress( ip, mask ); | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="IPv4Block"/> struct. | |
/// </summary> | |
/// <param name="address">The IPv4 address.</param> | |
/// <param name="prefix">The number of significant bits in the routing prefix.</param> | |
public IPv4Block( IPAddress address, int prefix ) | |
{ | |
Args.ThrowIfNull( address, "address" ); | |
Args.ThrowIfOutOfRange( prefix, "prefix", 0, 32 ); | |
CheckAddressFamily( address, "address" ); | |
var mask = ConvertSubnetBitsToMask( prefix ); | |
Prefix = prefix; | |
Address = IPAddressExtensions.GetNetworkAddress( address, mask ); | |
Broadcast = IPAddressExtensions.GetBroadcastAddress( address, mask ); | |
} | |
/// <summary> | |
/// Initializes a new instance of the <see cref="IPv4Block"/> class. | |
/// </summary> | |
/// <param name="address">The IPv4 address.</param> | |
/// <param name="subnetMask">The subnet mask.</param> | |
public IPv4Block( IPAddress address, IPAddress subnetMask ) | |
{ | |
Args.ThrowIfNull( address, "address" ); | |
Args.ThrowIfNull( subnetMask, "subnetMask" ); | |
CheckAddressFamily( address, "address" ); | |
CheckAddressFamily( subnetMask, "subnetMask" ); | |
Prefix = ConvertSubnetMaskToBits( subnetMask ); | |
Address = IPAddressExtensions.GetNetworkAddress( address, subnetMask ); | |
Broadcast = IPAddressExtensions.GetBroadcastAddress( address, subnetMask ); | |
} | |
#endregion | |
#region Properties | |
/// <summary> | |
/// Gets the starting address of the subnet range. | |
/// </summary> | |
public IPAddress Address | |
{ | |
get; | |
private set; | |
} | |
/// <summary> | |
/// Gets the broadcast address of the subnet range. | |
/// </summary> | |
public IPAddress Broadcast | |
{ | |
get; | |
private set; | |
} | |
/// <summary> | |
/// Gets the number of hosts supported by this <see cref="T:IPv4Block"/>. | |
/// </summary> | |
public long Hosts | |
{ | |
get | |
{ | |
return Convert.ToInt64( Math.Pow( 2, ( 32 - Prefix ) ) ); | |
} | |
} | |
/// <summary> | |
/// The number of significant bits in the routing mask, for example | |
/// in the block of 256 addresses that make up 192.168.1.0/24, the | |
/// 24 is the prefix. | |
/// </summary> | |
public int Prefix | |
{ | |
get; | |
private set; | |
} | |
/// <summary> | |
/// Gets the subnet portion of the block as an IP address such as 255.255.255.240. | |
/// </summary> | |
public IPAddress SubnetMask | |
{ | |
get | |
{ | |
return ConvertSubnetBitsToMask( Prefix ); | |
} | |
} | |
#endregion | |
#region Methods | |
/// <summary> | |
/// Throws an <see cref="T:ArgumentException"/> if the specified address is not a IPv4 address. | |
/// </summary> | |
/// <param name="address">The address to check.</param> | |
/// <param name="parameterName">The name of the parameter to include in any exceptions that are thrown.</param> | |
private static void CheckAddressFamily( IPAddress address, string parameterName ) | |
{ | |
if ( address == null ) { | |
throw new ArgumentNullException( parameterName ); | |
} | |
if ( address.AddressFamily != AddressFamily.InterNetwork ) { | |
throw new ArgumentException( "subnetMask must be an IPv4 address.", parameterName ); | |
} | |
} | |
/// <summary> | |
/// Determines whether the block contains the specified IP address or not. | |
/// </summary> | |
/// <param name="address">The address to check.</param> | |
/// <returns>True if the block contains the address, otherwise false.</returns> | |
public bool Contains( IPAddress address ) | |
{ | |
// is ip greater than or equal to our network address | |
// AND less than or equal to the broadcast address | |
if ( address.Between( Address, Broadcast ) ) { | |
return true; | |
} | |
return false; | |
} | |
/// <summary> | |
/// Determines whether the block contains another block. | |
/// </summary> | |
/// <param name="block">The block to check.</param> | |
/// <returns>True if the block contains the other block, otherwise false.</returns> | |
public bool Contains( IPv4Block block ) | |
{ | |
// is other network address greater than or equal to our network address | |
// AND less than or equal to the broadcast address | |
if ( block.Address.Between( Address, Broadcast ) ) { | |
// AND ALSO, | |
// the other broadcast address greater than or equal to our network address | |
// AND less than or equal to the broadcast address | |
if ( block.Broadcast.Between( Address, Broadcast ) ) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/// <summary> | |
/// Converts the specified subnet mask (such as 255.255.255.240) into a number of subnet bits (such as /28). | |
/// </summary> | |
/// <param name="subnetMask">The subnet mask to convert.</param> | |
/// <returns>The number of subnet bits in the mask.</returns> | |
public static int ConvertSubnetMaskToBits( IPAddress subnetMask ) | |
{ | |
Args.ThrowIfNull( subnetMask, "subnetMask" ); | |
if ( subnetMask.AddressFamily != AddressFamily.InterNetwork ) { | |
throw new ArgumentException( "subnetMask must be an IPv4 address.", "subnetMask" ); | |
} | |
byte[] bytes = subnetMask.GetAddressBytes( ); | |
Array.Reverse( bytes ); | |
long address = BitConverter.ToUInt32( bytes, 0 ); | |
long hosts = 0xFFFFFFFF - address + 1; | |
return Convert.ToInt32( 32 - Math.Log( hosts, 2 ) ); | |
} | |
/// <summary> | |
/// Converts the specified subnet bits (such as /28) into a subnet mask (such as 255.255.255.240). | |
/// </summary> | |
/// <param name="bits">The number of leading bits in the subnet mask. Must be between 0 and 32 inclusive.</param> | |
/// <returns>A subnet mask such as 255.255.255.240.</returns> | |
public static IPAddress ConvertSubnetBitsToMask( int bits ) | |
{ | |
Args.ThrowIfOutOfRange( bits, "bits", 0, 32 ); | |
if ( bits == 0 ) { | |
return new IPAddress( 0 ); | |
} | |
long mask = 0xFFFFFFFF << ( 32 - bits ); | |
mask = IPAddress.HostToNetworkOrder( mask ); | |
mask = ( mask >> 32 ) & 0xFFFFFFFF; | |
return new IPAddress( mask ); | |
} | |
/// <summary> | |
/// Indicates whether this instance and a specified object are equal. | |
/// </summary> | |
/// <param name="obj">Another object to compare to.</param> | |
/// <returns> | |
/// true if <paramref name="obj"/> and this instance are the same type and represent the same value; otherwise, false. | |
/// </returns> | |
public override bool Equals( object obj ) | |
{ | |
// Null shortcut | |
if ( ReferenceEquals( obj, null ) ) { | |
return false; | |
} | |
// Self shortcut | |
if ( ReferenceEquals( obj, this ) ) { | |
return true; | |
} | |
// Type shortcut | |
if ( obj.GetType( ) != GetType( ) ) { | |
return false; | |
} | |
var other = (IPv4Block)obj; | |
return Address.Equals( other.Address ) && Prefix.Equals( other.Prefix ); | |
} | |
/// <summary> | |
/// Returns the hash code for this instance. | |
/// </summary> | |
/// <returns> | |
/// A 32-bit signed integer that is the hash code for this instance. | |
/// </returns> | |
public override int GetHashCode( ) | |
{ | |
var x = new { | |
Address, | |
Prefix | |
}; | |
return x.GetHashCode( ); | |
} | |
/// <summary> | |
/// Gets an array of <see cref="T:IPAddress"/> that are within the range of this block. | |
/// </summary> | |
/// <returns>The IP addresses in the block.</returns> | |
public IEnumerable<IPAddress> GetAddressesInRange( ) | |
{ | |
byte[] address = Address.GetAddressBytes( ); | |
byte[] subnet = SubnetMask.GetAddressBytes( ); | |
byte[] start = new byte[address.Length]; | |
for ( int i = 0 ; i < address.Length ; i++ ) { | |
start[i] = (byte)( address[i] & subnet[i] ); | |
} | |
int startAddress = BitConverter.ToInt32( start, 0 ); | |
startAddress = IPAddress.NetworkToHostOrder( startAddress ); | |
long hostCount = Hosts; | |
for ( int i = 0 ; i < hostCount ; i++ ) { | |
int nextAddress = startAddress + i; | |
nextAddress = IPAddress.HostToNetworkOrder( nextAddress ); | |
yield return new IPAddress( BitConverter.GetBytes( nextAddress ) ); | |
} | |
} | |
/// <summary> | |
/// Increments this IPv4Block by its block size, such that 192.168.1.0/24 increments | |
/// to 192.168.2.0/24. | |
/// </summary> | |
/// <remarks> | |
/// If the increment results in an overflow, then an <see cref="T:OverflowException"/> | |
/// will be thrown. For example, attepting to increment 255.255.255.0/24 will result | |
/// in an overflow. | |
/// </remarks> | |
/// <returns>The next valid IP block with the same block size.</returns> | |
/// <exception cref="T:OverflowException">The increment resulted in an overflow.</exception> | |
public IPv4Block Increment( ) | |
{ | |
// Go from the broadcast address to the next ip | |
var nextAddress = IPAddressExtensions.Increment( Broadcast ); | |
var nextBlock = new IPv4Block( nextAddress, Prefix ); | |
// Make sure the new address is greater than the previous or | |
// else throw an overflow exception. | |
if ( IPAddressComparer.Default.Compare( Address, nextBlock.Address ) >= 0 ) { | |
throw new OverflowException( ); | |
} | |
return nextBlock; | |
} | |
/// <summary> | |
/// Determines whether this block overlaps another block. | |
/// </summary> | |
/// <param name="block">The block to check.</param> | |
/// <returns>True if the block overlaps the other block, otherwise false.</returns> | |
public bool Overlaps( IPv4Block block ) | |
{ | |
if ( block.Address.Between( Address, Broadcast ) ) { | |
return true; | |
} | |
if ( block.Broadcast.Between( Address, Broadcast ) ) { | |
return true; | |
} | |
if ( Address.Between( block.Address, block.Broadcast ) ) { | |
return true; | |
} | |
if ( Broadcast.Between( block.Address, block.Broadcast ) ) { | |
return true; | |
} | |
return false; | |
} | |
/// <summary> | |
/// Parses the specified <paramref name="input"/> string into a <see cref="T:IPv4Block"/>. | |
/// </summary> | |
/// <remarks> | |
/// This method can only parse strings in the format of 192.168.1.0/24 or 192.168.1.0/255.255.255.0. | |
/// It cannot parse a range of addresses as is produced by passing the R format specifier to the | |
/// ToString method. | |
/// </remarks> | |
/// <param name="input">The input string.</param> | |
/// <returns>A parsed IPv4Block class.</returns> | |
public static IPv4Block Parse( string input ) | |
{ | |
IPv4Block output; | |
if ( IPv4Block.TryParse( input, out output ) ) { | |
return output; | |
} | |
else { | |
throw new FormatException( "The input string was not in the correct format." ); | |
} | |
} | |
/// <summary> | |
/// Attempts to parse the specified <paramref name="input"/> string into an <see cref="T:IPv4Block"/> | |
/// and returns true if the parse succeeds, otherwise false. | |
/// </summary> | |
/// <remarks> | |
/// This method can only parse strings in the format of 192.168.1.0/24 or 192.168.1.0/255.255.255.0. | |
/// It cannot parse a range of addresses as is produced by passing the R format specifier to the | |
/// ToString method. | |
/// </remarks> | |
/// <param name="input">The input string.</param> | |
/// <param name="output">A parsed IPv4Block class.</param> | |
/// <returns>True if the parse succeeded, otherwise false.</returns> | |
public static bool TryParse( string input, out IPv4Block output ) | |
{ | |
if ( !String.IsNullOrEmpty( input ) ) { | |
int n = input.IndexOf( '/' ); | |
if ( n > 0 ) { | |
string address = input.Substring( 0, n ).Trim( ); | |
string subnet = input.Substring( n + 1 ).Trim( ); | |
IPAddress ip; | |
if ( IPAddress.TryParse( address, out ip ) ) { | |
// try to parse as ip/bits | |
int s; | |
if ( Int32.TryParse( subnet, out s ) ) { | |
try { | |
output = new IPv4Block( ip, s ); | |
return true; | |
} | |
catch { | |
} | |
} | |
// try to parse as ip/mask | |
IPAddress subnetMask; | |
if ( IPAddress.TryParse( subnet, out subnetMask ) ) { | |
try { | |
output = new IPv4Block( ip, subnetMask ); | |
return true; | |
} | |
catch { | |
} | |
} | |
} | |
} | |
} | |
output = null; | |
return false; | |
} | |
/// <summary> | |
/// Returns the fully qualified type name of this instance. | |
/// </summary> | |
/// <returns> | |
/// A <see cref="T:System.String"/> containing a fully qualified type name. | |
/// </returns> | |
public override string ToString( ) | |
{ | |
return String.Format( CultureInfo.InvariantCulture, "{0}/{1}", Address, Prefix ); | |
} | |
#endregion | |
#region IFormattable Members | |
/// <summary> | |
/// Returns a <see cref="T:String" /> that represents this instance. | |
/// </summary> | |
/// <param name="format">The format.</param> | |
/// <returns> | |
/// A <see cref="T:String" /> that represents this instance. | |
/// </returns> | |
public string ToString( string format ) | |
{ | |
return ToString( format, null ); | |
} | |
/// <summary> | |
/// Returns a <see cref="T:String" /> that represents the current IPv4Block in a specified | |
/// format. | |
/// </summary> | |
/// <param name="format"> | |
/// The format specifier, which can be C (CIDR, the default format), CC (CIDR with padded zeroes), | |
/// S (address with subnet mask), SS (padded address with padded subnet), R (range of IP addresses), | |
/// or RR (a range of addresses that are padded with zeroes as necessary.) | |
/// </param> | |
/// <param name="formatProvider">The format provider or null to use the default provider.</param> | |
/// <returns>A <see cref="T:String" /> that represents this instance.</returns> | |
public string ToString( string format, IFormatProvider formatProvider ) | |
{ | |
if ( String.IsNullOrWhiteSpace( format ) ) { format = "c"; } | |
format = format.ToLowerInvariant( ); | |
// CIDR: 192.168.1.0/24 | |
if ( format == "c" ) { | |
return Address.ToString( ) + "/" + Prefix.ToString( "0" ); | |
} | |
// CIDR: 192.168.001.000/08 | |
if ( format == "cc" ) { | |
return IPAddressExtensions.ToString( Address, "p", formatProvider ) + "/" + Prefix.ToString( "00" ); | |
} | |
// Subnet Mask: 192.168.0.0/255.255.255.0 | |
if ( format == "s" ) { | |
return Address.ToString( ) + "/" + SubnetMask.ToString( ); | |
} | |
// Subnet Mask: 192.168.001.000/255.255.255.000 | |
if ( format == "ss" ) { | |
return IPAddressExtensions.ToString( Address, "p", formatProvider ) + "/" + IPAddressExtensions.ToString( SubnetMask, "p", formatProvider ); | |
} | |
// Range: 192.168.1.0-192.168.1.255 | |
if ( format == "r" ) { | |
return Address.ToString( ) + "-" + Broadcast.ToString( ); | |
} | |
// Range: 192.168.001.000-192.168.001.255 | |
if ( format == "rr" ) { | |
return IPAddressExtensions.ToString( Address, "p", formatProvider ) + "-" + IPAddressExtensions.ToString( Broadcast, "p", formatProvider ); | |
} | |
throw new ArgumentOutOfRangeException( "format", format, "Invalid format string." ); | |
} | |
#endregion | |
#region Operators | |
/// <summary> | |
/// Implements the operator ==. | |
/// </summary> | |
/// <param name="lhs">The LHS.</param> | |
/// <param name="rhs">The RHS.</param> | |
/// <returns>The result of the operator.</returns> | |
public static bool operator ==( IPv4Block lhs, IPv4Block rhs ) | |
{ | |
// Self shortcut | |
if ( ReferenceEquals( lhs, rhs ) ) { | |
return true; | |
} | |
// Null shortcut | |
if ( ReferenceEquals( lhs, null ) ) { | |
return false; | |
} | |
// Null shortcut | |
if ( ReferenceEquals( rhs, null ) ) { | |
return false; | |
} | |
return lhs.Equals( rhs ); | |
} | |
/// <summary> | |
/// Implements the operator !=. | |
/// </summary> | |
/// <param name="lhs">The LHS.</param> | |
/// <param name="rhs">The RHS.</param> | |
/// <returns>The result of the operator.</returns> | |
public static bool operator !=( IPv4Block lhs, IPv4Block rhs ) | |
{ | |
return !( lhs == rhs ); | |
} | |
#endregion | |
#region Conversions | |
/// <summary> | |
/// Performs an explicit conversion from <see cref="T:String"/> to <see cref="T:IPv4Block"/>. | |
/// </summary> | |
/// <param name="str">The string.</param> | |
/// <returns>The result of the conversion.</returns> | |
public static explicit operator IPv4Block( string str ) | |
{ | |
return IPv4Block.Parse( str ); | |
} | |
/// <summary> | |
/// Performs an explicit conversion from <see cref="T:IPv4Block"/> to <see cref="T:String"/>. | |
/// </summary> | |
/// <param name="ip">The ip block.</param> | |
/// <returns>The result of the conversion.</returns> | |
public static explicit operator string( IPv4Block ip ) | |
{ | |
return ip.ToString( ); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment