DHCP Message Class

Here is a class I wrote to generate a DHCP message.  It’s primary purpose is to send a DHCP message, which can be useful for finding DHCP servers on a network

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Globalization;
using System.Text.RegularExpressions;

namespace SigkillDHCP
{
public class DhcpMessage
{
private const UInt32 MagicNumber = 1666417251;
//http://search.cpan.org/~shadinger/Net-DHCP-0.66/lib/Net/DHCP/Packet.pm

//Declare Fields
private DhcpOp _op;
private DhcpHtype _htype;
private byte _hlen;
private byte _hops;
private int _xid;
private UInt16 _secs;
private UInt16 _flags = 128;
private string _ciaddr = "0.0.0.0";
private string _yiaddr = "0.0.0.0";
private string _siaddr = "0.0.0.0";
private string _giaddr = "0.0.0.0";
private string _chaddr;
private byte[] _sname = new byte[64];
private byte[] _file = new byte[128];
//OPTIONS
private byte[] _mcookie = new byte[4];
private Dictionary<dhcpoption, byte[]=""> _options = new Dictionary<dhcpoption, byte[]="">();

#region Constructors

public DhcpMessage()
{
//Default Constructor Zero Fields
}

public DhcpMessage(byte[] data)
{
//Initiate DHCP Instance Fields When Reading Packet From DHCP Server
int Offset = 0;
byte[] tempip = new byte[4];
//MessageBox.Show("Op:" + BitConverter.ToString(data, Offset, 1).Replace("-", ""));
this._op = (DhcpOp)data[Offset];
Offset++;

this._htype = (DhcpHtype)data[Offset];
Offset++;

this._hlen = data[Offset];
Offset++;

this._hops = data[Offset];
Offset++;

this._xid = BitConverter.ToInt32(data, Offset);
Offset += 4;

this._secs = BitConverter.ToUInt16(data, Offset); //Need to reverse Byte order???
Offset += 2;

this._flags = BitConverter.ToUInt16(data, Offset);
Offset += 2;

Array.Copy(data, Offset, tempip, 0, 4);
this._ciaddr = new IPAddress(tempip).ToString();
Offset += 4;

Array.Copy(data, Offset, tempip, 0, 4);
this._yiaddr = new IPAddress(tempip).ToString();
Offset += 4;

Array.Copy(data, Offset, tempip, 0, 4);
this._siaddr = new IPAddress(tempip).ToString();
Offset += 4;

Array.Copy(data, Offset, tempip, 0, 4);
this._giaddr = new IPAddress(tempip).ToString();
Offset += 4;

this._chaddr = BitConverter.ToString(data, Offset, 6);
Offset += 16;

Array.Copy(data, Offset, this._sname, 0, 64);
Offset += 64;

Array.Copy(data, Offset, this._file, 0, 128);
Offset += 128;

//Check for DHCP Magic Cookie
if (BitConverter.ToUInt32(data, Offset) == MagicNumber)
{
Offset += 4;
Boolean end = false;

//Loop Other DHCP Options
while (!end && Offset < data.Length)
{
DhcpOption Option = (DhcpOption)data[Offset];
Offset++;

int OptionLength;
switch (Option)
{
case DhcpOption.Pad:
continue;
case DhcpOption.End:
end = true;
continue;
default:
OptionLength = (int)data[Offset];
Offset++;
break;
}

byte[] OptionData = new byte[OptionLength];
Array.Copy(data, Offset, OptionData, 0, OptionLength);
Offset += OptionLength;
this._options.Add(Option, OptionData);
}

}
}
#endregion

#region Properties
public DhcpOp Op
{
get { return this._op; }
set { this._op = value; }
}

public DhcpHtype Htype
{
get { return this._htype; }
set { this._htype = value; }
}

public byte Hlen
{
get { return this._hlen; }
set { this._hlen = value; }
}

public byte Hops
{
get { return this._hops; }
set { this._hops = value; }
}

public int Xid
{
get { return this._xid; }
set { this._xid = value; }
}

public UInt16 Secs
{
get { return this._secs; }
set { this._secs = value; }
}

public UInt16 Flags
{
get { return this._flags; }
set { this._flags = value; }
}

public string Ciaddr
{
get { return this._ciaddr; }
set
{
if (ValidateIp(value) == false)
{
throw new Exception("Invalid IP Specified for Ciaddr");
}
this._ciaddr = value;
}
}

public string Yiaddr
{
get { return this._yiaddr; }
set
{
if (ValidateIp(value) == false)
{
throw new Exception("Invalid IP Specified for Yiaddr");
}
this._yiaddr = value;
}
}

public string Siaddr
{
get { return this._siaddr; }
set
{
if (ValidateIp(value) == false)
{
throw new Exception("Invalid IP Specified for Siaddr");
}
this._siaddr = value;
}
}

public string Giaddr
{
get { return this._giaddr; }
set
{
if (ValidateIp(value) == false)
{
throw new Exception("Invalid IP Specified for Giaddr");
}
this._giaddr = value;
}
}

public string Chaddr
{
get { return this._chaddr; }
set
{
if (ValidateMac(value) == false)
{
throw new Exception("Invalid MAC Specified for Chaddr");
}
this._chaddr = value.Replace(":", "").Replace("-", "").Replace(".", "");
}
}

public byte[] Sname
{
get { return this._sname; }
set { this._sname = value; }
}

public byte[] File
{
get { return this._file; }
set { this._file = value; }
}

#endregion

#region IP-Binary Conversions
public string IpToBitString(string Address)
{
//Convert IP String to Octets
byte[] IP = IPAddress.Parse(Address).GetAddressBytes();
string IPBits = "";

//Loop Octets and Convert to Binary
for (int i = 0; i < IP.Length; i++)
{
IPBits += PadBinary(Convert.ToString(IP[i], 2), 8);
}
return IPBits;
}

public string PadBinary(string Bits, int TotalBits)
{
//Pad Bit Strings to Ensure Proper Length
//ex: Decimal 4 converts to 100 as a Binary String
// But We Want to Pad it to 8 bits as 00000100
string PadBits = "";
if (Bits.Length < TotalBits)
{
for (int i = 0; i < (TotalBits - Bits.Length); i++)
{
PadBits += "0";
}
Bits = PadBits + Bits;
}
return Bits;
}

public string BitStringToIp(string Bits)
{
//Convert 32bit Binary to IP String
string IP = "";
if (Bits.Length == 32)
{
IP = BinaryStringToInt(Bits.Substring(0, 8)).ToString() + ".";
IP += BinaryStringToInt(Bits.Substring(8, 8)).ToString() + ".";
IP += BinaryStringToInt(Bits.Substring(16, 8)).ToString() + ".";
IP += BinaryStringToInt(Bits.Substring(24, 8)).ToString();
}
else
{
IP = "127.0.0.1";
}
return IP;
}

public int BinaryStringToInt(string Bits)
{
//Converts a Binary String to Integer Based on Most Significant Bit
//ex: Binary 00000100 and Binary 100 Converts to Integer 4
int Number = 0;
for (int i = 0, Exp = (Bits.Length - 1); i < Bits.Length; i++, Exp--)
{
if (Bits[i].ToString() == "1")
{
Number += (int)Math.Pow(2, Exp);
}
}
return Number;
}
#endregion

#region Subnet Calculations
public int GetNetworkPrefix(string Mask)
{
//Convert Subnet Mask to Bit String
Mask = IpToBitString(Mask);

//Get Network Prefix
int Prefix = 0;
for (int i = 0; i < Mask.Length; i++)
{
if (Mask[i].ToString() == "1")
{
Prefix++;
}
}
return Prefix;
}

public string GetSubnetNumber(string Address, string Mask)
{
//Convert IP and SubnetMask String to Octets
byte[] IP = IPAddress.Parse(Address).GetAddressBytes();
byte[] SM = IPAddress.Parse(Mask).GetAddressBytes();
string SubnetNumber = "";

//Loop Octets Then Bitwise Boolean AND the IP and Subnet Mask
for (int i = 0; i < IP.Length; i++)
{
if (i < (IP.Length - 1))
{
SubnetNumber += (IP[i] & SM[i]).ToString() + ".";
}
else
{
SubnetNumber += (IP[i] & SM[i]).ToString();
}
}
return SubnetNumber;
}

public string GetSubnetBroadcast(string Mask, string SubnetNumber)
{
//Get Prefix
int Prefix = GetNetworkPrefix(Mask);

//Copy Network/Subnet Bits of SubnetNumber Then Replace Host Bits with 1's
string SNBits = IpToBitString(SubnetNumber);
string Broadcast = "";
for (int i = 0; i < SNBits.Length; i++)
{
if (i < Prefix)
{
Broadcast += SNBits[i];
}
else
{
Broadcast += "1";
}
}

//Convert Broadcast From Binary to IP
Broadcast = BitStringToIp(Broadcast);

return Broadcast;
}

public string GetSubnetFirstAddress(string SubnetNumber)
{
//Convert Subnet Number String to Octets
byte[] IP = IPAddress.Parse(SubnetNumber).GetAddressBytes();
string FirstAddress = "";

//First Address Equals Subnet Number Plus 1 in the Last Octet
IP[IP.Length - 1] += 1;

//Loop Octets to Build IP
for (int i = 0; i < IP.Length; i++)
{
if (i < (IP.Length - 1))
{
FirstAddress += IP[i].ToString() + ".";
}
else
{
FirstAddress += IP[i].ToString();
}
}
return FirstAddress;
}

public string GetSubnetLastAddress(string Broadcast)
{
//Convert Broadcast String to Octets
byte[] IP = IPAddress.Parse(Broadcast).GetAddressBytes();
string LastAddress = "";

//Last Address Equals Broadcast Minus 1 in the Last Octet
IP[IP.Length - 1] -= 1;

//Loop Octets to Build IP
for (int i = 0; i < IP.Length; i++)
{
if (i < (IP.Length - 1))
{
LastAddress += IP[i].ToString() + ".";
}
else
{
LastAddress += IP[i].ToString();
}
}
return LastAddress;
}

public int GetNetworkBits(string Address)
{
//Convert IP String to Octets
byte[] IP = IPAddress.Parse(Address).GetAddressBytes();

//Determine Network Bits Based on IP Class
if ((IP[0] >= 1) && (IP[0] <= 126))
{
//Class A 8 Network Bits
return 8;
}
else if ((IP[0] >= 128) && (IP[0] <= 191))
{
//Class B 16 Network Bits
return 16;
}
else if ((IP[0] >= 192) && (IP[0] <= 223))
{
//Class C 24 Network Bits
return 24;
}
else
{
//Error Invalid
return 0;
}
}

public int GetHostBits(string Mask)
{
//Convert Subnet Mask to Binary
string SM = IpToBitString(Mask);
int HostBits = 0;

for (int i = 0; i < SM.Length; i++)
{
if (SM[i].ToString() == "0")
{
HostBits++;
}
}
return HostBits;
}

public int GetSubnetBits(int NetworkBits, int HostBits)
{

return (32 - (NetworkBits + HostBits));
}

public int GetMaxSubnets(int SubnetBits, bool ClassfulProtocol)
{
//Determine Number of Subnets
if (ClassfulProtocol == true)
{
//Use ((2^s)-2) When:
//Classful routing protocol, Rip Ver 1 or IGRP protocol used, or no ip subnet zero command is configured
//Subtract 2 because Subnet Zero and Broadcast Subnet are reserved
return ((int)Math.Pow(2, SubnetBits) - 2);
}
else
{
//Use 2^s When:
//Classless routing protocol, Rip Ver 2 EIGRP or OSPF protocol used, ip subnet zero is configured or omitted, VLSM used, no other clues
//Subnet Zero and Broadcast Subnet are not reserved and can be used
return (int)Math.Pow(2, SubnetBits);
}
}

public int GetMaxHosts(int HostBits)
{
//Use ((2^h) - 2) Subtract 2 for the Subnet Number and Broadcast Address
return ((int)Math.Pow(2, HostBits) - 2);
}
#endregion

#region Methods

public bool ValidateIp(string testip)
{
IPAddress ip;
bool valid = false;
if (string.IsNullOrEmpty(testip))
{
valid = false;
}
else
{
valid = IPAddress.TryParse(testip, out ip);
}
return valid;
}

public bool ValidateMac(string testmac)
{
testmac = testmac.Replace(":", "").Replace("-", "").Replace(".", "");
bool valid = false;
if (testmac.Length == 12)
{
Regex rx = new Regex("([0-9a-fA-F][0-9a-fA-F]){6}");
if (rx.IsMatch(testmac))
{
valid = true;
}
}
return valid;
}

public byte[] MacToByte(string mac)
{
byte[] macBytes = BitConverter.GetBytes(long.Parse(mac, NumberStyles.HexNumber));
byte[] macAddress = new byte[6];
if (BitConverter.IsLittleEndian)
{
Array.Reverse(macBytes);
for (int i = 0; i <= 5; i++)
{
macAddress[i] = macBytes[i + 2];
}
}
return macAddress;
}

public byte[] GetOption(DhcpOption option)
{
if (this._options.ContainsKey(option))
{
return this._options[option];
}
else
{
return null;
}
}

public void AddOption(DhcpOption option, params byte[] val)
{
byte[] data = new byte[1 + val.Length];
data[0] = (byte)val.Length;
val.CopyTo(data, 1);
this._options.Add(option, data);
}

public int SessionId()
{
return (int)DateTime.Now.Ticks;
}

public byte[] StringToByteArray(string str, EncodingType encodingType)
{
System.Text.Encoding encoding = null;
switch (encodingType)
{
case EncodingType.ASCII:
encoding = new System.Text.ASCIIEncoding();
break;
case EncodingType.Unicode:
encoding = new System.Text.UnicodeEncoding();
break;
case EncodingType.UTF7:
encoding = new System.Text.UTF7Encoding();
break;
case EncodingType.UTF8:
encoding = new System.Text.UTF8Encoding();
break;
}
return encoding.GetBytes(str);
}

public static string ByteArrayToString(byte[] bytes, EncodingType encodingType)
{
System.Text.Encoding encoding = null;
switch (encodingType)
{
case EncodingType.ASCII:
encoding = new System.Text.ASCIIEncoding();
break;
case EncodingType.Unicode:
encoding = new System.Text.UnicodeEncoding();
break;
case EncodingType.UTF7:
encoding = new System.Text.UTF7Encoding();
break;
case EncodingType.UTF8:
encoding = new System.Text.UTF8Encoding();
break;
}
return encoding.GetString(bytes);
}

public byte[] ClassToByteArray()
{
//Minimum DHCP Message Size + Magic Cookie + End
int DhcpMsgSize = 236 + 4 + 1;
foreach (KeyValuePair<dhcpoption, byte[]=""> pair in this._options)
{
DhcpMsgSize += 1 + pair.Value.Length;
}

byte[] data = new byte[DhcpMsgSize];
int Offset = 0;

data[Offset] = (byte)this._op;
Offset++;

data[Offset] = (byte)this._htype;
Offset++;

data[Offset] = this._hlen;
Offset++;

data[Offset] = this._hops;
Offset++;

BitConverter.GetBytes(this._xid).CopyTo(data, Offset);
Offset += 4;

BitConverter.GetBytes(this._secs).CopyTo(data, Offset); //Need to reverse Byte order???
Offset += 2;

BitConverter.GetBytes(this._flags).CopyTo(data, Offset);
Offset += 2;

IPAddress.Parse(this._ciaddr).GetAddressBytes().CopyTo(data, Offset);
Offset += 4;

IPAddress.Parse(this._yiaddr).GetAddressBytes().CopyTo(data, Offset);
Offset += 4;

IPAddress.Parse(this._siaddr).GetAddressBytes().CopyTo(data, Offset);
Offset += 4;

IPAddress.Parse(this._giaddr).GetAddressBytes().CopyTo(data, Offset);
Offset += 4;

MacToByte(this._chaddr).CopyTo(data, Offset);
Offset += 16;

this._sname.CopyTo(data, Offset);
Offset += 64;

this._file.CopyTo(data, Offset);
Offset += 128;

BitConverter.GetBytes(MagicNumber).CopyTo(data, Offset);
Offset += 4;

if (this._options.Count > 0)
{
foreach (DhcpOption option in this._options.Keys)
{
//Copy Option
data[Offset] = (byte)option;
Offset++;

//Copy Option Data
Array.Copy(this._options[option], 0, data, Offset, this._options[option].Length);
Offset += this._options[option].Length;
}
}
data[Offset] = (byte)DhcpOption.End;
return data;
}
#endregion

}
#region Packet Examples
//byte[] discoverMessage = {
// /* Opt */0x01,
// /* Htype */0x01,
// /* Hlen */0x06,
// /* Hops */0x00,
// /* XID */0x00, 0x00, 0x00, 0x01,
// /* Sec */0x00, 0x00,
// /* Flags */0x80, 0x00,
// /* Ciaddr */0x00, 0x00, 0x00, 0x00,
// /* Yiaddr */0x00, 0x00, 0x00, 0x00,
// /* Siaddr */0x00, 0x00, 0x00, 0x00,
// /* Giaddr */0x00, 0x00, 0x00, 0x00,
// /* Chaddr */0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
// /* Sname */0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// /* File */0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// /* DHCP OPTIONS */
// /* Mcookie */0x63, 0x82, 0x53, 0x63,
// /* ClientID */0x3d, 0x06, 0x00, 0x1f, 0x3b, 0x5b, 0xbd, 0xad,
// /* Hostname */0x0c, 0x05, 0x52, 0x6f, 0x67, 0x75, 0x65,
// /* DHCPDiscover */0x35, 0x01, 0x01,
// /* ParamReqList */0x37, 0x01, 0x03,
// /* EndOption */0xff
// };
#endregion

#region Enumerations
public enum DhcpOp : byte
{
//Number Operation Code - RFC1700
BootRequest = 0x01,
BootReply
}

public enum DhcpHtype : byte
{
//Number Hardware Type - RFC1700
Ethernet = 0x01,
ExperimentalEthernet,
AmateurRadio,
ProteonTokenRing,
Chaos,
Ieee802Networks,
Arcnet,
Hyperchannel,
Lanstar
}

public enum DhcpMsgType : byte
{
//DHCP Message Type - RFC2132, RFC4388
Discover = 0x01,
Offer,
Request,
Decline,
Ack,
Nak,
Release,
Inform,
ForceRenew,
LeaseQuery,
LeaseUnassigned,
LeaseUnknown,
LeaseActive
}

public enum DhcpOption : byte
{
//DHCP Options - RFC2132
Pad = 0x00,
SubnetMask = 0x01,
TimeOffset = 0x02,
Router = 0x03,
TimeServer = 0x04,
NameServer = 0x05,
DomainNameServer = 0x06,
Hostname = 0x0C,
DomainNameSuffix = 0x0F,
AddressRequest = 0x32,
AddressTime = 0x33,
DhcpMessageType = 0x35,
DhcpAddress = 0x36,
ParameterList = 0x37,
DhcpMessage = 0x38,
DhcpMaxMessageSize = 0x39,
ClassId = 0x3C,
ClientId = 0x3D,
AutoConfig = 0x74,
End = 0xFF
}

public enum EncodingType
{
ASCII,
Unicode,
UTF7,
UTF8
}

#endregion

}