Browse Source

commit: first - dhcp OK

HOME 4 years ago
commit
bdeb82eee4

+ 233 - 0
.gitignore

@@ -0,0 +1,233 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+.vs/
+
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+build/
+bld/
+[Bb]in/
+[Oo]bj/
+
+# Roslyn cache directories
+*.ide/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+#NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+*_i.c
+*_p.c
+*_i.h
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding addin-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# TODO: Comment the next line if you want to checkin your web deploy settings 
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/packages/*
+# except build/, which is used as an MSBuild target.
+!**/packages/build/
+# If using the old MSBuild-Integrated Package Restore, uncomment this:
+#!**/packages/repositories.config
+
+# Windows Azure Build Output
+csx/
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.pfx
+*.publishsettings
+node_modules/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+*.mdf
+*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# =========================
+# Operating System Files
+# =========================
+
+# OSX
+# =========================
+
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+# Windows
+# =========================
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+# Windows shortcuts
+*.lnk

+ 31 - 0
BootServer.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29728.190
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DhcpServer", "DhcpServer\DhcpServer.csproj", "{D28B4902-AF2D-4D86-B6F9-66B3D6767EF2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TftpServer", "TftpServer\TftpServer.csproj", "{21F3E98E-0210-4118-84F1-8DA4D90BCB0B}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{D28B4902-AF2D-4D86-B6F9-66B3D6767EF2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D28B4902-AF2D-4D86-B6F9-66B3D6767EF2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D28B4902-AF2D-4D86-B6F9-66B3D6767EF2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D28B4902-AF2D-4D86-B6F9-66B3D6767EF2}.Release|Any CPU.Build.0 = Release|Any CPU
+		{21F3E98E-0210-4118-84F1-8DA4D90BCB0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{21F3E98E-0210-4118-84F1-8DA4D90BCB0B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{21F3E98E-0210-4118-84F1-8DA4D90BCB0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{21F3E98E-0210-4118-84F1-8DA4D90BCB0B}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {32F8F112-44BD-458B-A006-0C58E04663C1}
+	EndGlobalSection
+EndGlobal

+ 4 - 0
BootServer.sln.DotSettings

@@ -0,0 +1,4 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=Bootp/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=Dhcp/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/UserDictionary/Words/=Tftp/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

+ 18 - 0
DhcpServer/App.config

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+  <configSections>
+    <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
+      <section name="DhcpServer.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
+    </sectionGroup>
+  </configSections>
+  <startup>
+    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
+  </startup>
+  <applicationSettings>
+    <DhcpServer.Properties.Settings>
+      <setting name="ListenOn" serializeAs="String">
+        <value>192.168.66.55</value>
+      </setting>
+    </DhcpServer.Properties.Settings>
+  </applicationSettings>
+</configuration>

+ 16 - 0
DhcpServer/App/ClientEntry.cs

@@ -0,0 +1,16 @@
+namespace DhcpServer
+{
+    internal class ClientEntry
+    {
+        public bool Enable { get; set; }
+
+        public string IpAddress { get; set; }
+        public string SubNetMask { get; set; }
+        public string GateWay { get; set; }
+
+        public string DnsServer { get; set; }
+
+        public string BootFileName { get; set; }
+        public string TftpServer { get; set; }
+    }
+}

+ 20 - 0
DhcpServer/App/DhcpPacket.cs

@@ -0,0 +1,20 @@
+using System.Linq;
+using System.Net;
+
+namespace DhcpServer
+{
+    public partial class DhcpPacket
+    {
+        public string ClientMacAddressHex => string.Join("-", ClientMacAddress.Select(p => p.ToString("X2")));
+
+        internal void LoadClientEntry(ClientEntry entry)
+        {
+            if (entry.IpAddress != null) YourIpAddress = IPAddress.Parse(entry.IpAddress);
+            if (entry.SubNetMask != null) SubNetMask = IPAddress.Parse(entry.SubNetMask);
+            if (entry.GateWay != null) Router = IPAddress.Parse(entry.GateWay);
+            if (entry.DnsServer != null) DnsServer = IPAddress.Parse(entry.DnsServer);
+            if (entry.TftpServer != null) TftpServer = entry.TftpServer;
+            if (entry.BootFileName != null) BootFileName = entry.BootFileName;
+        }
+    }
+}

+ 44 - 0
DhcpServer/AppConfigs.cs

@@ -0,0 +1,44 @@
+using System;
+using System.IO;
+using Newtonsoft.Json;
+
+namespace DhcpServer
+{
+    internal static class AppConfigs
+    {
+        private const string DefaultConfigFileName = "Default.json";
+
+        public static readonly string ConfigFileDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs");
+        public static readonly string DefaultConfigFilePath = Path.Combine(ConfigFileDir, DefaultConfigFileName);
+
+        public static string GetClientEntryPath(string mac)
+        {
+            return Path.Combine(ConfigFileDir, mac + ".json");
+        }
+
+        public static bool CreateDefaultClientEntryIfNoExist()
+        {
+            if (File.Exists(DefaultConfigFilePath)) return false;
+            if (false == Directory.Exists(ConfigFileDir)) Directory.CreateDirectory(ConfigFileDir);
+            File.WriteAllText(DefaultConfigFilePath, JsonConvert.SerializeObject(new ClientEntry(), Formatting.Indented));
+            return true;
+        }
+
+        public static ClientEntry GetDefaultClientEntry()
+        {
+            return File.Exists(DefaultConfigFilePath)
+                ? JsonConvert.DeserializeObject<ClientEntry>(File.ReadAllText(DefaultConfigFilePath))
+                : null;
+        }
+
+        public static ClientEntry GetClientEntry(string mac)
+        {
+            var path = GetClientEntryPath(mac);
+            if (File.Exists(path)) return JsonConvert.DeserializeObject<ClientEntry>(File.ReadAllText(path));
+
+            var createNew = new ClientEntry();
+            File.WriteAllText(path, JsonConvert.SerializeObject(new ClientEntry(), Formatting.Indented));
+            return createNew;
+        }
+    }
+}

+ 132 - 0
DhcpServer/DhcpProgram.cs

@@ -0,0 +1,132 @@
+using System;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+
+namespace DhcpServer
+{
+    internal class DhcpProgram
+    {
+        private static bool _isRunning;
+
+        private static void Main(string[] args)
+        {
+            Console.WriteLine("Starting...");
+
+            if (AppConfigs.CreateDefaultClientEntryIfNoExist()) Console.WriteLine("Default client entry config created.");
+
+            var tWorker = new Thread(Working);
+            _isRunning = true;
+            tWorker.Start();
+
+            Console.WriteLine("Press ENTER to Stop.");
+            Console.ReadLine();
+
+            Console.Write("Shutting down...");
+            _isRunning = false;
+            tWorker.Join();
+
+            Console.Write("Stopped.");
+
+            Console.WriteLine();
+            Console.Write("Press ENTER to Exit.");
+            Console.ReadLine();
+        }
+
+        private static void Working(object obj)
+        {
+            var listenEndPoint = new IPEndPoint(IPAddress.Parse(Properties.Settings.Default.ListenOn), 67);
+
+            var socket = new Socket(listenEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
+            {
+                EnableBroadcast = true,
+                SendBufferSize = 65536,
+                ReceiveBufferSize = 65536
+            };
+            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
+            socket.Bind(listenEndPoint);
+
+            var upTime = DateTime.Now;
+
+            Console.WriteLine($"DHCP Server started, listing on: {listenEndPoint}");
+
+            var buffer = new byte[65536];
+            EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 68);
+            var polling = DateTime.Now;
+            while (_isRunning)
+            {
+                Console.CursorLeft = 0;
+                if (false == socket.Poll(500 * 1000, SelectMode.SelectRead))
+                {
+                    var timeSpan = DateTime.Now - polling;
+                    var up = DateTime.Now - upTime;
+                    Console.Write($"Polling..." +
+                                  $" {timeSpan.Days:00}D {timeSpan.Hours:00}H {timeSpan.Minutes:00}M {timeSpan.Seconds:00}S {timeSpan.Milliseconds:000}" +
+                                  $" / UP {up.Days:00}D {up.Hours:00}H {up.Minutes:00}M {up.Seconds:00}S {up.Milliseconds:000}");
+                }
+                else
+                {
+                    polling = DateTime.Now;
+                    Console.WriteLine();
+
+                    var bytes = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref remoteEndPoint);
+                    Console.Write($"Receive {bytes} byte From {remoteEndPoint}");
+
+                    try
+                    {
+                        var packet = new DhcpPacket(buffer);
+
+                        var mac = packet.ClientMacAddressHex;
+                        Console.Write($" {packet.MessageType} by {mac}");
+
+                        var entry = AppConfigs.GetClientEntry(mac);
+                        if (entry.Enable == false)
+                        {
+                            Console.WriteLine(" ** No enabled, please edit config file.");
+                        }
+                        else
+                        {
+                            packet.OpCode = DhcpOpCode.BootReply;
+                            DhcpMessageType reply;
+                            switch (packet.MessageType)
+                            {
+                                default:
+                                    reply = DhcpMessageType.Unknown;
+                                    break;
+
+                                case DhcpMessageType.Discover:
+                                    reply = DhcpMessageType.Offer;
+                                    break;
+
+                                case DhcpMessageType.Request:
+                                    reply = DhcpMessageType.Ack;
+                                    break;
+                            }
+
+                            if (reply != DhcpMessageType.Unknown)
+                            {
+                                packet.Options.Clear();
+                                packet.DhcpServerIdentifier = listenEndPoint.Address;
+                                packet.MessageType = reply;
+                                packet.LoadClientEntry(AppConfigs.GetDefaultClientEntry());
+                                packet.LoadClientEntry(entry);
+
+                                bytes = packet.WriteToBuffer(buffer);
+                                var to = new IPEndPoint(IPAddress.Broadcast, 68);
+                                Console.WriteLine();
+                                Console.WriteLine($"Send {bytes} bytes to {to} {packet.MessageType} by {mac}");
+                                socket.SendTo(buffer, 0, bytes, SocketFlags.None, to);
+                            }
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        Console.WriteLine(e);
+                    }
+                }
+            }
+
+            socket.Close();
+        }
+    }
+}

+ 78 - 0
DhcpServer/DhcpServer.csproj

@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{D28B4902-AF2D-4D86-B6F9-66B3D6767EF2}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>DhcpServer</RootNamespace>
+    <AssemblyName>DhcpServer</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8</LangVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8</LangVersion>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
+      <HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
+    </Reference>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="AppConfigs.cs" />
+    <Compile Include="App\ClientEntry.cs" />
+    <Compile Include="App\DhcpPacket.cs" />
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+      <DependentUpon>Settings.settings</DependentUpon>
+    </Compile>
+    <Compile Include="Protocol\DhcpBootpFlag.cs" />
+    <Compile Include="Protocol\DhcpHardwareType.cs" />
+    <Compile Include="Protocol\DhcpMessageType.cs" />
+    <Compile Include="Protocol\DhcpOpCode.cs" />
+    <Compile Include="Protocol\DhcpOption.cs" />
+    <Compile Include="Protocol\DhcpPacket.cs" />
+    <Compile Include="DhcpProgram.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Utils\ExtensionMethods.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+    <None Include="packages.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 3 - 0
DhcpServer/DhcpServer.csproj.DotSettings

@@ -0,0 +1,3 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=app/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=protocol/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

+ 36 - 0
DhcpServer/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("DhcpServer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DhcpServer")]
+[assembly: AssemblyCopyright("Copyright ©  2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("d28b4902-af2d-4d86-b6f9-66b3d6767ef2")]
+
+// 程序集的版本信息由下列四个值组成: 
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
+//通过使用 "*",如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 35 - 0
DhcpServer/Properties/Settings.Designer.cs

@@ -0,0 +1,35 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace DhcpServer.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("0.0.0.0")]
+        public string ListenOn {
+            get {
+                return ((string)(this["ListenOn"]));
+            }
+        }
+    }
+}

+ 9 - 0
DhcpServer/Properties/Settings.settings

@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="DhcpServer.Properties" GeneratedClassName="Settings">
+  <Profiles />
+  <Settings>
+    <Setting Name="ListenOn" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">0.0.0.0</Value>
+    </Setting>
+  </Settings>
+</SettingsFile>

+ 10 - 0
DhcpServer/Protocol/DhcpBootpFlag.cs

@@ -0,0 +1,10 @@
+using System;
+
+namespace DhcpServer
+{
+    [Flags]
+    public enum DhcpBootpFlag : ushort
+    {
+        Broadcast = 1 << 15
+    }
+}

+ 8 - 0
DhcpServer/Protocol/DhcpHardwareType.cs

@@ -0,0 +1,8 @@
+namespace DhcpServer
+{
+    public enum DhcpHardwareType
+    {
+        Unknown = 0,
+        Ethernet = 1,
+    }
+}

+ 15 - 0
DhcpServer/Protocol/DhcpMessageType.cs

@@ -0,0 +1,15 @@
+namespace DhcpServer
+{
+    public enum DhcpMessageType : byte
+    {
+        Unknown = 0,
+        Discover = 1,
+        Offer = 2,
+        Request = 3,
+        Decline = 4,
+        Ack = 5,
+        Nak = 6,
+        Release = 7,
+        Conform = 8
+    }
+}

+ 9 - 0
DhcpServer/Protocol/DhcpOpCode.cs

@@ -0,0 +1,9 @@
+namespace DhcpServer
+{
+    public enum DhcpOpCode
+    {
+        Unknown = 0,
+        BootRequest = 1,
+        BootReply = 2,
+    }
+}

+ 12 - 0
DhcpServer/Protocol/DhcpOption.cs

@@ -0,0 +1,12 @@
+namespace DhcpServer
+{
+    public enum DhcpOption : byte
+    {
+        SubNetMask = 1,
+        Router = 3,
+        DnsServer = 6,
+        DhcpMessageType = 53,
+        DhcpServerIdentifier = 54,
+        TftpServer = 66,
+    }
+}

+ 153 - 0
DhcpServer/Protocol/DhcpPacket.cs

@@ -0,0 +1,153 @@
+using DhcpServer.Utils;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+
+namespace DhcpServer
+{
+    public partial class DhcpPacket
+    {
+        public DhcpOpCode OpCode { get; set; }
+        public DhcpHardwareType HardwareType { get; set; }
+        public byte Hops { get; set; }
+        public int TransactionId { get; set; }
+
+        public short SecondsElapsed { get; set; }
+
+        public DhcpBootpFlag BootpFlags { get; set; }
+        public IPAddress ClientIpAddress { get; set; }
+
+        public IPAddress YourIpAddress { get; set; }
+
+        public IPAddress NextServerIpAddress { get; set; }
+
+        public IPAddress RelayAgentIpAddress { get; set; }
+
+        public byte[] ClientMacAddress { get; set; }
+
+        public string ServerHostName { get; set; }
+        public string BootFileName { get; set; }
+
+        public int MagicCookie { get; set; } = 0x63825363;
+
+        public Dictionary<DhcpOption, byte[]> Options { get; set; } = new Dictionary<DhcpOption, byte[]>();
+
+        public DhcpMessageType MessageType
+        {
+            get => Options.MapValue(DhcpOption.DhcpMessageType, value => (DhcpMessageType)value[0]);
+
+            set => Options[DhcpOption.DhcpMessageType] = new[] { (byte)value };
+        }
+
+        public IPAddress SubNetMask
+        {
+            get => Options.MapValue(DhcpOption.SubNetMask, value => new IPAddress(value));
+            set => Options[DhcpOption.SubNetMask] = value.GetAddressBytes();
+        }
+
+        public IPAddress Router
+        {
+            get => Options.MapValue(DhcpOption.Router, value => new IPAddress(value));
+            set => Options[DhcpOption.Router] = value.GetAddressBytes();
+        }
+
+        public IPAddress DnsServer
+        {
+            get => Options.MapValue(DhcpOption.DnsServer, value => new IPAddress(value));
+            set => Options[DhcpOption.DnsServer] = value.GetAddressBytes();
+        }
+
+        public string TftpServer
+        {
+            get => Options.MapValue(DhcpOption.TftpServer, value => Encoding.ASCII.GetString(value));
+            set => Options[DhcpOption.TftpServer] = Encoding.ASCII.GetBytes(value);
+        }
+
+        public IPAddress DhcpServerIdentifier
+        {
+            get => Options.MapValue(DhcpOption.DhcpServerIdentifier, value => new IPAddress(value));
+            set => Options[DhcpOption.DhcpServerIdentifier] = value.GetAddressBytes();
+        }
+
+        public DhcpPacket(byte[] buffer)
+        {
+            using var stream = new MemoryStream(buffer);
+            using var reader = new BinaryReader(stream);
+            OpCode = (DhcpOpCode)reader.ReadByte();
+            HardwareType = (DhcpHardwareType)reader.ReadByte();
+            var hardwareAddressLength = reader.ReadByte();
+            Hops = reader.ReadByte();
+            TransactionId = reader.ReadInt32();
+            SecondsElapsed = IPAddress.NetworkToHostOrder(reader.ReadInt16());
+            BootpFlags = (DhcpBootpFlag)IPAddress.NetworkToHostOrder(reader.ReadInt16());
+            ClientIpAddress = new IPAddress(reader.ReadBytes(4));
+            YourIpAddress = new IPAddress(reader.ReadBytes(4));
+            NextServerIpAddress = new IPAddress(reader.ReadBytes(4));
+            RelayAgentIpAddress = new IPAddress(reader.ReadBytes(4));
+            ClientMacAddress = reader.ReadBytes(hardwareAddressLength);
+            stream.Position += 16 - hardwareAddressLength; // Skip padding bytes
+            ServerHostName = Encoding.ASCII.GetString(reader.ReadBytes(64)).Trim('\0');
+            BootFileName = Encoding.ASCII.GetString(reader.ReadBytes(128)).Trim('\0');
+            MagicCookie = reader.ReadInt32();
+
+            do
+            {
+                var option = reader.ReadByte();
+                if (option == 0xFF) break;
+
+                Options[(DhcpOption)option] = reader.ReadBytes(reader.ReadByte());
+            } while (true);
+        }
+
+        public int WriteToBuffer(byte[] buffer)
+        {
+            using var stream = new MemoryStream(buffer);
+            using var writer = new BinaryWriter(stream);
+            writer.Write((byte)OpCode);
+            writer.Write((byte)HardwareType);
+            writer.Write((byte)ClientMacAddress.Length);
+            writer.Write(Hops);
+            writer.Write(TransactionId);
+            writer.Write(IPAddress.HostToNetworkOrder(SecondsElapsed));
+            writer.Write(IPAddress.HostToNetworkOrder((short)BootpFlags));
+            writer.Write(ClientIpAddress.GetAddressBytes());
+            writer.Write(YourIpAddress.GetAddressBytes());
+            writer.Write(NextServerIpAddress.GetAddressBytes());
+            writer.Write(RelayAgentIpAddress.GetAddressBytes());
+
+            writer.Write(ClientMacAddress);
+            stream.Position += 16 - ClientMacAddress.Length;
+
+            var buf = Encoding.ASCII.GetBytes(ServerHostName);
+            writer.Write(buf);
+            stream.Position += 64 - buf.Length;
+
+            buf = Encoding.ASCII.GetBytes(BootFileName);
+            writer.Write(buf);
+            stream.Position += 128 - buf.Length;
+
+            writer.Write(MagicCookie);
+
+            foreach (var option in Options)
+            {
+                writer.Write((byte)option.Key);
+                writer.Write((byte)option.Value.Length);
+                writer.Write(option.Value);
+            }
+            writer.Write((byte)0xFF);
+
+            return (int)(stream.Position);
+        }
+
+        public DhcpPacket()
+        {
+        }
+
+        public int GetByteCount()
+        {
+            throw new NotImplementedException();
+        }
+    }
+}

+ 22 - 0
DhcpServer/Utils/ExtensionMethods.cs

@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+
+namespace DhcpServer.Utils
+{
+    internal static class ExtensionMethods
+    {
+        public static TR MapValue<TK, TV, TR>(this Dictionary<TK, TV> dic, TK key, Func<TV, TR> found)
+        {
+            return dic.TryGetValue(key, out var value)
+                ? found(value)
+                : default;
+        }
+
+        public static TR MapValue<TK, TV, TR>(this Dictionary<TK, TV> dic, TK key, Func<TV, TR> found, Func<TR> notFound)
+        {
+            return dic.TryGetValue(key, out var value)
+                ? found(value)
+                : notFound();
+        }
+    }
+}

+ 4 - 0
DhcpServer/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="Newtonsoft.Json" version="12.0.3" targetFramework="net472" />
+</packages>

+ 21 - 0
TftpServer/App.config

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<configuration>
+    <configSections>
+        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
+            <section name="TftpServer.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
+        </sectionGroup>
+    </configSections>
+    <startup> 
+        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
+    </startup>
+    <applicationSettings>
+        <TftpServer.Properties.Settings>
+            <setting name="ListenOn" serializeAs="String">
+                <value>0.0.0.0</value>
+            </setting>
+            <setting name="Root" serializeAs="String">
+                <value>Root</value>
+            </setting>
+        </TftpServer.Properties.Settings>
+    </applicationSettings>
+</configuration>

+ 36 - 0
TftpServer/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// 有关程序集的一般信息由以下
+// 控制。更改这些特性值可修改
+// 与程序集关联的信息。
+[assembly: AssemblyTitle("TftpServer")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("TftpServer")]
+[assembly: AssemblyCopyright("Copyright ©  2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 将 ComVisible 设置为 false 会使此程序集中的类型
+//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型
+//请将此类型的 ComVisible 特性设置为 true。
+[assembly: ComVisible(false)]
+
+// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID
+[assembly: Guid("21f3e98e-0210-4118-84f1-8da4d90bcb0b")]
+
+// 程序集的版本信息由下列四个值组成: 
+//
+//      主版本
+//      次版本
+//      生成号
+//      修订号
+//
+//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
+//通过使用 "*",如下所示:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 44 - 0
TftpServer/Properties/Settings.Designer.cs

@@ -0,0 +1,44 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace TftpServer.Properties {
+    
+    
+    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")]
+    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
+        
+        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+        
+        public static Settings Default {
+            get {
+                return defaultInstance;
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("0.0.0.0")]
+        public string ListenOn {
+            get {
+                return ((string)(this["ListenOn"]));
+            }
+        }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("Root")]
+        public string Root {
+            get {
+                return ((string)(this["Root"]));
+            }
+        }
+    }
+}

+ 12 - 0
TftpServer/Properties/Settings.settings

@@ -0,0 +1,12 @@
+<?xml version='1.0' encoding='utf-8'?>
+<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="TftpServer.Properties" GeneratedClassName="Settings">
+  <Profiles />
+  <Settings>
+    <Setting Name="ListenOn" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">0.0.0.0</Value>
+    </Setting>
+    <Setting Name="Root" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">Root</Value>
+    </Setting>
+  </Settings>
+</SettingsFile>

+ 11 - 0
TftpServer/Protocol/TftpOpCode.cs

@@ -0,0 +1,11 @@
+namespace TftpServer
+{
+    public enum TftpOpCode : short
+    {
+        Read = 1,
+        Write = 2,
+        Data = 3,
+        Ack = 4,
+        Error = 5,
+    }
+}

+ 48 - 0
TftpServer/Protocol/TftpPacket.cs

@@ -0,0 +1,48 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Net;
+using System.Text;
+using TftpServer.Utils;
+
+namespace TftpServer
+{
+    public class TftpPacket
+    {
+        public TftpOpCode OpCode { get; set; }
+
+        public string FileName { get; set; }
+        public string Mode { get; set; }
+
+        public short BlockNumber { get; set; }
+        public byte[] Data { get; set; }
+
+        public short ErrorNumber { get; set; }
+        public string ErrorMessage { get; set; }
+
+        public Dictionary<string, byte> Options { get; set; }
+
+        public TftpPacket(byte[] buffer, int length)
+        {
+            using var stream = new MemoryStream(buffer);
+            using var reader = new BinaryReader(stream);
+
+            OpCode = (TftpOpCode)IPAddress.NetworkToHostOrder(reader.ReadInt16());
+
+            switch (OpCode)
+            {
+                default: throw new InvalidDataException($"Unknown OpCode: {OpCode} (0x{OpCode:X4})");
+                case TftpOpCode.Read:
+                    var len = buffer.ScanNullTerminated(stream.Position);
+                    FileName = Encoding.ASCII.GetString(buffer, (int)stream.Position, len);
+                    stream.Position += len;
+                    len = buffer.ScanNullTerminated(stream.Position);
+                    Mode = Encoding.ASCII.GetString(buffer, (int)stream.Position, len);
+                    stream.Position += len;
+                    break;
+                case TftpOpCode.Ack:
+                    BlockNumber = IPAddress.NetworkToHostOrder(reader.ReadInt16());
+                    break;
+            }
+        }
+    }
+}

+ 95 - 0
TftpServer/TftpProgram.cs

@@ -0,0 +1,95 @@
+using System;
+using System.IO;
+using System.Net;
+using System.Net.Sockets;
+using System.Threading;
+using TftpServer.Utils;
+
+namespace TftpServer
+{
+    internal class TftpProgram
+    {
+        private static bool _isRunning;
+        private static string Root;
+        private static void Main(string[] args)
+        {
+            Console.WriteLine("Starting...");
+
+            Root = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Properties.Settings.Default.Root);
+
+            var tWorker = new Thread(Working);
+            _isRunning = true;
+            tWorker.Start();
+
+            Console.WriteLine("Press ENTER to Stop.");
+            Console.ReadLine();
+
+            Console.Write("Shutting down...");
+            _isRunning = false;
+            tWorker.Join();
+
+            Console.Write("Stopped.");
+
+            Console.WriteLine();
+            Console.Write("Press ENTER to Exit.");
+            Console.ReadLine();
+        }
+
+        private static void Working()
+        {
+            var listenEndPoint = new IPEndPoint(IPAddress.Parse(Properties.Settings.Default.ListenOn), 69);
+
+            var socket = new Socket(listenEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
+            {
+                SendBufferSize = 65536,
+                ReceiveBufferSize = 65536
+            };
+            socket.Bind(listenEndPoint);
+
+            var upTime = DateTime.Now;
+
+            Console.WriteLine($"TFTP Server started, listing on: {listenEndPoint}");
+
+            var buffer = new byte[65536];
+            EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 68);
+            var polling = DateTime.Now;
+            while (_isRunning)
+            {
+                Console.CursorLeft = 0;
+                if (false == socket.Poll(500 * 1000, SelectMode.SelectRead))
+                {
+                    var timeSpan = DateTime.Now - polling;
+                    var up = DateTime.Now - upTime;
+                    Console.Write($"Polling..." +
+                                  $" {timeSpan.Days:00}D {timeSpan.Hours:00}H {timeSpan.Minutes:00}M {timeSpan.Seconds:00}S {timeSpan.Milliseconds:000}" +
+                                  $" / UP {up.Days:00}D {up.Hours:00}H {up.Minutes:00}M {up.Seconds:00}S {up.Milliseconds:000}");
+                }
+                else
+                {
+                    polling = DateTime.Now;
+                    Console.WriteLine();
+
+                    var bytes = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref remoteEndPoint);
+                    Console.Write($"Receive {bytes} byte From {remoteEndPoint}");
+
+                    try
+                    {
+                        var packet = new TftpPacket(buffer, bytes);
+                        switch (packet.OpCode)
+                        {
+                            default:
+
+                                break;
+                        }
+
+                    }
+                    catch (Exception e)
+                    {
+                        Console.WriteLine(e);
+                    }
+                    Console.WriteLine();
+                }
+            }
+        }
+    }
+}

+ 70 - 0
TftpServer/TftpServer.csproj

@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProjectGuid>{21F3E98E-0210-4118-84F1-8DA4D90BCB0B}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>TftpServer</RootNamespace>
+    <AssemblyName>TftpServer</AssemblyName>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8</LangVersion>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <PlatformTarget>AnyCPU</PlatformTarget>
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+    <LangVersion>8</LangVersion>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Xml.Linq" />
+    <Reference Include="System.Data.DataSetExtensions" />
+    <Reference Include="Microsoft.CSharp" />
+    <Reference Include="System.Data" />
+    <Reference Include="System.Net.Http" />
+    <Reference Include="System.Xml" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="TftpProgram.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Settings.Designer.cs">
+      <DependentUpon>Settings.settings</DependentUpon>
+      <AutoGen>True</AutoGen>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+    </Compile>
+    <Compile Include="Protocol\TftpOpCode.cs" />
+    <Compile Include="Protocol\TftpPacket.cs" />
+    <Compile Include="Utils\ExtensionMethods.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <Folder Include="App\" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 3 - 0
TftpServer/TftpServer.csproj.DotSettings

@@ -0,0 +1,3 @@
+<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
+	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=app/@EntryIndexedValue">True</s:Boolean>
+	<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=protocol/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

+ 23 - 0
TftpServer/Utils/ExtensionMethods.cs

@@ -0,0 +1,23 @@
+namespace TftpServer.Utils
+{
+    internal static class ExtensionMethods
+    {
+        public static int ScanNullTerminated(this byte[] bytes, long offset)
+        {
+            return ScanTo(bytes, offset, 0);
+        }
+
+        public static int ScanTo(this byte[] bytes, long offset, byte match)
+        {
+            for (var i = offset; i < bytes.Length; i++)
+            {
+                if (bytes[i] == match)
+                {
+                    return (int)(i - offset);
+                }
+            }
+
+            return -1;
+        }
+    }
+}