using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace TftpServer { internal static class TftpProgram { private static bool _isVerbose; private static bool _isRunning; private static string _root; private static int _timeOutSecond = 10; private static void Main(string[] args) { Console.WriteLine("Starting..."); _root = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Properties.Settings.Default.Root); if (false == Directory.Exists(_root)) { Console.WriteLine($"Error: Can not found path `{_root}'"); } else { _timeOutSecond = Properties.Settings.Default.TimeOutSecond; _isVerbose = Properties.Settings.Default.VerboseMode; 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 dicSessions = new Dictionary(); var dicBlockReaders = new Dictionary(); BlockReader GetBlockReader(string path) { if (dicBlockReaders.TryGetValue(path, out var br)) return br; br = new BlockReader(path); dicBlockReaders[path] = br; return br; } var upTime = DateTime.Now; Console.WriteLine($"TFTP Server started, listing on: {listenEndPoint}"); Console.WriteLine(); var buffer = new byte[65536]; EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 68); var pollingTime = DateTime.Now; var printed = false; while (_isRunning) { var now = DateTime.Now; var sb = new StringBuilder(); foreach (var item in dicBlockReaders.ToArray()) { if (!((now - item.Value.LastActive).TotalSeconds > _timeOutSecond)) continue; dicBlockReaders.Remove(item.Key); item.Value.Dispose(); Console.WriteLine($"File closed by Timeout: {item.Value.Path}"); printed = true; } foreach (var item in dicSessions.ToArray()) { if (!((now - item.Value.LastActive).TotalSeconds > _timeOutSecond)) continue; dicSessions.Remove(item.Key); Console.WriteLine($"Session removed by Timeout: {item.Key}"); printed = true; } if (false == socket.Poll(500 * 1000, SelectMode.SelectRead)) { Console.CursorLeft = 0; if (printed) { printed = false; } else { --Console.CursorTop; } var polling = DateTime.Now - pollingTime; var up = DateTime.Now - upTime; Console.WriteLine("Polling sockets..." + $" {polling.Days:00}D {polling.Hours:00}H {polling.Minutes:00}M {polling.Seconds:00}S {polling.Milliseconds:000}" + $" / UP {up.Days:00}D {up.Hours:00}H {up.Minutes:00}M {up.Seconds:00}S {up.Milliseconds:000}"); } else { sb.Clear(); var bytes = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref remoteEndPoint); if (_isVerbose) sb.Append($"Receive {bytes} byte From {remoteEndPoint} "); try { var packet = new TftpPacket(buffer, bytes); if (_isVerbose) sb.Append($"OpCode:{packet.OpCode}"); TftpTransferSession session; switch (packet.OpCode) { default: throw new NotImplementedException(); case TftpOpCode.Error: if (false == _isVerbose) sb.Append($"Receive {bytes} byte From {remoteEndPoint} OpCode:{packet.OpCode} "); sb.Append($"{packet.ErrorNumber} (0x{(short)packet.ErrorNumber:X4}) {packet.ErrorMessage} "); if (dicSessions.Remove(remoteEndPoint)) sb.Append("Session removed"); break; case TftpOpCode.Read: if (false == _isVerbose) sb.Append($"Receive {bytes} byte From {remoteEndPoint} OpCode:{packet.OpCode} "); sb.Append($"FileName:{packet.FileName} [{packet.Mode}] BlockSize:{packet.BlockSize} "); var path = Path.Combine(_root, packet.FileName.TrimStart('/')); if (false == File.Exists(path)) { sb.AppendLine(); sb.Append(" ** File Not Found "); var response = new TftpPacket("File Not Found", TftpErrorNumber.FileNoFound); bytes = response.WriteToBuffer(buffer); sb.AppendLine(); sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} Message:{response.ErrorMessage}"); socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint); } else if (0 == packet.TransferSize) { sb.Append($" TransferSize:{packet.TransferSize}"); var response = new TftpPacket(TftpOpCode.OptionAck) { TransferSize = (int?)new FileInfo(path).Length }; bytes = response.WriteToBuffer(buffer); sb.AppendLine(); sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} TransferSize:{response.TransferSize}"); socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint); } else { session = new TftpTransferSession(path, packet.BlockSize); var response = new TftpPacket(1, session.BlockBuffer, session.Read(1, GetBlockReader(path))); bytes = response.WriteToBuffer(buffer); sb.AppendLine(); sb.Append($"Transfer session <{remoteEndPoint}> start. Path: <{session.FilePath}>"); if (_isVerbose) { sb.AppendLine(); sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} BlockNumber:{response.BlockNumber}/{session.BlockCount}"); } socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint); dicSessions[remoteEndPoint] = session; } break; case TftpOpCode.Ack: if (_isVerbose) sb.Append($" BlockNumber:{packet.BlockNumber}"); if (dicSessions.TryGetValue(remoteEndPoint, out session)) { if (packet.BlockNumber == session.BlockCount) { if (_isVerbose) sb.AppendLine(); sb.Append($"Transfer complete session <{remoteEndPoint}> removed"); dicSessions.Remove(remoteEndPoint); } else { var nextBlockNumber = packet.BlockNumber; ++nextBlockNumber; var response = new TftpPacket(nextBlockNumber, session.BlockBuffer, session.Read(nextBlockNumber, GetBlockReader(session.FilePath))); bytes = response.WriteToBuffer(buffer); if (_isVerbose) { sb.AppendLine(); sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} BlockNumber:{response.BlockNumber}/{session.BlockCount}"); } socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint); } } else { if (false == _isVerbose) sb.Append($"Receive {bytes} byte From {remoteEndPoint} OpCode:{packet.OpCode} BlockNumber:{packet.BlockNumber}"); sb.Append(" ** Transfer session Not Found"); var response = new TftpPacket("Session not found", TftpErrorNumber.UnknownTransferId); bytes = response.WriteToBuffer(buffer); sb.AppendLine(); sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} Message:{response.ErrorMessage}"); socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint); } break; } } catch (Exception e) { sb.Append(e); } if (sb.Length != 0) { Console.WriteLine(sb.ToString()); printed = true; } pollingTime = DateTime.Now; } } } } }