From 0f5cbfb9af23a0ff8e927f4181f402a0fa1e8a66 Mon Sep 17 00:00:00 2001 From: Shreyas Zare Date: Sun, 9 Jun 2019 17:32:28 +0530 Subject: [PATCH] dhcp: implemented dhcp message parsing changes. implemented all required options with parsing tests. implemented option fragmentation parsing using option overload. --- DnsServerCore/Dhcp/ClientIdentifierOption.cs | 78 ------- DnsServerCore/Dhcp/DhcpMessage.cs | 217 +++++++++++++++--- DnsServerCore/Dhcp/DhcpOption.cs | 93 ++++++-- .../{ => Options}/BroadcastAddressOption.cs | 31 +-- .../Options/ClasslessStaticRouteOption.cs | 153 ++++++++++++ .../ClientFullyQualifiedDomainNameOption.cs | 127 ++++++++++ .../Dhcp/Options/ClientIdentifierOption.cs | 126 ++++++++++ .../{ => Options}/DhcpMessageTypeOption.cs | 42 ++-- .../Dhcp/{ => Options}/DomainNameOption.cs | 31 +-- .../{ => Options}/DomainNameServerOption.cs | 36 +-- .../Dhcp/{ => Options}/HostNameOption.cs | 31 +-- .../{ => Options}/IpAddressLeaseTimeOption.cs | 36 +-- .../MaximumDhcpMessageSizeOption.cs | 40 ++-- .../Dhcp/{ => Options}/MessageOption.cs | 28 ++- .../{ => Options}/NetBiosNameServerOption.cs | 36 +-- .../NetworkTimeProtocolServersOption.cs | 36 +-- .../{ => Options}/OptionOverloadOption.cs | 30 +-- .../ParameterRequestListOption.cs | 34 +-- .../{ => Options}/RebindingTimeValueOption.cs | 36 +-- .../{ => Options}/RenewalTimeValueOption.cs | 36 +-- .../{ => Options}/RequestedIpAddressOption.cs | 30 +-- .../Dhcp/{ => Options}/RouterOption.cs | 36 +-- .../{ => Options}/ServerIdentifierOption.cs | 30 +-- .../Dhcp/{ => Options}/SubnetMaskOption.cs | 30 +-- DnsServerCore/Dhcp/StaticRouteOption.cs | 77 ------- 25 files changed, 1012 insertions(+), 468 deletions(-) delete mode 100644 DnsServerCore/Dhcp/ClientIdentifierOption.cs rename DnsServerCore/Dhcp/{ => Options}/BroadcastAddressOption.cs (76%) create mode 100644 DnsServerCore/Dhcp/Options/ClasslessStaticRouteOption.cs create mode 100644 DnsServerCore/Dhcp/Options/ClientFullyQualifiedDomainNameOption.cs create mode 100644 DnsServerCore/Dhcp/Options/ClientIdentifierOption.cs rename DnsServerCore/Dhcp/{ => Options}/DhcpMessageTypeOption.cs (67%) rename DnsServerCore/Dhcp/{ => Options}/DomainNameOption.cs (74%) rename DnsServerCore/Dhcp/{ => Options}/DomainNameServerOption.cs (75%) rename DnsServerCore/Dhcp/{ => Options}/HostNameOption.cs (74%) rename DnsServerCore/Dhcp/{ => Options}/IpAddressLeaseTimeOption.cs (66%) rename DnsServerCore/Dhcp/{ => Options}/MaximumDhcpMessageSizeOption.cs (60%) rename DnsServerCore/Dhcp/{ => Options}/MessageOption.cs (76%) rename DnsServerCore/Dhcp/{ => Options}/NetBiosNameServerOption.cs (75%) rename DnsServerCore/Dhcp/{ => Options}/NetworkTimeProtocolServersOption.cs (75%) rename DnsServerCore/Dhcp/{ => Options}/OptionOverloadOption.cs (79%) rename DnsServerCore/Dhcp/{ => Options}/ParameterRequestListOption.cs (77%) rename DnsServerCore/Dhcp/{ => Options}/RebindingTimeValueOption.cs (66%) rename DnsServerCore/Dhcp/{ => Options}/RenewalTimeValueOption.cs (66%) rename DnsServerCore/Dhcp/{ => Options}/RequestedIpAddressOption.cs (77%) rename DnsServerCore/Dhcp/{ => Options}/RouterOption.cs (76%) rename DnsServerCore/Dhcp/{ => Options}/ServerIdentifierOption.cs (78%) rename DnsServerCore/Dhcp/{ => Options}/SubnetMaskOption.cs (77%) delete mode 100644 DnsServerCore/Dhcp/StaticRouteOption.cs diff --git a/DnsServerCore/Dhcp/ClientIdentifierOption.cs b/DnsServerCore/Dhcp/ClientIdentifierOption.cs deleted file mode 100644 index d7210527..00000000 --- a/DnsServerCore/Dhcp/ClientIdentifierOption.cs +++ /dev/null @@ -1,78 +0,0 @@ -/* -Technitium DNS Server -Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -*/ - -using System; -using System.IO; -using TechnitiumLibrary.IO; - -namespace DnsServerCore.Dhcp -{ - class ClientIdentifierOption : DhcpOption - { - #region variables - - readonly byte _type; - readonly byte[] _identifier; - - #endregion - - #region constructor - - public ClientIdentifierOption(Stream s) - : base(DhcpOptionCode.ClientIdentifier) - { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len < 2) - throw new InvalidDataException(); - - int type = s.ReadByte(); - if (type < 0) - throw new EndOfStreamException(); - - _type = (byte)type; - _identifier = s.ReadBytes(len - 1); - } - - #endregion - - #region protected - - protected override void WriteOptionTo(Stream s) - { - s.WriteByte(Convert.ToByte(_identifier.Length + 1)); - s.WriteByte(_type); - s.Write(_identifier); - } - - #endregion - - #region properties - - public byte Type - { get { return _type; } } - - public byte[] Identifier - { get { return _identifier; } } - - #endregion - } -} diff --git a/DnsServerCore/Dhcp/DhcpMessage.cs b/DnsServerCore/Dhcp/DhcpMessage.cs index 9ed83928..18cf8ead 100644 --- a/DnsServerCore/Dhcp/DhcpMessage.cs +++ b/DnsServerCore/Dhcp/DhcpMessage.cs @@ -17,6 +17,7 @@ along with this program. If not, see . */ +using DnsServerCore.Dhcp.Options; using System; using System.Collections.Generic; using System.IO; @@ -39,6 +40,7 @@ namespace DnsServerCore.Dhcp enum DhcpMessageFlags : ushort { + None = 0, Broadcast = 0x8000 } @@ -46,16 +48,16 @@ namespace DnsServerCore.Dhcp { #region variables - const uint MAGIC_COOKIE = 0x63825363; + const uint MAGIC_COOKIE = 0x63538263; //in reverse format readonly DhcpMessageOpCode _op; readonly DhcpMessageHardwareAddressType _htype; readonly byte _hlen; readonly byte _hops; - readonly uint _xid; + readonly byte[] _xid; - readonly ushort _secs; + readonly byte[] _secs; readonly DhcpMessageFlags _flags; readonly IPAddress _ciaddr; @@ -67,13 +69,26 @@ namespace DnsServerCore.Dhcp readonly byte[] _sname; readonly byte[] _file; - readonly List _options; + readonly IReadOnlyCollection _options; + + readonly byte[] _clientHardwareAddress; + + OptionOverloadOption _optionOverload; + + DhcpMessageTypeOption _dhcpMessageType; + ClientIdentifierOption _clientIdentifier; + HostNameOption _hostName; + ClientFullyQualifiedDomainNameOption _clientFullyQualifiedDomainName; + ParameterRequestListOption _parameterRequestList; + MaximumDhcpMessageSizeOption _maximumDhcpMessageSize; + ServerIdentifierOption _serverIdentifier; + RequestedIpAddressOption _requestedIpAddress; #endregion #region constructor - public DhcpMessage(DhcpMessageOpCode op, uint xid, ushort secs, DhcpMessageFlags flags, IPAddress ciaddr, IPAddress yiaddr, IPAddress siaddr, IPAddress giaddr, byte[] chaddr, List options) + public DhcpMessage(DhcpMessageOpCode op, byte[] xid, byte[] secs, DhcpMessageFlags flags, IPAddress ciaddr, IPAddress yiaddr, IPAddress siaddr, IPAddress giaddr, byte[] clientHardwareAddress, IReadOnlyCollection options) { if (ciaddr.AddressFamily != AddressFamily.InterNetwork) throw new ArgumentException("Address family not supported.", "ciaddr"); @@ -87,22 +102,14 @@ namespace DnsServerCore.Dhcp if (giaddr.AddressFamily != AddressFamily.InterNetwork) throw new ArgumentException("Address family not supported.", "giaddr"); - if (chaddr == null) - { - chaddr = new byte[16]; - } - else - { - if (chaddr.Length > 16) - throw new ArgumentException("Value cannot be greater that 16 bytes.", "chaddr"); + if ((clientHardwareAddress != null) && (clientHardwareAddress.Length != 6)) + throw new ArgumentException("Value must be 6 bytes long for a valid Ethernet hardware address.", "chaddr"); - if (chaddr.Length < 16) - { - byte[] newchaddr = new byte[16]; - Buffer.BlockCopy(chaddr, 0, newchaddr, 0, chaddr.Length); - chaddr = newchaddr; - } - } + if (xid.Length != 4) + throw new ArgumentException("Transaction ID must be 4 bytes.", "xid"); + + if (secs.Length != 2) + throw new ArgumentException("Seconds elapsed must be 2 bytes.", "secs"); _op = op; _htype = DhcpMessageHardwareAddressType.Ethernet; @@ -119,13 +126,20 @@ namespace DnsServerCore.Dhcp _siaddr = siaddr; _giaddr = giaddr; - _chaddr = chaddr; + _clientHardwareAddress = clientHardwareAddress; + _chaddr = new byte[16]; + Buffer.BlockCopy(_clientHardwareAddress, 0, _chaddr, 0, 6); + _sname = new byte[64]; _file = new byte[128]; _options = options; } + public DhcpMessage(DhcpMessage request, IPAddress yiaddr, IPAddress siaddr, IReadOnlyCollection options) + : this(DhcpMessageOpCode.BootReply, request.TransactionId, request.SecondsElapsed, request.Flags, request.ClientIpAddress, yiaddr, siaddr, request.RelayAgentIpAddress, request.ClientHardwareAddress, options) + { } + public DhcpMessage(Stream s) { byte[] buffer = new byte[4]; @@ -136,12 +150,13 @@ namespace DnsServerCore.Dhcp _hlen = buffer[2]; _hops = buffer[3]; - s.ReadBytes(buffer, 0, 4); - _xid = BitConverter.ToUInt32(buffer, 0); + _xid = s.ReadBytes(4); s.ReadBytes(buffer, 0, 4); - _secs = BitConverter.ToUInt16(buffer, 0); - _flags = (DhcpMessageFlags)BitConverter.ToUInt16(buffer, 2); + _secs = new byte[2]; + Buffer.BlockCopy(buffer, 0, _secs, 0, 2); + Array.Reverse(buffer); + _flags = (DhcpMessageFlags)BitConverter.ToUInt16(buffer, 0); s.ReadBytes(buffer, 0, 4); _ciaddr = new IPAddress(buffer); @@ -156,25 +171,125 @@ namespace DnsServerCore.Dhcp _giaddr = new IPAddress(buffer); _chaddr = s.ReadBytes(16); + _clientHardwareAddress = new byte[_hlen]; + Buffer.BlockCopy(_chaddr, 0, _clientHardwareAddress, 0, _hlen); + _sname = s.ReadBytes(64); _file = s.ReadBytes(128); //read options - _options = new List(); + List options = new List(); + _options = options; s.ReadBytes(buffer, 0, 4); - Array.Reverse(buffer); uint magicCookie = BitConverter.ToUInt32(buffer, 0); if (magicCookie == MAGIC_COOKIE) { - while (true) + ParseOptions(s, options); + + if (_optionOverload != null) { - DhcpOption option = DhcpOption.Parse(s); - if (option.Code == DhcpOptionCode.End) + if (_optionOverload.Value.HasFlag(OptionOverloadValue.FileFieldUsed)) + { + using (MemoryStream mS = new MemoryStream(_file)) + { + ParseOptions(mS, options); + } + } + + if (_optionOverload.Value.HasFlag(OptionOverloadValue.SnameFieldUsed)) + { + using (MemoryStream mS = new MemoryStream(_sname)) + { + ParseOptions(mS, options); + } + } + } + + //parse all option values + foreach (DhcpOption option in options) + option.ParseOptionValue(); + } + + if (_clientIdentifier == null) + _clientIdentifier = new ClientIdentifierOption((byte)_htype, _clientHardwareAddress); + + if (_maximumDhcpMessageSize != null) + _maximumDhcpMessageSize = new MaximumDhcpMessageSizeOption(576); + } + + #endregion + + #region private + + private void ParseOptions(Stream s, List options) + { + while (true) + { + DhcpOption option = DhcpOption.Parse(s); + if (option.Code == DhcpOptionCode.End) + break; + + if (option.Code == DhcpOptionCode.Pad) + continue; + + bool optionExists = false; + + foreach (DhcpOption existingOption in options) + { + if (existingOption.Code == option.Code) + { + //option already exists so append current option value into existing option + existingOption.AppendOptionValue(option); + optionExists = true; + break; + } + } + + if (optionExists) + continue; + + //add option to list + options.Add(option); + + switch (option.Code) + { + case DhcpOptionCode.DhcpMessageType: + _dhcpMessageType = option as DhcpMessageTypeOption; break; - _options.Add(option); + case DhcpOptionCode.ClientIdentifier: + _clientIdentifier = option as ClientIdentifierOption; + break; + + case DhcpOptionCode.HostName: + _hostName = option as HostNameOption; + break; + + case DhcpOptionCode.ClientFullyQualifiedDomainName: + _clientFullyQualifiedDomainName = option as ClientFullyQualifiedDomainNameOption; + break; + + case DhcpOptionCode.ParameterRequestList: + _parameterRequestList = option as ParameterRequestListOption; + break; + + case DhcpOptionCode.MaximumDhcpMessageSize: + _maximumDhcpMessageSize = option as MaximumDhcpMessageSizeOption; + break; + + case DhcpOptionCode.ServerIdentifier: + _serverIdentifier = option as ServerIdentifierOption; + break; + + case DhcpOptionCode.RequestedIpAddress: + _requestedIpAddress = option as RequestedIpAddressOption; + break; + + case DhcpOptionCode.OptionOverload: + _optionOverload = option as OptionOverloadOption; + break; } } } @@ -190,10 +305,12 @@ namespace DnsServerCore.Dhcp s.WriteByte(_hlen); s.WriteByte(_hops); - s.Write(BitConverter.GetBytes(_xid)); + s.Write(_xid); - s.Write(BitConverter.GetBytes(_secs)); - s.Write(BitConverter.GetBytes((ushort)_flags)); + s.Write(_secs); + byte[] buffer = BitConverter.GetBytes((ushort)_flags); + Array.Reverse(buffer); + s.Write(buffer); s.Write(_ciaddr.GetAddressBytes()); s.Write(_yiaddr.GetAddressBytes()); @@ -227,10 +344,10 @@ namespace DnsServerCore.Dhcp public byte Hops { get { return _hops; } } - public uint TransactionId + public byte[] TransactionId { get { return _xid; } } - public ushort SecondsElapsed + public byte[] SecondsElapsed { get { return _secs; } } public DhcpMessageFlags Flags @@ -249,7 +366,7 @@ namespace DnsServerCore.Dhcp { get { return _giaddr; } } public byte[] ClientHardwareAddress - { get { return _chaddr; } } + { get { return _clientHardwareAddress; } } public byte[] ServerHostName { get { return _sname; } } @@ -257,9 +374,33 @@ namespace DnsServerCore.Dhcp public byte[] BootFileName { get { return _file; } } - public IReadOnlyList Options + public IReadOnlyCollection Options { get { return _options; } } + public DhcpMessageTypeOption DhcpMessageType + { get { return _dhcpMessageType; } } + + public ClientIdentifierOption ClientIdentifier + { get { return _clientIdentifier; } } + + public HostNameOption HostName + { get { return _hostName; } } + + public ClientFullyQualifiedDomainNameOption ClientFullyQualifiedDomainName + { get { return _clientFullyQualifiedDomainName; } } + + public ParameterRequestListOption ParameterRequestList + { get { return _parameterRequestList; } } + + public MaximumDhcpMessageSizeOption MaximumDhcpMessageSize + { get { return _maximumDhcpMessageSize; } } + + public ServerIdentifierOption ServerIdentifier + { get { return _serverIdentifier; } } + + public RequestedIpAddressOption RequestedIpAddress + { get { return _requestedIpAddress; } } + #endregion } } diff --git a/DnsServerCore/Dhcp/DhcpOption.cs b/DnsServerCore/Dhcp/DhcpOption.cs index a44691f8..3b3840e6 100644 --- a/DnsServerCore/Dhcp/DhcpOption.cs +++ b/DnsServerCore/Dhcp/DhcpOption.cs @@ -17,6 +17,7 @@ along with this program. If not, see . */ +using DnsServerCore.Dhcp.Options; using System; using System.IO; using TechnitiumLibrary.IO; @@ -100,6 +101,8 @@ namespace DnsServerCore.Dhcp DefaultIrc = 74, StreetTalkServer = 75, StreetTalkDirectoryAssistance = 76, + ClientFullyQualifiedDomainName = 81, + ClasslessStaticRoute = 121, End = 255 } @@ -108,21 +111,26 @@ namespace DnsServerCore.Dhcp #region variables readonly DhcpOptionCode _code; - readonly byte[] _value; + byte[] _value; #endregion #region constructor - protected DhcpOption(DhcpOptionCode type) + protected DhcpOption(DhcpOptionCode code, Stream s) { - _code = type; + _code = code; + + int len = s.ReadByte(); + if (len < 0) + throw new EndOfStreamException(); + + _value = s.ReadBytes(len); } - private DhcpOption(DhcpOptionCode type, byte[] value) + protected DhcpOption(DhcpOptionCode code) { - _code = type; - _value = value; + _code = code; } #endregion @@ -162,9 +170,6 @@ namespace DnsServerCore.Dhcp case DhcpOptionCode.BroadcastAddress: return new BroadcastAddressOption(s); - case DhcpOptionCode.StaticRoute: - return new StaticRouteOption(s); - case DhcpOptionCode.NetBiosOverTcpIpNameServer: return new NetBiosNameServerOption(s); @@ -201,17 +206,44 @@ namespace DnsServerCore.Dhcp case DhcpOptionCode.ClientIdentifier: return new ClientIdentifierOption(s); + case DhcpOptionCode.ClientFullyQualifiedDomainName: + return new ClientFullyQualifiedDomainNameOption(s); + + case DhcpOptionCode.ClasslessStaticRoute: + return new ClasslessStaticRouteOption(s); + case DhcpOptionCode.Pad: case DhcpOptionCode.End: - return new DhcpOption(optionCode, null); + return new DhcpOption(optionCode); default: //unknown option - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); + return new DhcpOption(optionCode, s); + } + } - return new DhcpOption(optionCode, s.ReadBytes(len)); + #endregion + + #region internal + + internal void AppendOptionValue(DhcpOption option) + { + byte[] value = new byte[_value.Length + option._value.Length]; + + Buffer.BlockCopy(_value, 0, value, 0, _value.Length); + Buffer.BlockCopy(option._value, 0, value, _value.Length, option._value.Length); + + _value = value; + } + + internal void ParseOptionValue() + { + if (_value != null) + { + using (MemoryStream mS = new MemoryStream(_value)) + { + ParseOptionValue(mS); + } } } @@ -219,12 +251,14 @@ namespace DnsServerCore.Dhcp #region protected - protected virtual void WriteOptionTo(Stream s) + protected virtual void ParseOptionValue(Stream s) + { } + + protected virtual void WriteOptionValue(Stream s) { if (_value == null) throw new NotImplementedException(); - s.WriteByte(Convert.ToByte(_value.Length)); s.Write(_value); } @@ -234,16 +268,37 @@ namespace DnsServerCore.Dhcp public void WriteTo(Stream s) { - s.WriteByte((byte)_code); - switch (_code) { case DhcpOptionCode.Pad: case DhcpOptionCode.End: + s.WriteByte((byte)_code); break; default: - WriteOptionTo(s); + using (MemoryStream mS = new MemoryStream()) + { + WriteOptionValue(mS); + + int len = 255; + int valueLen = Convert.ToInt32(mS.Position); + mS.Position = 0; + + do + { + if (valueLen < len) + len = valueLen; + + //write option + s.WriteByte((byte)_code); //code + s.WriteByte((byte)len); //len + mS.CopyTo(s, len, len); //value + + valueLen -= len; + } + while (valueLen > 0); + } + break; } } diff --git a/DnsServerCore/Dhcp/BroadcastAddressOption.cs b/DnsServerCore/Dhcp/Options/BroadcastAddressOption.cs similarity index 76% rename from DnsServerCore/Dhcp/BroadcastAddressOption.cs rename to DnsServerCore/Dhcp/Options/BroadcastAddressOption.cs index 9cfa9fb9..e13dbcf2 100644 --- a/DnsServerCore/Dhcp/BroadcastAddressOption.cs +++ b/DnsServerCore/Dhcp/Options/BroadcastAddressOption.cs @@ -21,38 +21,43 @@ using System.IO; using System.Net; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class BroadcastAddressOption : DhcpOption { #region variables - readonly IPAddress _broadcastAddress; + IPAddress _broadcastAddress; #endregion #region constructor - public BroadcastAddressOption(Stream s) + public BroadcastAddressOption(IPAddress broadcastAddress) : base(DhcpOptionCode.BroadcastAddress) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len != 4) - throw new InvalidDataException(); - - _broadcastAddress = new IPAddress(s.ReadBytes(4)); + _broadcastAddress = broadcastAddress; } + public BroadcastAddressOption(Stream s) + : base(DhcpOptionCode.BroadcastAddress, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) + { + if (s.Length != 4) + throw new InvalidDataException(); + + _broadcastAddress = new IPAddress(s.ReadBytes(4)); + + } + + protected override void WriteOptionValue(Stream s) { - s.WriteByte(4); s.Write(_broadcastAddress.GetAddressBytes()); } diff --git a/DnsServerCore/Dhcp/Options/ClasslessStaticRouteOption.cs b/DnsServerCore/Dhcp/Options/ClasslessStaticRouteOption.cs new file mode 100644 index 00000000..021e952a --- /dev/null +++ b/DnsServerCore/Dhcp/Options/ClasslessStaticRouteOption.cs @@ -0,0 +1,153 @@ +/* +Technitium DNS Server +Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using TechnitiumLibrary.IO; + +namespace DnsServerCore.Dhcp.Options +{ + class ClasslessStaticRouteOption : DhcpOption + { + #region variables + + ICollection _routes; + + #endregion + + #region constructor + + public ClasslessStaticRouteOption(ICollection routes) + : base(DhcpOptionCode.ClasslessStaticRoute) + { + _routes = routes; + } + + public ClasslessStaticRouteOption(Stream s) + : base(DhcpOptionCode.ClasslessStaticRoute, s) + { } + + #endregion + + #region protected + + protected override void ParseOptionValue(Stream s) + { + if (s.Length < 5) + throw new InvalidDataException(); + + _routes = new List(); + + while (s.Position < s.Length) + { + _routes.Add(new Route(s)); + } + } + + protected override void WriteOptionValue(Stream s) + { + foreach (Route route in _routes) + route.WriteTo(s); + } + + #endregion + + #region properties + + public ICollection Routes + { get { return _routes; } } + + #endregion + + public class Route + { + #region private + + readonly IPAddress _destination; + readonly IPAddress _subnetMask; + readonly IPAddress _router; + + #endregion + + #region constructor + + public Route(IPAddress destination, IPAddress subnetMask, IPAddress router) + { + _destination = destination; + _subnetMask = subnetMask; + _router = router; + } + + public Route(Stream s) + { + int subnetMaskWidth = s.ReadByte(); + if (subnetMaskWidth < 0) + throw new EndOfStreamException(); + + _destination = new IPAddress(s.ReadBytes(Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(subnetMaskWidth) / 8)))); + + byte[] subnetMaskBuffer = BitConverter.GetBytes(0xFFFFFFFFu << (32 - subnetMaskWidth)); + Array.Reverse(subnetMaskBuffer); + _subnetMask = new IPAddress(subnetMaskBuffer); + + _router = new IPAddress(s.ReadBytes(4)); + } + + #endregion + + #region public + + public void WriteTo(Stream s) + { + byte[] subnetMaskBuffer = _subnetMask.GetAddressBytes(); + Array.Reverse(subnetMaskBuffer); + uint subnetMaskNumber = BitConverter.ToUInt32(subnetMaskBuffer, 0); + + byte subnetMaskWidth = 0; + + while (subnetMaskNumber > 0u) + { + subnetMaskNumber <<= 1; + subnetMaskWidth++; + } + + s.WriteByte(subnetMaskWidth); + s.Write(_destination.GetAddressBytes(), 0, Convert.ToInt32(Math.Ceiling(Convert.ToDecimal(subnetMaskWidth) / 8))); + s.Write(_router.GetAddressBytes()); + } + + #endregion + + #region properties + + public IPAddress Destination + { get { return _destination; } } + + public IPAddress SubnetMask + { get { return _subnetMask; } } + + public IPAddress Router + { get { return _router; } } + + #endregion + } + } +} diff --git a/DnsServerCore/Dhcp/Options/ClientFullyQualifiedDomainNameOption.cs b/DnsServerCore/Dhcp/Options/ClientFullyQualifiedDomainNameOption.cs new file mode 100644 index 00000000..05e33760 --- /dev/null +++ b/DnsServerCore/Dhcp/Options/ClientFullyQualifiedDomainNameOption.cs @@ -0,0 +1,127 @@ +/* +Technitium DNS Server +Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System.IO; +using System.Text; +using TechnitiumLibrary.IO; +using TechnitiumLibrary.Net.Dns; + +namespace DnsServerCore.Dhcp.Options +{ + enum ClientFullyQualifiedDomainNameFlags : byte + { + None = 0, + ShouldUpdateDns = 1, + OverrideByServer = 2, + EncodeUsingCanonicalWireFormat = 4, + NoDnsUpdate = 8, + } + + class ClientFullyQualifiedDomainNameOption : DhcpOption + { + #region variables + + ClientFullyQualifiedDomainNameFlags _flags; + byte _rcode1; + byte _rcode2; + string _domainName; + + #endregion + + #region constructor + + public ClientFullyQualifiedDomainNameOption(ClientFullyQualifiedDomainNameFlags flags, byte rcode1, byte rcode2, string domainName) + : base(DhcpOptionCode.ClientFullyQualifiedDomainName) + { + _flags = flags; + _rcode1 = rcode1; + _rcode2 = rcode2; + _domainName = domainName; + } + + public ClientFullyQualifiedDomainNameOption(Stream s) + : base(DhcpOptionCode.ClientFullyQualifiedDomainName, s) + { } + + #endregion + + #region protected + + protected override void ParseOptionValue(Stream s) + { + if (s.Length < 3) + throw new InvalidDataException(); + + int flags = s.ReadByte(); + if (flags < 0) + throw new EndOfStreamException(); + + _flags = (ClientFullyQualifiedDomainNameFlags)flags; + + int rcode; + + rcode = s.ReadByte(); + if (rcode < 0) + throw new EndOfStreamException(); + + _rcode1 = (byte)rcode; + + rcode = s.ReadByte(); + if (rcode < 0) + throw new EndOfStreamException(); + + _rcode2 = (byte)rcode; + + if (_flags.HasFlag(ClientFullyQualifiedDomainNameFlags.EncodeUsingCanonicalWireFormat)) + _domainName = DnsDatagram.DeserializeDomainName(s); + else + _domainName = Encoding.ASCII.GetString(s.ReadBytes((int)s.Length - 3)); + } + + protected override void WriteOptionValue(Stream s) + { + s.WriteByte((byte)_flags); + s.WriteByte(_rcode1); + s.WriteByte(_rcode2); + + if (_flags.HasFlag(ClientFullyQualifiedDomainNameFlags.EncodeUsingCanonicalWireFormat)) + DnsDatagram.SerializeDomainName(_domainName, s); + else + s.Write(Encoding.ASCII.GetBytes(_domainName)); + } + + #endregion + + #region properties + + public ClientFullyQualifiedDomainNameFlags Flags + { get { return _flags; } } + + public byte RCODE1 + { get { return _rcode1; } } + + public byte RCODE2 + { get { return _rcode2; } } + + public string DomainName + { get { return _domainName; } } + + #endregion + } +} diff --git a/DnsServerCore/Dhcp/Options/ClientIdentifierOption.cs b/DnsServerCore/Dhcp/Options/ClientIdentifierOption.cs new file mode 100644 index 00000000..34184b9f --- /dev/null +++ b/DnsServerCore/Dhcp/Options/ClientIdentifierOption.cs @@ -0,0 +1,126 @@ +/* +Technitium DNS Server +Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +*/ + +using System; +using System.IO; +using TechnitiumLibrary.IO; + +namespace DnsServerCore.Dhcp.Options +{ + class ClientIdentifierOption : DhcpOption, IEquatable + { + #region variables + + byte _type; + byte[] _identifier; + + #endregion + + #region constructor + + public ClientIdentifierOption(byte type, byte[] identifier) + : base(DhcpOptionCode.ClientIdentifier) + { + _type = type; + _identifier = identifier; + } + + public ClientIdentifierOption(Stream s) + : base(DhcpOptionCode.ClientIdentifier, s) + { } + + #endregion + + #region protected + + protected override void ParseOptionValue(Stream s) + { + if (s.Length < 2) + throw new InvalidDataException(); + + int type = s.ReadByte(); + if (type < 0) + throw new EndOfStreamException(); + + _type = (byte)type; + _identifier = s.ReadBytes((int)s.Length - 1); + } + + protected override void WriteOptionValue(Stream s) + { + s.WriteByte(_type); + s.Write(_identifier); + } + + #endregion + + #region public + + public override bool Equals(object obj) + { + if (obj is null) + return false; + + if (ReferenceEquals(this, obj)) + return true; + + return Equals(obj as ClientIdentifierOption); + } + + public bool Equals(ClientIdentifierOption other) + { + if (other is null) + return false; + + if (this._type != other._type) + return false; + + if (this._identifier.Length != other._identifier.Length) + return false; + + for (int i = 0; i < this._identifier.Length; i++) + { + if (this._identifier[i] != other._identifier[i]) + return false; + } + + return true; + } + + public override int GetHashCode() + { + int hashCode = 937899003; + hashCode = hashCode * -1521134295 + _type.GetHashCode(); + hashCode = hashCode * -1521134295 + BitConverter.ToInt32(_identifier, 0); + return hashCode; + } + + #endregion + + #region properties + + public byte Type + { get { return _type; } } + + public byte[] Identifier + { get { return _identifier; } } + + #endregion + } +} diff --git a/DnsServerCore/Dhcp/DhcpMessageTypeOption.cs b/DnsServerCore/Dhcp/Options/DhcpMessageTypeOption.cs similarity index 67% rename from DnsServerCore/Dhcp/DhcpMessageTypeOption.cs rename to DnsServerCore/Dhcp/Options/DhcpMessageTypeOption.cs index bf38e3a7..781cedbd 100644 --- a/DnsServerCore/Dhcp/DhcpMessageTypeOption.cs +++ b/DnsServerCore/Dhcp/Options/DhcpMessageTypeOption.cs @@ -19,10 +19,11 @@ along with this program. If not, see . using System.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { enum DhcpMessageType : byte { + Unknown = 0, Discover = 1, Offer = 2, Request = 3, @@ -37,45 +38,58 @@ namespace DnsServerCore.Dhcp { #region variables - readonly DhcpMessageType _messageType; + DhcpMessageType _type; #endregion #region constructor - public DhcpMessageTypeOption(Stream s) + public DhcpMessageTypeOption(DhcpMessageType type) : base(DhcpOptionCode.DhcpMessageType) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); + _type = type; + } - if (len != 1) + public DhcpMessageTypeOption(Stream s) + : base(DhcpOptionCode.DhcpMessageType, s) + { } + + #endregion + + #region protected + + protected override void ParseOptionValue(Stream s) + { + if (s.Length != 1) throw new InvalidDataException(); int type = s.ReadByte(); if (type < 0) throw new EndOfStreamException(); - _messageType = (DhcpMessageType)type; + _type = (DhcpMessageType)type; + } + + protected override void WriteOptionValue(Stream s) + { + s.WriteByte((byte)_type); } #endregion - #region protected + #region string - protected override void WriteOptionTo(Stream s) + public override string ToString() { - s.WriteByte(1); - s.WriteByte((byte)_messageType); + return _type.ToString(); } #endregion #region properties - public DhcpMessageType MessageType - { get { return _messageType; } } + public DhcpMessageType Type + { get { return _type; } } #endregion } diff --git a/DnsServerCore/Dhcp/DomainNameOption.cs b/DnsServerCore/Dhcp/Options/DomainNameOption.cs similarity index 74% rename from DnsServerCore/Dhcp/DomainNameOption.cs rename to DnsServerCore/Dhcp/Options/DomainNameOption.cs index 5904bcf2..a9cdcde1 100644 --- a/DnsServerCore/Dhcp/DomainNameOption.cs +++ b/DnsServerCore/Dhcp/Options/DomainNameOption.cs @@ -17,43 +17,46 @@ along with this program. If not, see . */ -using System; using System.IO; using System.Text; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class DomainNameOption : DhcpOption { #region variables - readonly string _domainName; + string _domainName; #endregion #region constructor - public DomainNameOption(Stream s) + public DomainNameOption(string domainName) : base(DhcpOptionCode.DomainName) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len < 1) - throw new InvalidDataException(); - - _domainName = Encoding.ASCII.GetString(s.ReadBytes(len)); + _domainName = domainName; } + public DomainNameOption(Stream s) + : base(DhcpOptionCode.DomainName, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) + { + if (s.Length < 1) + throw new InvalidDataException(); + + _domainName = Encoding.ASCII.GetString(s.ReadBytes((int)s.Length)); + } + + protected override void WriteOptionValue(Stream s) { - s.WriteByte(Convert.ToByte(_domainName.Length)); s.Write(Encoding.ASCII.GetBytes(_domainName)); } diff --git a/DnsServerCore/Dhcp/DomainNameServerOption.cs b/DnsServerCore/Dhcp/Options/DomainNameServerOption.cs similarity index 75% rename from DnsServerCore/Dhcp/DomainNameServerOption.cs rename to DnsServerCore/Dhcp/Options/DomainNameServerOption.cs index 7b5f94b5..34ef27f6 100644 --- a/DnsServerCore/Dhcp/DomainNameServerOption.cs +++ b/DnsServerCore/Dhcp/Options/DomainNameServerOption.cs @@ -17,47 +17,49 @@ along with this program. If not, see . */ -using System; using System.IO; using System.Net; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class DomainNameServerOption : DhcpOption { #region variables - readonly IPAddress[] _addresses; + IPAddress[] _addresses; #endregion #region constructor - public DomainNameServerOption(Stream s) + public DomainNameServerOption(IPAddress[] addresses) : base(DhcpOptionCode.DomainNameServer) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if ((len % 4 != 0) || (len < 4)) - throw new InvalidDataException(); - - _addresses = new IPAddress[len / 4]; - - for (int i = 0; i < _addresses.Length; i++) - _addresses[i] = new IPAddress(s.ReadBytes(4)); + _addresses = addresses; } + public DomainNameServerOption(Stream s) + : base(DhcpOptionCode.DomainNameServer, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) { - s.WriteByte(Convert.ToByte(_addresses.Length * 4)); + if ((s.Length % 4 != 0) || (s.Length < 4)) + throw new InvalidDataException(); + _addresses = new IPAddress[s.Length / 4]; + + for (int i = 0; i < _addresses.Length; i++) + _addresses[i] = new IPAddress(s.ReadBytes(4)); + } + + protected override void WriteOptionValue(Stream s) + { foreach (IPAddress address in _addresses) s.Write(address.GetAddressBytes()); } diff --git a/DnsServerCore/Dhcp/HostNameOption.cs b/DnsServerCore/Dhcp/Options/HostNameOption.cs similarity index 74% rename from DnsServerCore/Dhcp/HostNameOption.cs rename to DnsServerCore/Dhcp/Options/HostNameOption.cs index d2e9f865..96830cfd 100644 --- a/DnsServerCore/Dhcp/HostNameOption.cs +++ b/DnsServerCore/Dhcp/Options/HostNameOption.cs @@ -17,43 +17,46 @@ along with this program. If not, see . */ -using System; using System.IO; using System.Text; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class HostNameOption : DhcpOption { #region variables - readonly string _hostName; + string _hostName; #endregion #region constructor - public HostNameOption(Stream s) + public HostNameOption(string hostName) : base(DhcpOptionCode.HostName) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len < 1) - throw new InvalidDataException(); - - _hostName = Encoding.ASCII.GetString(s.ReadBytes(len)); + _hostName = hostName; } + public HostNameOption(Stream s) + : base(DhcpOptionCode.HostName, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) + { + if (s.Length < 1) + throw new InvalidDataException(); + + _hostName = Encoding.ASCII.GetString(s.ReadBytes((int)s.Length)); + } + + protected override void WriteOptionValue(Stream s) { - s.WriteByte(Convert.ToByte(_hostName.Length)); s.Write(Encoding.ASCII.GetBytes(_hostName)); } diff --git a/DnsServerCore/Dhcp/IpAddressLeaseTimeOption.cs b/DnsServerCore/Dhcp/Options/IpAddressLeaseTimeOption.cs similarity index 66% rename from DnsServerCore/Dhcp/IpAddressLeaseTimeOption.cs rename to DnsServerCore/Dhcp/Options/IpAddressLeaseTimeOption.cs index 7a2e69c8..088940e9 100644 --- a/DnsServerCore/Dhcp/IpAddressLeaseTimeOption.cs +++ b/DnsServerCore/Dhcp/Options/IpAddressLeaseTimeOption.cs @@ -21,39 +21,47 @@ using System; using System.IO; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class IpAddressLeaseTimeOption : DhcpOption { #region variables - readonly uint _leaseTime; + uint _leaseTime; #endregion #region constructor - public IpAddressLeaseTimeOption(Stream s) + public IpAddressLeaseTimeOption(uint leaseTime) : base(DhcpOptionCode.IpAddressLeaseTime) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len != 4) - throw new InvalidDataException(); - - _leaseTime = BitConverter.ToUInt32(s.ReadBytes(4), 0); + _leaseTime = leaseTime; } + public IpAddressLeaseTimeOption(Stream s) + : base(DhcpOptionCode.IpAddressLeaseTime, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) { - s.WriteByte(4); - s.Write(BitConverter.GetBytes(_leaseTime)); + if (s.Length != 4) + throw new InvalidDataException(); + + byte[] buffer = s.ReadBytes(4); + Array.Reverse(buffer); + _leaseTime = BitConverter.ToUInt32(buffer, 0); + } + + protected override void WriteOptionValue(Stream s) + { + byte[] buffer = BitConverter.GetBytes(_leaseTime); + Array.Reverse(buffer); + s.Write(buffer); } #endregion diff --git a/DnsServerCore/Dhcp/MaximumDhcpMessageSizeOption.cs b/DnsServerCore/Dhcp/Options/MaximumDhcpMessageSizeOption.cs similarity index 60% rename from DnsServerCore/Dhcp/MaximumDhcpMessageSizeOption.cs rename to DnsServerCore/Dhcp/Options/MaximumDhcpMessageSizeOption.cs index 51012570..7152dd69 100644 --- a/DnsServerCore/Dhcp/MaximumDhcpMessageSizeOption.cs +++ b/DnsServerCore/Dhcp/Options/MaximumDhcpMessageSizeOption.cs @@ -21,39 +21,53 @@ using System; using System.IO; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class MaximumDhcpMessageSizeOption : DhcpOption { #region variables - readonly ushort _length; + ushort _length; #endregion #region constructor - public MaximumDhcpMessageSizeOption(Stream s) + public MaximumDhcpMessageSizeOption(ushort length) : base(DhcpOptionCode.MaximumDhcpMessageSize) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); + if (length < 576) + throw new ArgumentOutOfRangeException("length", "Length must be 576 bytes or more."); - if (len != 2) - throw new InvalidDataException(); - - _length = BitConverter.ToUInt16(s.ReadBytes(2), 0); + _length = length; } + public MaximumDhcpMessageSizeOption(Stream s) + : base(DhcpOptionCode.MaximumDhcpMessageSize, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) { - s.WriteByte(2); - s.Write(BitConverter.GetBytes(_length)); + if (s.Length != 2) + throw new InvalidDataException(); + + byte[] buffer = s.ReadBytes(2); + Array.Reverse(buffer); + _length = BitConverter.ToUInt16(buffer, 0); + + if (_length < 576) + _length = 576; + } + + protected override void WriteOptionValue(Stream s) + { + byte[] buffer = BitConverter.GetBytes(_length); + Array.Reverse(buffer); + s.Write(buffer); } #endregion diff --git a/DnsServerCore/Dhcp/MessageOption.cs b/DnsServerCore/Dhcp/Options/MessageOption.cs similarity index 76% rename from DnsServerCore/Dhcp/MessageOption.cs rename to DnsServerCore/Dhcp/Options/MessageOption.cs index 17d9cbe1..7e039a1a 100644 --- a/DnsServerCore/Dhcp/MessageOption.cs +++ b/DnsServerCore/Dhcp/Options/MessageOption.cs @@ -17,43 +17,51 @@ along with this program. If not, see . */ -using System; using System.IO; using System.Text; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class MessageOption : DhcpOption { #region variables - readonly string _text; + string _text; #endregion #region constructor - public MessageOption(Stream s) + public MessageOption(string text) : base(DhcpOptionCode.Message) + { + _text = text; + } + + public MessageOption(Stream s) + : base(DhcpOptionCode.Message, s) { int len = s.ReadByte(); if (len < 0) throw new EndOfStreamException(); - if (len < 1) - throw new InvalidDataException(); - - _text = Encoding.ASCII.GetString(s.ReadBytes(len)); } #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) + { + if (s.Length < 1) + throw new InvalidDataException(); + + _text = Encoding.ASCII.GetString(s.ReadBytes((int)s.Length)); + } + + protected override void WriteOptionValue(Stream s) { - s.WriteByte(Convert.ToByte(_text.Length)); s.Write(Encoding.ASCII.GetBytes(_text)); } diff --git a/DnsServerCore/Dhcp/NetBiosNameServerOption.cs b/DnsServerCore/Dhcp/Options/NetBiosNameServerOption.cs similarity index 75% rename from DnsServerCore/Dhcp/NetBiosNameServerOption.cs rename to DnsServerCore/Dhcp/Options/NetBiosNameServerOption.cs index f32158a4..dba24824 100644 --- a/DnsServerCore/Dhcp/NetBiosNameServerOption.cs +++ b/DnsServerCore/Dhcp/Options/NetBiosNameServerOption.cs @@ -17,47 +17,49 @@ along with this program. If not, see . */ -using System; using System.IO; using System.Net; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class NetBiosNameServerOption : DhcpOption { #region variables - readonly IPAddress[] _addresses; + IPAddress[] _addresses; #endregion #region constructor - public NetBiosNameServerOption(Stream s) + public NetBiosNameServerOption(IPAddress[] addresses) : base(DhcpOptionCode.NetBiosOverTcpIpNameServer) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if ((len % 4 != 0) || (len < 4)) - throw new InvalidDataException(); - - _addresses = new IPAddress[len / 4]; - - for (int i = 0; i < _addresses.Length; i++) - _addresses[i] = new IPAddress(s.ReadBytes(4)); + _addresses = addresses; } + public NetBiosNameServerOption(Stream s) + : base(DhcpOptionCode.NetBiosOverTcpIpNameServer, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) { - s.WriteByte(Convert.ToByte(_addresses.Length * 4)); + if ((s.Length % 4 != 0) || (s.Length < 4)) + throw new InvalidDataException(); + _addresses = new IPAddress[s.Length / 4]; + + for (int i = 0; i < _addresses.Length; i++) + _addresses[i] = new IPAddress(s.ReadBytes(4)); + } + + protected override void WriteOptionValue(Stream s) + { foreach (IPAddress address in _addresses) s.Write(address.GetAddressBytes()); } diff --git a/DnsServerCore/Dhcp/NetworkTimeProtocolServersOption.cs b/DnsServerCore/Dhcp/Options/NetworkTimeProtocolServersOption.cs similarity index 75% rename from DnsServerCore/Dhcp/NetworkTimeProtocolServersOption.cs rename to DnsServerCore/Dhcp/Options/NetworkTimeProtocolServersOption.cs index b7d6d3af..65e16abb 100644 --- a/DnsServerCore/Dhcp/NetworkTimeProtocolServersOption.cs +++ b/DnsServerCore/Dhcp/Options/NetworkTimeProtocolServersOption.cs @@ -17,47 +17,49 @@ along with this program. If not, see . */ -using System; using System.IO; using System.Net; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class NetworkTimeProtocolServersOption : DhcpOption { #region variables - readonly IPAddress[] _addresses; + IPAddress[] _addresses; #endregion #region constructor - public NetworkTimeProtocolServersOption(Stream s) + public NetworkTimeProtocolServersOption(IPAddress[] addresses) : base(DhcpOptionCode.NetworkTimeProtocolServers) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if ((len % 4 != 0) || (len < 4)) - throw new InvalidDataException(); - - _addresses = new IPAddress[len / 4]; - - for (int i = 0; i < _addresses.Length; i++) - _addresses[i] = new IPAddress(s.ReadBytes(4)); + _addresses = addresses; } + public NetworkTimeProtocolServersOption(Stream s) + : base(DhcpOptionCode.NetworkTimeProtocolServers, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) { - s.WriteByte(Convert.ToByte(_addresses.Length * 4)); + if ((s.Length % 4 != 0) || (s.Length < 4)) + throw new InvalidDataException(); + _addresses = new IPAddress[s.Length / 4]; + + for (int i = 0; i < _addresses.Length; i++) + _addresses[i] = new IPAddress(s.ReadBytes(4)); + } + + protected override void WriteOptionValue(Stream s) + { foreach (IPAddress address in _addresses) s.Write(address.GetAddressBytes()); } diff --git a/DnsServerCore/Dhcp/OptionOverloadOption.cs b/DnsServerCore/Dhcp/Options/OptionOverloadOption.cs similarity index 79% rename from DnsServerCore/Dhcp/OptionOverloadOption.cs rename to DnsServerCore/Dhcp/Options/OptionOverloadOption.cs index 39b57350..2f075138 100644 --- a/DnsServerCore/Dhcp/OptionOverloadOption.cs +++ b/DnsServerCore/Dhcp/Options/OptionOverloadOption.cs @@ -19,7 +19,7 @@ along with this program. If not, see . using System.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { enum OptionOverloadValue : byte { @@ -32,20 +32,29 @@ namespace DnsServerCore.Dhcp { #region variables - readonly OptionOverloadValue _value; + OptionOverloadValue _value; #endregion #region constructor - public OptionOverloadOption(Stream s) + public OptionOverloadOption(OptionOverloadValue value) : base(DhcpOptionCode.OptionOverload) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); + _value = value; + } - if (len != 1) + public OptionOverloadOption(Stream s) + : base(DhcpOptionCode.OptionOverload, s) + { } + + #endregion + + #region protected + + protected override void ParseOptionValue(Stream s) + { + if (s.Length != 1) throw new InvalidDataException(); int value = s.ReadByte(); @@ -55,13 +64,8 @@ namespace DnsServerCore.Dhcp _value = (OptionOverloadValue)value; } - #endregion - - #region protected - - protected override void WriteOptionTo(Stream s) + protected override void WriteOptionValue(Stream s) { - s.WriteByte(4); s.WriteByte((byte)_value); } diff --git a/DnsServerCore/Dhcp/ParameterRequestListOption.cs b/DnsServerCore/Dhcp/Options/ParameterRequestListOption.cs similarity index 77% rename from DnsServerCore/Dhcp/ParameterRequestListOption.cs rename to DnsServerCore/Dhcp/Options/ParameterRequestListOption.cs index 159776dc..2951dede 100644 --- a/DnsServerCore/Dhcp/ParameterRequestListOption.cs +++ b/DnsServerCore/Dhcp/Options/ParameterRequestListOption.cs @@ -17,32 +17,40 @@ along with this program. If not, see . */ -using System; using System.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class ParameterRequestListOption : DhcpOption { #region variables - readonly DhcpOptionCode[] _optionCodes; + DhcpOptionCode[] _optionCodes; #endregion #region constructor - public ParameterRequestListOption(Stream s) + public ParameterRequestListOption(DhcpOptionCode[] optionCodes) : base(DhcpOptionCode.ParameterRequestList) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); + _optionCodes = optionCodes; + } - if (len < 1) + public ParameterRequestListOption(Stream s) + : base(DhcpOptionCode.ParameterRequestList, s) + { } + + #endregion + + #region protected + + protected override void ParseOptionValue(Stream s) + { + if (s.Length < 1) throw new InvalidDataException(); - _optionCodes = new DhcpOptionCode[len]; + _optionCodes = new DhcpOptionCode[s.Length]; int optionCode; for (int i = 0; i < _optionCodes.Length; i++) @@ -55,14 +63,8 @@ namespace DnsServerCore.Dhcp } } - #endregion - - #region protected - - protected override void WriteOptionTo(Stream s) + protected override void WriteOptionValue(Stream s) { - s.WriteByte(Convert.ToByte(_optionCodes.Length)); - foreach (DhcpOptionCode optionCode in _optionCodes) s.WriteByte((byte)optionCode); } diff --git a/DnsServerCore/Dhcp/RebindingTimeValueOption.cs b/DnsServerCore/Dhcp/Options/RebindingTimeValueOption.cs similarity index 66% rename from DnsServerCore/Dhcp/RebindingTimeValueOption.cs rename to DnsServerCore/Dhcp/Options/RebindingTimeValueOption.cs index f25270b7..1416d4e3 100644 --- a/DnsServerCore/Dhcp/RebindingTimeValueOption.cs +++ b/DnsServerCore/Dhcp/Options/RebindingTimeValueOption.cs @@ -21,39 +21,47 @@ using System; using System.IO; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class RebindingTimeValueOption : DhcpOption { #region variables - readonly uint _t2Interval; + uint _t2Interval; #endregion #region constructor - public RebindingTimeValueOption(Stream s) + public RebindingTimeValueOption(uint t2Interval) : base(DhcpOptionCode.RebindingTimeValue) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len != 4) - throw new InvalidDataException(); - - _t2Interval = BitConverter.ToUInt32(s.ReadBytes(4), 0); + _t2Interval = t2Interval; } + public RebindingTimeValueOption(Stream s) + : base(DhcpOptionCode.RebindingTimeValue, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) { - s.WriteByte(4); - s.Write(BitConverter.GetBytes(_t2Interval)); + if (s.Length != 4) + throw new InvalidDataException(); + + byte[] buffer = s.ReadBytes(4); + Array.Reverse(buffer); + _t2Interval = BitConverter.ToUInt32(buffer, 0); + } + + protected override void WriteOptionValue(Stream s) + { + byte[] buffer = BitConverter.GetBytes(_t2Interval); + Array.Reverse(buffer); + s.Write(buffer); } #endregion diff --git a/DnsServerCore/Dhcp/RenewalTimeValueOption.cs b/DnsServerCore/Dhcp/Options/RenewalTimeValueOption.cs similarity index 66% rename from DnsServerCore/Dhcp/RenewalTimeValueOption.cs rename to DnsServerCore/Dhcp/Options/RenewalTimeValueOption.cs index b3817659..64808844 100644 --- a/DnsServerCore/Dhcp/RenewalTimeValueOption.cs +++ b/DnsServerCore/Dhcp/Options/RenewalTimeValueOption.cs @@ -21,39 +21,47 @@ using System; using System.IO; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class RenewalTimeValueOption : DhcpOption { #region variables - readonly uint _t1Interval; + uint _t1Interval; #endregion #region constructor - public RenewalTimeValueOption(Stream s) + public RenewalTimeValueOption(uint t1Interval) : base(DhcpOptionCode.RenewalTimeValue) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len != 4) - throw new InvalidDataException(); - - _t1Interval = BitConverter.ToUInt32(s.ReadBytes(4), 0); + _t1Interval = t1Interval; } + public RenewalTimeValueOption(Stream s) + : base(DhcpOptionCode.RenewalTimeValue, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) { - s.WriteByte(4); - s.Write(BitConverter.GetBytes(_t1Interval)); + if (s.Length != 4) + throw new InvalidDataException(); + + byte[] buffer = s.ReadBytes(4); + Array.Reverse(buffer); + _t1Interval = BitConverter.ToUInt32(buffer, 0); + } + + protected override void WriteOptionValue(Stream s) + { + byte[] buffer = BitConverter.GetBytes(_t1Interval); + Array.Reverse(buffer); + s.Write(buffer); } #endregion diff --git a/DnsServerCore/Dhcp/RequestedIpAddressOption.cs b/DnsServerCore/Dhcp/Options/RequestedIpAddressOption.cs similarity index 77% rename from DnsServerCore/Dhcp/RequestedIpAddressOption.cs rename to DnsServerCore/Dhcp/Options/RequestedIpAddressOption.cs index a25bef35..1c8fd438 100644 --- a/DnsServerCore/Dhcp/RequestedIpAddressOption.cs +++ b/DnsServerCore/Dhcp/Options/RequestedIpAddressOption.cs @@ -21,38 +21,42 @@ using System.IO; using System.Net; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class RequestedIpAddressOption : DhcpOption { #region variables - readonly IPAddress _address; + IPAddress _address; #endregion #region constructor - public RequestedIpAddressOption(Stream s) + public RequestedIpAddressOption(IPAddress address) : base(DhcpOptionCode.RequestedIpAddress) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len != 4) - throw new InvalidDataException(); - - _address = new IPAddress(s.ReadBytes(4)); + _address = address; } + public RequestedIpAddressOption(Stream s) + : base(DhcpOptionCode.RequestedIpAddress, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) + { + if (s.Length != 4) + throw new InvalidDataException(); + + _address = new IPAddress(s.ReadBytes(4)); + } + + protected override void WriteOptionValue(Stream s) { - s.WriteByte(4); s.Write(_address.GetAddressBytes()); } diff --git a/DnsServerCore/Dhcp/RouterOption.cs b/DnsServerCore/Dhcp/Options/RouterOption.cs similarity index 76% rename from DnsServerCore/Dhcp/RouterOption.cs rename to DnsServerCore/Dhcp/Options/RouterOption.cs index ae0a6e31..74b464f5 100644 --- a/DnsServerCore/Dhcp/RouterOption.cs +++ b/DnsServerCore/Dhcp/Options/RouterOption.cs @@ -17,47 +17,49 @@ along with this program. If not, see . */ -using System; using System.IO; using System.Net; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class RouterOption : DhcpOption { #region variables - readonly IPAddress[] _addresses; + IPAddress[] _addresses; #endregion #region constructor - public RouterOption(Stream s) + public RouterOption(IPAddress[] addresses) : base(DhcpOptionCode.Router) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if ((len % 4 != 0) || (len < 4)) - throw new InvalidDataException(); - - _addresses = new IPAddress[len / 4]; - - for (int i = 0; i < _addresses.Length; i++) - _addresses[i] = new IPAddress(s.ReadBytes(4)); + _addresses = addresses; } + public RouterOption(Stream s) + : base(DhcpOptionCode.Router, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) { - s.WriteByte(Convert.ToByte(_addresses.Length * 4)); + if ((s.Length % 4 != 0) || (s.Length < 4)) + throw new InvalidDataException(); + _addresses = new IPAddress[s.Length / 4]; + + for (int i = 0; i < _addresses.Length; i++) + _addresses[i] = new IPAddress(s.ReadBytes(4)); + } + + protected override void WriteOptionValue(Stream s) + { foreach (IPAddress address in _addresses) s.Write(address.GetAddressBytes()); } diff --git a/DnsServerCore/Dhcp/ServerIdentifierOption.cs b/DnsServerCore/Dhcp/Options/ServerIdentifierOption.cs similarity index 78% rename from DnsServerCore/Dhcp/ServerIdentifierOption.cs rename to DnsServerCore/Dhcp/Options/ServerIdentifierOption.cs index dfd84be6..c0889409 100644 --- a/DnsServerCore/Dhcp/ServerIdentifierOption.cs +++ b/DnsServerCore/Dhcp/Options/ServerIdentifierOption.cs @@ -21,38 +21,42 @@ using System.IO; using System.Net; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class ServerIdentifierOption : DhcpOption { #region variables - readonly IPAddress _address; + IPAddress _address; #endregion #region constructor - public ServerIdentifierOption(Stream s) + public ServerIdentifierOption(IPAddress address) : base(DhcpOptionCode.ServerIdentifier) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len != 4) - throw new InvalidDataException(); - - _address = new IPAddress(s.ReadBytes(4)); + _address = address; } + public ServerIdentifierOption(Stream s) + : base(DhcpOptionCode.ServerIdentifier, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) + { + if (s.Length != 4) + throw new InvalidDataException(); + + _address = new IPAddress(s.ReadBytes(4)); + } + + protected override void WriteOptionValue(Stream s) { - s.WriteByte(4); s.Write(_address.GetAddressBytes()); } diff --git a/DnsServerCore/Dhcp/SubnetMaskOption.cs b/DnsServerCore/Dhcp/Options/SubnetMaskOption.cs similarity index 77% rename from DnsServerCore/Dhcp/SubnetMaskOption.cs rename to DnsServerCore/Dhcp/Options/SubnetMaskOption.cs index e40ef789..2bb87acf 100644 --- a/DnsServerCore/Dhcp/SubnetMaskOption.cs +++ b/DnsServerCore/Dhcp/Options/SubnetMaskOption.cs @@ -21,38 +21,42 @@ using System.IO; using System.Net; using TechnitiumLibrary.IO; -namespace DnsServerCore.Dhcp +namespace DnsServerCore.Dhcp.Options { class SubnetMaskOption : DhcpOption { #region variables - readonly IPAddress _subnetMask; + IPAddress _subnetMask; #endregion #region constructor - public SubnetMaskOption(Stream s) + public SubnetMaskOption(IPAddress subnetMask) : base(DhcpOptionCode.SubnetMask) { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if (len != 4) - throw new InvalidDataException(); - - _subnetMask = new IPAddress(s.ReadBytes(4)); + _subnetMask = subnetMask; } + public SubnetMaskOption(Stream s) + : base(DhcpOptionCode.SubnetMask, s) + { } + #endregion #region protected - protected override void WriteOptionTo(Stream s) + protected override void ParseOptionValue(Stream s) + { + if (s.Length != 4) + throw new InvalidDataException(); + + _subnetMask = new IPAddress(s.ReadBytes(4)); + } + + protected override void WriteOptionValue(Stream s) { - s.WriteByte(4); s.Write(_subnetMask.GetAddressBytes()); } diff --git a/DnsServerCore/Dhcp/StaticRouteOption.cs b/DnsServerCore/Dhcp/StaticRouteOption.cs deleted file mode 100644 index 59a3cea3..00000000 --- a/DnsServerCore/Dhcp/StaticRouteOption.cs +++ /dev/null @@ -1,77 +0,0 @@ -/* -Technitium DNS Server -Copyright (C) 2019 Shreyas Zare (shreyas@technitium.com) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -*/ - -using System; -using System.IO; -using System.Net; -using TechnitiumLibrary.IO; - -namespace DnsServerCore.Dhcp -{ - class StaticRouteOption : DhcpOption - { - #region variables - - readonly Tuple[] _routes; - - #endregion - - #region constructor - - public StaticRouteOption(Stream s) - : base(DhcpOptionCode.StaticRoute) - { - int len = s.ReadByte(); - if (len < 0) - throw new EndOfStreamException(); - - if ((len % 8 != 0) || (len < 8)) - throw new InvalidDataException(); - - _routes = new Tuple[len / 8]; - - for (int i = 0; i < _routes.Length; i++) - _routes[i] = new Tuple(new IPAddress(s.ReadBytes(4)), new IPAddress(s.ReadBytes(4))); - } - - #endregion - - #region protected - - protected override void WriteOptionTo(Stream s) - { - s.WriteByte(Convert.ToByte(_routes.Length * 4)); - - foreach (Tuple route in _routes) - { - s.Write(route.Item1.GetAddressBytes()); - s.Write(route.Item2.GetAddressBytes()); - } - } - - #endregion - - #region properties - - public Tuple[] Routes - { get { return _routes; } } - - #endregion - } -}