ln.dhcp/BootPPacket.cs

151 lines
4.8 KiB
C#

using System;
using System.Net;
using ln.types.net;
using ln.types;
using System.Collections.Generic;
using System.IO;
using System.Runtime;
namespace ln.dhcp
{
public enum OP:byte { BOOTREQUEST = 1,BOOTREPLY = 2 }
[Flags]
public enum BootPFlags:short
{
BROADCAST = 0x0001
}
public class BootPPacket
{
public IPEndPoint RemoteEndpoint { get; set; }
public OP OP { get; set; }
public byte HType { get; set; }
public byte HLen { get; set; }
public byte Hops { get; set; }
public uint XID { get; set; }
public ushort Secs { get; set; }
public BootPFlags Flags { get; set; }
public IPv4 CIAddr { get; set; } = IPv4.ANY;
public IPv4 YIAddr { get; set; } = IPv4.ANY;
public IPv4 SIAddr { get; set; } = IPv4.ANY;
public IPv4 GIAddr { get; set; } = IPv4.ANY;
public MAC CHAddr { get; set; } = new MAC(new byte[6]);
public byte[] SName { get; set; }
public byte[] File { get; set; }
public byte[] Vend { get; set; }
List<Option> options = new List<Option>();
public Option[] Options => options.ToArray();
static byte[] magicVend = new byte[] { 0x63, 0x82, 0x53, 0x63 };
public BootPPacket()
{
}
public BootPPacket(byte[] bytes,IPEndPoint remoteEndpoint)
{
if (bytes.Length < 290)
throw new ArgumentOutOfRangeException(nameof(bytes), "BootP Packet too small");
RemoteEndpoint = remoteEndpoint;
OP = (OP)bytes[0];
HType = bytes[1];
HLen = bytes[2];
Hops = bytes[3];
XID = bytes.GetUInt(4,true);
Secs = bytes.GetUShort(8, true);
Flags = (BootPFlags)bytes.GetShort(10, true);
CIAddr = new IPv4(bytes, 12);
YIAddr = new IPv4(bytes, 16);
SIAddr = new IPv4(bytes, 20);
GIAddr = new IPv4(bytes, 24);
if (HLen != 6)
throw new ArgumentOutOfRangeException(nameof(bytes),"Only 6 byte HW Addresses are supported by this implementation");
CHAddr = new MAC(bytes.Slice(28, 6));
SName = bytes.Slice(44, 64);
File = bytes.Slice(108, 128);
Vend = bytes.Slice(236);
if ((Vend.Length >= 4) && Vend.Slice(0, 4).AreEqual(magicVend))
{
options.AddRange(Option.ParseOptions(Vend, 4));
}
Option overload = GetFirstOption(0x34);
if (overload != null)
{
if ((overload.Bytes[0] & 0x01) != 0)
options.AddRange(Option.ParseOptions(File, 4));
if ((overload.Bytes[0] & 0x02) != 0)
options.AddRange(Option.ParseOptions(SName, 4));
}
}
public byte[] ToBytes()
{
MemoryStream memoryStream = new MemoryStream();
memoryStream.WriteByte((byte)OP);
memoryStream.WriteByte(HType);
memoryStream.WriteByte(HLen);
memoryStream.WriteByte(Hops);
memoryStream.WriteUInteger(XID);
memoryStream.WriteUShort(Secs);
memoryStream.WriteShort((short)Flags);
memoryStream.WriteBytes(CIAddr.IPBytes);
memoryStream.WriteBytes(YIAddr.IPBytes);
memoryStream.WriteBytes(SIAddr.IPBytes);
memoryStream.WriteBytes(GIAddr.IPBytes);
memoryStream.WriteBytes(CHAddr.Bytes);
memoryStream.WriteBytes(new byte[10]);
memoryStream.WriteBytes(new byte[192]);
memoryStream.WriteBytes(magicVend);
byte[] options = Option.ToBytes(Options);
memoryStream.WriteBytes(options);
return memoryStream.ToArray();
}
public OT GetFirstOption<OT>() where OT : Option
{
foreach (Option option in options)
{
if (option is OT)
return (OT)option;
}
return null;
}
public Option GetFirstOption(byte tag)
{
foreach (Option option in options)
{
if (option.Tag == tag)
return option;
}
return null;
}
public void RemoveOption(Option option) => options.Remove(option);
public void AddOption(Option option) => options.Add(option);
public void SetOption(Option option)
{
foreach (Option op in options)
if (op.Tag == option.Tag)
RemoveOption(op);
AddOption(option);
}
public override string ToString()
{
return String.Format("[OP={0} Options: {1}]",OP,String.Join(", ", (object[])Options));
}
}
}