|
@@ -4,12 +4,14 @@ 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;
|
|
@@ -23,10 +25,12 @@ namespace TftpServer
|
|
|
_root = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Properties.Settings.Default.Root);
|
|
|
if (false == Directory.Exists(_root))
|
|
|
{
|
|
|
- Console.WriteLine("Error: Root path no exist");
|
|
|
+ Console.WriteLine($"Error: Can not found path `{_root}'");
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
+ _isVerbose = Properties.Settings.Default.VerboseMode;
|
|
|
+
|
|
|
var tWorker = new Thread(Working);
|
|
|
_isRunning = true;
|
|
|
tWorker.Start();
|
|
@@ -72,144 +76,171 @@ namespace TftpServer
|
|
|
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 timeout = false;
|
|
|
+
|
|
|
+ var sb = new StringBuilder();
|
|
|
+
|
|
|
foreach (var item in dicBlockReaders.ToArray())
|
|
|
{
|
|
|
if (!((now - item.Value.LastActive).TotalSeconds > _timeOutSecond)) continue;
|
|
|
- timeout = true;
|
|
|
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;
|
|
|
- timeout = true;
|
|
|
dicSessions.Remove(item.Key);
|
|
|
Console.WriteLine($"Session removed by Timeout: {item.Key}");
|
|
|
+ printed = true;
|
|
|
}
|
|
|
|
|
|
- if (timeout) Console.WriteLine();
|
|
|
|
|
|
if (false == socket.Poll(500 * 1000, SelectMode.SelectRead))
|
|
|
{
|
|
|
Console.CursorLeft = 0;
|
|
|
- --Console.CursorTop;
|
|
|
+ if (printed)
|
|
|
+ {
|
|
|
+ printed = false;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ --Console.CursorTop;
|
|
|
+ }
|
|
|
var polling = DateTime.Now - pollingTime;
|
|
|
var up = DateTime.Now - upTime;
|
|
|
- Console.WriteLine($"Polling..." +
|
|
|
+ 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
|
|
|
{
|
|
|
- do
|
|
|
+ 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 bytes = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref remoteEndPoint);
|
|
|
- Console.Write($"Receive {bytes} byte From {remoteEndPoint} ");
|
|
|
+ var packet = new TftpPacket(buffer, bytes);
|
|
|
+ if (_isVerbose) sb.Append($"OpCode:{packet.OpCode}");
|
|
|
|
|
|
- try
|
|
|
+ TftpTransferSession session;
|
|
|
+ switch (packet.OpCode)
|
|
|
{
|
|
|
- var packet = new TftpPacket(buffer, bytes);
|
|
|
- Console.Write($"OpCode:{packet.OpCode}");
|
|
|
-
|
|
|
- TftpTransferSession session;
|
|
|
- switch (packet.OpCode)
|
|
|
- {
|
|
|
- default: throw new NotImplementedException();
|
|
|
-
|
|
|
- case TftpOpCode.Error:
|
|
|
- Console.Write($"{packet.ErrorNumber} 0x{(short)packet.ErrorNumber:X4} {packet.ErrorMessage} ");
|
|
|
- if (dicSessions.Remove(remoteEndPoint)) Console.Write("Session closed");
|
|
|
- break;
|
|
|
-
|
|
|
- case TftpOpCode.Read:
|
|
|
- Console.Write($"FileName:{packet.FileName} [{packet.Mode}] BlockSize:{packet.BlockSize} ");
|
|
|
- var path = Path.Combine(_root, packet.FileName.TrimStart('/'));
|
|
|
- if (false == File.Exists(path))
|
|
|
+ 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)
|
|
|
{
|
|
|
- Console.Write("** File Not Found ");
|
|
|
- var response = new TftpPacket("File Not Found", TftpErrorNumber.FileNoFound);
|
|
|
- bytes = response.WriteToBuffer(buffer);
|
|
|
-
|
|
|
- Console.WriteLine();
|
|
|
- Console.Write($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} Message:{response.ErrorMessage}");
|
|
|
- socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
|
|
|
+ sb.AppendLine();
|
|
|
+ sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} BlockNumber:{response.BlockNumber}/{session.BlockCount}");
|
|
|
}
|
|
|
- else if (0 == packet.TransferSize)
|
|
|
+ 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)
|
|
|
{
|
|
|
- var response = new TftpPacket(TftpOpCode.OptionAck) { TransferSize = (int?)new FileInfo(path).Length };
|
|
|
- bytes = response.WriteToBuffer(buffer);
|
|
|
-
|
|
|
- Console.WriteLine();
|
|
|
- Console.Write($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} TransferSize:{response.TransferSize}");
|
|
|
- socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
|
|
|
+ if (_isVerbose) sb.AppendLine();
|
|
|
+ sb.Append($"Transfer complete session <{remoteEndPoint}> removed");
|
|
|
+ dicSessions.Remove(remoteEndPoint);
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- session = new TftpTransferSession(path, packet.BlockSize);
|
|
|
- var response = new TftpPacket(1, session.BlockBuffer, session.Read(1, GetBlockReader(path)));
|
|
|
- bytes = response.WriteToBuffer(buffer);
|
|
|
- Console.WriteLine();
|
|
|
- Console.Write($"Transfer session <{remoteEndPoint}> start. Path: <{session.FilePath}>");
|
|
|
+ var nextBlockNumber = packet.BlockNumber;
|
|
|
+ ++nextBlockNumber;
|
|
|
|
|
|
- Console.WriteLine();
|
|
|
- Console.Write($"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;
|
|
|
+ var response = new TftpPacket(nextBlockNumber, session.BlockBuffer, session.Read(nextBlockNumber, GetBlockReader(session.FilePath)));
|
|
|
+ bytes = response.WriteToBuffer(buffer);
|
|
|
|
|
|
- case TftpOpCode.Ack:
|
|
|
- Console.Write($" BlockNumber:{packet.BlockNumber}");
|
|
|
- if (dicSessions.TryGetValue(remoteEndPoint, out session))
|
|
|
- {
|
|
|
- if (packet.BlockNumber == session.BlockCount)
|
|
|
+ if (_isVerbose)
|
|
|
{
|
|
|
- Console.WriteLine();
|
|
|
- Console.Write($"Transfer complete session <{remoteEndPoint}> closed");
|
|
|
- dicSessions.Remove(remoteEndPoint);
|
|
|
+ sb.AppendLine();
|
|
|
+ sb.Append($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} BlockNumber:{response.BlockNumber}/{session.BlockCount}");
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- var nextBlockNumber = packet.BlockNumber;
|
|
|
- ++nextBlockNumber;
|
|
|
-
|
|
|
- var response = new TftpPacket(nextBlockNumber, session.BlockBuffer, session.Read(nextBlockNumber, GetBlockReader(session.FilePath)));
|
|
|
- bytes = response.WriteToBuffer(buffer);
|
|
|
-
|
|
|
- Console.WriteLine();
|
|
|
- Console.Write($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} BlockNumber:{response.BlockNumber}/{session.BlockCount}");
|
|
|
- socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
|
|
|
- }
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- Console.Write(" ** Transfer session Not Found");
|
|
|
- var response = new TftpPacket("Session not found", TftpErrorNumber.UnknownTransferId);
|
|
|
- bytes = response.WriteToBuffer(buffer);
|
|
|
-
|
|
|
- Console.WriteLine();
|
|
|
- Console.Write($"Send {bytes} bytes to {remoteEndPoint} OpCode:{response.OpCode} Message:{response.ErrorMessage}");
|
|
|
socket.SendTo(buffer, 0, bytes, SocketFlags.None, remoteEndPoint);
|
|
|
}
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- catch (Exception e)
|
|
|
- {
|
|
|
- Console.Write(e);
|
|
|
+ }
|
|
|
+ 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;
|
|
|
}
|
|
|
- Console.WriteLine();
|
|
|
- } while (socket.Poll(50 * 1000, SelectMode.SelectRead));
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ sb.Append(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (sb.Length != 0)
|
|
|
+ {
|
|
|
+ Console.WriteLine(sb.ToString());
|
|
|
+ printed = true;
|
|
|
+ }
|
|
|
|
|
|
pollingTime = DateTime.Now;
|
|
|
}
|