TftpProgram.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Net.Sockets;
  7. using System.Text;
  8. using System.Threading;
  9. namespace TftpServer
  10. {
  11. internal static class TftpProgram
  12. {
  13. private static bool _isVerbose;
  14. private static bool _isRunning;
  15. private static string _root;
  16. private static int _timeOutSecond = 10;
  17. private static void Main(string[] args)
  18. {
  19. Console.WriteLine("Starting...");
  20. _root = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Properties.Settings.Default.Root);
  21. if (false == Directory.Exists(_root))
  22. {
  23. Console.WriteLine($"Error: Can not found path `{_root}'");
  24. }
  25. else
  26. {
  27. _timeOutSecond = Properties.Settings.Default.TimeOutSecond;
  28. _isVerbose = Properties.Settings.Default.VerboseMode;
  29. var tWorker = new Thread(Working);
  30. _isRunning = true;
  31. tWorker.Start();
  32. Console.WriteLine("Press ENTER to Stop.");
  33. Console.ReadLine();
  34. Console.Write("Shutting down...");
  35. _isRunning = false;
  36. tWorker.Join();
  37. Console.Write("Stopped.");
  38. }
  39. Console.WriteLine();
  40. Console.Write("Press ENTER to Exit.");
  41. Console.ReadLine();
  42. }
  43. private static void Working()
  44. {
  45. var listenEndPoint = new IPEndPoint(IPAddress.Parse(Properties.Settings.Default.ListenOn), 69);
  46. var socket = new Socket(listenEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp)
  47. {
  48. SendBufferSize = 65536,
  49. ReceiveBufferSize = 65536
  50. };
  51. socket.Bind(listenEndPoint);
  52. var dicSessions = new Dictionary<EndPoint, TftpTransferSession>();
  53. var dicBlockReaders = new Dictionary<string, BlockReader>();
  54. BlockReader GetBlockReader(string path)
  55. {
  56. if (dicBlockReaders.TryGetValue(path, out var br)) return br;
  57. br = new BlockReader(path);
  58. dicBlockReaders[path] = br;
  59. return br;
  60. }
  61. var upTime = DateTime.Now;
  62. Console.WriteLine($"TFTP Server started, listing on: {listenEndPoint}");
  63. Console.WriteLine();
  64. var buffer = new byte[65536];
  65. EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 68);
  66. var pollingTime = DateTime.Now;
  67. var printed = false;
  68. while (_isRunning)
  69. {
  70. var now = DateTime.Now;
  71. var sb = new StringBuilder();
  72. foreach (var item in dicBlockReaders.ToArray())
  73. {
  74. if (!((now - item.Value.LastActive).TotalSeconds > _timeOutSecond)) continue;
  75. dicBlockReaders.Remove(item.Key);
  76. item.Value.Dispose();
  77. Console.WriteLine($"File closed by Timeout: {item.Value.Path}");
  78. printed = true;
  79. }
  80. foreach (var item in dicSessions.ToArray())
  81. {
  82. if (!((now - item.Value.LastActivity).TotalSeconds > _timeOutSecond)) continue;
  83. dicSessions.Remove(item.Key);
  84. Console.WriteLine($"Session removed by Timeout: {item.Key}");
  85. printed = true;
  86. }
  87. if (false == socket.Poll(500 * 1000, SelectMode.SelectRead))
  88. {
  89. Console.CursorLeft = 0;
  90. if (printed)
  91. {
  92. printed = false;
  93. }
  94. else
  95. {
  96. --Console.CursorTop;
  97. }
  98. var polling = DateTime.Now - pollingTime;
  99. var up = DateTime.Now - upTime;
  100. Console.WriteLine("Polling sockets..." +
  101. $" {polling.Days:00}D {polling.Hours:00}H {polling.Minutes:00}M {polling.Seconds:00}S {polling.Milliseconds:000}" +
  102. $" / UP {up.Days:00}D {up.Hours:00}H {up.Minutes:00}M {up.Seconds:00}S {up.Milliseconds:000}");
  103. }
  104. else // polled
  105. {
  106. sb.Clear();
  107. var bytes = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref remoteEndPoint);
  108. if (_isVerbose) sb.Append($"Receive {bytes} byte From {remoteEndPoint} ");
  109. try
  110. {
  111. var packet = new TftpPacket(buffer, bytes);
  112. if (_isVerbose) sb.Append($"OpCode:{packet.OpCode}");
  113. TftpTransferSession session;
  114. switch (packet.OpCode)
  115. {
  116. default: throw new NotImplementedException();
  117. case TftpOpCode.Error:
  118. if (false == _isVerbose) sb.Append($"Receive {bytes} byte From {remoteEndPoint} OpCode:{packet.OpCode} ");
  119. sb.Append($"{packet.ErrorNumber} (0x{(short)packet.ErrorNumber:X4}) {packet.ErrorMessage} ");
  120. if (dicSessions.Remove(remoteEndPoint)) sb.Append("Session removed");
  121. break;
  122. case TftpOpCode.Read:
  123. if (false == _isVerbose) sb.Append($"Receive {bytes} byte From {remoteEndPoint} OpCode:{packet.OpCode} ");
  124. sb.Append($"FileName:{packet.FileName} [{packet.Mode}] BlockSize:{packet.BlockSize} ");
  125. var path = Path.Combine(_root, packet.FileName.TrimStart('/'));
  126. if (false == File.Exists(path))
  127. {
  128. sb.AppendLine();
  129. sb.Append(" ** File Not Found ");
  130. var response = new TftpPacket("File Not Found", TftpErrorNumber.FileNoFound);
  131. bytes = response.WriteToBuffer(buffer);
  132. sb.AppendLine();
  133. sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} Message:{response.ErrorMessage}");
  134. socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
  135. }
  136. else if (0 == packet.TransferSize)
  137. {
  138. sb.Append($" TransferSize:{packet.TransferSize}");
  139. var response = new TftpPacket(TftpOpCode.OptionAck) { TransferSize = (int?)new FileInfo(path).Length };
  140. bytes = response.WriteToBuffer(buffer);
  141. sb.AppendLine();
  142. sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} TransferSize:{response.TransferSize}");
  143. socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
  144. }
  145. else
  146. {
  147. session = new TftpTransferSession(path, packet.BlockSize);
  148. var response = new TftpPacket(1, session.BlockBuffer, session.Read(1, GetBlockReader(path)));
  149. bytes = response.WriteToBuffer(buffer);
  150. sb.AppendLine();
  151. sb.Append($"Transfer session <{remoteEndPoint}> start. Path: <{session.FilePath}>");
  152. if (_isVerbose)
  153. {
  154. sb.AppendLine();
  155. sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} BlockNumber:{response.BlockNumber}/{session.BlockCount}");
  156. }
  157. socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
  158. dicSessions[remoteEndPoint] = session;
  159. }
  160. break;
  161. case TftpOpCode.Ack:
  162. if (_isVerbose) sb.Append($" BlockNumber:{packet.BlockNumber}");
  163. if (dicSessions.TryGetValue(remoteEndPoint, out session))
  164. {
  165. if (packet.BlockNumber == session.BlockCount)
  166. {
  167. if (_isVerbose) sb.AppendLine();
  168. sb.Append($"Transfer complete session <{remoteEndPoint}> removed");
  169. dicSessions.Remove(remoteEndPoint);
  170. }
  171. else
  172. {
  173. var nextBlockNumber = packet.BlockNumber;
  174. ++nextBlockNumber;
  175. var response = new TftpPacket(nextBlockNumber, session.BlockBuffer, session.Read(nextBlockNumber, GetBlockReader(session.FilePath)));
  176. bytes = response.WriteToBuffer(buffer);
  177. if (_isVerbose)
  178. {
  179. sb.AppendLine();
  180. sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} BlockNumber:{response.BlockNumber}/{session.BlockCount}");
  181. }
  182. socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
  183. }
  184. }
  185. else
  186. {
  187. if (false == _isVerbose) sb.Append($"Receive {bytes} byte From {remoteEndPoint} OpCode:{packet.OpCode} BlockNumber:{packet.BlockNumber}");
  188. sb.Append(" ** Transfer session Not Found");
  189. var response = new TftpPacket("Session not found", TftpErrorNumber.UnknownTransferId);
  190. bytes = response.WriteToBuffer(buffer);
  191. sb.AppendLine();
  192. sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} Message:{response.ErrorMessage}");
  193. socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
  194. }
  195. break;
  196. }
  197. }
  198. catch (Exception e)
  199. {
  200. sb.Append(e);
  201. }
  202. if (sb.Length != 0)
  203. {
  204. Console.WriteLine(sb.ToString());
  205. printed = true;
  206. }
  207. pollingTime = DateTime.Now;
  208. } // end if poll
  209. }
  210. }
  211. }
  212. }