├── .gitattributes ├── .gitignore ├── DHCPServer ├── AutoPumpQueue.cs ├── DHCPClient.cs ├── DHCPClientInformation.cs ├── DHCPMessage.cs ├── DHCPServer.cs ├── DHCPServer.csproj ├── IDHCPOption.cs ├── IDHCPServer.cs ├── Options │ ├── DHCPOptionBase.cs │ ├── DHCPOptionBootFileName.cs │ ├── DHCPOptionClientIdentifier.cs │ ├── DHCPOptionDomainNameServer.cs │ ├── DHCPOptionFixedLength.cs │ ├── DHCPOptionFullyQualifiedDomainName.cs │ ├── DHCPOptionGeneric.cs │ ├── DHCPOptionHostName.cs │ ├── DHCPOptionIPAddressLeaseTime.cs │ ├── DHCPOptionMaximumDHCPMessageSize.cs │ ├── DHCPOptionMessage.cs │ ├── DHCPOptionMessageType.cs │ ├── DHCPOptionNetworkTimeProtocolServers.cs │ ├── DHCPOptionOptionOverload.cs │ ├── DHCPOptionParameterRequestList.cs │ ├── DHCPOptionRebindingTimeValue.cs │ ├── DHCPOptionRenewalTimeValue.cs │ ├── DHCPOptionRequestedIPAddress.cs │ ├── DHCPOptionRouter.cs │ ├── DHCPOptionServerIdentifier.cs │ ├── DHCPOptionServerListBase.cs │ ├── DHCPOptionSubnetMask.cs │ ├── DHCPOptionTFTPServerName.cs │ ├── DHCPOptionVendorClassIdentifier.cs │ └── DHCPOptionVendorSpecificInformation.cs ├── ParseHelper.cs ├── README.md ├── UDPSocket.cs └── Utils.cs ├── LICENSE ├── LICENSE.txt ├── PXE Server.sln ├── PXE Server ├── DHCPServer.cs ├── HttpFileServer.cs ├── IPSegment.cs ├── PXE Server.csproj ├── PXEConfig.cs ├── PXEServer.cs ├── Program.cs ├── TFTPServer.cs ├── Utils.cs ├── pxe.conf └── resources │ └── app.manifest ├── README.md ├── Tftp.Net ├── BlockCounterWrapping.cs ├── Channel │ ├── ITransferChannel.cs │ ├── TransferChannelFactory.cs │ └── UdpChannel.cs ├── Commands │ ├── CommandParser.cs │ ├── CommandSerializer.cs │ ├── Commands.cs │ ├── TftpStreamReader.cs │ ├── TftpStreamWriter.cs │ └── TransferOption.cs ├── ITftpTransfer.cs ├── README.md ├── Tftp.Net.csproj ├── TftpClient.cs ├── TftpServer.cs ├── TftpTransferError.cs ├── TftpTransferProgress.cs ├── Trace │ ├── LoggingStateDecorator.cs │ └── TftpTrace.cs └── Transfer │ ├── InitialStateFactory.cs │ ├── LocalReadTransfer.cs │ ├── LocalWriteTransfer.cs │ ├── RemoteReadTransfer.cs │ ├── RemoteWriteTransfer.cs │ ├── SimpleTimer.cs │ ├── States │ ├── AcknowledgeWriteRequest.cs │ ├── BaseState.cs │ ├── CancelledByUser.cs │ ├── Closed.cs │ ├── ITransferState.cs │ ├── ReceivedError.cs │ ├── Receiving.cs │ ├── SendOptionAcknowledgementBase.cs │ ├── SendOptionAcknowledgementForReadRequest.cs │ ├── SendOptionAcknowledgementForWriteRequest.cs │ ├── SendReadRequest.cs │ ├── SendWriteRequest.cs │ ├── Sending.cs │ ├── StartIncomingRead.cs │ ├── StartIncomingWrite.cs │ ├── StartOutgoingRead.cs │ ├── StartOutgoingWrite.cs │ ├── StateThatExpectsMessagesFromDefaultEndPoint.cs │ └── StateWithNetworkTimeout.cs │ ├── TftpTransfer.cs │ └── TransferOptionSet.cs └── wwwroot ├── minilinux.zip ├── wwwroot_grub2_efi.zip ├── wwwroot_ipxe.zip └── wwwroot_syslinux.zip /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /DHCPServer/AutoPumpQueue.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2010 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.Collections.Generic; 26 | using System.Text; 27 | using System.Threading; 28 | 29 | namespace GitHub.JPMikkers.DHCP 30 | { 31 | public class AutoPumpQueue 32 | { 33 | public delegate void DataDelegate(AutoPumpQueue sender, T data); 34 | 35 | private readonly object m_QueueSync = new object(); 36 | private readonly object m_DispatchSync = new object(); 37 | private readonly Queue m_Queue; 38 | private DataDelegate m_DataDelegate; 39 | 40 | /// 41 | /// Constructor 42 | /// 43 | public AutoPumpQueue(DataDelegate dataDelegate) 44 | { 45 | m_Queue = new Queue(); 46 | m_DataDelegate = dataDelegate; 47 | } 48 | 49 | private void WaitCallback(object state) 50 | { 51 | lock (m_DispatchSync) // ensures individual invokes are serialized 52 | { 53 | bool empty = false; 54 | T data = default(T); 55 | 56 | while (!empty) 57 | { 58 | lock (m_QueueSync) 59 | { 60 | if (m_Queue.Count == 0) 61 | { 62 | // no data 63 | empty = true; 64 | } 65 | else 66 | { 67 | // there are commands; 68 | data = m_Queue.Dequeue(); 69 | empty = false; 70 | } 71 | } 72 | 73 | if (!empty) 74 | { 75 | try 76 | { 77 | m_DataDelegate(this, data); 78 | } 79 | catch 80 | { 81 | } 82 | } 83 | } 84 | } 85 | } 86 | 87 | public void Enqueue(T data) 88 | { 89 | bool queueWasEmpty; 90 | 91 | lock (m_QueueSync) 92 | { 93 | queueWasEmpty = (m_Queue.Count == 0); 94 | m_Queue.Enqueue(data); 95 | } 96 | 97 | if (queueWasEmpty) 98 | { 99 | ThreadPool.QueueUserWorkItem(WaitCallback); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /DHCPServer/DHCPClientInformation.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2010 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.Collections.Generic; 26 | using System.ComponentModel; 27 | using System.IO; 28 | using System.Net; 29 | using System.Text; 30 | using System.Net.Sockets; 31 | using System.Net.NetworkInformation; 32 | //using System.Net.Configuration; 33 | using System.Xml.Serialization; 34 | 35 | namespace GitHub.JPMikkers.DHCP 36 | { 37 | [Serializable()] 38 | public class DHCPClientInformation 39 | { 40 | private List m_Clients = new List(); 41 | 42 | public DateTime TimeStamp 43 | { 44 | get 45 | { 46 | return DateTime.Now; 47 | } 48 | set 49 | { 50 | } 51 | } 52 | 53 | public List Clients 54 | { 55 | get 56 | { 57 | return m_Clients; 58 | } 59 | set 60 | { 61 | m_Clients = value; 62 | } 63 | } 64 | 65 | private static XmlSerializer serializer = new XmlSerializer(typeof(DHCPClientInformation)); 66 | 67 | public static DHCPClientInformation Read(string file) 68 | { 69 | DHCPClientInformation result; 70 | 71 | if (File.Exists(file)) 72 | { 73 | using (Stream s = File.OpenRead(file)) 74 | { 75 | result = (DHCPClientInformation)serializer.Deserialize(s); 76 | } 77 | } 78 | else 79 | { 80 | result = new DHCPClientInformation(); 81 | } 82 | 83 | return result; 84 | } 85 | 86 | public void Write(string file) 87 | { 88 | string dirName = Path.GetDirectoryName(file); 89 | 90 | if (!string.IsNullOrEmpty(dirName) && !Directory.Exists(dirName)) 91 | { 92 | Directory.CreateDirectory(dirName); 93 | } 94 | 95 | using (Stream s = File.Open(file, FileMode.Create, FileAccess.ReadWrite, FileShare.None)) 96 | { 97 | serializer.Serialize(s, this); 98 | s.Flush(); 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /DHCPServer/DHCPServer.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DHCPServer/IDHCPOption.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public interface IDHCPOption 29 | { 30 | bool ZeroTerminatedStrings { get; set; } 31 | TDHCPOption OptionType { get; } 32 | IDHCPOption FromStream(Stream s); 33 | void ToStream(Stream s); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /DHCPServer/IDHCPServer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2010 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.Net; 26 | using System.Collections.Generic; 27 | using System.Text; 28 | using System.Text.RegularExpressions; 29 | using System.Globalization; 30 | 31 | namespace GitHub.JPMikkers.DHCP 32 | { 33 | public class DHCPTraceEventArgs : EventArgs 34 | { 35 | private string m_Message; 36 | public string Message { get { return m_Message; } set { m_Message = value; } } 37 | } 38 | 39 | public class DHCPStopEventArgs : EventArgs 40 | { 41 | private Exception m_Reason; 42 | public Exception Reason { get { return m_Reason; } set { m_Reason = value; } } 43 | } 44 | 45 | public enum OptionMode 46 | { 47 | Default, 48 | Force 49 | } 50 | 51 | public struct OptionItem 52 | { 53 | public OptionMode Mode; 54 | public IDHCPOption Option; 55 | 56 | public OptionItem(OptionMode mode, IDHCPOption option) 57 | { 58 | this.Mode = mode; 59 | this.Option = option; 60 | } 61 | } 62 | 63 | public class ReservationItem 64 | { 65 | private static readonly Regex regex = new Regex(@"^(?([0-9a-fA-F][0-9a-fA-F][:\-\.]?)+)(?/[0-9]+)?", RegexOptions.Compiled | RegexOptions.ExplicitCapture); 66 | private string m_MacTaste; 67 | private byte[] m_Prefix; 68 | private int m_PrefixBits; 69 | 70 | public string MacTaste 71 | { 72 | get { return m_MacTaste; } 73 | 74 | set 75 | { 76 | m_MacTaste = value; 77 | m_Prefix = null; 78 | m_PrefixBits = 0; 79 | 80 | if (!string.IsNullOrWhiteSpace(MacTaste)) 81 | { 82 | try 83 | { 84 | Match match = regex.Match(m_MacTaste); 85 | if (match.Success && match.Groups["mac"].Success) 86 | { 87 | m_Prefix = Utils.HexStringToBytes(match.Groups["mac"].Value); 88 | m_PrefixBits = m_Prefix.Length * 8; 89 | 90 | if (match.Groups["netmask"].Success) 91 | { 92 | m_PrefixBits = Int32.Parse(match.Groups["netmask"].Value.Substring(1)); 93 | } 94 | } 95 | } 96 | catch 97 | { 98 | } 99 | } 100 | } 101 | } 102 | 103 | public string HostName { get; set; } 104 | public IPAddress PoolStart { get; set; } 105 | public IPAddress PoolEnd { get; set; } 106 | public bool Preempt { get; set; } 107 | 108 | private static bool MacMatch(byte[] mac, byte[] prefix, int bits) 109 | { 110 | // prefix should have more bits than masklength 111 | if (((bits + 7) >> 3) > prefix.Length) return false; 112 | // prefix should be shorter or equal to mac address 113 | if (prefix.Length > mac.Length) return false; 114 | for (int t = 0; t < (bits - 7); t += 8) 115 | { 116 | if (mac[t >> 3] != prefix[t >> 3]) return false; 117 | } 118 | 119 | if ((bits & 7) > 0) 120 | { 121 | byte bitMask = (byte)(0xFF00 >> (bits & 7)); 122 | if ((mac[bits >> 3] & bitMask) != (prefix[bits >> 3] & bitMask)) return false; 123 | } 124 | return true; 125 | } 126 | 127 | public bool Match(DHCPMessage message) 128 | { 129 | var client = DHCPClient.CreateFromMessage(message); 130 | 131 | if (!string.IsNullOrWhiteSpace(MacTaste) && m_Prefix!=null) 132 | { 133 | return MacMatch(client.HardwareAddress, m_Prefix, m_PrefixBits); 134 | } 135 | else if (!string.IsNullOrWhiteSpace(HostName)) 136 | { 137 | if (!string.IsNullOrWhiteSpace(client.HostName)) 138 | { 139 | if (client.HostName.StartsWith(HostName,true,CultureInfo.InvariantCulture)) 140 | { 141 | return true; 142 | } 143 | } 144 | } 145 | return false; 146 | } 147 | } 148 | 149 | public interface IDHCPServer : IDisposable 150 | { 151 | event EventHandler OnTrace; 152 | event EventHandler OnStatusChange; 153 | 154 | IPEndPoint EndPoint { get; set; } 155 | IPAddress SubnetMask { get; set; } 156 | IPAddress PoolStart { get; set; } 157 | IPAddress PoolEnd { get; set; } 158 | 159 | TimeSpan OfferExpirationTime { get; set; } 160 | TimeSpan LeaseTime { get; set; } 161 | IList Clients { get; } 162 | string HostName { get; } 163 | bool Active { get; } 164 | List Options { get; set; } 165 | List Reservations { get; set; } 166 | 167 | void Start(); 168 | void Stop(); 169 | } 170 | } -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionBase.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.Collections.Generic; 25 | using System.IO; 26 | 27 | namespace GitHub.JPMikkers.DHCP 28 | { 29 | public enum TDHCPOption 30 | { 31 | // 3: RFC 1497 Vendor Extensions 32 | Pad = 0, 33 | SubnetMask = 1, 34 | TimeOffset = 2, 35 | Router = 3, 36 | TimeServer = 4, 37 | NameServer = 5, 38 | DomainNameServer = 6, 39 | LogServer = 7, 40 | CookieServer = 8, 41 | LPRServer = 9, 42 | ImpressServer = 10, 43 | ResourceLocationServer = 11, 44 | HostName = 12, 45 | BootFileSize = 13, 46 | MeritDumpFile = 14, 47 | DomainName = 15, 48 | SwapServer = 16, 49 | RootPath = 17, 50 | ExtensionPath = 18, 51 | 52 | // 4: IP Layer Parameters per Host 53 | IPForwardingEnable = 19, 54 | NonLocalSourceRoutingEnable = 20, 55 | PolicyFilter = 21, 56 | MaximumDatagramReassembly = 22, 57 | DefaultIPTTL = 23, 58 | PathMTUAgingTimeout = 24, 59 | PathMTUPlateauTable = 25, 60 | 61 | // 5: IP Layer Parameters per Interface 62 | InterfaceMTU = 26, 63 | AllSubnetsAreLocal = 27, 64 | BroadcastAddress = 28, 65 | PerformMaskDiscovery = 29, 66 | MaskSupplier = 30, 67 | PerformRouterDiscovery = 31, 68 | RouterSolicitationAddress = 32, 69 | StaticRoute = 33, 70 | 71 | // 6: Link Layer Parameters per Interface 72 | TrailerEncapsulation = 34, 73 | ARPCacheTimeout = 35, 74 | EthernetEncapsulation = 36, 75 | 76 | // 7: TCP Parameters 77 | TCPDefaultTTL = 37, 78 | TCPKeepaliveInterval = 38, 79 | TCPKeepaliveGarbage = 39, 80 | 81 | // 8: Application and Service parameters 82 | NetworkInformationServiceDomain = 40, 83 | NetworkInformationServiceServers = 41, 84 | NetworkTimeProtocolServers = 42, 85 | VendorSpecificInformation = 43, 86 | NetBIOSOverTCPIPNameServer = 44, 87 | NetBIOSOverTCPIPDatagramDistributionServer = 45, 88 | NetBIOSOverTCPIPNodeType = 46, 89 | NetBIOSOverTCPIPScope = 47, 90 | XWindowSystemFontServer = 48, 91 | XWindowSystemDisplayManager = 49, 92 | NetworkInformationServicePlusDomain = 64, 93 | NetworkInformationServicePlusServers = 65, 94 | MobileIPHomeAgent = 68, 95 | SimpleMailTransportProtocolServer = 69, 96 | PostOfficeProtocolServer = 70, 97 | NetworkNewsTransportProtocolServer = 71, 98 | DefaultWorldWideWebServer = 72, 99 | DefaultFingerServer = 73, 100 | DefaultInternetRelayChat = 74, 101 | StreetTalkServer = 75, 102 | StreetTalkDirectoryAssistanceServer = 76, 103 | 104 | // 9: DHCP Extensions 105 | RequestedIPAddress = 50, // this option is used in a client request to allow the client to request a particular IP address to be assigned 106 | IPAddressLeaseTime = 51, 107 | OptionOverload = 52, 108 | MessageType = 53, 109 | ServerIdentifier = 54, 110 | ParameterRequestList = 55, 111 | Message = 56, 112 | MaximumDHCPMessageSize = 57, 113 | RenewalTimeValue = 58, 114 | RebindingTimeValue = 59, 115 | VendorClassIdentifier = 60, 116 | ClientIdentifier = 61, 117 | TFTPServerName = 66, 118 | BootFileName = 67, 119 | 120 | FullyQualifiedDomainName = 81, // RFC4702 121 | 122 | ClientSystemArchitectureType = 93, // RFC4578 123 | ClientNetworkInterfaceIdentifier = 94, // RFC4578 124 | ClientMachineIdentifier = 97, // RFC4578 125 | 126 | AutoConfigure = 116, // RFC2563 127 | ClasslessStaticRoutesA = 121, // RFC3442 128 | 129 | /* 130 | 128 TFPT Server IP address // RFC 4578 131 | 129 Call Server IP address // RFC 4578 132 | 130 Discrimination string // RFC 4578 133 | 131 Remote statistics server IP address // RFC 4578 134 | 132 802.1P VLAN ID 135 | 133 802.1Q L2 Priority 136 | 134 Diffserv Code Point 137 | 135 HTTP Proxy for phone-specific applications 138 | */ 139 | 140 | ClasslessStaticRoutesB = 249, 141 | 142 | End = 255, 143 | } 144 | 145 | public abstract class DHCPOptionBase : IDHCPOption 146 | { 147 | protected TDHCPOption m_OptionType; 148 | 149 | public TDHCPOption OptionType 150 | { 151 | get 152 | { 153 | return m_OptionType; 154 | } 155 | } 156 | 157 | public bool ZeroTerminatedStrings { get; set; } 158 | 159 | public abstract IDHCPOption FromStream(Stream s); 160 | public abstract void ToStream(Stream s); 161 | 162 | protected DHCPOptionBase(TDHCPOption optionType) 163 | { 164 | m_OptionType = optionType; 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionBootFileName.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionBootFileName : DHCPOptionBase 29 | { 30 | private string m_Name; 31 | 32 | #region IDHCPOption Members 33 | 34 | public string Name 35 | { 36 | get 37 | { 38 | return m_Name; 39 | } 40 | } 41 | 42 | public override IDHCPOption FromStream(Stream s) 43 | { 44 | DHCPOptionBootFileName result = new DHCPOptionBootFileName(); 45 | result.m_Name = ParseHelper.ReadString(s); 46 | return result; 47 | } 48 | 49 | public override void ToStream(Stream s) 50 | { 51 | ParseHelper.WriteString(s, ZeroTerminatedStrings, m_Name); 52 | } 53 | 54 | #endregion 55 | 56 | public DHCPOptionBootFileName() 57 | : base(TDHCPOption.BootFileName) 58 | { 59 | } 60 | 61 | public DHCPOptionBootFileName(string name) 62 | : base(TDHCPOption.BootFileName) 63 | { 64 | m_Name = name; 65 | } 66 | 67 | public override string ToString() 68 | { 69 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_Name); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionClientIdentifier.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionClientIdentifier : DHCPOptionBase 29 | { 30 | private DHCPMessage.THardwareType m_HardwareType; 31 | private byte[] m_Data; 32 | 33 | public DHCPMessage.THardwareType HardwareType 34 | { 35 | get { return m_HardwareType; } 36 | set { m_HardwareType = value; } 37 | } 38 | 39 | public byte[] Data 40 | { 41 | get { return m_Data; } 42 | set { m_Data = value; } 43 | } 44 | 45 | #region IDHCPOption Members 46 | 47 | public override IDHCPOption FromStream(Stream s) 48 | { 49 | DHCPOptionClientIdentifier result = new DHCPOptionClientIdentifier(); 50 | m_HardwareType = (DHCPMessage.THardwareType)ParseHelper.ReadUInt8(s); 51 | result.m_Data = new byte[s.Length - s.Position]; 52 | s.Read(result.m_Data, 0, result.m_Data.Length); 53 | return result; 54 | } 55 | 56 | public override void ToStream(Stream s) 57 | { 58 | ParseHelper.WriteUInt8(s, (byte)m_HardwareType); 59 | s.Write(m_Data, 0, m_Data.Length); 60 | } 61 | 62 | #endregion 63 | 64 | public DHCPOptionClientIdentifier() 65 | : base(TDHCPOption.ClientIdentifier) 66 | { 67 | m_HardwareType = DHCPMessage.THardwareType.Unknown; 68 | m_Data = new byte[0]; 69 | } 70 | 71 | public DHCPOptionClientIdentifier(DHCPMessage.THardwareType hardwareType,byte[] data) 72 | : base(TDHCPOption.ClientIdentifier) 73 | { 74 | m_HardwareType = hardwareType; 75 | m_Data = data; 76 | } 77 | 78 | public override string ToString() 79 | { 80 | return string.Format("Option(name=[{0}],htype=[{1}],value=[{2}])", OptionType, m_HardwareType, Utils.BytesToHexString(m_Data," ")); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionDomainNameServer.cs: -------------------------------------------------------------------------------- 1 | namespace GitHub.JPMikkers.DHCP 2 | { 3 | public class DHCPOptionDomainNameServer : DHCPOptionServerListBase 4 | { 5 | public override DHCPOptionServerListBase Create() 6 | { 7 | return new DHCPOptionDomainNameServer(); 8 | } 9 | 10 | public DHCPOptionDomainNameServer() : base(TDHCPOption.DomainNameServer) 11 | { 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionFixedLength.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionFixedLength : DHCPOptionBase 29 | { 30 | #region IDHCPOption Members 31 | 32 | public override IDHCPOption FromStream(Stream s) 33 | { 34 | return this; 35 | } 36 | 37 | public override void ToStream(Stream s) 38 | { 39 | } 40 | 41 | #endregion 42 | 43 | public DHCPOptionFixedLength(TDHCPOption option) : base(option) 44 | { 45 | } 46 | 47 | public override string ToString() 48 | { 49 | return string.Format("Option(name=[{0}],value=[])", m_OptionType); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionFullyQualifiedDomainName.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionFullyQualifiedDomainName : DHCPOptionBase 29 | { 30 | private byte[] m_Data; 31 | 32 | public byte[] Data 33 | { 34 | get { return m_Data; } 35 | set { m_Data = value; } 36 | } 37 | 38 | #region IDHCPOption Members 39 | 40 | public override IDHCPOption FromStream(Stream s) 41 | { 42 | DHCPOptionFullyQualifiedDomainName result = new DHCPOptionFullyQualifiedDomainName(); 43 | result.m_Data = new byte[s.Length]; 44 | s.Read(result.m_Data, 0, result.m_Data.Length); 45 | return result; 46 | } 47 | 48 | public override void ToStream(Stream s) 49 | { 50 | s.Write(m_Data, 0, m_Data.Length); 51 | } 52 | 53 | #endregion 54 | 55 | public DHCPOptionFullyQualifiedDomainName() 56 | : base(TDHCPOption.FullyQualifiedDomainName) 57 | { 58 | m_Data = new byte[0]; 59 | } 60 | 61 | public override string ToString() 62 | { 63 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, Utils.BytesToHexString(m_Data, " ")); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionGeneric.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionGeneric : DHCPOptionBase 29 | { 30 | private byte[] m_Data; 31 | 32 | public byte[] Data 33 | { 34 | get { return m_Data; } 35 | set { m_Data = value; } 36 | } 37 | 38 | #region IDHCPOption Members 39 | 40 | public override IDHCPOption FromStream(Stream s) 41 | { 42 | DHCPOptionGeneric result = new DHCPOptionGeneric(m_OptionType); 43 | result.m_Data = new byte[s.Length]; 44 | s.Read(result.m_Data, 0, result.m_Data.Length); 45 | return result; 46 | } 47 | 48 | public override void ToStream(Stream s) 49 | { 50 | s.Write(m_Data, 0, m_Data.Length); 51 | } 52 | 53 | #endregion 54 | 55 | public DHCPOptionGeneric(TDHCPOption option) : base(option) 56 | { 57 | m_Data = new byte[0]; 58 | } 59 | 60 | public DHCPOptionGeneric(TDHCPOption option, byte[] data) : base(option) 61 | { 62 | m_Data = data; 63 | } 64 | 65 | public override string ToString() 66 | { 67 | return string.Format("Option(name=[{0}],value=[{1}])", m_OptionType, Utils.BytesToHexString(m_Data," ")); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionHostName.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionHostName : DHCPOptionBase 29 | { 30 | private string m_HostName; 31 | 32 | #region IDHCPOption Members 33 | 34 | public string HostName 35 | { 36 | get 37 | { 38 | return m_HostName; 39 | } 40 | } 41 | 42 | public override IDHCPOption FromStream(Stream s) 43 | { 44 | DHCPOptionHostName result = new DHCPOptionHostName(); 45 | result.m_HostName = ParseHelper.ReadString(s); 46 | return result; 47 | } 48 | 49 | public override void ToStream(Stream s) 50 | { 51 | ParseHelper.WriteString(s, ZeroTerminatedStrings, m_HostName); 52 | } 53 | 54 | #endregion 55 | 56 | public DHCPOptionHostName() 57 | : base(TDHCPOption.HostName) 58 | { 59 | } 60 | 61 | public DHCPOptionHostName(string hostName) 62 | : base(TDHCPOption.HostName) 63 | { 64 | m_HostName = hostName; 65 | } 66 | 67 | public override string ToString() 68 | { 69 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_HostName); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionIPAddressLeaseTime.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.IO; 26 | 27 | namespace GitHub.JPMikkers.DHCP 28 | { 29 | public class DHCPOptionIPAddressLeaseTime : DHCPOptionBase 30 | { 31 | private TimeSpan m_LeaseTime; 32 | 33 | #region IDHCPOption Members 34 | 35 | public TimeSpan LeaseTime 36 | { 37 | get 38 | { 39 | return m_LeaseTime; 40 | } 41 | } 42 | 43 | public override IDHCPOption FromStream(Stream s) 44 | { 45 | DHCPOptionIPAddressLeaseTime result = new DHCPOptionIPAddressLeaseTime(); 46 | if (s.Length != 4) throw new IOException("Invalid DHCP option length"); 47 | result.m_LeaseTime = TimeSpan.FromSeconds(ParseHelper.ReadUInt32(s)); 48 | return result; 49 | } 50 | 51 | public override void ToStream(Stream s) 52 | { 53 | ParseHelper.WriteUInt32(s, (uint)m_LeaseTime.TotalSeconds); 54 | } 55 | 56 | #endregion 57 | 58 | public DHCPOptionIPAddressLeaseTime() 59 | : base(TDHCPOption.IPAddressLeaseTime) 60 | { 61 | } 62 | 63 | public DHCPOptionIPAddressLeaseTime(TimeSpan leaseTime) 64 | : base(TDHCPOption.IPAddressLeaseTime) 65 | { 66 | m_LeaseTime = leaseTime; 67 | if (m_LeaseTime > Utils.InfiniteTimeSpan) 68 | { 69 | m_LeaseTime = Utils.InfiniteTimeSpan; 70 | } 71 | } 72 | 73 | public override string ToString() 74 | { 75 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_LeaseTime == Utils.InfiniteTimeSpan ? "Infinite" : m_LeaseTime.ToString()); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionMaximumDHCPMessageSize.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionMaximumDHCPMessageSize : DHCPOptionBase 29 | { 30 | private ushort m_MaxSize; 31 | 32 | #region IDHCPOption Members 33 | 34 | public ushort MaxSize 35 | { 36 | get 37 | { 38 | return m_MaxSize; 39 | } 40 | } 41 | 42 | public override IDHCPOption FromStream(Stream s) 43 | { 44 | DHCPOptionMaximumDHCPMessageSize result = new DHCPOptionMaximumDHCPMessageSize(); 45 | if (s.Length != 2) throw new IOException("Invalid DHCP option length"); 46 | result.m_MaxSize = ParseHelper.ReadUInt16(s); 47 | return result; 48 | } 49 | 50 | public override void ToStream(Stream s) 51 | { 52 | ParseHelper.WriteUInt16(s,m_MaxSize); 53 | } 54 | 55 | #endregion 56 | 57 | public DHCPOptionMaximumDHCPMessageSize() 58 | : base(TDHCPOption.MaximumDHCPMessageSize) 59 | { 60 | } 61 | 62 | public DHCPOptionMaximumDHCPMessageSize(ushort maxSize) 63 | : base(TDHCPOption.MaximumDHCPMessageSize) 64 | { 65 | m_MaxSize = maxSize; 66 | } 67 | 68 | public override string ToString() 69 | { 70 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_MaxSize); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionMessage.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionMessage : DHCPOptionBase 29 | { 30 | private string m_Message; 31 | 32 | #region IDHCPOption Members 33 | 34 | public string Message 35 | { 36 | get 37 | { 38 | return m_Message; 39 | } 40 | } 41 | 42 | public override IDHCPOption FromStream(Stream s) 43 | { 44 | DHCPOptionMessage result = new DHCPOptionMessage(); 45 | result.m_Message = ParseHelper.ReadString(s); 46 | return result; 47 | } 48 | 49 | public override void ToStream(Stream s) 50 | { 51 | ParseHelper.WriteString(s, ZeroTerminatedStrings, m_Message); 52 | } 53 | 54 | #endregion 55 | 56 | public DHCPOptionMessage() 57 | : base(TDHCPOption.Message) 58 | { 59 | } 60 | 61 | public DHCPOptionMessage(string message) 62 | : base(TDHCPOption.Message) 63 | { 64 | m_Message = message; 65 | } 66 | 67 | public override string ToString() 68 | { 69 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_Message); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionMessageType.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public enum TDHCPMessageType 29 | { 30 | DISCOVER = 1, 31 | OFFER, 32 | REQUEST, 33 | DECLINE, 34 | ACK, 35 | NAK, 36 | RELEASE, 37 | INFORM, 38 | Undefined 39 | } 40 | 41 | public class DHCPOptionMessageType : DHCPOptionBase 42 | { 43 | private TDHCPMessageType m_MessageType; 44 | 45 | #region IDHCPOption Members 46 | 47 | public TDHCPMessageType MessageType 48 | { 49 | get 50 | { 51 | return m_MessageType; 52 | } 53 | } 54 | 55 | public override IDHCPOption FromStream(Stream s) 56 | { 57 | DHCPOptionMessageType result = new DHCPOptionMessageType(); 58 | if (s.Length != 1) throw new IOException("Invalid DHCP option length"); 59 | result.m_MessageType = (TDHCPMessageType)s.ReadByte(); 60 | return result; 61 | } 62 | 63 | public override void ToStream(Stream s) 64 | { 65 | s.WriteByte((byte)m_MessageType); 66 | } 67 | 68 | #endregion 69 | 70 | public DHCPOptionMessageType() 71 | : base(TDHCPOption.MessageType) 72 | { 73 | } 74 | 75 | public DHCPOptionMessageType(TDHCPMessageType messageType) 76 | : base(TDHCPOption.MessageType) 77 | { 78 | m_MessageType = messageType; 79 | } 80 | 81 | public override string ToString() 82 | { 83 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_MessageType); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionNetworkTimeProtocolServers.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | namespace GitHub.JPMikkers.DHCP 25 | { 26 | public class DHCPOptionNetworkTimeProtocolServers : DHCPOptionServerListBase 27 | { 28 | public override DHCPOptionServerListBase Create() 29 | { 30 | return new DHCPOptionNetworkTimeProtocolServers(); 31 | } 32 | 33 | public DHCPOptionNetworkTimeProtocolServers() : base(TDHCPOption.NetworkTimeProtocolServers) 34 | { 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionOptionOverload.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionOptionOverload : DHCPOptionBase 29 | { 30 | private byte m_Overload; 31 | 32 | #region IDHCPOption Members 33 | 34 | public byte Overload 35 | { 36 | get 37 | { 38 | return m_Overload; 39 | } 40 | } 41 | 42 | public override IDHCPOption FromStream(Stream s) 43 | { 44 | DHCPOptionOptionOverload result = new DHCPOptionOptionOverload(); 45 | if (s.Length != 1) throw new IOException("Invalid DHCP option length"); 46 | result.m_Overload = (byte)s.ReadByte(); 47 | return result; 48 | } 49 | 50 | public override void ToStream(Stream s) 51 | { 52 | s.WriteByte(m_Overload); 53 | } 54 | 55 | #endregion 56 | 57 | public DHCPOptionOptionOverload() 58 | : base(TDHCPOption.OptionOverload) 59 | { 60 | } 61 | 62 | public DHCPOptionOptionOverload(byte overload) 63 | : base(TDHCPOption.OptionOverload) 64 | { 65 | m_Overload = overload; 66 | } 67 | 68 | public override string ToString() 69 | { 70 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_Overload); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionParameterRequestList.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.Collections.Generic; 25 | using System.IO; 26 | using System.Text; 27 | 28 | namespace GitHub.JPMikkers.DHCP 29 | { 30 | public class DHCPOptionParameterRequestList : DHCPOptionBase 31 | { 32 | private List m_RequestList = new List(); 33 | 34 | #region IDHCPOption Members 35 | 36 | public List RequestList 37 | { 38 | get 39 | { 40 | return m_RequestList; 41 | } 42 | } 43 | 44 | public override IDHCPOption FromStream(Stream s) 45 | { 46 | DHCPOptionParameterRequestList result = new DHCPOptionParameterRequestList(); 47 | while(true) 48 | { 49 | int c = s.ReadByte(); 50 | if(c<0) break; 51 | result.m_RequestList.Add((TDHCPOption)c); 52 | } 53 | return result; 54 | } 55 | 56 | public override void ToStream(Stream s) 57 | { 58 | foreach(TDHCPOption opt in m_RequestList) 59 | { 60 | s.WriteByte((byte)opt); 61 | } 62 | } 63 | 64 | #endregion 65 | 66 | public DHCPOptionParameterRequestList() 67 | : base(TDHCPOption.ParameterRequestList) 68 | { 69 | } 70 | 71 | public override string ToString() 72 | { 73 | StringBuilder sb = new StringBuilder(); 74 | foreach(TDHCPOption opt in m_RequestList) 75 | { 76 | sb.Append(opt.ToString()); 77 | sb.Append(","); 78 | } 79 | if(m_RequestList.Count>0) sb.Remove(sb.Length-1,1); 80 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, sb.ToString()); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionRebindingTimeValue.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.IO; 26 | 27 | namespace GitHub.JPMikkers.DHCP 28 | { 29 | public class DHCPOptionRebindingTimeValue : DHCPOptionBase 30 | { 31 | private TimeSpan m_TimeSpan; 32 | 33 | #region IDHCPOption Members 34 | 35 | public TimeSpan TimeSpan 36 | { 37 | get 38 | { 39 | return m_TimeSpan; 40 | } 41 | } 42 | 43 | public override IDHCPOption FromStream(Stream s) 44 | { 45 | DHCPOptionRebindingTimeValue result = new DHCPOptionRebindingTimeValue(); 46 | if (s.Length != 4) throw new IOException("Invalid DHCP option length"); 47 | result.m_TimeSpan = TimeSpan.FromSeconds(ParseHelper.ReadUInt32(s)); 48 | return result; 49 | } 50 | 51 | public override void ToStream(Stream s) 52 | { 53 | ParseHelper.WriteUInt32(s, (uint)m_TimeSpan.TotalSeconds); 54 | } 55 | 56 | #endregion 57 | 58 | public DHCPOptionRebindingTimeValue() 59 | : base(TDHCPOption.RebindingTimeValue) 60 | { 61 | } 62 | 63 | public DHCPOptionRebindingTimeValue(TimeSpan timeSpan) 64 | : base(TDHCPOption.RebindingTimeValue) 65 | { 66 | m_TimeSpan = timeSpan; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_TimeSpan.ToString()); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionRenewalTimeValue.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.IO; 26 | 27 | namespace GitHub.JPMikkers.DHCP 28 | { 29 | public class DHCPOptionRenewalTimeValue : DHCPOptionBase 30 | { 31 | private TimeSpan m_TimeSpan; 32 | 33 | #region IDHCPOption Members 34 | 35 | public TimeSpan TimeSpan 36 | { 37 | get 38 | { 39 | return m_TimeSpan; 40 | } 41 | } 42 | 43 | public override IDHCPOption FromStream(Stream s) 44 | { 45 | DHCPOptionRenewalTimeValue result = new DHCPOptionRenewalTimeValue(); 46 | if (s.Length != 4) throw new IOException("Invalid DHCP option length"); 47 | result.m_TimeSpan = TimeSpan.FromSeconds(ParseHelper.ReadUInt32(s)); 48 | return result; 49 | } 50 | 51 | public override void ToStream(Stream s) 52 | { 53 | ParseHelper.WriteUInt32(s, (uint)m_TimeSpan.TotalSeconds); 54 | } 55 | 56 | #endregion 57 | 58 | public DHCPOptionRenewalTimeValue() 59 | : base(TDHCPOption.RenewalTimeValue) 60 | { 61 | } 62 | 63 | public DHCPOptionRenewalTimeValue(TimeSpan timeSpan) 64 | : base(TDHCPOption.RenewalTimeValue) 65 | { 66 | m_TimeSpan = timeSpan; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_TimeSpan.ToString()); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionRequestedIPAddress.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | using System.Net; 26 | 27 | namespace GitHub.JPMikkers.DHCP 28 | { 29 | public class DHCPOptionRequestedIPAddress : DHCPOptionBase 30 | { 31 | private IPAddress m_IPAddress; 32 | 33 | #region IDHCPOption Members 34 | 35 | public IPAddress IPAddress 36 | { 37 | get 38 | { 39 | return m_IPAddress; 40 | } 41 | } 42 | 43 | public override IDHCPOption FromStream(Stream s) 44 | { 45 | DHCPOptionRequestedIPAddress result = new DHCPOptionRequestedIPAddress(); 46 | if (s.Length != 4) throw new IOException("Invalid DHCP option length"); 47 | result.m_IPAddress = ParseHelper.ReadIPAddress(s); 48 | return result; 49 | } 50 | 51 | public override void ToStream(Stream s) 52 | { 53 | ParseHelper.WriteIPAddress(s, m_IPAddress); 54 | } 55 | 56 | #endregion 57 | 58 | public DHCPOptionRequestedIPAddress() 59 | : base(TDHCPOption.RequestedIPAddress) 60 | { 61 | } 62 | 63 | public DHCPOptionRequestedIPAddress(IPAddress ipAddress) 64 | : base(TDHCPOption.RequestedIPAddress) 65 | { 66 | m_IPAddress = ipAddress; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_IPAddress.ToString()); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionRouter.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.Collections.Generic; 26 | using System.Linq; 27 | using System.Text; 28 | using System.Threading.Tasks; 29 | 30 | namespace GitHub.JPMikkers.DHCP 31 | { 32 | public class DHCPOptionRouter : DHCPOptionServerListBase 33 | { 34 | public override DHCPOptionServerListBase Create() 35 | { 36 | return new DHCPOptionRouter(); 37 | } 38 | 39 | public DHCPOptionRouter() : base(TDHCPOption.Router) 40 | { 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionServerIdentifier.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | using System.Net; 26 | 27 | namespace GitHub.JPMikkers.DHCP 28 | { 29 | public class DHCPOptionServerIdentifier : DHCPOptionBase 30 | { 31 | private IPAddress m_IPAddress; 32 | 33 | public IPAddress IPAddress 34 | { 35 | get 36 | { 37 | return m_IPAddress; 38 | } 39 | } 40 | 41 | #region IDHCPOption Members 42 | 43 | public override IDHCPOption FromStream(Stream s) 44 | { 45 | DHCPOptionServerIdentifier result = new DHCPOptionServerIdentifier(); 46 | if (s.Length != 4) throw new IOException("Invalid DHCP option length"); 47 | result.m_IPAddress = ParseHelper.ReadIPAddress(s); 48 | return result; 49 | } 50 | 51 | public override void ToStream(Stream s) 52 | { 53 | ParseHelper.WriteIPAddress(s, m_IPAddress); 54 | } 55 | 56 | #endregion 57 | 58 | public DHCPOptionServerIdentifier() 59 | : base(TDHCPOption.ServerIdentifier) 60 | { 61 | } 62 | 63 | public DHCPOptionServerIdentifier(IPAddress ipAddress) 64 | : base(TDHCPOption.ServerIdentifier) 65 | { 66 | m_IPAddress = ipAddress; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_IPAddress.ToString()); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionServerListBase.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.Collections.Generic; 25 | using System.IO; 26 | using System.Linq; 27 | using System.Net; 28 | 29 | namespace GitHub.JPMikkers.DHCP 30 | { 31 | public abstract class DHCPOptionServerListBase : DHCPOptionBase 32 | { 33 | private List m_IPAddresses = new List(); 34 | 35 | public IEnumerable IPAddresses 36 | { 37 | get 38 | { 39 | return m_IPAddresses; 40 | } 41 | set 42 | { 43 | m_IPAddresses = value.ToList(); 44 | } 45 | } 46 | 47 | public abstract DHCPOptionServerListBase Create(); 48 | 49 | #region IDHCPOption Members 50 | 51 | public override IDHCPOption FromStream(Stream s) 52 | { 53 | if (s.Length % 4 != 0) throw new IOException("Invalid DHCP option length"); 54 | 55 | var result = Create(); 56 | 57 | for(int t=0;t x.ToString()))}])"; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionSubnetMask.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | using System.Net; 26 | 27 | namespace GitHub.JPMikkers.DHCP 28 | { 29 | public class DHCPOptionSubnetMask : DHCPOptionBase 30 | { 31 | private IPAddress m_SubnetMask; 32 | 33 | #region IDHCPOption Members 34 | 35 | public IPAddress SubnetMask 36 | { 37 | get 38 | { 39 | return m_SubnetMask; 40 | } 41 | } 42 | 43 | public override IDHCPOption FromStream(Stream s) 44 | { 45 | DHCPOptionSubnetMask result = new DHCPOptionSubnetMask(); 46 | if (s.Length != 4) throw new IOException("Invalid DHCP option length"); 47 | result.m_SubnetMask = ParseHelper.ReadIPAddress(s); 48 | return result; 49 | } 50 | 51 | public override void ToStream(Stream s) 52 | { 53 | ParseHelper.WriteIPAddress(s, m_SubnetMask); 54 | } 55 | 56 | #endregion 57 | 58 | public DHCPOptionSubnetMask() 59 | : base(TDHCPOption.SubnetMask) 60 | { 61 | } 62 | 63 | public DHCPOptionSubnetMask(IPAddress subnetMask) 64 | : base(TDHCPOption.SubnetMask) 65 | { 66 | m_SubnetMask = subnetMask; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_SubnetMask.ToString()); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionTFTPServerName.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionTFTPServerName : DHCPOptionBase 29 | { 30 | private string m_Name; 31 | 32 | #region IDHCPOption Members 33 | 34 | public string Name 35 | { 36 | get 37 | { 38 | return m_Name; 39 | } 40 | } 41 | 42 | public override IDHCPOption FromStream(Stream s) 43 | { 44 | DHCPOptionTFTPServerName result = new DHCPOptionTFTPServerName(); 45 | result.m_Name = ParseHelper.ReadString(s); 46 | return result; 47 | } 48 | 49 | public override void ToStream(Stream s) 50 | { 51 | ParseHelper.WriteString(s, ZeroTerminatedStrings, m_Name); 52 | } 53 | 54 | #endregion 55 | 56 | public DHCPOptionTFTPServerName() 57 | : base(TDHCPOption.TFTPServerName) 58 | { 59 | } 60 | 61 | public DHCPOptionTFTPServerName(string name) 62 | : base(TDHCPOption.TFTPServerName) 63 | { 64 | m_Name = name; 65 | } 66 | 67 | public override string ToString() 68 | { 69 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, m_Name); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionVendorClassIdentifier.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionVendorClassIdentifier : DHCPOptionBase 29 | { 30 | private byte[] m_Data; 31 | 32 | public byte[] Data 33 | { 34 | get { return m_Data; } 35 | set { m_Data = value; } 36 | } 37 | 38 | #region IDHCPOption Members 39 | 40 | public override IDHCPOption FromStream(Stream s) 41 | { 42 | DHCPOptionVendorClassIdentifier result = new DHCPOptionVendorClassIdentifier(); 43 | result.m_Data = new byte[s.Length]; 44 | s.Read(result.m_Data, 0, result.m_Data.Length); 45 | return result; 46 | } 47 | 48 | public override void ToStream(Stream s) 49 | { 50 | s.Write(m_Data, 0, m_Data.Length); 51 | } 52 | 53 | #endregion 54 | 55 | public DHCPOptionVendorClassIdentifier() 56 | : base(TDHCPOption.VendorClassIdentifier) 57 | { 58 | m_Data = new byte[0]; 59 | } 60 | 61 | public DHCPOptionVendorClassIdentifier(byte[] data) 62 | : base(TDHCPOption.VendorClassIdentifier) 63 | { 64 | m_Data = data; 65 | } 66 | 67 | public override string ToString() 68 | { 69 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, Utils.BytesToHexString(m_Data," ")); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /DHCPServer/Options/DHCPOptionVendorSpecificInformation.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2020 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System.IO; 25 | 26 | namespace GitHub.JPMikkers.DHCP 27 | { 28 | public class DHCPOptionVendorSpecificInformation : DHCPOptionBase 29 | { 30 | private byte[] m_Data; 31 | 32 | public byte[] Data 33 | { 34 | get { return m_Data; } 35 | set { m_Data = value; } 36 | } 37 | 38 | #region IDHCPOption Members 39 | 40 | public override IDHCPOption FromStream(Stream s) 41 | { 42 | DHCPOptionVendorSpecificInformation result = new DHCPOptionVendorSpecificInformation(); 43 | result.m_Data = new byte[s.Length]; 44 | s.Read(result.m_Data, 0, result.m_Data.Length); 45 | return result; 46 | } 47 | 48 | public override void ToStream(Stream s) 49 | { 50 | s.Write(m_Data, 0, m_Data.Length); 51 | } 52 | 53 | #endregion 54 | 55 | public DHCPOptionVendorSpecificInformation() 56 | : base(TDHCPOption.VendorSpecificInformation) 57 | { 58 | m_Data = new byte[0]; 59 | } 60 | 61 | public DHCPOptionVendorSpecificInformation(byte[] data) 62 | : base(TDHCPOption.VendorSpecificInformation) 63 | { 64 | m_Data = data; 65 | } 66 | 67 | public DHCPOptionVendorSpecificInformation(string data) 68 | : base(TDHCPOption.VendorSpecificInformation) 69 | { 70 | MemoryStream ms = new MemoryStream(); 71 | ParseHelper.WriteString(ms, ZeroTerminatedStrings, data); 72 | ms.Flush(); 73 | m_Data = ms.ToArray(); 74 | } 75 | 76 | public override string ToString() 77 | { 78 | return string.Format("Option(name=[{0}],value=[{1}])", OptionType, Utils.BytesToHexString(m_Data, " ")); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /DHCPServer/ParseHelper.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2010 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.Collections.Generic; 26 | using System.IO; 27 | using System.Net; 28 | using System.Text; 29 | 30 | namespace GitHub.JPMikkers.DHCP 31 | { 32 | public class ParseHelper 33 | { 34 | public static IPAddress ReadIPAddress(Stream s) 35 | { 36 | byte[] bytes = new byte[4]; 37 | s.Read(bytes, 0, bytes.Length); 38 | return new IPAddress(bytes); 39 | } 40 | 41 | public static void WriteIPAddress(Stream s, IPAddress v) 42 | { 43 | byte[] bytes = v.GetAddressBytes(); 44 | s.Write(bytes, 0, bytes.Length); 45 | } 46 | 47 | public static byte ReadUInt8(Stream s) 48 | { 49 | BinaryReader br = new BinaryReader(s); 50 | return br.ReadByte(); 51 | } 52 | 53 | public static void WriteUInt8(Stream s, byte v) 54 | { 55 | BinaryWriter bw = new BinaryWriter(s); 56 | bw.Write(v); 57 | } 58 | 59 | public static ushort ReadUInt16(Stream s) 60 | { 61 | BinaryReader br = new BinaryReader(s); 62 | return (ushort)IPAddress.NetworkToHostOrder((short)br.ReadUInt16()); 63 | } 64 | 65 | public static void WriteUInt16(Stream s, ushort v) 66 | { 67 | BinaryWriter bw = new BinaryWriter(s); 68 | bw.Write((ushort)IPAddress.HostToNetworkOrder((short)v)); 69 | } 70 | 71 | public static uint ReadUInt32(Stream s) 72 | { 73 | BinaryReader br = new BinaryReader(s); 74 | return (uint)IPAddress.NetworkToHostOrder((int)br.ReadUInt32()); 75 | } 76 | 77 | public static void WriteUInt32(Stream s, uint v) 78 | { 79 | BinaryWriter bw = new BinaryWriter(s); 80 | bw.Write((uint)IPAddress.HostToNetworkOrder((int)v)); 81 | } 82 | 83 | public static string ReadZString(Stream s) 84 | { 85 | StringBuilder sb = new StringBuilder(); 86 | int c = s.ReadByte(); 87 | while (c>0) 88 | { 89 | sb.Append((char)c); 90 | c = s.ReadByte(); 91 | } 92 | return sb.ToString(); 93 | } 94 | 95 | public static void WriteZString(Stream s, string msg) 96 | { 97 | TextWriter tw = new StreamWriter(s, Encoding.ASCII); 98 | tw.Write(msg); 99 | tw.Flush(); 100 | s.WriteByte(0); 101 | } 102 | 103 | public static void WriteZString(Stream s, string msg, int length) 104 | { 105 | if (msg.Length >= length) 106 | { 107 | msg = msg.Substring(0, length - 1); 108 | } 109 | 110 | TextWriter tw = new StreamWriter(s, Encoding.ASCII); 111 | tw.Write(msg); 112 | tw.Flush(); 113 | 114 | // write terminating and padding zero's 115 | for (int t = msg.Length; t < length; t++) 116 | { 117 | s.WriteByte(0); 118 | } 119 | } 120 | 121 | public static string ReadString(Stream s, int maxLength) 122 | { 123 | StringBuilder sb = new StringBuilder(); 124 | int c = s.ReadByte(); 125 | while (c > 0 && sb.Length < maxLength) 126 | { 127 | sb.Append((char)c); 128 | c = s.ReadByte(); 129 | } 130 | return sb.ToString(); 131 | } 132 | 133 | public static string ReadString(Stream s) 134 | { 135 | return ReadString(s, 16*1024); 136 | } 137 | 138 | public static void WriteString(Stream s, string msg) 139 | { 140 | WriteString(s, false, msg); 141 | } 142 | 143 | public static void WriteString(Stream s, bool zeroTerminated, string msg) 144 | { 145 | TextWriter tw = new StreamWriter(s, Encoding.ASCII); 146 | tw.Write(msg); 147 | tw.Flush(); 148 | if(zeroTerminated) s.WriteByte(0); 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /DHCPServer/README.md: -------------------------------------------------------------------------------- 1 | # DHCPServer 2 | 3 | Open source managed ipv4 DHCP server implementation, written in C#. With extensive support for DHCP options it is ideally suited for configuring and netbooting local systems such as PLCs and blade racks. The permissive MIT license enables free commercial use and distribution. 4 | 5 | Check out the wiki for more details on how to embed the DHCP server in your application. 6 | 7 | https://github.com/jpmikkers/DHCPServer 8 | MIT License -------------------------------------------------------------------------------- /DHCPServer/Utils.cs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2010 Jean-Paul Mikkers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | using System; 25 | using System.Collections.Generic; 26 | using System.Net; 27 | using System.Net.NetworkInformation; 28 | using System.Net.Sockets; 29 | using System.Text; 30 | using System.IO; 31 | 32 | namespace GitHub.JPMikkers.DHCP 33 | { 34 | public class Utils 35 | { 36 | public static TimeSpan InfiniteTimeSpan = TimeSpan.FromSeconds(UInt32.MaxValue); 37 | 38 | public static bool IsInfiniteTimeSpan(TimeSpan timeSpan) 39 | { 40 | return (timeSpan < TimeSpan.FromSeconds(0.0) || timeSpan >= InfiniteTimeSpan); 41 | } 42 | 43 | public static TimeSpan SanitizeTimeSpan(TimeSpan timeSpan) 44 | { 45 | return IsInfiniteTimeSpan(timeSpan) ? InfiniteTimeSpan : timeSpan; 46 | } 47 | 48 | public static bool ByteArraysAreEqual(byte[] array1, byte[] array2) 49 | { 50 | if (array1.Length != array2.Length) 51 | { 52 | return false; 53 | } 54 | 55 | for (int i = 0; i < array1.Length; i++) 56 | { 57 | if (array1[i] != array2[i]) 58 | { 59 | return false; 60 | } 61 | } 62 | 63 | return true; 64 | } 65 | 66 | public static string BytesToHexString(byte[] data,string separator) 67 | { 68 | StringBuilder sb = new StringBuilder(); 69 | for (int t = 0; t < data.Length; t++) 70 | { 71 | sb.AppendFormat("{0:X2}", data[t]); 72 | if(t<(data.Length-1)) 73 | { 74 | sb.Append(separator); 75 | } 76 | } 77 | return sb.ToString(); 78 | } 79 | 80 | public static byte[] HexStringToBytes(string data) 81 | { 82 | int c; 83 | List result = new List(); 84 | 85 | MemoryStream ms = new MemoryStream(); 86 | StreamWriter sw = new StreamWriter(ms); 87 | sw.Write(data); 88 | sw.Flush(); 89 | ms.Position = 0; 90 | StreamReader sr = new StreamReader(ms); 91 | 92 | StringBuilder number = new StringBuilder(); 93 | 94 | while((c=sr.Read())>0) 95 | { 96 | if( (c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F')) 97 | { 98 | number.Append((char) c); 99 | 100 | if(number.Length>=2) 101 | { 102 | result.Add(Convert.ToByte(number.ToString(), 16)); 103 | number.Length = 0; 104 | } 105 | } 106 | } 107 | return result.ToArray(); 108 | } 109 | 110 | public static IPAddress GetSubnetMask(IPAddress address) 111 | { 112 | foreach (NetworkInterface adapter in NetworkInterface.GetAllNetworkInterfaces()) 113 | { 114 | foreach (UnicastIPAddressInformation unicastIPAddressInformation in adapter.GetIPProperties().UnicastAddresses) 115 | { 116 | if (unicastIPAddressInformation.Address.AddressFamily == AddressFamily.InterNetwork) 117 | { 118 | if (address.Equals(unicastIPAddressInformation.Address)) 119 | { 120 | // the following mask can be null.. return 255.255.255.0 in that case 121 | return unicastIPAddressInformation.IPv4Mask ?? new IPAddress(new byte[] {255, 255, 255, 0}); 122 | } 123 | } 124 | } 125 | } 126 | throw new ArgumentException(string.Format("Can't find subnetmask for IP address '{0}'", address)); 127 | } 128 | 129 | public static IPAddress UInt32ToIPAddress(UInt32 address) 130 | { 131 | return new IPAddress(new byte[] { 132 | (byte)((address>>24) & 0xFF) , 133 | (byte)((address>>16) & 0xFF) , 134 | (byte)((address>>8) & 0xFF) , 135 | (byte)( address & 0xFF)}); 136 | } 137 | 138 | public static UInt32 IPAddressToUInt32(IPAddress address) 139 | { 140 | return 141 | (((UInt32)address.GetAddressBytes()[0]) << 24) | 142 | (((UInt32)address.GetAddressBytes()[1]) << 16) | 143 | (((UInt32)address.GetAddressBytes()[2]) << 8) | 144 | (((UInt32)address.GetAddressBytes()[3])); 145 | } 146 | 147 | public static string PrefixLines(string src,string prefix) 148 | { 149 | StringBuilder sb = new StringBuilder(); 150 | MemoryStream ms = new MemoryStream(); 151 | StreamWriter sw = new StreamWriter(ms); 152 | sw.Write(src); 153 | sw.Flush(); 154 | ms.Position = 0; 155 | StreamReader sr = new StreamReader(ms); 156 | string line; 157 | while ((line = sr.ReadLine()) != null) 158 | { 159 | sb.Append(prefix); 160 | sb.AppendLine(line); 161 | } 162 | return sb.ToString(); 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 A. Shkarlatov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 A. Shkarlatov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /PXE Server.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30621.155 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PXE Server", "PXE Server\PXE Server.csproj", "{A9AEE220-2771-4A6B-B25C-01198BEC4D45}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tftp.Net", "Tftp.Net\Tftp.Net.csproj", "{AC5742F4-C024-41AE-B202-D8448B4A2220}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DHCPServer", "DHCPServer\DHCPServer.csproj", "{C3BAAC17-8781-4881-B03D-2D05E8E26851}" 11 | EndProject 12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2EF69AAC-BB7E-47D9-A6F8-16B40DCB7926}" 13 | ProjectSection(SolutionItems) = preProject 14 | LICENSE.txt = LICENSE.txt 15 | README.md = README.md 16 | EndProjectSection 17 | EndProject 18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "wwwroot", "wwwroot", "{C56D682B-F046-4C82-850A-7502B9894409}" 19 | ProjectSection(SolutionItems) = preProject 20 | wwwroot\minilinux.zip = wwwroot\minilinux.zip 21 | wwwroot\wwwroot_grub2_efi.zip = wwwroot\wwwroot_grub2_efi.zip 22 | wwwroot\wwwroot_ipxe.zip = wwwroot\wwwroot_ipxe.zip 23 | wwwroot\wwwroot_syslinux.zip = wwwroot\wwwroot_syslinux.zip 24 | EndProjectSection 25 | EndProject 26 | Global 27 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 28 | Debug|Any CPU = Debug|Any CPU 29 | Release|Any CPU = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {A9AEE220-2771-4A6B-B25C-01198BEC4D45}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {A9AEE220-2771-4A6B-B25C-01198BEC4D45}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {A9AEE220-2771-4A6B-B25C-01198BEC4D45}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {A9AEE220-2771-4A6B-B25C-01198BEC4D45}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {AC5742F4-C024-41AE-B202-D8448B4A2220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {AC5742F4-C024-41AE-B202-D8448B4A2220}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {AC5742F4-C024-41AE-B202-D8448B4A2220}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {AC5742F4-C024-41AE-B202-D8448B4A2220}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {C3BAAC17-8781-4881-B03D-2D05E8E26851}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {C3BAAC17-8781-4881-B03D-2D05E8E26851}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {C3BAAC17-8781-4881-B03D-2D05E8E26851}.Release|Any CPU.ActiveCfg = Release|Any CPU 43 | {C3BAAC17-8781-4881-B03D-2D05E8E26851}.Release|Any CPU.Build.0 = Release|Any CPU 44 | EndGlobalSection 45 | GlobalSection(SolutionProperties) = preSolution 46 | HideSolutionNode = FALSE 47 | EndGlobalSection 48 | GlobalSection(NestedProjects) = preSolution 49 | {C56D682B-F046-4C82-850A-7502B9894409} = {2EF69AAC-BB7E-47D9-A6F8-16B40DCB7926} 50 | EndGlobalSection 51 | GlobalSection(ExtensibilityGlobals) = postSolution 52 | SolutionGuid = {67CAD50A-21FB-492C-8C34-EEE24CB74230} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /PXE Server/DHCPServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Text; 7 | 8 | using GitHub.JPMikkers.DHCP; 9 | 10 | using DHCP = GitHub.JPMikkers.DHCP; 11 | 12 | namespace PXE_Server 13 | { 14 | 15 | public class DHCPServer : DHCP.DHCPServer 16 | { 17 | public IPAddress BindAddress { get; set; } = IPAddress.Parse("192.168.1.27"); 18 | public Loader Loader { get; set; } = Loader.SYSLINUX; 19 | public string HTTPBootFile {get;set;} 20 | 21 | public DHCPServer(IPAddress address) : this(address, 67) { } 22 | public DHCPServer(IPAddress address, int port) : base(null) 23 | { 24 | BindAddress = address; 25 | 26 | this.EndPoint = new IPEndPoint(BindAddress, port); // default port 27 | this.SubnetMask = IPAddress.Parse("255.255.255.0"); 28 | this.PoolStart = IPAddress.Parse("192.168.1.100"); 29 | this.PoolEnd = IPAddress.Parse("192.168.1.200"); 30 | this.LeaseTime = DHCP.Utils.InfiniteTimeSpan; 31 | this.OfferExpirationTime = TimeSpan.FromSeconds(30); 32 | 33 | this.MinimumPacketSize = 576; 34 | 35 | 36 | this.OnStatusChange += Dhcpd_OnStatusChange; 37 | this.OnTrace += Dhcpd_OnTrace; 38 | 39 | Options.Add(new DHCP.OptionItem(mode: DHCP.OptionMode.Force, 40 | option: new DHCP.DHCPOptionRouter() 41 | { 42 | IPAddresses = new[] { BindAddress } 43 | })); 44 | 45 | Options.Add(new DHCP.OptionItem(mode: DHCP.OptionMode.Force, 46 | option: new DHCP.DHCPOptionServerIdentifier(BindAddress))); 47 | 48 | Options.Add(new DHCP.OptionItem(mode: DHCP.OptionMode.Force, 49 | option: new DHCP.DHCPOptionTFTPServerName(Dns.GetHostName()))); 50 | 51 | Options.Add(new DHCP.OptionItem(mode: DHCP.OptionMode.Force, 52 | option: new DHCP.DHCPOptionHostName(Dns.GetHostName()))); 53 | 54 | } 55 | 56 | // https://www.ietf.org/assignments/dhcpv6-parameters/dhcpv6-parameters.xml#processor-architecture 57 | private readonly Dictionary<(Loader, byte), string> avalibleArch = new Dictionary<(Loader, byte), string>() 58 | { 59 | { (Loader.SYSLINUX,00),"lpxelinux.0" }, 60 | { (Loader.SYSLINUX,06),"syslinux32.efi" }, 61 | { (Loader.SYSLINUX,07),"syslinux64.efi" }, 62 | 63 | 64 | { (Loader.IPXE,00),"ipxe.pxe" }, 65 | { (Loader.IPXE,07),"ipxe.efi" }, 66 | 67 | { (Loader.SHIM_GRUB2,00),"grub2.pxe" }, 68 | { (Loader.SHIM_GRUB2,07),"shimx64.efi" }, 69 | 70 | { (Loader.UEFI_HTTP,07),"shimx64.efi" }, 71 | 72 | }; 73 | protected override void ProcessingReceiveMessage(DHCPMessage sourceMsg, DHCPMessage targetMsg) 74 | { 75 | var bootFile = string.Empty; 76 | 77 | 78 | if (sourceMsg.isHTTP()) 79 | { 80 | bootFile = HTTPBootFile; 81 | } 82 | else 83 | if (sourceMsg.isIPXE()) 84 | { 85 | // this is ipxe script 86 | // bootFile = "http://192.168.1.27:8080/boot.ipxe"; 87 | bootFile = HTTPBootFile; 88 | } 89 | else 90 | 91 | 92 | if (sourceMsg.isPXE()) 93 | { 94 | var arch = sourceMsg.GetArch(); 95 | bootFile = avalibleArch[(Loader,arch)]; 96 | } 97 | 98 | 99 | targetMsg.BootFileName = bootFile; 100 | targetMsg.NextServerIPAddress = BindAddress; 101 | } 102 | 103 | public new void Start() 104 | { 105 | base.Start(); 106 | } 107 | 108 | 109 | private void Dhcpd_OnTrace(object sender, DHCP.DHCPTraceEventArgs e) 110 | { 111 | Trace.WriteLine(e?.Message); 112 | Trace.Flush(); 113 | } 114 | 115 | private void Dhcpd_OnStatusChange(object sender, DHCP.DHCPStopEventArgs e) 116 | { 117 | Trace.WriteLine(e?.Reason); 118 | Trace.Flush(); 119 | } 120 | } 121 | 122 | 123 | public static class DHCPMessageExtensions 124 | { 125 | public static byte GetArch(this DHCPMessage message) 126 | { 127 | try 128 | { 129 | return message.Options 130 | .Where(x => x.OptionType == TDHCPOption.ClientSystemArchitectureType) 131 | .Cast() 132 | .Select(x => x.Data[1]) 133 | .First(); 134 | } 135 | catch { return 0; } 136 | } 137 | 138 | public static string GetVendorClass(this DHCPMessage message) 139 | { 140 | var sb = new StringBuilder(); 141 | var strings = message.Options 142 | .Where(x => x.OptionType == TDHCPOption.VendorClassIdentifier) 143 | .Cast() 144 | .Select(x => Encoding.ASCII.GetString(x.Data)); 145 | 146 | try 147 | { 148 | foreach (var s in strings) 149 | { 150 | sb.AppendLine(s); 151 | } 152 | } 153 | catch { } 154 | return sb.ToString(); 155 | } 156 | public static bool isHTTP(this DHCPMessage message) => GetVendorClass(message).Contains("HTTPClient"); 157 | public static bool isPXE(this DHCPMessage message) => GetVendorClass(message).Contains("PXEClient"); 158 | public static bool isIPXE(this DHCPMessage message) 159 | { 160 | try 161 | { 162 | return message.Options 163 | .Where(x => x.OptionType == (TDHCPOption)77) 164 | .Cast() 165 | .Select(x => Encoding.ASCII.GetString(x.Data)) 166 | .Any(x => x.Equals("iPXE", StringComparison.InvariantCultureIgnoreCase)); 167 | } 168 | catch { return false; } 169 | } 170 | 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /PXE Server/HttpFileServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Net; 5 | using System.Threading.Tasks; 6 | 7 | namespace PXE_Server 8 | { 9 | public class HttpFileServer : IDisposable 10 | { 11 | private HttpListener listener; 12 | private bool disposedValue; 13 | 14 | private int Port { get; set; } = 80; 15 | private string RootDirectory { get; set; } 16 | 17 | 18 | public HttpFileServer(int port) : this(port,Path.Combine(Environment.CurrentDirectory,"wwwroot")) { } 19 | public HttpFileServer(int port,string rootPath) 20 | { 21 | Port = port; 22 | RootDirectory = rootPath; 23 | } 24 | 25 | public void Start() 26 | { 27 | Stop(); 28 | 29 | listener = new HttpListener(); 30 | var prefix = "http://+:" + Port.ToString() + "/"; 31 | listener.Prefixes.Add(prefix); 32 | //listener.Prefixes.Add("http://*:8181/"); 33 | listener.Start(); 34 | Trace.WriteLine("Start HTTPD on "+prefix); 35 | Trace.Flush(); 36 | _ = DoLoop(); 37 | 38 | 39 | } 40 | 41 | 42 | async Task DoLoop() 43 | { 44 | while(listener.IsListening) 45 | { 46 | var ctx=await listener.GetContextAsync(); 47 | _=ProcessingRequest(ctx); 48 | } 49 | } 50 | 51 | async Task ProcessingRequest(HttpListenerContext ctx) 52 | { 53 | var filename = ctx.Request.Url.AbsolutePath; 54 | Trace.WriteLine("HTTP request file: "+filename); 55 | Trace.Flush(); 56 | 57 | filename=Utils.CheckFileInRootDir(RootDirectory, filename); 58 | 59 | var info = new FileInfo(filename); 60 | 61 | if (info.Exists) 62 | { 63 | try 64 | { 65 | ctx.Response.ContentType = "application/octet-stream"; 66 | ctx.Response.ContentLength64 = info.Length; 67 | ctx.Response.AddHeader("Date", DateTime.Now.ToString("r")); 68 | ctx.Response.AddHeader("Last-Modified", info.LastWriteTime.ToString("r")); 69 | 70 | using (var f = info.OpenRead()) 71 | { 72 | await f.CopyToAsync(ctx.Response.OutputStream); 73 | } 74 | ctx.Response.StatusCode = (int)HttpStatusCode.OK; 75 | await ctx.Response.OutputStream.FlushAsync(); 76 | } 77 | catch (Exception ex) 78 | { 79 | ctx.Response.StatusCode = (int)HttpStatusCode.InternalServerError; 80 | } 81 | } 82 | else 83 | { 84 | ctx.Response.StatusCode = (int)HttpStatusCode.NotFound; 85 | } 86 | ctx.Response.OutputStream.Close(); 87 | } 88 | 89 | 90 | public void Stop() 91 | { 92 | listener?.Stop(); 93 | listener?.Close(); 94 | } 95 | 96 | protected virtual void Dispose(bool disposing) 97 | { 98 | if (!disposedValue) 99 | { 100 | if (disposing) 101 | { 102 | Stop(); 103 | } 104 | 105 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer 106 | // TODO: set large fields to null 107 | disposedValue = true; 108 | } 109 | } 110 | 111 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 112 | // ~HttpFileServer() 113 | // { 114 | // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 115 | // Dispose(disposing: false); 116 | // } 117 | 118 | public void Dispose() 119 | { 120 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 121 | Dispose(disposing: true); 122 | GC.SuppressFinalize(this); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /PXE Server/IPSegment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Net; 5 | 6 | namespace PXE_Server 7 | { 8 | // https://stackoverflow.com/a/14328085 9 | public class IPSegment 10 | { 11 | 12 | private UInt32 _ip; 13 | private UInt32 _mask; 14 | 15 | public IPSegment(string ip, string mask) 16 | { 17 | _ip = ip.ParseIp(); 18 | _mask = mask.ParseIp(); 19 | } 20 | 21 | public UInt32 NumberOfHosts 22 | { 23 | get { return ~_mask + 1; } 24 | } 25 | 26 | public UInt32 NetworkAddress 27 | { 28 | get { return _ip & _mask; } 29 | } 30 | 31 | public UInt32 BroadcastAddress 32 | { 33 | get { return NetworkAddress + ~_mask; } 34 | } 35 | 36 | public IEnumerable Hosts() 37 | { 38 | for (var host = NetworkAddress + 1; host < BroadcastAddress; host++) 39 | { 40 | yield return host; 41 | } 42 | } 43 | 44 | } 45 | 46 | public static class IpHelpers 47 | { 48 | public static string ToIpString(this UInt32 value) 49 | { 50 | var bitmask = 0xff000000; 51 | var parts = new string[4]; 52 | for (var i = 0; i < 4; i++) 53 | { 54 | var masked = (value & bitmask) >> ((3 - i) * 8); 55 | bitmask >>= 8; 56 | parts[i] = masked.ToString(CultureInfo.InvariantCulture); 57 | } 58 | return String.Join(".", parts); 59 | } 60 | 61 | public static UInt32 ParseIp(this string ipAddress) 62 | { 63 | var splitted = ipAddress.Split('.'); 64 | UInt32 ip = 0; 65 | for (var i = 0; i < 4; i++) 66 | { 67 | ip = (ip << 8) + UInt32.Parse(splitted[i]); 68 | } 69 | return ip; 70 | } 71 | 72 | public static IPAddress ToIpAddress(this UInt32 value) 73 | { 74 | return IPAddress.Parse(value.ToIpString()); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /PXE Server/PXE Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | PXE_Server 7 | resources\app.manifest 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | PreserveNewest 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /PXE Server/PXEConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text.Json; 4 | 5 | 6 | namespace PXE_Server 7 | { 8 | public class PXEConfig 9 | { 10 | public string BindAddress { get; set; } = "192.168.1.27"; 11 | public string NetMask { get; set; } = "255.255.255.0"; 12 | 13 | public bool Verbose { get; set; } = true; 14 | 15 | public int DHCPPort { get; set; } = 67; 16 | public int HTTPPort { get; set; } = 80; 17 | public int TFTPPort { get; set; } = 69; 18 | 19 | public string ServerDirectory { get; set; } = Path.Combine(Environment.CurrentDirectory, "wwwroot"); 20 | public string Loader { get; set; } = "SYSLINUX"; 21 | 22 | public string HTTPBootFile { get; set; } 23 | 24 | #region save/load 25 | const string cfg_file_name = "pxe.conf"; 26 | static JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions 27 | { 28 | IgnoreNullValues = true, 29 | WriteIndented = true 30 | }; 31 | 32 | 33 | public static PXEConfig Load() 34 | { 35 | try 36 | { 37 | var bytes = File.ReadAllBytes(cfg_file_name); 38 | return JsonSerializer.Deserialize(bytes, jsonSerializerOptions); 39 | 40 | } 41 | catch { return new PXEConfig(); } 42 | } 43 | 44 | public void Save() 45 | { 46 | try 47 | { 48 | var json = JsonSerializer.Serialize(this, jsonSerializerOptions); 49 | File.WriteAllText(cfg_file_name, json); 50 | } 51 | catch { } 52 | } 53 | 54 | #endregion 55 | 56 | 57 | } 58 | 59 | public enum Loader 60 | { 61 | SYSLINUX, 62 | IPXE, 63 | SHIM_GRUB2, 64 | UEFI_HTTP 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /PXE Server/PXEServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Net; 6 | 7 | using DHCP = GitHub.JPMikkers.DHCP; 8 | 9 | namespace PXE_Server 10 | { 11 | 12 | public class PXEServer : IDisposable 13 | { 14 | public IPAddress BindAddress { get; set; } 15 | public IPAddress NetMask { get; set; } 16 | 17 | public int DHCPPort { get; set; } 18 | public int HTTPPort { get; set; } 19 | public int TFTPPort { get; set; } 20 | 21 | public string ServerDirectory { get; set; } 22 | 23 | public string HTTPBootFile { get; set; } 24 | 25 | private TFTPServer tftp_server; 26 | private DHCPServer dhcp_server; 27 | private HttpFileServer http_server; 28 | 29 | private Loader loader; 30 | 31 | 32 | public PXEServer(PXEConfig config) 33 | { 34 | BindAddress = IPAddress.Parse(config.BindAddress); 35 | NetMask = IPAddress.Parse(config.NetMask); 36 | 37 | DHCPPort = config.DHCPPort; 38 | HTTPPort = config.HTTPPort; 39 | TFTPPort = config.TFTPPort; 40 | 41 | HTTPBootFile = config.HTTPBootFile; 42 | 43 | ServerDirectory = config.ServerDirectory; 44 | loader = Enum.Parse(config.Loader); 45 | if(config.Verbose) 46 | { 47 | Trace.Listeners.Add(new ConsoleTraceListener()); 48 | } 49 | 50 | 51 | } 52 | public PXEServer() 53 | { 54 | DHCPPort = 67; 55 | HTTPPort = 80; 56 | TFTPPort = 69; 57 | ServerDirectory = Path.Combine(Environment.CurrentDirectory, "wwwroot"); 58 | 59 | BindAddress = IPAddress.Parse("192.168.1.27"); 60 | NetMask = IPAddress.Parse("255.255.255.0"); 61 | 62 | 63 | } 64 | public void Start() 65 | { 66 | 67 | Stop(); 68 | 69 | http_server = new HttpFileServer(HTTPPort, ServerDirectory); 70 | http_server.Start(); 71 | 72 | tftp_server = new TFTPServer(BindAddress, TFTPPort, ServerDirectory); 73 | 74 | var net = new IPSegment(BindAddress.ToString(), NetMask.ToString()); 75 | 76 | dhcp_server = new DHCPServer(BindAddress, DHCPPort); 77 | dhcp_server.Loader = loader; 78 | dhcp_server.HTTPBootFile = HTTPBootFile; 79 | dhcp_server.SubnetMask = IPAddress.Parse("255.255.255.0"); 80 | dhcp_server.PoolStart = net.Hosts().First().ToIpAddress(); 81 | dhcp_server.PoolEnd = net.Hosts().Last().ToIpAddress(); 82 | dhcp_server.Start(); 83 | } 84 | public void Stop() 85 | { 86 | tftp_server?.Dispose(); 87 | 88 | dhcp_server?.Dispose(); 89 | 90 | http_server?.Dispose(); 91 | } 92 | 93 | 94 | #region dispose 95 | private bool disposedValue; 96 | protected virtual void Dispose(bool disposing) 97 | { 98 | if (!disposedValue) 99 | { 100 | if (disposing) 101 | { 102 | Stop(); 103 | } 104 | 105 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer 106 | // TODO: set large fields to null 107 | disposedValue = true; 108 | } 109 | } 110 | 111 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 112 | // ~PXEServer() 113 | // { 114 | // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 115 | // Dispose(disposing: false); 116 | // } 117 | 118 | public void Dispose() 119 | { 120 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 121 | Dispose(disposing: true); 122 | GC.SuppressFinalize(this); 123 | } 124 | #endregion 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /PXE Server/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace PXE_Server 5 | { 6 | class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | var config = PXEConfig.Load(); 11 | config.Save(); 12 | 13 | var pxe_server = new PXEServer(config); 14 | pxe_server.Start(); 15 | 16 | 17 | Console.WriteLine("Press ENTER to exit"); 18 | Console.ReadLine(); 19 | 20 | pxe_server.Stop(); 21 | 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /PXE Server/TFTPServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Net; 6 | using System.Text; 7 | 8 | using Tftp.Net; 9 | 10 | namespace PXE_Server 11 | { 12 | public class TFTPServer : IDisposable 13 | { 14 | TftpServer server; 15 | private static string ServerDirectory; 16 | private bool disposedValue; 17 | 18 | 19 | public TFTPServer(IPAddress localAddress, int port,string rootDirectory) 20 | { 21 | server = new TftpServer(localAddress,port); 22 | ServerDirectory = rootDirectory; 23 | 24 | server.OnReadRequest += Server_OnReadRequest; 25 | server.OnWriteRequest += Server_OnWriteRequest; 26 | server.Start(); 27 | Trace.WriteLine($"Start TFTPD on {localAddress.ToString()}:{port.ToString()}"); 28 | Trace.WriteLine($"TFTP root dir: {rootDirectory}"); 29 | Trace.Flush(); 30 | } 31 | 32 | private static void Server_OnWriteRequest(ITftpTransfer transfer, System.Net.EndPoint client) 33 | { 34 | String file = Path.Combine(ServerDirectory, transfer.Filename); 35 | 36 | if (File.Exists(file)) 37 | { 38 | CancelTransfer(transfer, TftpErrorPacket.FileAlreadyExists); 39 | } 40 | else 41 | { 42 | OutputTransferStatus(transfer, "Accepting write request from " + client); 43 | StartTransfer(transfer, new FileStream(file, FileMode.CreateNew)); 44 | } 45 | } 46 | 47 | private static void Server_OnReadRequest(ITftpTransfer transfer, System.Net.EndPoint client) 48 | { 49 | var path=Utils.CheckFileInRootDir(ServerDirectory, transfer.Filename); 50 | 51 | 52 | FileInfo file = new FileInfo(path); 53 | //Is the file within the server directory? 54 | if (!file.FullName.StartsWith(ServerDirectory, StringComparison.InvariantCultureIgnoreCase)) 55 | { 56 | CancelTransfer(transfer, TftpErrorPacket.AccessViolation); 57 | } 58 | else if (!file.Exists) 59 | { 60 | CancelTransfer(transfer, TftpErrorPacket.FileNotFound); 61 | } 62 | else 63 | { 64 | OutputTransferStatus(transfer, "Accepting request from " + client); 65 | StartTransfer(transfer, new FileStream(file.FullName, FileMode.Open,FileAccess.Read,FileShare.Read)); 66 | //StartTransfer(transfer, new MemoryStream(File.ReadAllBytes(file.FullName))); ; 67 | } 68 | } 69 | 70 | private static void StartTransfer(ITftpTransfer transfer, Stream stream) 71 | { 72 | transfer.OnProgress += new TftpProgressHandler(transfer_OnProgress); 73 | transfer.OnError += new TftpErrorHandler(transfer_OnError); 74 | transfer.OnFinished += new TftpEventHandler(transfer_OnFinished); 75 | transfer.Start(stream); 76 | } 77 | 78 | private static void CancelTransfer(ITftpTransfer transfer, TftpErrorPacket reason) 79 | { 80 | OutputTransferStatus(transfer, "Cancelling transfer: " + reason.ErrorMessage); 81 | transfer.Cancel(reason); 82 | } 83 | static void transfer_OnError(ITftpTransfer transfer, TftpTransferError error) 84 | { 85 | OutputTransferStatus(transfer, "Error: " + error); 86 | } 87 | 88 | static void transfer_OnFinished(ITftpTransfer transfer) 89 | { 90 | OutputTransferStatus(transfer, "Finished"); 91 | } 92 | 93 | static void transfer_OnProgress(ITftpTransfer transfer, TftpTransferProgress progress) 94 | { 95 | OutputTransferStatus(transfer, "Progress " + progress); 96 | } 97 | 98 | private static void OutputTransferStatus(ITftpTransfer transfer, string message) 99 | { 100 | Trace.WriteLine("[" + transfer.Filename + "] " + message); 101 | Trace.Flush(); 102 | } 103 | 104 | protected virtual void Dispose(bool disposing) 105 | { 106 | if (!disposedValue) 107 | { 108 | if (disposing) 109 | { 110 | // TODO: dispose managed state (managed objects) 111 | server.Dispose(); 112 | } 113 | 114 | // TODO: free unmanaged resources (unmanaged objects) and override finalizer 115 | // TODO: set large fields to null 116 | disposedValue = true; 117 | } 118 | } 119 | 120 | // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources 121 | // ~TFTPServer() 122 | // { 123 | // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 124 | // Dispose(disposing: false); 125 | // } 126 | 127 | public void Dispose() 128 | { 129 | // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method 130 | Dispose(disposing: true); 131 | GC.SuppressFinalize(this); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /PXE Server/Utils.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | 6 | namespace PXE_Server 7 | { 8 | class Utils 9 | { 10 | public static string CheckFileInRootDir(string rootDir,string file) 11 | { 12 | var tmp = file.Replace('/', '\\'); 13 | if (tmp.StartsWith('\\')) 14 | tmp = tmp.Substring(1); 15 | 16 | String path = Path.Combine(rootDir, tmp); 17 | return path; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /PXE Server/pxe.conf: -------------------------------------------------------------------------------- 1 | { 2 | "BindAddress": "192.168.1.27", 3 | "NetMask": "255.255.255.0", 4 | "Verbose": true, 5 | "DHCPPort": 67, 6 | "HTTPPort": 8080, 7 | "TFTPPort": 69, 8 | "ServerDirectory": "C:\\Users\\User\\Desktop\\PXE Server\\PXE Server\\bin\\Debug\\netcoreapp3.1\\wwwroot", 9 | "Loader": "SYSLINUX" 10 | } -------------------------------------------------------------------------------- /PXE Server/resources/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 52 | 59 | 60 | 61 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PXE Server 2 | 3 | PXE Server provides a DHCP server with support PXE and network boot, TFTP server and HTTP server (only send static files). 4 | 5 | ## Features 6 | 7 | Avalible loaders: 8 | - SYSLINUX (bios, efi) 9 | - IPXE (bios, efi) 10 | - SHIM_GRUB2 (bios, efi with secure boot) 11 | - UEFI_HTTP (efi-only, not tested on real hardware) 12 | 13 | ## Todo 14 | 15 | - Rewrite hard-coded dhcp boot filename for support user configuration 16 | 17 | ## Quick start guide 18 | 19 | 1. Download prepared wwwroot 20 | 2. Download minilinux and extract to wwwroot 21 | 3. Change loader config: 22 | - syslinux: pxelinux.cfg\default 23 | - ipxe: boot.ipxe (it is text script) 24 | - SHIM GRUB2: grub\grub.cfg 25 | - EFI HTTP: set url to loader (example: http://192.168.1.100:80/shimx64.efi) 26 | 4. Configure PXE (pxe.conf) 27 | 5. Run server 28 | 29 | ## Setup test enviroment 30 | 31 | Tested only on Windows 10 x64. 32 | 33 | Test EFI x64 boot: 34 | 1. Install [TAP-Windows](https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe) 35 | 2. Install [QEMU](https://qemu.weilnetz.de/w64/) 36 | 3. Download [OVMF EDK2](https://retrage.github.io/edk2-nightly/) 37 | 4. Run QEMU 38 | ``` 39 | qemu-system-x86_64.exe ^ 40 | -M q35 ^ 41 | -cpu max ^ 42 | -m 512M ^ 43 | -bios RELEASEX64_OVMF.fd ^ 44 | -netdev tap,id=mynet0,ifname= -device e1000,netdev=mynet0 45 | ``` 46 | 47 | 48 | Test PXE BIOS boot: 49 | 1. Install [VirtualBox](https://www.virtualbox.org/) 50 | 2. Create new VM and select network boot. 51 | 3. Enjoy! 52 | 53 | ## If server not work 54 | 55 | Check your firewall and open ports: 56 | - UDP: 67, 69 57 | - TCP: 80 58 | 59 | On Windows: 60 | ``` 61 | netsh advfirewall firewall add rule name="PXE DHCP" dir=in action=allow protocol=UDP localport=67 62 | netsh advfirewall firewall add rule name="PXE DHCP" dir=out action=allow protocol=UDP localport=67 63 | netsh advfirewall firewall add rule name="PXE TFTP" dir=in action=allow protocol=UDP localport=69 64 | netsh advfirewall firewall add rule name="PXE TFTP" dir=out action=allow protocol=UDP localport=69 65 | netsh advfirewall firewall add rule name="PXE HTTP" dir=in action=allow protocol=TCP localport=80 66 | netsh advfirewall firewall add rule name="PXE HTTP" dir=out action=allow protocol=TCP localport=80 67 | ``` 68 | 69 | ## Notes 70 | 71 | 1. How create grub2 pxe loader: 72 | `grub-mkimage -d /usr/lib/grub/i386-pc/ -O i386-pc-pxe -p "(pxe)/grub" -o grub2.pxe pxe tftp pxechain boot http linux` 73 | 74 | 2. How work shim? Shim not work on http. 75 | I use signed shim from ubuntu 20.04. Its pre-compiled with hard-coded filename "grubx64.efi". So I can't set http path. Only work with tftp. 76 | 77 | 3. Memdisk not work in EFI. 78 | 4. Grub loopback can't loop ISO over HTTP :) 79 | 5. IPXE sometimes slow downloading over HTTP (maybe bug?) 80 | 81 | ## List of other open source projects used 82 | 83 | - [DHCPServer](https://github.com/jpmikkers/DHCPServer) 84 | - [Tftp.Net](https://github.com/Callisto82/tftp.net) 85 | - [Syslinux](https://wiki.syslinux.org/wiki/index.php?title=The_Syslinux_Project) 86 | - [IPXE](https://ipxe.org/) 87 | - [GRUB2](https://www.gnu.org/software/grub/) 88 | - [UEFI SHIM Loader](https://github.com/rhboot/shim) 89 | - [Minimal Linux](https://github.com/ivandavidov/minimal) and [Minimal Linux Live](http://minimal.idzona.com/#home) 90 | 91 | ## License 92 | 93 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details -------------------------------------------------------------------------------- /Tftp.Net/BlockCounterWrapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tftp.Net 7 | { 8 | public enum BlockCounterWrapAround 9 | { 10 | ToZero, 11 | ToOne 12 | } 13 | 14 | static class BlockCounterWrappingHelpers 15 | { 16 | private const ushort LAST_AVAILABLE_BLOCK_NUMBER = 65535; 17 | 18 | public static ushort CalculateNextBlockNumber(this BlockCounterWrapAround wrapping, ushort previousBlockNumber) 19 | { 20 | if (previousBlockNumber == LAST_AVAILABLE_BLOCK_NUMBER) 21 | return wrapping == BlockCounterWrapAround.ToZero ? (ushort)0 : (ushort)1; 22 | 23 | return (ushort)(previousBlockNumber + 1); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Tftp.Net/Channel/ITransferChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Net; 6 | 7 | namespace Tftp.Net.Channel 8 | { 9 | delegate void TftpCommandHandler(ITftpCommand command, EndPoint endpoint); 10 | delegate void TftpChannelErrorHandler(TftpTransferError error); 11 | 12 | interface ITransferChannel : IDisposable 13 | { 14 | event TftpCommandHandler OnCommandReceived; 15 | event TftpChannelErrorHandler OnError; 16 | 17 | EndPoint RemoteEndpoint { get; set; } 18 | 19 | void Open(); 20 | void Send(ITftpCommand command); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tftp.Net/Channel/TransferChannelFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | 8 | namespace Tftp.Net.Channel 9 | { 10 | static class TransferChannelFactory 11 | { 12 | public static ITransferChannel CreateServer(EndPoint localAddress) 13 | { 14 | if (localAddress is IPEndPoint) 15 | return CreateServerUdp((IPEndPoint)localAddress); 16 | 17 | throw new NotSupportedException("Unsupported endpoint type."); 18 | } 19 | 20 | public static ITransferChannel CreateConnection(EndPoint remoteAddress) 21 | { 22 | if (remoteAddress is IPEndPoint) 23 | return CreateConnectionUdp((IPEndPoint)remoteAddress); 24 | 25 | throw new NotSupportedException("Unsupported endpoint type."); 26 | } 27 | 28 | #region UDP connections 29 | 30 | private static ITransferChannel CreateServerUdp(IPEndPoint localAddress) 31 | { 32 | UdpClient socket = new UdpClient(localAddress); 33 | return new UdpChannel(socket); 34 | } 35 | 36 | private static ITransferChannel CreateConnectionUdp(IPEndPoint remoteAddress) 37 | { 38 | IPEndPoint localAddress = new IPEndPoint(IPAddress.Any, 0); 39 | UdpChannel channel = new UdpChannel(new UdpClient(localAddress)); 40 | channel.RemoteEndpoint = remoteAddress; 41 | return channel; 42 | } 43 | #endregion 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tftp.Net/Channel/UdpChannel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Net.Sockets; 6 | using System.Net; 7 | using System.IO; 8 | using System.Diagnostics; 9 | 10 | namespace Tftp.Net.Channel 11 | { 12 | class UdpChannel : ITransferChannel 13 | { 14 | public event TftpCommandHandler OnCommandReceived; 15 | public event TftpChannelErrorHandler OnError; 16 | 17 | private IPEndPoint endpoint; 18 | private UdpClient client; 19 | private readonly CommandSerializer serializer = new CommandSerializer(); 20 | private readonly CommandParser parser = new CommandParser(); 21 | 22 | public UdpChannel(UdpClient client) 23 | { 24 | this.client = client; 25 | this.endpoint = null; 26 | } 27 | 28 | public void Open() 29 | { 30 | if (client == null) 31 | throw new ObjectDisposedException("UdpChannel"); 32 | 33 | client.BeginReceive(UdpReceivedCallback, null); 34 | } 35 | 36 | void UdpReceivedCallback(IAsyncResult result) 37 | { 38 | IPEndPoint endpoint = new IPEndPoint(0, 0); 39 | ITftpCommand command = null; 40 | 41 | try 42 | { 43 | byte[] data = null; 44 | 45 | lock (this) 46 | { 47 | if (client == null) 48 | return; 49 | 50 | data = client.EndReceive(result, ref endpoint); 51 | } 52 | command = parser.Parse(data); 53 | } 54 | catch (SocketException e) 55 | { 56 | //Handle receive error 57 | RaiseOnError(new NetworkError(e)); 58 | } 59 | catch (TftpParserException e2) 60 | { 61 | //Handle parser error 62 | RaiseOnError(new NetworkError(e2)); 63 | } 64 | 65 | if (command != null) 66 | { 67 | RaiseOnCommand(command, endpoint); 68 | } 69 | 70 | lock (this) 71 | { 72 | if (client != null) 73 | client.BeginReceive(UdpReceivedCallback, null); 74 | } 75 | } 76 | 77 | private void RaiseOnCommand(ITftpCommand command, IPEndPoint endpoint) 78 | { 79 | if (OnCommandReceived != null) 80 | OnCommandReceived(command, endpoint); 81 | } 82 | 83 | private void RaiseOnError(TftpTransferError error) 84 | { 85 | if (OnError != null) 86 | OnError(error); 87 | } 88 | 89 | public void Send(ITftpCommand command) 90 | { 91 | if (client == null) 92 | throw new ObjectDisposedException("UdpChannel"); 93 | 94 | if (endpoint == null) 95 | throw new InvalidOperationException("RemoteEndpoint needs to be set before you can send TFTP commands."); 96 | 97 | using (MemoryStream stream = new MemoryStream()) 98 | { 99 | serializer.Serialize(command, stream); 100 | byte[] data = stream.GetBuffer(); 101 | client.Send(data, (int)stream.Length, endpoint); 102 | } 103 | } 104 | 105 | public void Dispose() 106 | { 107 | lock (this) 108 | { 109 | if (this.client == null) 110 | return; 111 | 112 | client.Close(); 113 | this.client = null; 114 | } 115 | } 116 | 117 | public EndPoint RemoteEndpoint 118 | { 119 | get 120 | { 121 | return endpoint; 122 | } 123 | 124 | set 125 | { 126 | if (!(value is IPEndPoint)) 127 | throw new NotSupportedException("UdpChannel can only connect to IPEndPoints."); 128 | 129 | if (client == null) 130 | throw new ObjectDisposedException("UdpChannel"); 131 | 132 | this.endpoint = (IPEndPoint)value; 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Tftp.Net/Commands/CommandParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Tftp.Net.Transfer; 7 | 8 | namespace Tftp.Net 9 | { 10 | /// 11 | /// Parses a ITftpCommand. 12 | /// 13 | class CommandParser 14 | { 15 | /// 16 | /// Parses an ITftpCommand from the given byte array. If the byte array cannot be parsed for some reason, a TftpParserException is thrown. 17 | /// 18 | public ITftpCommand Parse(byte[] message) 19 | { 20 | try 21 | { 22 | return ParseInternal(message); 23 | } 24 | catch (TftpParserException) 25 | { 26 | throw; 27 | } 28 | catch (Exception e) 29 | { 30 | throw new TftpParserException(e); 31 | } 32 | } 33 | 34 | private ITftpCommand ParseInternal(byte[] message) 35 | { 36 | TftpStreamReader reader = new TftpStreamReader(new MemoryStream(message)); 37 | 38 | ushort opcode = reader.ReadUInt16(); 39 | switch (opcode) 40 | { 41 | case ReadRequest.OpCode: 42 | return ParseReadRequest(reader); 43 | 44 | case WriteRequest.OpCode: 45 | return ParseWriteRequest(reader); 46 | 47 | case Data.OpCode: 48 | return ParseData(reader); 49 | 50 | case Acknowledgement.OpCode: 51 | return ParseAcknowledgement(reader); 52 | 53 | case Error.OpCode: 54 | return ParseError(reader); 55 | 56 | case OptionAcknowledgement.OpCode: 57 | return ParseOptionAcknowledgement(reader); 58 | 59 | default: 60 | throw new TftpParserException("Invalid opcode"); 61 | } 62 | } 63 | 64 | private OptionAcknowledgement ParseOptionAcknowledgement(TftpStreamReader reader) 65 | { 66 | IEnumerable options = ParseTransferOptions(reader); 67 | return new OptionAcknowledgement(options); 68 | } 69 | 70 | private Error ParseError(TftpStreamReader reader) 71 | { 72 | ushort errorCode = reader.ReadUInt16(); 73 | String message = ParseNullTerminatedString(reader); 74 | return new Error(errorCode, message); 75 | } 76 | 77 | private Acknowledgement ParseAcknowledgement(TftpStreamReader reader) 78 | { 79 | ushort blockNumber = reader.ReadUInt16(); 80 | return new Acknowledgement(blockNumber); 81 | } 82 | 83 | private Data ParseData(TftpStreamReader reader) 84 | { 85 | ushort blockNumber = reader.ReadUInt16(); 86 | byte[] data = reader.ReadBytes(10000); 87 | return new Data(blockNumber, data); 88 | } 89 | 90 | private WriteRequest ParseWriteRequest(TftpStreamReader reader) 91 | { 92 | String filename = ParseNullTerminatedString(reader); 93 | TftpTransferMode mode = ParseModeType(ParseNullTerminatedString(reader)); 94 | IEnumerable options = ParseTransferOptions(reader); 95 | return new WriteRequest(filename, mode, options); 96 | } 97 | 98 | private ReadRequest ParseReadRequest(TftpStreamReader reader) 99 | { 100 | String filename = ParseNullTerminatedString(reader); 101 | TftpTransferMode mode = ParseModeType(ParseNullTerminatedString(reader)); 102 | IEnumerable options = ParseTransferOptions(reader); 103 | return new ReadRequest(filename, mode, options); 104 | } 105 | 106 | private List ParseTransferOptions(TftpStreamReader reader) 107 | { 108 | List options = new List(); 109 | 110 | while (true) 111 | { 112 | String name; 113 | 114 | try 115 | { 116 | name = ParseNullTerminatedString(reader); 117 | } 118 | catch (IOException) 119 | { 120 | name = ""; 121 | } 122 | 123 | if (name.Length == 0) 124 | break; 125 | 126 | string value = ParseNullTerminatedString(reader); 127 | options.Add(new TransferOption(name, value)); 128 | } 129 | return options; 130 | } 131 | 132 | private String ParseNullTerminatedString(TftpStreamReader reader) 133 | { 134 | byte b; 135 | StringBuilder str = new StringBuilder(); 136 | while ((b = reader.ReadByte()) > 0) 137 | { 138 | str.Append((char)b); 139 | } 140 | 141 | return str.ToString(); 142 | } 143 | 144 | private TftpTransferMode ParseModeType(String mode) 145 | { 146 | mode = mode.ToLowerInvariant(); 147 | 148 | if (mode == "netascii") 149 | return TftpTransferMode.netascii; 150 | 151 | if (mode == "mail") 152 | return TftpTransferMode.mail; 153 | 154 | if (mode == "octet") 155 | return TftpTransferMode.octet; 156 | 157 | throw new TftpParserException("Unknown mode type: " + mode); 158 | } 159 | } 160 | 161 | [Serializable] 162 | class TftpParserException : Exception 163 | { 164 | public TftpParserException(String message) 165 | : base(message) { } 166 | 167 | public TftpParserException(Exception e) 168 | : base("Error while parsing message.", e) { } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /Tftp.Net/Commands/CommandSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | namespace Tftp.Net 8 | { 9 | /// 10 | /// Serializes an ITftpCommand into a stream of bytes. 11 | /// 12 | class CommandSerializer 13 | { 14 | /// 15 | /// Call this method to serialize the given command using the given writer. 16 | /// 17 | public void Serialize(ITftpCommand command, Stream stream) 18 | { 19 | CommandComposerVisitor visitor = new CommandComposerVisitor(stream); 20 | command.Visit(visitor); 21 | } 22 | 23 | private class CommandComposerVisitor : ITftpCommandVisitor 24 | { 25 | private readonly TftpStreamWriter writer; 26 | 27 | public CommandComposerVisitor(Stream stream) 28 | { 29 | this.writer = new TftpStreamWriter(stream); 30 | } 31 | 32 | private void OnReadOrWriteRequest(ReadOrWriteRequest command) 33 | { 34 | writer.WriteBytes(Encoding.ASCII.GetBytes(command.Filename)); 35 | writer.WriteByte(0); 36 | writer.WriteBytes(Encoding.ASCII.GetBytes(command.Mode.ToString())); 37 | writer.WriteByte(0); 38 | 39 | if (command.Options != null) 40 | { 41 | foreach (var option in command.Options) 42 | { 43 | writer.WriteBytes(Encoding.ASCII.GetBytes(option.Name)); 44 | writer.WriteByte(0); 45 | writer.WriteBytes(Encoding.ASCII.GetBytes(option.Value)); 46 | writer.WriteByte(0); 47 | } 48 | } 49 | } 50 | 51 | public void OnReadRequest(ReadRequest command) 52 | { 53 | writer.WriteUInt16(ReadRequest.OpCode); 54 | OnReadOrWriteRequest(command); 55 | } 56 | 57 | public void OnWriteRequest(WriteRequest command) 58 | { 59 | writer.WriteUInt16(WriteRequest.OpCode); 60 | OnReadOrWriteRequest(command); 61 | } 62 | 63 | public void OnData(Data command) 64 | { 65 | writer.WriteUInt16(Data.OpCode); 66 | writer.WriteUInt16(command.BlockNumber); 67 | writer.WriteBytes(command.Bytes); 68 | } 69 | 70 | public void OnAcknowledgement(Acknowledgement command) 71 | { 72 | writer.WriteUInt16(Acknowledgement.OpCode); 73 | writer.WriteUInt16(command.BlockNumber); 74 | } 75 | 76 | public void OnError(Error command) 77 | { 78 | writer.WriteUInt16(Error.OpCode); 79 | writer.WriteUInt16(command.ErrorCode); 80 | writer.WriteBytes(Encoding.ASCII.GetBytes(command.Message)); 81 | writer.WriteByte(0); 82 | } 83 | 84 | public void OnOptionAcknowledgement(OptionAcknowledgement command) 85 | { 86 | writer.WriteUInt16(OptionAcknowledgement.OpCode); 87 | 88 | foreach (var option in command.Options) 89 | { 90 | writer.WriteBytes(Encoding.ASCII.GetBytes(option.Name)); 91 | writer.WriteByte(0); 92 | writer.WriteBytes(Encoding.ASCII.GetBytes(option.Value)); 93 | writer.WriteByte(0); 94 | } 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Tftp.Net/Commands/Commands.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Tftp.Net.Transfer; 7 | 8 | namespace Tftp.Net 9 | { 10 | interface ITftpCommand 11 | { 12 | void Visit(ITftpCommandVisitor visitor); 13 | } 14 | 15 | interface ITftpCommandVisitor 16 | { 17 | void OnReadRequest(ReadRequest command); 18 | void OnWriteRequest(WriteRequest command); 19 | void OnData(Data command); 20 | void OnAcknowledgement(Acknowledgement command); 21 | void OnError(Error command); 22 | void OnOptionAcknowledgement(OptionAcknowledgement command); 23 | } 24 | 25 | abstract class ReadOrWriteRequest 26 | { 27 | private readonly ushort opCode; 28 | 29 | public String Filename { get; private set; } 30 | public TftpTransferMode Mode { get; private set; } 31 | public IEnumerable Options { get; private set; } 32 | 33 | protected ReadOrWriteRequest(ushort opCode, String filename, TftpTransferMode mode, IEnumerable options) 34 | { 35 | this.opCode = opCode; 36 | this.Filename = filename; 37 | this.Mode = mode; 38 | this.Options = options; 39 | } 40 | } 41 | 42 | class ReadRequest : ReadOrWriteRequest, ITftpCommand 43 | { 44 | public const ushort OpCode = 1; 45 | 46 | public ReadRequest(String filename, TftpTransferMode mode, IEnumerable options) 47 | : base(OpCode, filename, mode, options) { } 48 | 49 | public void Visit(ITftpCommandVisitor visitor) 50 | { 51 | visitor.OnReadRequest(this); 52 | } 53 | } 54 | 55 | class WriteRequest : ReadOrWriteRequest, ITftpCommand 56 | { 57 | public const ushort OpCode = 2; 58 | 59 | public WriteRequest(String filename, TftpTransferMode mode, IEnumerable options) 60 | : base(OpCode, filename, mode, options) { } 61 | 62 | public void Visit(ITftpCommandVisitor visitor) 63 | { 64 | visitor.OnWriteRequest(this); 65 | } 66 | } 67 | 68 | class Data : ITftpCommand 69 | { 70 | public const ushort OpCode = 3; 71 | 72 | public ushort BlockNumber { get; private set; } 73 | public byte[] Bytes { get; private set; } 74 | 75 | public Data(ushort blockNumber, byte[] data) 76 | { 77 | this.BlockNumber = blockNumber; 78 | this.Bytes = data; 79 | } 80 | 81 | public void Visit(ITftpCommandVisitor visitor) 82 | { 83 | visitor.OnData(this); 84 | } 85 | } 86 | 87 | class Acknowledgement : ITftpCommand 88 | { 89 | public const ushort OpCode = 4; 90 | 91 | public ushort BlockNumber { get; private set; } 92 | 93 | public Acknowledgement(ushort blockNumber) 94 | { 95 | this.BlockNumber = blockNumber; 96 | } 97 | 98 | public void Visit(ITftpCommandVisitor visitor) 99 | { 100 | visitor.OnAcknowledgement(this); 101 | } 102 | } 103 | 104 | class Error : ITftpCommand 105 | { 106 | public const ushort OpCode = 5; 107 | 108 | public ushort ErrorCode { get; private set; } 109 | public String Message { get; private set; } 110 | 111 | public Error(ushort errorCode, String message) 112 | { 113 | this.ErrorCode = errorCode; 114 | this.Message = message; 115 | } 116 | 117 | public void Visit(ITftpCommandVisitor visitor) 118 | { 119 | visitor.OnError(this); 120 | } 121 | } 122 | 123 | class OptionAcknowledgement : ITftpCommand 124 | { 125 | public const ushort OpCode = 6; 126 | public IEnumerable Options { get; private set; } 127 | 128 | public OptionAcknowledgement(IEnumerable options) 129 | { 130 | this.Options = options; 131 | } 132 | 133 | public void Visit(ITftpCommandVisitor visitor) 134 | { 135 | visitor.OnOptionAcknowledgement(this); 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Tftp.Net/Commands/TftpStreamReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using System.Net.Sockets; 7 | 8 | namespace Tftp.Net 9 | { 10 | class TftpStreamReader 11 | { 12 | private readonly Stream stream; 13 | 14 | public TftpStreamReader(Stream stream) 15 | { 16 | this.stream = stream; 17 | } 18 | 19 | public ushort ReadUInt16() 20 | { 21 | int byte1 = stream.ReadByte(); 22 | int byte2 = stream.ReadByte(); 23 | return (ushort)((byte)byte1 << 8 | (byte)byte2); 24 | } 25 | 26 | public byte ReadByte() 27 | { 28 | int nextByte = stream.ReadByte(); 29 | 30 | if (nextByte == -1) 31 | throw new IOException(); 32 | 33 | return (byte)nextByte; 34 | } 35 | 36 | public byte[] ReadBytes(int maxBytes) 37 | { 38 | byte[] buffer = new byte[maxBytes]; 39 | int bytesRead = stream.Read(buffer, 0, buffer.Length); 40 | 41 | if (bytesRead == -1) 42 | throw new IOException(); 43 | 44 | Array.Resize(ref buffer, bytesRead); 45 | return buffer; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Tftp.Net/Commands/TftpStreamWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Tftp.Net 8 | { 9 | class TftpStreamWriter 10 | { 11 | private readonly Stream stream; 12 | 13 | public TftpStreamWriter(Stream stream) 14 | { 15 | this.stream = stream; 16 | } 17 | 18 | public void WriteUInt16(ushort value) 19 | { 20 | stream.WriteByte((byte)(value >> 8)); 21 | stream.WriteByte((byte)(value & 0xFF)); 22 | } 23 | 24 | public void WriteByte(byte b) 25 | { 26 | stream.WriteByte(b); 27 | } 28 | 29 | public void WriteBytes(byte[] data) 30 | { 31 | stream.Write(data, 0, data.Length); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tftp.Net/Commands/TransferOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tftp.Net 7 | { 8 | /// 9 | /// A single transfer options according to RFC2347. 10 | /// 11 | public class TransferOption 12 | { 13 | public String Name { get; private set; } 14 | public String Value { get; set; } 15 | public bool IsAcknowledged { get; internal set; } 16 | 17 | internal TransferOption(string name, string value) 18 | { 19 | if (String.IsNullOrEmpty(name)) 20 | throw new ArgumentException("name must not be null or empty."); 21 | 22 | if (value == null) 23 | throw new ArgumentNullException("value must not be null."); 24 | 25 | this.Name = name; 26 | this.Value = value; 27 | } 28 | 29 | public override string ToString() 30 | { 31 | return Name + "=" + Value; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tftp.Net/ITftpTransfer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Tftp.Net 8 | { 9 | public delegate void TftpEventHandler(ITftpTransfer transfer); 10 | public delegate void TftpProgressHandler(ITftpTransfer transfer, TftpTransferProgress progress); 11 | public delegate void TftpErrorHandler(ITftpTransfer transfer, TftpTransferError error); 12 | 13 | public enum TftpTransferMode 14 | { 15 | netascii, 16 | octet, 17 | mail 18 | } 19 | 20 | /// 21 | /// Represents a single data transfer between a TFTP server and client. 22 | /// 23 | public interface ITftpTransfer : IDisposable 24 | { 25 | /// 26 | /// Event that is being called while data is being transferred. 27 | /// 28 | event TftpProgressHandler OnProgress; 29 | 30 | /// 31 | /// Event that will be called once the data transfer is finished. 32 | /// 33 | event TftpEventHandler OnFinished; 34 | 35 | /// 36 | /// Event that will be called if there is an error during the data transfer. 37 | /// Currently, this will return instances of ErrorFromRemoteEndpoint or NetworkError. 38 | /// 39 | event TftpErrorHandler OnError; 40 | 41 | /// 42 | /// Requested TFTP transfer mode. For outgoing transfers, this member may be used to set the transfer mode. 43 | /// 44 | TftpTransferMode TransferMode { get; set; } 45 | 46 | /// 47 | /// Transfer blocksize. Set this member to control the TFTP blocksize option (RFC 2349). 48 | /// 49 | int BlockSize { get; set; } 50 | 51 | /// 52 | /// Timeout after which commands are sent again. 53 | /// This member is also transmitted as the TFTP timeout interval option (RFC 2349). 54 | /// 55 | TimeSpan RetryTimeout { get; set; } 56 | 57 | /// 58 | /// Number of times that a RetryTimeout may occour before the transfer is cancelled with a TimeoutError. 59 | /// 60 | int RetryCount { get; set; } 61 | 62 | /// 63 | /// Tftp can transfer up to 65535 blocks. After that, the block counter wraps to either zero or one, depending on the expectations of the client. 64 | /// 65 | BlockCounterWrapAround BlockCounterWrapping { get; set; } 66 | 67 | /// 68 | /// Expected transfer size in bytes. 0 if size is unknown. 69 | /// 70 | long ExpectedSize { get; set; } 71 | 72 | /// 73 | /// Filename for the transferred file. 74 | /// 75 | String Filename { get; } 76 | 77 | /// 78 | /// You can set your own object here to associate custom data with this transfer. 79 | /// 80 | object UserContext { get; set; } 81 | 82 | /// 83 | /// Call this function to start the transfer. 84 | /// 85 | /// The stream from which data is either read (when sending) or written to (when receiving). 86 | void Start(Stream data); 87 | 88 | /// 89 | /// Cancel the currently running transfer, possibly sending the provided reason to the remote endpoint. 90 | /// 91 | void Cancel(TftpErrorPacket reason); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Tftp.Net/README.md: -------------------------------------------------------------------------------- 1 | # Tftp.Net 2 | This is a .NET/C# library that allows you to easily integrate a TFTP Client or TFTP Server in your own C# applications. If you're looking for a fully-fledged GUI client, you should probably look into other projects. However, if you're looking for code that allows you to implement your own TFTP client/server in only a few lines of C# code, you've come to the right place. 3 | 4 | ## Features: 5 | At the moment the library features: 6 | 7 | - Complete TFTP protocol implementation (as defined in RFC 1350, RFC 2347 and RFC 2349) 8 | - TFTP client components 9 | - TFTP server components 10 | - Unit-Tested code using NUnit 11 | - Sample TFTP server 12 | - Sample TFTP client 13 | - New: Now supports TFTP timeout interval option (RFC 2349). 14 | - New: Now supports TFTP transfer size option (RFC 2349). 15 | - New: Now supports TFTP option extension (RFC 2347). 16 | - New: Now supports TFTP block size option (RFC 2348). 17 | - New: Now supports configurable block counter wrap around to zero/one. 18 | 19 | https://github.com/Callisto82/tftp.net 20 | MS-PL License -------------------------------------------------------------------------------- /Tftp.Net/Tftp.Net.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Tftp.Net/TftpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using Tftp.Net.Channel; 8 | using Tftp.Net.Transfer.States; 9 | using Tftp.Net.Transfer; 10 | 11 | namespace Tftp.Net 12 | { 13 | /// 14 | /// A TFTP client that can connect to a TFTP server. 15 | /// 16 | public class TftpClient 17 | { 18 | private const int DEFAULT_SERVER_PORT = 69; 19 | private readonly IPEndPoint remoteAddress; 20 | 21 | /// 22 | /// Default constructor. 23 | /// 24 | /// Address of the server that you would like to connect to. 25 | public TftpClient(IPEndPoint remoteAddress) 26 | { 27 | this.remoteAddress = remoteAddress; 28 | } 29 | 30 | /// 31 | /// Connects to a server 32 | /// 33 | /// Address of the server that you want connect to. 34 | /// Port on the server that you want connect to (default: 69) 35 | public TftpClient(IPAddress ip, int port) 36 | : this(new IPEndPoint(ip, port)) 37 | { 38 | } 39 | 40 | /// 41 | /// Connects to a server on port 69. 42 | /// 43 | /// Address of the server that you want connect to. 44 | public TftpClient(IPAddress ip) 45 | : this(new IPEndPoint(ip, DEFAULT_SERVER_PORT)) 46 | { 47 | } 48 | 49 | /// 50 | /// Connect to a server by hostname. 51 | /// 52 | /// Hostname or ip to connect to 53 | public TftpClient(String host) 54 | : this(host, DEFAULT_SERVER_PORT) 55 | { 56 | } 57 | 58 | /// 59 | /// Connect to a server by hostname and port . 60 | /// 61 | /// Hostname or ip to connect to 62 | /// Port to connect to 63 | public TftpClient(String host, int port) 64 | { 65 | IPAddress ip = Dns.GetHostAddresses(host).FirstOrDefault(x => x.AddressFamily == AddressFamily.InterNetwork); 66 | 67 | if (ip == null) 68 | throw new ArgumentException("Could not convert '" + host + "' to an IPv4 address.", "host"); 69 | 70 | this.remoteAddress = new IPEndPoint(ip, port); 71 | } 72 | 73 | /// 74 | /// GET a file from the server. 75 | /// You have to call Start() on the returned ITftpTransfer to start the transfer. 76 | /// 77 | public ITftpTransfer Download(String filename) 78 | { 79 | ITransferChannel channel = TransferChannelFactory.CreateConnection(remoteAddress); 80 | return new RemoteReadTransfer(channel, filename); 81 | } 82 | 83 | /// 84 | /// PUT a file from the server. 85 | /// You have to call Start() on the returned ITftpTransfer to start the transfer. 86 | /// 87 | public ITftpTransfer Upload(String filename) 88 | { 89 | ITransferChannel channel = TransferChannelFactory.CreateConnection(remoteAddress); 90 | return new RemoteWriteTransfer(channel, filename); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /Tftp.Net/TftpServer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Net.Sockets; 6 | using System.Net; 7 | using Tftp.Net.Channel; 8 | using Tftp.Net.Transfer; 9 | 10 | namespace Tftp.Net 11 | { 12 | public delegate void TftpServerEventHandler(ITftpTransfer transfer, EndPoint client); 13 | public delegate void TftpServerErrorHandler(TftpTransferError error); 14 | 15 | /// 16 | /// A simple TFTP server class. Dispose() the server to close the socket that it listens on. 17 | /// 18 | public class TftpServer : IDisposable 19 | { 20 | public const int DEFAULT_SERVER_PORT = 69; 21 | 22 | /// 23 | /// Fired when the server receives a new read request. 24 | /// 25 | public event TftpServerEventHandler OnReadRequest; 26 | 27 | /// 28 | /// Fired when the server receives a new write request. 29 | /// 30 | public event TftpServerEventHandler OnWriteRequest; 31 | 32 | /// 33 | /// Fired when the server encounters an error (for example, a non-parseable request) 34 | /// 35 | public event TftpServerErrorHandler OnError; 36 | 37 | /// 38 | /// Server port that we're listening on. 39 | /// 40 | private readonly ITransferChannel serverSocket; 41 | 42 | public TftpServer(IPEndPoint localAddress) 43 | { 44 | if (localAddress == null) 45 | throw new ArgumentNullException("localAddress"); 46 | 47 | serverSocket = TransferChannelFactory.CreateServer(localAddress); 48 | serverSocket.OnCommandReceived += new TftpCommandHandler(serverSocket_OnCommandReceived); 49 | serverSocket.OnError += new TftpChannelErrorHandler(serverSocket_OnError); 50 | } 51 | 52 | public TftpServer(IPAddress localAddress) 53 | : this(localAddress, DEFAULT_SERVER_PORT) 54 | { 55 | } 56 | 57 | public TftpServer(IPAddress localAddress, int port) 58 | : this(new IPEndPoint(localAddress, port)) 59 | { 60 | } 61 | 62 | public TftpServer(int port) 63 | : this(new IPEndPoint(IPAddress.Any, port)) 64 | { 65 | } 66 | 67 | public TftpServer() 68 | : this(DEFAULT_SERVER_PORT) 69 | { 70 | } 71 | 72 | 73 | /// 74 | /// Start accepting incoming connections. 75 | /// 76 | public void Start() 77 | { 78 | serverSocket.Open(); 79 | } 80 | 81 | void serverSocket_OnError(TftpTransferError error) 82 | { 83 | RaiseOnError(error); 84 | } 85 | 86 | private void serverSocket_OnCommandReceived(ITftpCommand command, EndPoint endpoint) 87 | { 88 | //Ignore all other commands 89 | if (!(command is ReadOrWriteRequest)) 90 | return; 91 | 92 | //Open a connection to the client 93 | ITransferChannel channel = TransferChannelFactory.CreateConnection(endpoint); 94 | 95 | //Create a wrapper for the transfer request 96 | ReadOrWriteRequest request = (ReadOrWriteRequest)command; 97 | ITftpTransfer transfer = request is ReadRequest ? (ITftpTransfer)new LocalReadTransfer(channel, request.Filename, request.Options) : new LocalWriteTransfer(channel, request.Filename, request.Options); 98 | 99 | if (command is ReadRequest) 100 | RaiseOnReadRequest(transfer, endpoint); 101 | else if (command is WriteRequest) 102 | RaiseOnWriteRequest(transfer, endpoint); 103 | else 104 | throw new Exception("Unexpected tftp transfer request: " + command); 105 | } 106 | 107 | #region IDisposable 108 | public void Dispose() 109 | { 110 | serverSocket.Dispose(); 111 | } 112 | #endregion 113 | 114 | private void RaiseOnError(TftpTransferError error) 115 | { 116 | if (OnError != null) 117 | OnError(error); 118 | } 119 | 120 | private void RaiseOnWriteRequest(ITftpTransfer transfer, EndPoint client) 121 | { 122 | if (OnWriteRequest != null) 123 | { 124 | OnWriteRequest(transfer, client); 125 | } 126 | else 127 | { 128 | transfer.Cancel(new TftpErrorPacket(0, "Server did not provide a OnWriteRequest handler.")); 129 | } 130 | } 131 | 132 | private void RaiseOnReadRequest(ITftpTransfer transfer, EndPoint client) 133 | { 134 | if (OnReadRequest != null) 135 | { 136 | OnReadRequest(transfer, client); 137 | } 138 | else 139 | { 140 | transfer.Cancel(new TftpErrorPacket(0, "Server did not provide a OnReadRequest handler.")); 141 | } 142 | } 143 | } 144 | } 145 | 146 | -------------------------------------------------------------------------------- /Tftp.Net/TftpTransferError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tftp.Net 7 | { 8 | /// 9 | /// Base class for all errors that may be passed to ITftpTransfer.OnError. 10 | /// 11 | public abstract class TftpTransferError 12 | { 13 | public abstract override String ToString(); 14 | } 15 | 16 | /// 17 | /// Errors that are sent from the remote party using the TFTP Error Packet are represented 18 | /// by this class. 19 | /// 20 | public class TftpErrorPacket : TftpTransferError 21 | { 22 | /// 23 | /// Error code that was sent from the other party. 24 | /// 25 | public ushort ErrorCode { get; private set; } 26 | 27 | /// 28 | /// Error description that was sent by the other party. 29 | /// 30 | public string ErrorMessage { get; private set; } 31 | 32 | public TftpErrorPacket(ushort errorCode, string errorMessage) 33 | { 34 | if (String.IsNullOrEmpty(errorMessage)) 35 | throw new ArgumentException("You must provide an errorMessage."); 36 | 37 | this.ErrorCode = errorCode; 38 | this.ErrorMessage = errorMessage; 39 | } 40 | 41 | public override string ToString() 42 | { 43 | return ErrorCode + " - " + ErrorMessage; 44 | } 45 | 46 | #region Predefined error packets from RFC 1350 47 | public static readonly TftpErrorPacket FileNotFound = new TftpErrorPacket(1, "File not found"); 48 | public static readonly TftpErrorPacket AccessViolation = new TftpErrorPacket(2, "Access violation"); 49 | public static readonly TftpErrorPacket DiskFull = new TftpErrorPacket(3, "Disk full or allocation exceeded"); 50 | public static readonly TftpErrorPacket IllegalOperation = new TftpErrorPacket(4, "Illegal TFTP operation"); 51 | public static readonly TftpErrorPacket UnknownTransferId = new TftpErrorPacket(5, "Unknown transfer ID"); 52 | public static readonly TftpErrorPacket FileAlreadyExists = new TftpErrorPacket(6, "File already exists"); 53 | public static readonly TftpErrorPacket NoSuchUser = new TftpErrorPacket(7, "No such user"); 54 | #endregion 55 | } 56 | 57 | /// 58 | /// Network errors (i.e. socket exceptions) are represented by this class. 59 | /// 60 | public class NetworkError : TftpTransferError 61 | { 62 | public Exception Exception { get; private set; } 63 | 64 | public NetworkError(Exception exception) 65 | { 66 | this.Exception = exception; 67 | } 68 | 69 | public override string ToString() 70 | { 71 | return Exception.ToString(); 72 | } 73 | } 74 | 75 | /// 76 | /// $(ITftpTransfer.RetryTimeout) has been exceeded more than $(ITftpTransfer.RetryCount) times in a row. 77 | /// 78 | public class TimeoutError : TftpTransferError 79 | { 80 | private readonly TimeSpan RetryTimeout; 81 | private readonly int RetryCount; 82 | 83 | public TimeoutError(TimeSpan retryTimeout, int retryCount) 84 | { 85 | this.RetryTimeout = retryTimeout; 86 | this.RetryCount = retryCount; 87 | } 88 | 89 | public override string ToString() 90 | { 91 | return "Timeout error. RetryTimeout (" + RetryTimeout + ") violated more than " + RetryCount + " times in a row"; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Tftp.Net/TftpTransferProgress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tftp.Net 7 | { 8 | public class TftpTransferProgress 9 | { 10 | /// 11 | /// Number of bytes that have already been transferred. 12 | /// 13 | public long TransferredBytes { get; private set; } 14 | 15 | /// 16 | /// Total number of bytes being transferred. May be 0 if unknown. 17 | /// 18 | public long TotalBytes { get; private set; } 19 | 20 | public TftpTransferProgress(long transferred, long total) 21 | { 22 | TransferredBytes = transferred; 23 | TotalBytes = total; 24 | } 25 | 26 | public override string ToString() 27 | { 28 | if (TotalBytes > 0) 29 | return (TransferredBytes * 100L) / TotalBytes + "% completed"; 30 | else 31 | return TransferredBytes + " bytes transferred"; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tftp.Net/Trace/LoggingStateDecorator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Diagnostics; 6 | using Tftp.Net.Transfer.States; 7 | using System.IO; 8 | using Tftp.Net.Channel; 9 | using System.Net; 10 | using Tftp.Net.Trace; 11 | using Tftp.Net.Transfer; 12 | 13 | namespace Tftp.Net.Trace 14 | { 15 | class LoggingStateDecorator : ITransferState 16 | { 17 | public TftpTransfer Context 18 | { 19 | get { return decoratee.Context; } 20 | set { decoratee.Context = value; } 21 | } 22 | 23 | private readonly ITransferState decoratee; 24 | private readonly TftpTransfer transfer; 25 | 26 | public LoggingStateDecorator(ITransferState decoratee, TftpTransfer transfer) 27 | { 28 | this.decoratee = decoratee; 29 | this.transfer = transfer; 30 | } 31 | 32 | public String GetStateName() 33 | { 34 | return "[" + decoratee.GetType().Name + "]"; 35 | } 36 | 37 | public void OnStateEnter() 38 | { 39 | TftpTrace.Trace(GetStateName() + " OnStateEnter", transfer); 40 | decoratee.OnStateEnter(); 41 | } 42 | 43 | public void OnStart() 44 | { 45 | TftpTrace.Trace(GetStateName() + " OnStart", transfer); 46 | decoratee.OnStart(); 47 | } 48 | 49 | public void OnCancel(TftpErrorPacket reason) 50 | { 51 | TftpTrace.Trace(GetStateName() + " OnCancel: " + reason, transfer); 52 | decoratee.OnCancel(reason); 53 | } 54 | 55 | public void OnCommand(ITftpCommand command, EndPoint endpoint) 56 | { 57 | TftpTrace.Trace(GetStateName() + " OnCommand: " + command + " from " + endpoint, transfer); 58 | decoratee.OnCommand(command, endpoint); 59 | } 60 | 61 | public void OnTimer() 62 | { 63 | decoratee.OnTimer(); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Tftp.Net/Trace/TftpTrace.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Transfer; 6 | 7 | namespace Tftp.Net.Trace 8 | { 9 | /// 10 | /// Class that controls all tracing in the TFTP module. 11 | /// 12 | public static class TftpTrace 13 | { 14 | /// 15 | /// Set this property to false to disable tracing. 16 | /// 17 | public static bool Enabled { get; set; } 18 | 19 | static TftpTrace() 20 | { 21 | Enabled = false; 22 | } 23 | 24 | internal static void Trace(String message, ITftpTransfer transfer) 25 | { 26 | if (!Enabled) 27 | return; 28 | 29 | System.Diagnostics.Trace.WriteLine(message, transfer.ToString()); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/InitialStateFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Transfer.States; 6 | 7 | namespace Tftp.Net.Transfer 8 | { 9 | static class InitialStateFactory 10 | { 11 | /*public enum TransferInitiatedBy 12 | { 13 | LocalSystem, 14 | RemoteSystem 15 | } 16 | 17 | public enum TransferMode 18 | { 19 | Read, 20 | Write 21 | } 22 | 23 | public static ITftpState GetInitialState(TransferInitiatedBy direction, TransferMode mode) 24 | { 25 | if (direction == TransferInitiatedBy.RemoteSystem && mode == TransferMode.Read) 26 | { 27 | return new StartIncomingRead(this); 28 | } 29 | 30 | if (direction == TransferInitiatedBy.RemoteSystem && mode == TransferMode.Write) 31 | { 32 | return new StartIncomingWrite(this); 33 | } 34 | 35 | if (direction == TransferInitiatedBy.LocalSystem && mode == TransferMode.Read) 36 | { 37 | return new StartOutgoingRead(this); 38 | } 39 | 40 | if (direction == TransferInitiatedBy.LocalSystem && mode == TransferMode.Write) 41 | { 42 | 43 | } 44 | 45 | return null; 46 | }*/ 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/LocalReadTransfer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Transfer.States; 6 | using Tftp.Net.Channel; 7 | using Tftp.Net.Transfer; 8 | 9 | namespace Tftp.Net.Transfer 10 | { 11 | class LocalReadTransfer : TftpTransfer 12 | { 13 | public LocalReadTransfer(ITransferChannel connection, string filename, IEnumerable options) 14 | : base(connection, filename, new StartIncomingRead(options)) 15 | { 16 | } 17 | 18 | public override TftpTransferMode TransferMode 19 | { 20 | get { return base.TransferMode; } 21 | set { throw new NotSupportedException("Cannot change the transfer mode for incoming transfers. The transfer mode is determined by the client."); } 22 | } 23 | 24 | public override int BlockSize 25 | { 26 | get { return base.BlockSize; } 27 | set { throw new NotSupportedException("For incoming transfers, the blocksize is determined by the client."); } 28 | } 29 | 30 | public override TimeSpan RetryTimeout 31 | { 32 | get { return base.RetryTimeout; } 33 | set { throw new NotSupportedException("For incoming transfers, the retry timeout is determined by the client."); } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/LocalWriteTransfer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Transfer.States; 6 | using Tftp.Net.Channel; 7 | using Tftp.Net.Transfer; 8 | 9 | namespace Tftp.Net.Transfer 10 | { 11 | class LocalWriteTransfer : TftpTransfer 12 | { 13 | public LocalWriteTransfer(ITransferChannel connection, string filename, IEnumerable options) 14 | : base(connection, filename, new StartIncomingWrite(options)) 15 | { 16 | } 17 | 18 | public override TftpTransferMode TransferMode 19 | { 20 | get { return base.TransferMode; } 21 | set { throw new NotSupportedException("Cannot change the transfer mode for incoming transfers. The transfer mode is determined by the client."); } 22 | } 23 | 24 | public override int BlockSize 25 | { 26 | get { return base.BlockSize; } 27 | set { throw new NotSupportedException("For incoming transfers, the blocksize is determined by the client."); } 28 | } 29 | 30 | public override TimeSpan RetryTimeout 31 | { 32 | get { return base.RetryTimeout; } 33 | set { throw new NotSupportedException("For incoming transfers, the retry timeout is determined by the client."); } 34 | } 35 | 36 | public override long ExpectedSize 37 | { 38 | get { return base.ExpectedSize; } 39 | set { throw new NotSupportedException("You cannot set the expected size of a file that is remotely transferred to this system."); } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/RemoteReadTransfer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Transfer.States; 6 | using Tftp.Net.Channel; 7 | using Tftp.Net.Transfer; 8 | 9 | namespace Tftp.Net.Transfer 10 | { 11 | class RemoteReadTransfer : TftpTransfer 12 | { 13 | public RemoteReadTransfer(ITransferChannel connection, String filename) 14 | : base(connection, filename, new StartOutgoingRead()) 15 | { 16 | } 17 | 18 | public override long ExpectedSize 19 | { 20 | get { return base.ExpectedSize; } 21 | set { throw new NotSupportedException("You cannot set the expected size of a file that is remotely transferred to this system."); } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/RemoteWriteTransfer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Channel; 6 | using Tftp.Net.Transfer.States; 7 | using Tftp.Net.Transfer; 8 | 9 | namespace Tftp.Net.Transfer 10 | { 11 | class RemoteWriteTransfer : TftpTransfer 12 | { 13 | public RemoteWriteTransfer(ITransferChannel connection, String filename) 14 | : base(connection, filename, new StartOutgoingWrite()) 15 | { 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/SimpleTimer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tftp.Net.Transfer 7 | { 8 | /// 9 | /// Simple implementation of a timer. 10 | /// 11 | class SimpleTimer 12 | { 13 | private DateTime nextTimeout; 14 | private readonly TimeSpan timeout; 15 | 16 | public SimpleTimer(TimeSpan timeout) 17 | { 18 | this.timeout = timeout; 19 | Restart(); 20 | } 21 | 22 | public void Restart() 23 | { 24 | this.nextTimeout = DateTime.Now.Add(timeout); 25 | } 26 | 27 | public bool IsTimeout() 28 | { 29 | return DateTime.Now >= nextTimeout; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/AcknowledgeWriteRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Trace; 6 | 7 | namespace Tftp.Net.Transfer.States 8 | { 9 | class AcknowledgeWriteRequest : StateThatExpectsMessagesFromDefaultEndPoint 10 | { 11 | public override void OnStateEnter() 12 | { 13 | base.OnStateEnter(); 14 | SendAndRepeat(new Acknowledgement(0)); 15 | } 16 | 17 | public override void OnData(Data command) 18 | { 19 | var nextState = new Receiving(); 20 | Context.SetState(nextState); 21 | nextState.OnCommand(command, Context.GetConnection().RemoteEndpoint); 22 | } 23 | 24 | public override void OnCancel(TftpErrorPacket reason) 25 | { 26 | Context.SetState(new CancelledByUser(reason)); 27 | } 28 | 29 | public override void OnError(Error command) 30 | { 31 | Context.SetState(new ReceivedError(command)); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/BaseState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using System.Net; 7 | 8 | namespace Tftp.Net.Transfer.States 9 | { 10 | class BaseState : ITransferState 11 | { 12 | public TftpTransfer Context { get; set; } 13 | 14 | public virtual void OnStateEnter() 15 | { 16 | //no-op 17 | } 18 | 19 | public virtual void OnStart() 20 | { 21 | } 22 | 23 | public virtual void OnCancel(TftpErrorPacket reason) 24 | { 25 | } 26 | 27 | public virtual void OnCommand(ITftpCommand command, EndPoint endpoint) 28 | { 29 | } 30 | 31 | public virtual void OnTimer() 32 | { 33 | //Ignore timer events 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/CancelledByUser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tftp.Net.Transfer.States 7 | { 8 | class CancelledByUser : BaseState 9 | { 10 | private readonly TftpErrorPacket reason; 11 | 12 | public CancelledByUser(TftpErrorPacket reason) 13 | { 14 | this.reason = reason; 15 | } 16 | 17 | public override void OnStateEnter() 18 | { 19 | Error command = new Error(reason.ErrorCode, reason.ErrorMessage); 20 | Context.GetConnection().Send(command); 21 | Context.SetState(new Closed()); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/Closed.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tftp.Net.Transfer.States 7 | { 8 | class Closed : BaseState 9 | { 10 | public override void OnStateEnter() 11 | { 12 | Context.Dispose(); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/ITransferState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Tftp.Net.Channel; 7 | using System.Net; 8 | 9 | namespace Tftp.Net.Transfer.States 10 | { 11 | interface ITransferState 12 | { 13 | TftpTransfer Context { get; set; } 14 | 15 | //Called by TftpTransfer 16 | void OnStateEnter(); 17 | 18 | //Called if the user calls TftpTransfer.Start() or TftpTransfer.Cancel() 19 | void OnStart(); 20 | void OnCancel(TftpErrorPacket reason); 21 | 22 | //Called regularely by the context 23 | void OnTimer(); 24 | 25 | //Called when a command is received 26 | void OnCommand(ITftpCommand command, EndPoint endpoint); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/ReceivedError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Trace; 6 | 7 | namespace Tftp.Net.Transfer.States 8 | { 9 | class ReceivedError : BaseState 10 | { 11 | private readonly TftpTransferError error; 12 | 13 | public ReceivedError(Error error) 14 | : this(new TftpErrorPacket(error.ErrorCode, GetNonEmptyErrorMessage(error))) { } 15 | 16 | private static string GetNonEmptyErrorMessage(Error error) 17 | { 18 | return string.IsNullOrEmpty(error.Message) ? "(No error message provided)" : error.Message; 19 | } 20 | 21 | public ReceivedError(TftpTransferError error) 22 | { 23 | this.error = error; 24 | } 25 | 26 | public override void OnStateEnter() 27 | { 28 | TftpTrace.Trace("Received error: " + error, Context); 29 | Context.RaiseOnError(error); 30 | Context.SetState(new Closed()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/Receiving.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Tftp.Net.Transfer.States 8 | { 9 | class Receiving : StateThatExpectsMessagesFromDefaultEndPoint 10 | { 11 | private ushort lastBlockNumber = 0; 12 | private ushort nextBlockNumber = 1; 13 | private long bytesReceived = 0; 14 | 15 | public override void OnData(Data command) 16 | { 17 | if (command.BlockNumber == nextBlockNumber) 18 | { 19 | //We received a new block of data 20 | Context.InputOutputStream.Write(command.Bytes, 0, command.Bytes.Length); 21 | SendAcknowledgement(command.BlockNumber); 22 | 23 | //Was that the last block of data? 24 | if (command.Bytes.Length < Context.BlockSize) 25 | { 26 | Context.RaiseOnFinished(); 27 | Context.SetState(new Closed()); 28 | } 29 | else 30 | { 31 | lastBlockNumber = command.BlockNumber; 32 | nextBlockNumber = Context.BlockCounterWrapping.CalculateNextBlockNumber(command.BlockNumber); 33 | bytesReceived += command.Bytes.Length; 34 | Context.RaiseOnProgress(bytesReceived); 35 | } 36 | } 37 | else 38 | if (command.BlockNumber == lastBlockNumber) 39 | { 40 | //We received the previous block again. Re-sent the acknowledgement 41 | SendAcknowledgement(command.BlockNumber); 42 | } 43 | } 44 | 45 | public override void OnCancel(TftpErrorPacket reason) 46 | { 47 | Context.SetState(new CancelledByUser(reason)); 48 | } 49 | 50 | public override void OnError(Error command) 51 | { 52 | Context.SetState(new ReceivedError(command)); 53 | } 54 | 55 | private void SendAcknowledgement(ushort blockNumber) 56 | { 57 | Acknowledgement ack = new Acknowledgement(blockNumber); 58 | Context.GetConnection().Send(ack); 59 | ResetTimeout(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/SendOptionAcknowledgementBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Transfer.States; 6 | using Tftp.Net.Trace; 7 | 8 | namespace Tftp.Net.Transfer 9 | { 10 | class SendOptionAcknowledgementBase : StateThatExpectsMessagesFromDefaultEndPoint 11 | { 12 | public override void OnStateEnter() 13 | { 14 | base.OnStateEnter(); 15 | SendAndRepeat(new OptionAcknowledgement(Context.NegotiatedOptions.ToOptionList())); 16 | } 17 | 18 | public override void OnError(Error command) 19 | { 20 | Context.SetState(new ReceivedError(command)); 21 | } 22 | 23 | public override void OnCancel(TftpErrorPacket reason) 24 | { 25 | Context.SetState(new CancelledByUser(reason)); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/SendOptionAcknowledgementForReadRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Transfer.States; 6 | 7 | namespace Tftp.Net.Transfer.States 8 | { 9 | class SendOptionAcknowledgementForReadRequest : SendOptionAcknowledgementBase 10 | { 11 | public override void OnAcknowledgement(Acknowledgement command) 12 | { 13 | if (command.BlockNumber == 0) 14 | { 15 | //We received an OACK, so let's get going ;) 16 | Context.SetState(new Sending()); 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/SendOptionAcknowledgementForWriteRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Tftp.Net.Transfer.States 7 | { 8 | class SendOptionAcknowledgementForWriteRequest : SendOptionAcknowledgementBase 9 | { 10 | public override void OnData(Data command) 11 | { 12 | if (command.BlockNumber == 1) 13 | { 14 | //The client confirmed the options, so let's start receiving 15 | ITransferState nextState = new Receiving(); 16 | Context.SetState(nextState); 17 | nextState.OnCommand(command, Context.GetConnection().RemoteEndpoint); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/SendReadRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Tftp.Net.Channel; 7 | using System.Net; 8 | using Tftp.Net.Transfer; 9 | using Tftp.Net.Trace; 10 | 11 | namespace Tftp.Net.Transfer.States 12 | { 13 | class SendReadRequest : StateWithNetworkTimeout 14 | { 15 | public override void OnStateEnter() 16 | { 17 | base.OnStateEnter(); 18 | SendRequest(); //Send a read request to the server 19 | } 20 | 21 | private void SendRequest() 22 | { 23 | ReadRequest request = new ReadRequest(Context.Filename, Context.TransferMode, Context.ProposedOptions.ToOptionList()); 24 | SendAndRepeat(request); 25 | } 26 | 27 | public override void OnCommand(ITftpCommand command, EndPoint endpoint) 28 | { 29 | if (command is Data || command is OptionAcknowledgement) 30 | { 31 | //The server acknowledged our read request. 32 | //Fix out remote endpoint 33 | Context.GetConnection().RemoteEndpoint = endpoint; 34 | } 35 | 36 | if (command is Data) 37 | { 38 | if (Context.NegotiatedOptions == null) 39 | Context.FinishOptionNegotiation(TransferOptionSet.NewEmptySet()); 40 | 41 | //Switch to the receiving state... 42 | ITransferState nextState = new Receiving(); 43 | Context.SetState(nextState); 44 | 45 | //...and let it handle the data packet 46 | nextState.OnCommand(command, endpoint); 47 | } 48 | else if (command is OptionAcknowledgement) 49 | { 50 | //Check which options were acknowledged 51 | Context.FinishOptionNegotiation(new TransferOptionSet( (command as OptionAcknowledgement).Options )); 52 | 53 | //the server acknowledged our options. Confirm the final options 54 | SendAndRepeat(new Acknowledgement(0)); 55 | } 56 | else if (command is Error) 57 | { 58 | Context.SetState(new ReceivedError((Error)command)); 59 | } 60 | else 61 | base.OnCommand(command, endpoint); 62 | } 63 | 64 | public override void OnCancel(TftpErrorPacket reason) 65 | { 66 | Context.SetState(new CancelledByUser(reason)); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/SendWriteRequest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Tftp.Net.Transfer; 7 | using Tftp.Net.Trace; 8 | 9 | namespace Tftp.Net.Transfer.States 10 | { 11 | class SendWriteRequest : StateWithNetworkTimeout 12 | { 13 | public override void OnStateEnter() 14 | { 15 | base.OnStateEnter(); 16 | SendRequest(); 17 | } 18 | 19 | private void SendRequest() 20 | { 21 | WriteRequest request = new WriteRequest(Context.Filename, Context.TransferMode, Context.ProposedOptions.ToOptionList()); 22 | SendAndRepeat(request); 23 | } 24 | 25 | public override void OnCommand(ITftpCommand command, System.Net.EndPoint endpoint) 26 | { 27 | if (command is OptionAcknowledgement) 28 | { 29 | TransferOptionSet acknowledged = new TransferOptionSet((command as OptionAcknowledgement).Options); 30 | Context.FinishOptionNegotiation(acknowledged); 31 | BeginSendingTo(endpoint); 32 | } 33 | else 34 | if (command is Acknowledgement && (command as Acknowledgement).BlockNumber == 0) 35 | { 36 | Context.FinishOptionNegotiation(TransferOptionSet.NewEmptySet()); 37 | BeginSendingTo(endpoint); 38 | } 39 | else 40 | if (command is Error) 41 | { 42 | //The server denied our request 43 | Error error = (Error)command; 44 | Context.SetState(new ReceivedError(error)); 45 | } 46 | else 47 | base.OnCommand(command, endpoint); 48 | } 49 | 50 | private void BeginSendingTo(System.Net.EndPoint endpoint) 51 | { 52 | //Switch to the endpoint that we received from the server 53 | Context.GetConnection().RemoteEndpoint = endpoint; 54 | 55 | //Start sending packets 56 | Context.SetState(new Sending()); 57 | } 58 | 59 | public override void OnCancel(TftpErrorPacket reason) 60 | { 61 | Context.SetState(new CancelledByUser(reason)); 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/Sending.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Tftp.Net.Trace; 7 | 8 | namespace Tftp.Net.Transfer.States 9 | { 10 | class Sending : StateThatExpectsMessagesFromDefaultEndPoint 11 | { 12 | private byte[] lastData; 13 | private ushort lastBlockNumber; 14 | private long bytesSent = 0; 15 | private bool lastPacketWasSent = false; 16 | 17 | public override void OnStateEnter() 18 | { 19 | base.OnStateEnter(); 20 | lastData = new byte[Context.BlockSize]; 21 | SendNextPacket(1); 22 | } 23 | 24 | public override void OnAcknowledgement(Acknowledgement command) 25 | { 26 | //Drop acknowledgments for other packets than the previous one 27 | if (command.BlockNumber != lastBlockNumber) 28 | return; 29 | 30 | //Notify our observers about our progress 31 | bytesSent += lastData.Length; 32 | Context.RaiseOnProgress(bytesSent); 33 | 34 | if (lastPacketWasSent) 35 | { 36 | //We're done here 37 | Context.RaiseOnFinished(); 38 | Context.SetState(new Closed()); 39 | } 40 | else 41 | { 42 | SendNextPacket(Context.BlockCounterWrapping.CalculateNextBlockNumber(lastBlockNumber)); 43 | } 44 | } 45 | 46 | public override void OnError(Error command) 47 | { 48 | Context.SetState(new ReceivedError(command)); 49 | } 50 | 51 | public override void OnCancel(TftpErrorPacket reason) 52 | { 53 | Context.SetState(new CancelledByUser(reason)); 54 | } 55 | 56 | #region Helper Methods 57 | private void SendNextPacket(ushort blockNumber) 58 | { 59 | if (Context.InputOutputStream == null) 60 | return; 61 | 62 | int packetLength = Context.InputOutputStream.Read(lastData, 0, lastData.Length); 63 | lastBlockNumber = blockNumber; 64 | 65 | if (packetLength != lastData.Length) 66 | { 67 | //This means we just sent the last packet 68 | lastPacketWasSent = true; 69 | Array.Resize(ref lastData, packetLength); 70 | } 71 | 72 | ITftpCommand dataCommand = new Data(blockNumber, lastData); 73 | SendAndRepeat(dataCommand); 74 | } 75 | 76 | #endregion 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/StartIncomingRead.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Tftp.Net.Transfer; 7 | 8 | namespace Tftp.Net.Transfer.States 9 | { 10 | class StartIncomingRead : BaseState 11 | { 12 | private readonly IEnumerable optionsRequestedByClient; 13 | 14 | public StartIncomingRead(IEnumerable optionsRequestedByClient) 15 | { 16 | this.optionsRequestedByClient = optionsRequestedByClient; 17 | } 18 | 19 | public override void OnStateEnter() 20 | { 21 | Context.ProposedOptions = new TransferOptionSet(optionsRequestedByClient); 22 | } 23 | 24 | public override void OnStart() 25 | { 26 | Context.FillOrDisableTransferSizeOption(); 27 | Context.FinishOptionNegotiation(Context.ProposedOptions); 28 | List options = Context.NegotiatedOptions.ToOptionList(); 29 | if (options.Count > 0) 30 | { 31 | Context.SetState(new SendOptionAcknowledgementForReadRequest()); 32 | } 33 | else 34 | { 35 | //Otherwise just start sending 36 | Context.SetState(new Sending()); 37 | } 38 | } 39 | 40 | public override void OnCancel(TftpErrorPacket reason) 41 | { 42 | Context.SetState(new CancelledByUser(reason)); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/StartIncomingWrite.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | using Tftp.Net.Transfer; 7 | 8 | namespace Tftp.Net.Transfer.States 9 | { 10 | class StartIncomingWrite : BaseState 11 | { 12 | private readonly IEnumerable optionsRequestedByClient; 13 | public StartIncomingWrite(IEnumerable optionsRequestedByClient) 14 | { 15 | this.optionsRequestedByClient = optionsRequestedByClient; 16 | } 17 | 18 | public override void OnStateEnter() 19 | { 20 | Context.ProposedOptions = new TransferOptionSet(optionsRequestedByClient); 21 | } 22 | 23 | public override void OnStart() 24 | { 25 | //Do we have any acknowledged options? 26 | Context.FinishOptionNegotiation(Context.ProposedOptions); 27 | List options = Context.NegotiatedOptions.ToOptionList(); 28 | if (options.Count > 0) 29 | { 30 | Context.SetState(new SendOptionAcknowledgementForWriteRequest()); 31 | } 32 | else 33 | { 34 | //Start receiving 35 | Context.SetState(new AcknowledgeWriteRequest()); 36 | } 37 | } 38 | 39 | public override void OnCancel(TftpErrorPacket reason) 40 | { 41 | Context.SetState(new CancelledByUser(reason)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/StartOutgoingRead.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Tftp.Net.Transfer.States 8 | { 9 | class StartOutgoingRead : BaseState 10 | { 11 | public override void OnStart() 12 | { 13 | Context.SetState(new SendReadRequest()); 14 | } 15 | 16 | public override void OnCancel(TftpErrorPacket reason) 17 | { 18 | Context.SetState(new Closed()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/StartOutgoingWrite.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Tftp.Net.Transfer.States 8 | { 9 | class StartOutgoingWrite : BaseState 10 | { 11 | public override void OnStart() 12 | { 13 | Context.FillOrDisableTransferSizeOption(); 14 | Context.SetState(new SendWriteRequest()); 15 | } 16 | 17 | public override void OnCancel(TftpErrorPacket reason) 18 | { 19 | Context.SetState(new Closed()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/StateThatExpectsMessagesFromDefaultEndPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Channel; 6 | using System.Net; 7 | 8 | namespace Tftp.Net.Transfer.States 9 | { 10 | class StateThatExpectsMessagesFromDefaultEndPoint : StateWithNetworkTimeout, ITftpCommandVisitor 11 | { 12 | public override void OnCommand(ITftpCommand command, EndPoint endpoint) 13 | { 14 | if (!endpoint.Equals(Context.GetConnection().RemoteEndpoint)) 15 | throw new Exception("Received message from illegal endpoint. Actual: " + endpoint + ". Expected: " + Context.GetConnection().RemoteEndpoint); 16 | 17 | command.Visit(this); 18 | } 19 | 20 | public virtual void OnReadRequest(ReadRequest command) 21 | { 22 | throw new NotImplementedException(); 23 | } 24 | 25 | public virtual void OnWriteRequest(WriteRequest command) 26 | { 27 | throw new NotImplementedException(); 28 | } 29 | 30 | public virtual void OnData(Data command) 31 | { 32 | throw new NotImplementedException(); 33 | } 34 | 35 | public virtual void OnAcknowledgement(Acknowledgement command) 36 | { 37 | throw new NotImplementedException(); 38 | } 39 | 40 | public virtual void OnError(Error command) 41 | { 42 | throw new NotImplementedException(); 43 | } 44 | 45 | public virtual void OnOptionAcknowledgement(OptionAcknowledgement command) 46 | { 47 | throw new NotImplementedException(); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/States/StateWithNetworkTimeout.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Trace; 6 | 7 | namespace Tftp.Net.Transfer.States 8 | { 9 | class StateWithNetworkTimeout : BaseState 10 | { 11 | private SimpleTimer timer; 12 | private ITftpCommand lastCommand; 13 | private int retriesUsed = 0; 14 | 15 | public override void OnStateEnter() 16 | { 17 | timer = new SimpleTimer(Context.RetryTimeout); 18 | } 19 | 20 | public override void OnTimer() 21 | { 22 | if (timer.IsTimeout()) 23 | { 24 | TftpTrace.Trace("Network timeout.", Context); 25 | timer.Restart(); 26 | 27 | if (retriesUsed++ >= Context.RetryCount) 28 | { 29 | TftpTransferError error = new TimeoutError(Context.RetryTimeout, Context.RetryCount); 30 | Context.SetState(new ReceivedError(error)); 31 | } 32 | else 33 | HandleTimeout(); 34 | } 35 | } 36 | 37 | private void HandleTimeout() 38 | { 39 | if (lastCommand != null) 40 | { 41 | Context.GetConnection().Send(lastCommand); 42 | } 43 | } 44 | 45 | protected void SendAndRepeat(ITftpCommand command) 46 | { 47 | Context.GetConnection().Send(command); 48 | lastCommand = command; 49 | ResetTimeout(); 50 | } 51 | 52 | protected void ResetTimeout() 53 | { 54 | timer.Restart(); 55 | retriesUsed = 0; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tftp.Net/Transfer/TransferOptionSet.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Tftp.Net.Transfer; 6 | 7 | namespace Tftp.Net.Transfer 8 | { 9 | class TransferOptionSet 10 | { 11 | public const int DEFAULT_BLOCKSIZE = 512; 12 | public const int DEFAULT_TIMEOUT_SECS = 5; 13 | 14 | public bool IncludesBlockSizeOption = false; 15 | public int BlockSize = DEFAULT_BLOCKSIZE; 16 | 17 | public bool IncludesTimeoutOption = false; 18 | public int Timeout = DEFAULT_TIMEOUT_SECS; 19 | 20 | public bool IncludesTransferSizeOption = false; 21 | public long TransferSize = 0; 22 | 23 | public static TransferOptionSet NewDefaultSet() 24 | { 25 | return new TransferOptionSet() { IncludesBlockSizeOption = true, IncludesTimeoutOption = true, IncludesTransferSizeOption = true }; 26 | } 27 | 28 | public static TransferOptionSet NewEmptySet() 29 | { 30 | return new TransferOptionSet() { IncludesBlockSizeOption = false, IncludesTimeoutOption = false, IncludesTransferSizeOption = false }; 31 | } 32 | 33 | private TransferOptionSet() 34 | { 35 | } 36 | 37 | public TransferOptionSet(IEnumerable options) 38 | { 39 | IncludesBlockSizeOption = IncludesTimeoutOption = IncludesTransferSizeOption = false; 40 | 41 | foreach (TransferOption option in options) 42 | { 43 | Parse(option); 44 | } 45 | } 46 | 47 | private void Parse(TransferOption option) 48 | { 49 | switch (option.Name) 50 | { 51 | case "blksize": 52 | IncludesBlockSizeOption = ParseBlockSizeOption(option.Value); 53 | break; 54 | 55 | case "timeout": 56 | IncludesTimeoutOption = ParseTimeoutOption(option.Value); 57 | break; 58 | 59 | case "tsize": 60 | IncludesTransferSizeOption = ParseTransferSizeOption(option.Value); 61 | break; 62 | } 63 | } 64 | 65 | public List ToOptionList() 66 | { 67 | List result = new List(); 68 | if (IncludesBlockSizeOption) 69 | result.Add(new TransferOption("blksize", BlockSize.ToString())); 70 | 71 | if (IncludesTimeoutOption) 72 | result.Add(new TransferOption("timeout", Timeout.ToString())); 73 | 74 | if (IncludesTransferSizeOption) 75 | result.Add(new TransferOption("tsize", TransferSize.ToString())); 76 | 77 | return result; 78 | } 79 | 80 | private bool ParseTransferSizeOption(string value) 81 | { 82 | return long.TryParse(value, out TransferSize) && TransferSize >= 0; 83 | } 84 | 85 | private bool ParseTimeoutOption(string value) 86 | { 87 | int timeout; 88 | if (!int.TryParse(value, out timeout)) 89 | return false; 90 | 91 | //Only accept timeouts in the range [1, 255] 92 | if (timeout < 1 || timeout > 255) 93 | return false; 94 | 95 | Timeout = timeout; 96 | return true; 97 | } 98 | 99 | private bool ParseBlockSizeOption(string value) 100 | { 101 | int blockSize; 102 | if (!int.TryParse(value, out blockSize)) 103 | return false; 104 | 105 | //Only accept block sizes in the range [8, 65464] 106 | if (blockSize < 8 || blockSize > 65464) 107 | return false; 108 | 109 | BlockSize = blockSize; 110 | return true; 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /wwwroot/minilinux.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shkarlatov/PXE-Server/b9c00cb2a4a0d53e3b78e302a1dd6bd084261747/wwwroot/minilinux.zip -------------------------------------------------------------------------------- /wwwroot/wwwroot_grub2_efi.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shkarlatov/PXE-Server/b9c00cb2a4a0d53e3b78e302a1dd6bd084261747/wwwroot/wwwroot_grub2_efi.zip -------------------------------------------------------------------------------- /wwwroot/wwwroot_ipxe.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shkarlatov/PXE-Server/b9c00cb2a4a0d53e3b78e302a1dd6bd084261747/wwwroot/wwwroot_ipxe.zip -------------------------------------------------------------------------------- /wwwroot/wwwroot_syslinux.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Shkarlatov/PXE-Server/b9c00cb2a4a0d53e3b78e302a1dd6bd084261747/wwwroot/wwwroot_syslinux.zip --------------------------------------------------------------------------------