123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- 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<EndPoint, TftpTransferSession>();
- var dicBlockReaders = new Dictionary<string, BlockReader>();
- 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.LastActivity).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 // polled
- {
- 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;
- } // end if poll
- }
- }
- }
- }
|