Przeglądaj źródła

commit: http server for ipxe added.

HOME 5 lat temu
rodzic
commit
aa6b0f339b

+ 6 - 0
BootServer.sln

@@ -9,6 +9,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TftpServer", "TftpServer\Tf
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utils", "Utils\Utils.csproj", "{369BBF56-A30E-478F-AE99-8427BC7415E8}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HttpServer", "HttpServer\HttpServer.csproj", "{9FBE44BC-56DA-4F12-BA69-DEA537693014}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -27,6 +29,10 @@ Global
 		{369BBF56-A30E-478F-AE99-8427BC7415E8}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{369BBF56-A30E-478F-AE99-8427BC7415E8}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{369BBF56-A30E-478F-AE99-8427BC7415E8}.Release|Any CPU.Build.0 = Release|Any CPU
+		{9FBE44BC-56DA-4F12-BA69-DEA537693014}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{9FBE44BC-56DA-4F12-BA69-DEA537693014}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{9FBE44BC-56DA-4F12-BA69-DEA537693014}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{9FBE44BC-56DA-4F12-BA69-DEA537693014}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE

+ 1 - 0
BootServer.sln.DotSettings

@@ -2,5 +2,6 @@
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=blksize/@EntryIndexedValue">True</s:Boolean>
 	<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/=ipxe/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=Tftp/@EntryIndexedValue">True</s:Boolean>
 	<s:Boolean x:Key="/Default/UserDictionary/Words/=tsize/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

+ 1 - 1
DhcpServer/App/ClientEntry.cs

@@ -1,6 +1,6 @@
 namespace DhcpServer
 {
-    internal class ClientEntry
+    internal class DhcpEntry
     {
         public bool Enable { get; set; }
 

+ 22 - 22
DhcpServer/AppConfigs.cs

@@ -3,12 +3,13 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Text;
+using Utils;
 
 namespace DhcpServer
 {
-    internal static class AppConfigs
+    internal static class DhcpEntryManager
     {
-        private static readonly string ConfigFileDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs");
+        private static readonly string EntriesDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "DhcpEntries");
 
         private static readonly HashSet<char> InvalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());
 
@@ -17,51 +18,50 @@ namespace DhcpServer
             if (input == null) return null;
 
             var sb = new StringBuilder(input);
-            for (int i = 0; i < sb.Length; i++)
-            {
-                if (InvalidFileNameChars.Contains(sb[i])) sb[i] = '_';
-            }
+            sb.FilterChars(InvalidFileNameChars);
 
             sb.Insert(0, '-');
             return sb.ToString();
         }
 
-        private static string GetClientEntryPath(string mac, string userClass = null)
+        
+
+        private static string GetEntryPath(string mac, string userClass = null)
         {
-            return Path.Combine(ConfigFileDir, mac + FilterUserClass(userClass) + ".json");
+            return Path.Combine(EntriesDir, "MAC-" + mac + FilterUserClass(userClass) + ".json");
         }
 
         private static string GetDefaultEntryPath(string userClass = null)
         {
-            return Path.Combine(ConfigFileDir, "Default" + FilterUserClass(userClass) + ".json");
+            return Path.Combine(EntriesDir, "Default" + FilterUserClass(userClass) + ".json");
         }
 
-        public static bool CreateDefaultClientEntryIfNoExist()
+        public static bool CreateDefaultEntryIfNoExist()
         {
             var path = GetDefaultEntryPath();
             if (File.Exists(path)) return false;
-            if (false == Directory.Exists(ConfigFileDir)) Directory.CreateDirectory(ConfigFileDir);
-            File.WriteAllText(path, JsonConvert.SerializeObject(new ClientEntry(), Formatting.Indented));
+            if (false == Directory.Exists(EntriesDir)) Directory.CreateDirectory(EntriesDir);
+            File.WriteAllText(path, JsonConvert.SerializeObject(new DhcpEntry(), Formatting.Indented));
             return true;
         }
 
-        public static ClientEntry GetDefaultClientEntry(string userClass = null)
+        public static DhcpEntry GetDefaultEntry(string userClass = null)
         {
             var path = GetDefaultEntryPath(userClass);
-            if (File.Exists(path)) return JsonConvert.DeserializeObject<ClientEntry>(File.ReadAllText(path));
-            
-            var createNew = new ClientEntry();
-            File.WriteAllText(path, JsonConvert.SerializeObject(new ClientEntry(), Formatting.Indented));
+            if (File.Exists(path)) return JsonConvert.DeserializeObject<DhcpEntry>(File.ReadAllText(path));
+
+            var createNew = new DhcpEntry();
+            File.WriteAllText(path, JsonConvert.SerializeObject(new DhcpEntry(), Formatting.Indented));
             return createNew;
         }
 
-        public static ClientEntry GetClientEntry(string mac, string userClass = null)
+        public static DhcpEntry GetClientEntry(string mac, string userClass = null)
         {
-            var path = GetClientEntryPath(mac, userClass);
-            if (File.Exists(path)) return JsonConvert.DeserializeObject<ClientEntry>(File.ReadAllText(path));
+            var path = GetEntryPath(mac, userClass);
+            if (File.Exists(path)) return JsonConvert.DeserializeObject<DhcpEntry>(File.ReadAllText(path));
 
-            var createNew = new ClientEntry();
-            File.WriteAllText(path, JsonConvert.SerializeObject(new ClientEntry(), Formatting.Indented));
+            var createNew = new DhcpEntry();
+            File.WriteAllText(path, JsonConvert.SerializeObject(new DhcpEntry(), Formatting.Indented));
             return createNew;
         }
     }

+ 1 - 1
DhcpServer/App/DhcpPacket.cs

@@ -8,7 +8,7 @@ namespace DhcpServer
     {
         public string ClientMacAddressHex => string.Join("-", ClientMacAddress.Select(p => p.ToString("X2")));
 
-        internal void LoadClientEntry(ClientEntry entry)
+        internal void LoadClientEntry(DhcpEntry entry)
         {
             if (entry == null) throw new ArgumentNullException(nameof(entry));
 

+ 14 - 7
DhcpServer/DhcpProgram.cs

@@ -13,7 +13,7 @@ namespace DhcpServer
         {
             Console.WriteLine("Starting...");
 
-            if (AppConfigs.CreateDefaultClientEntryIfNoExist()) Console.WriteLine("Default client entry config created.");
+            if (DhcpEntryManager.CreateDefaultEntryIfNoExist()) Console.WriteLine("Default client entry config created.");
 
             var tWorker = new Thread(Working);
             _isRunning = true;
@@ -53,6 +53,8 @@ namespace DhcpServer
             var buffer = new byte[65536];
             EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 68);
             var polling = DateTime.Now;
+
+            var pooled = false;
             while (_isRunning)
             {
                 Console.CursorLeft = 0;
@@ -60,14 +62,19 @@ namespace DhcpServer
                 {
                     var timeSpan = DateTime.Now - polling;
                     var up = DateTime.Now - upTime;
-                    Console.Write($"Polling..." +
+                    Console.Write("Polling sockets..." +
                                   $" {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}");
+                    pooled = true;
                 }
                 else
                 {
                     polling = DateTime.Now;
-                    Console.WriteLine();
+                    if (pooled)
+                    {
+                        pooled = false;
+                        Console.WriteLine();
+                    }
 
                     var bytes = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref remoteEndPoint);
                     Console.Write($"Receive {bytes} byte From {remoteEndPoint}");
@@ -79,7 +86,7 @@ namespace DhcpServer
                         var mac = packet.ClientMacAddressHex;
                         Console.Write($" {packet.MessageType} by {mac}");
 
-                        var entry = AppConfigs.GetClientEntry(mac);
+                        var entry = DhcpEntryManager.GetClientEntry(mac);
                         if (entry.Enable == false)
                         {
                             Console.WriteLine(" ** No enabled, please edit config file.");
@@ -110,11 +117,11 @@ namespace DhcpServer
                                 packet.DhcpServerIdentifier = listenEndPoint.Address;
                                 packet.MessageType = reply;
 
-                                packet.LoadClientEntry(AppConfigs.GetDefaultClientEntry());
-                                packet.LoadClientEntry(AppConfigs.GetDefaultClientEntry(userClass));
+                                packet.LoadClientEntry(DhcpEntryManager.GetDefaultEntry());
+                                packet.LoadClientEntry(DhcpEntryManager.GetDefaultEntry(userClass));
 
                                 packet.LoadClientEntry(entry);
-                                packet.LoadClientEntry(AppConfigs.GetClientEntry(mac, userClass));
+                                packet.LoadClientEntry(DhcpEntryManager.GetClientEntry(mac, userClass));
 
                                 bytes = packet.WriteToBuffer(buffer);
                                 var to = new IPEndPoint(IPAddress.Broadcast, 68);

+ 2 - 2
DhcpServer/DhcpServer.csproj

@@ -48,8 +48,8 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="AppConfigs.cs" />
-    <Compile Include="App\ClientEntry.cs" />
+    <Compile Include="App\DhcpEntryManager.cs" />
+    <Compile Include="App\DhcpEntry.cs" />
     <Compile Include="App\DhcpPacket.cs" />
     <Compile Include="Properties\Settings.Designer.cs">
       <AutoGen>True</AutoGen>

+ 18 - 0
HttpServer/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="HttpServer.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>
+        <HttpServer.Properties.Settings>
+            <setting name="ListenPrefix" serializeAs="String">
+                <value>http://*:8444/</value>
+            </setting>
+        </HttpServer.Properties.Settings>
+    </applicationSettings>
+</configuration>

+ 36 - 0
HttpServer/App/IpxeScriptManager.cs

@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using Utils;
+
+namespace HttpServer.App
+{
+    internal static class IpxeScriptManager
+    {
+        private static readonly string ScriptDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "IpxeScripts");
+
+        private static readonly HashSet<char> InvalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());
+
+        private const string DefaultScript =
+            "#!ipxe\r\n" +
+            "echo Error: Boot script is no configured, please edit script file.";
+
+        public static string GetScript(string mac)
+        {
+            var path = Path.Combine(ScriptDir, "MAC-" + FilterMac(mac) + ".txt");
+            if (File.Exists(path)) return File.ReadAllText(path);
+
+            if (false == Directory.Exists(ScriptDir)) Directory.CreateDirectory(ScriptDir);
+            File.WriteAllText(path, DefaultScript);
+            return File.ReadAllText(path);
+        }
+
+        private static string FilterMac(string mac)
+        {
+            var sb = new StringBuilder(mac);
+            sb.FilterChars(InvalidFileNameChars);
+            return sb.ToString();
+        }
+    }
+}

+ 93 - 0
HttpServer/HttpProgram.cs

@@ -0,0 +1,93 @@
+using HttpServer.App;
+using System;
+using System.Net;
+using System.Text;
+using System.Threading;
+using System.Web;
+
+namespace HttpServer
+{
+    internal static class HttpProgram
+    {
+        private static bool _isRunning;
+
+        private static void Main(string[] args)
+        {
+            Console.WriteLine("Starting...");
+
+            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 listener = new HttpListener();
+            listener.Prefixes.Add(Properties.Settings.Default.ListenPrefix);
+            listener.Start();
+
+            var upTime = DateTime.Now;
+
+            Console.WriteLine($"HTTP Server started, listening on {Properties.Settings.Default.ListenPrefix}");
+
+            var task = listener.GetContextAsync();
+
+            var polling = DateTime.Now;
+            var polled = false;
+            while (_isRunning)
+            {
+                Console.CursorLeft = 0;
+                if (false == task.Wait(500))
+                {
+                    var timeSpan = DateTime.Now - polling;
+                    var up = DateTime.Now - upTime;
+                    Console.Write("Polling request..." +
+                                  $" {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}");
+
+                    polled = true;
+                }
+                else
+                {
+                    if (polled)
+                    {
+                        Console.WriteLine();
+                        polled = false;
+                    }
+
+                    var context = task.Result;
+                    task = listener.GetContextAsync();
+
+                    var request = context.Request;
+                    Console.WriteLine($"Request from {request.RemoteEndPoint} {request.HttpMethod} {request.RawUrl}");
+
+                    var queryString = HttpUtility.ParseQueryString(request.Url.Query);
+                    var mac = queryString["mac"].Replace(":", "-").ToUpper();
+
+                    Console.WriteLine($" MAC:{mac}");
+
+                    var buffer = Encoding.ASCII.GetBytes(IpxeScriptManager.GetScript(mac));
+                    context.Response.OutputStream.Write(buffer, 0, buffer.Length);
+                    context.Response.Close();
+
+                    Console.WriteLine(" Response closed.");
+                }
+            }
+
+            listener.Close();
+        }
+    }
+}

+ 70 - 0
HttpServer/HttpServer.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>{9FBE44BC-56DA-4F12-BA69-DEA537693014}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <RootNamespace>HttpServer</RootNamespace>
+    <AssemblyName>HttpServer</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>
+  </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>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+    <Reference Include="System.Core" />
+    <Reference Include="System.Web" />
+    <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="App\IpxeScriptManager.cs" />
+    <Compile Include="HttpProgram.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Properties\Settings.Designer.cs">
+      <AutoGen>True</AutoGen>
+      <DesignTimeSharedInput>True</DesignTimeSharedInput>
+      <DependentUpon>Settings.settings</DependentUpon>
+    </Compile>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="App.config" />
+    <None Include="Properties\Settings.settings">
+      <Generator>SettingsSingleFileGenerator</Generator>
+      <LastGenOutput>Settings.Designer.cs</LastGenOutput>
+    </None>
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\Utils\Utils.csproj">
+      <Project>{369BBF56-A30E-478F-AE99-8427BC7415E8}</Project>
+      <Name>Utils</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+</Project>

+ 36 - 0
HttpServer/Properties/AssemblyInfo.cs

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

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

@@ -0,0 +1,35 @@
+//------------------------------------------------------------------------------
+// <auto-generated>
+//     此代码由工具生成。
+//     运行时版本:4.0.30319.42000
+//
+//     对此文件的更改可能会导致不正确的行为,并且如果
+//     重新生成代码,这些更改将会丢失。
+// </auto-generated>
+//------------------------------------------------------------------------------
+
+namespace HttpServer.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("http://0.0.0.0:8444/")]
+        public string ListenPrefix {
+            get {
+                return ((string)(this["ListenPrefix"]));
+            }
+        }
+    }
+}

+ 9 - 0
HttpServer/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="HttpServer.Properties" GeneratedClassName="Settings">
+  <Profiles />
+  <Settings>
+    <Setting Name="ListenPrefix" Type="System.String" Scope="Application">
+      <Value Profile="(Default)">http://0.0.0.0:8444/</Value>
+    </Setting>
+  </Settings>
+</SettingsFile>

+ 3 - 0
TftpServer/App.config

@@ -19,6 +19,9 @@
             <setting name="VerboseMode" serializeAs="String">
                 <value>False</value>
             </setting>
+            <setting name="TimeOutSecond" serializeAs="String">
+                <value>30</value>
+            </setting>
         </TftpServer.Properties.Settings>
     </applicationSettings>
 </configuration>

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

@@ -49,5 +49,14 @@ namespace TftpServer.Properties {
                 return ((bool)(this["VerboseMode"]));
             }
         }
+        
+        [global::System.Configuration.ApplicationScopedSettingAttribute()]
+        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+        [global::System.Configuration.DefaultSettingValueAttribute("30")]
+        public int TimeOutSecond {
+            get {
+                return ((int)(this["TimeOutSecond"]));
+            }
+        }
     }
 }

+ 3 - 0
TftpServer/Properties/Settings.settings

@@ -11,5 +11,8 @@
     <Setting Name="VerboseMode" Type="System.Boolean" Scope="Application">
       <Value Profile="(Default)">False</Value>
     </Setting>
+    <Setting Name="TimeOutSecond" Type="System.Int32" Scope="Application">
+      <Value Profile="(Default)">30</Value>
+    </Setting>
   </Settings>
 </SettingsFile>

+ 1 - 1
TftpServer/TftpProgram.cs

@@ -29,6 +29,7 @@ namespace TftpServer
             }
             else
             {
+                _timeOutSecond = Properties.Settings.Default.TimeOutSecond;
                 _isVerbose = Properties.Settings.Default.VerboseMode;
 
                 var tWorker = new Thread(Working);
@@ -107,7 +108,6 @@ namespace TftpServer
                     printed = true;
                 }
 
-
                 if (false == socket.Poll(500 * 1000, SelectMode.SelectRead))
                 {
                     Console.CursorLeft = 0;

+ 1 - 3
TftpServer/TftpServer.csproj

@@ -72,8 +72,6 @@
       <Name>Utils</Name>
     </ProjectReference>
   </ItemGroup>
-  <ItemGroup>
-    <Folder Include="Root\" />
-  </ItemGroup>
+  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
 </Project>

+ 9 - 0
Utils/ExtensionMethods.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Text;
 
 namespace Utils
 {
@@ -18,5 +19,13 @@ namespace Utils
                 ? found(value)
                 : notFound();
         }
+
+        public static void FilterChars(this StringBuilder sb, HashSet<char> chars, char replace = '_')
+        {
+            for (var i = 0; i < sb.Length; i++)
+            {
+                if (chars.Contains(sb[i])) sb[i] = replace;
+            }
+        }
     }
 }