317 lines
8.7 KiB
C#
317 lines
8.7 KiB
C#
using System;
|
|
using ln.types;
|
|
using System.Collections.Generic;
|
|
using ln.types.net;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
namespace ln.dhcp
|
|
{
|
|
public delegate Option OptionFactory(byte tag, byte[] bytes);
|
|
|
|
public class Option
|
|
{
|
|
public byte Tag { get; }
|
|
public virtual byte[] Bytes { get; set; }
|
|
|
|
public Option(byte tag)
|
|
{
|
|
Tag = tag;
|
|
}
|
|
public Option(byte tag, byte[] bytes)
|
|
{
|
|
Tag = tag;
|
|
Bytes = bytes;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format("[Option TAG={0} Bytes={1}]", Tag, BitConverter.ToString(Bytes));
|
|
}
|
|
|
|
static Dictionary<byte, OptionFactory> factories = new Dictionary<byte, OptionFactory>();
|
|
static void AddFactory(byte tag, OptionFactory factory) => factories.Add(tag, factory);
|
|
|
|
public static Option[] ParseOptions(byte[] bytes, int offset)
|
|
{
|
|
List<Option> options = new List<Option>();
|
|
while ((offset < bytes.Length) && (bytes[offset] != 0xff))
|
|
if (bytes[offset] != 0x00)
|
|
options.Add(Option.Parse(bytes, ref offset));
|
|
else
|
|
offset++;
|
|
|
|
return options.ToArray();
|
|
}
|
|
public static Option Parse(byte[] bytes, ref int offset)
|
|
{
|
|
Console.WriteLine("OFFSET: {0}", offset);
|
|
switch (bytes[offset])
|
|
{
|
|
case 0:
|
|
case 255:
|
|
return new Option(bytes[offset++]);
|
|
default:
|
|
byte tag = bytes[offset++];
|
|
byte len = bytes[offset++];
|
|
byte[] slice = bytes.Slice(offset, len);
|
|
offset += len;
|
|
|
|
if (factories.ContainsKey(tag))
|
|
return factories[tag](tag, slice);
|
|
else
|
|
return new Option(tag, slice);
|
|
}
|
|
}
|
|
|
|
public static byte[] ToBytes(IEnumerable<Option> options)
|
|
{
|
|
MemoryStream memoryStream = new MemoryStream();
|
|
foreach(Option option in options)
|
|
{
|
|
byte[] b = option.Bytes;
|
|
memoryStream.WriteByte(option.Tag);
|
|
memoryStream.WriteByte((byte)(b.Length));
|
|
memoryStream.Write(b, 0, b.Length);
|
|
}
|
|
memoryStream.WriteByte(0xff);
|
|
return memoryStream.ToArray();
|
|
}
|
|
|
|
static Option()
|
|
{
|
|
AddFactory(0x01, (tag, bytes) => new NetmaskOption(bytes)); // Netmask
|
|
AddFactory(0x03, (tag, bytes) => new GatewaysOption(bytes)); // Gateways
|
|
AddFactory(0x0C, (tag, bytes) => new TextOption(tag, bytes)); // Host Name
|
|
AddFactory(0x0F, (tag, bytes) => new TextOption(tag, bytes)); // Domain Name
|
|
|
|
AddFactory(0x32, (tag, bytes) => new DHCPRequestedIPOption(bytes)); // Requested IP
|
|
AddFactory(0x33, (tag, bytes) => new DHCPLeaseTimeOption(bytes));// Lease Time
|
|
|
|
AddFactory(0x35, (tag, bytes) => new DHCPMessageTypeOption(bytes[0])); // DHCP Message Type
|
|
AddFactory(0x36, (tag, bytes) => new DHCPServerIdentifierOption(bytes)); // Server ID
|
|
|
|
}
|
|
}
|
|
public class IPv4ListOption : Option
|
|
{
|
|
public IPv4[] IPs { get; set; }
|
|
public override byte[] Bytes
|
|
{
|
|
get => IPs.SelectMany(ip => ip.IPBytes).ToArray();
|
|
set
|
|
{
|
|
IPs = new IPv4[value.Length / 4];
|
|
for (int n = 0; n < value.Length / 4; n++)
|
|
IPs[n] = new IPv4(value.Slice(n * 4, 4));
|
|
}
|
|
}
|
|
public IPv4ListOption(byte tag)
|
|
:base(tag)
|
|
{
|
|
IPs = new IPv4[0];
|
|
}
|
|
public IPv4ListOption(byte tag, IPv4[] ips)
|
|
: base(tag)
|
|
{
|
|
IPs = ips.Slice(0);
|
|
}
|
|
public IPv4ListOption(byte tag, byte[] bytes)
|
|
: base(tag)
|
|
{
|
|
IPs = new IPv4[] { new IPv4(bytes) };
|
|
}
|
|
}
|
|
public class GatewaysOption : IPv4ListOption
|
|
{
|
|
public GatewaysOption()
|
|
: base(0x03,new byte[0])
|
|
{
|
|
}
|
|
public GatewaysOption(byte[] bytes)
|
|
: base(0x03,bytes)
|
|
{
|
|
}
|
|
public GatewaysOption(IEnumerable<IPv4> gateways)
|
|
: base(0x03)
|
|
{
|
|
IPs = gateways.ToArray();
|
|
}
|
|
}
|
|
|
|
|
|
public class IPv4Option : Option
|
|
{
|
|
public IPv4 IP { get; set; }
|
|
public override byte[] Bytes
|
|
{
|
|
get => IP.IPBytes;
|
|
set => IP = new IPv4(value);
|
|
|
|
}
|
|
public IPv4Option(byte tag)
|
|
: base(tag)
|
|
{ }
|
|
public IPv4Option(byte tag, IPv4 ip)
|
|
: base(tag)
|
|
{
|
|
IP = ip;
|
|
}
|
|
public IPv4Option(byte tag, byte[] bytes)
|
|
: base(tag)
|
|
{
|
|
Bytes = bytes;
|
|
}
|
|
}
|
|
public class NetmaskOption : IPv4Option
|
|
{
|
|
public NetmaskOption()
|
|
: base(0x01)
|
|
{ }
|
|
public NetmaskOption(byte[] bytes)
|
|
: base(0x01)
|
|
{
|
|
Bytes = bytes;
|
|
}
|
|
public NetmaskOption(IPv4 ip)
|
|
: base(0x01, ip)
|
|
{ }
|
|
}
|
|
public class DHCPRequestedIPOption : IPv4Option
|
|
{
|
|
public DHCPRequestedIPOption()
|
|
: base(0x32)
|
|
{ }
|
|
public DHCPRequestedIPOption(byte[] bytes)
|
|
: base(0x32)
|
|
{
|
|
Bytes = bytes;
|
|
}
|
|
public DHCPRequestedIPOption(IPv4 ip)
|
|
: base(0x32, ip)
|
|
{ }
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("DHCPRequestedIP={0}",IP);
|
|
}
|
|
}
|
|
|
|
|
|
public class DHCPServerIdentifierOption : IPv4Option
|
|
{
|
|
public DHCPServerIdentifierOption()
|
|
: base(0x36)
|
|
{ }
|
|
public DHCPServerIdentifierOption(byte[] bytes)
|
|
: base(0x36)
|
|
{
|
|
IP = new IPv4(bytes);
|
|
}
|
|
public DHCPServerIdentifierOption(IPv4 ip)
|
|
: base(0x36, ip)
|
|
{ }
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("DHCPServerIdentifier={0}",IP);
|
|
}
|
|
}
|
|
|
|
public class TextOption : Option
|
|
{
|
|
public String Text { get; set; }
|
|
public override byte[] Bytes
|
|
{
|
|
get => Encoding.UTF8.GetBytes(Text);
|
|
set => Text = Encoding.UTF8.GetString(value);
|
|
}
|
|
public TextOption(byte tag)
|
|
: base(tag)
|
|
{ }
|
|
public TextOption(byte tag, byte[] bytes)
|
|
: base(tag)
|
|
{
|
|
Bytes = bytes;
|
|
}
|
|
public TextOption(byte tag, string text)
|
|
: this(tag)
|
|
{
|
|
Text = text;
|
|
}
|
|
}
|
|
public class IntegerOption : Option
|
|
{
|
|
public int Integer { get; set; }
|
|
public override byte[] Bytes
|
|
{
|
|
get => Integer.GetBytes(true);
|
|
set => Integer = value.GetInt(true);
|
|
}
|
|
public IntegerOption(byte tag)
|
|
: base(tag)
|
|
{ }
|
|
public IntegerOption(byte tag, byte[] bytes)
|
|
: base(tag)
|
|
{
|
|
Bytes = bytes;
|
|
}
|
|
public IntegerOption(byte tag, int i)
|
|
: this(tag)
|
|
{
|
|
Integer = i;
|
|
}
|
|
}
|
|
public class DHCPLeaseTimeOption : IntegerOption
|
|
{
|
|
public DHCPLeaseTimeOption(byte[] val)
|
|
: base(0x33, val)
|
|
{}
|
|
public DHCPLeaseTimeOption(int leaseTime)
|
|
: base(0x33)
|
|
{
|
|
Integer = leaseTime;
|
|
}
|
|
public DHCPLeaseTimeOption(DateTimeOffset validThrough)
|
|
:base(0x33)
|
|
{
|
|
Integer = (int)(validThrough - DateTimeOffset.Now).TotalSeconds;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format("DHCPLeaseTime={0}s",Integer);
|
|
}
|
|
}
|
|
|
|
public class DHCPMessageTypeOption : Option
|
|
{
|
|
public DHCPMessageType DHCPMessageType { get; set; }
|
|
public override byte[] Bytes
|
|
{
|
|
get => new byte[] { (byte)DHCPMessageType };
|
|
set => DHCPMessageType = value.Length > 0 ? (DHCPMessageType)value[0] : DHCPMessageType.DISCOVER;
|
|
}
|
|
public DHCPMessageTypeOption()
|
|
: base(0x35)
|
|
{
|
|
}
|
|
public DHCPMessageTypeOption(DHCPMessageType messageType)
|
|
: base(0x35)
|
|
{
|
|
DHCPMessageType = messageType;
|
|
}
|
|
public DHCPMessageTypeOption(byte value)
|
|
: this()
|
|
{
|
|
DHCPMessageType = (DHCPMessageType)value;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Format("DHCPMessageType={0}",DHCPMessageType);
|
|
}
|
|
}
|
|
|
|
}
|