Skip to content

Commit

Permalink
[#19228] Add modular network server with TFTP support
Browse files Browse the repository at this point in the history
  • Loading branch information
mateusz-holenko committed May 27, 2020
1 parent 25238e0 commit a0332d3
Show file tree
Hide file tree
Showing 11 changed files with 536 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,6 @@
[submodule "lib/CxxDemangler"]
path = lib/CxxDemangler
url = https://github.com/antmicro/CxxDemangler
[submodule "lib/InpliTftpServer"]
path = lib/InpliTftpServer
url = https://github.com/antmicro/InpliTftpServer.git
14 changes: 14 additions & 0 deletions Renode.sln
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "cores-riscv64", "src\Infras
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CxxDemangler", "lib\CxxDemangler\CxxDemangler\CxxDemangler.csproj", "{3A70B9B8-BBAB-47EA-8473-B7A0B4961D56}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tftp", "lib\InpliTftpServer\libtftp\libtftp_alt.csproj", "{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
DebugHeadless|Any CPU = DebugHeadless|Any CPU
Expand Down Expand Up @@ -487,6 +489,18 @@ Global
{3A70B9B8-BBAB-47EA-8473-B7A0B4961D56}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU
{3A70B9B8-BBAB-47EA-8473-B7A0B4961D56}.DebugWindows|Any CPU.ActiveCfg = Debug|Any CPU
{3A70B9B8-BBAB-47EA-8473-B7A0B4961D56}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.DebugHeadless|Any CPU.ActiveCfg = Debug|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.DebugHeadless|Any CPU.Build.0 = Debug|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.ReleaseHeadless|Any CPU.ActiveCfg = Release|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.ReleaseHeadless|Any CPU.Build.0 = Release|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.DebugMono|Any CPU.ActiveCfg = Debug|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.DebugMono|Any CPU.Build.0 = Debug|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.ReleaseMono|Any CPU.ActiveCfg = Release|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.ReleaseMono|Any CPU.Build.0 = Release|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.ReleaseWindows|Any CPU.ActiveCfg = Release|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.ReleaseWindows|Any CPU.Build.0 = Release|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.DebugWindows|Any CPU.ActiveCfg = Debug|Any CPU
{1B320B7A-BAB5-4370-9ABE-3BDAD4392BDD}.DebugWindows|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{0D9CF6E9-AFB0-4558-8DA5-24FDBAF856F0} = {8BD709BF-8C11-4CBA-9A7D-CC2CC639BD4F}
Expand Down
1 change: 1 addition & 0 deletions lib/InpliTftpServer
Submodule InpliTftpServer added at 118042
21 changes: 21 additions & 0 deletions src/Renode/Network/NetworkServer/IServerModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// Copyright (c) 2010-2020 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//

using System;
using System.Net;
using System.Collections.Generic;

using PacketDotNet;

namespace Antmicro.Renode.Network
{
public interface IServerModule
{
void HandleUdp(IPEndPoint source, UdpPacket packet, Action<IPEndPoint, UdpPacket> callback);
}
}

177 changes: 177 additions & 0 deletions src/Renode/Network/NetworkServer/Modules/TftpServerModule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//
// Copyright (c) 2010-2020 Antmicro
//
// This file is licensed under the MIT License.
// Full license text is available in 'licenses/MIT.txt'.
//

using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
using System.Threading.Tasks;

using libtftp;
using PacketDotNet;

using Antmicro.Renode.Core;
using Antmicro.Renode.Logging;
using Antmicro.Renode.Exceptions;

namespace Antmicro.Renode.Network
{
public static class TftpServerExtensions
{
public static void StartTFTP(this NetworkServer server, int port, string name = "tftp")
{
var module = new TftpServerModule(port);
if(!server.RegisterModule(module, port, name))
{
throw new RecoverableException($"Couldn't start TFTP server on port {port}. See log for details");
}
}
}

public class TftpServerModule : IServerModule, IEmulationElement
{
public TftpServerModule(int port)
{
Port = port;

callbacks = new Dictionary<IPEndPoint, Action<IPEndPoint, UdpPacket>>();
files = new Dictionary<string, string>();
directories = new List<string>();

server = TftpServer.Instance;

server.Log = HandleLog;
server.DataReady = HandleResponse;

server.GetStream += HandleStream;

this.Log(LogLevel.Info, "TFTP server started at port {0}", Port);
}

public void ServeFile(string path, string name = null)
{
files.Add(name ?? Path.GetFileName(path), path);
}

public void ServeDirectory(string directory)
{
directories.Add(directory);
}

public void HandleUdp(IPEndPoint source, UdpPacket packet, Action<IPEndPoint, UdpPacket> callback)
{
callbacks[source] = callback;
server.OnUdpData(source, packet.PayloadData);
}

public int Port { get; }

private void HandleLog(object src, TftpLogEventArgs args)
{
LogLevel logLevel;

switch(args.Severity)
{
case ETftpLogSeverity.Debug:
logLevel = LogLevel.Debug;
break;

case ETftpLogSeverity.Informational:
case ETftpLogSeverity.Notice:
logLevel = LogLevel.Info;
break;

case ETftpLogSeverity.Warning:
logLevel = LogLevel.Warning;
break;

case ETftpLogSeverity.Error:
case ETftpLogSeverity.Critical:
case ETftpLogSeverity.Alert:
case ETftpLogSeverity.Emergency:
logLevel = LogLevel.Error;
break;

default:
throw new ArgumentException($"Unhandled log severity: {args.Severity}");
}

this.Log(LogLevel.Info, args.Message);
}

private void HandleResponse(IPEndPoint source, byte[] buffer, int count)
{
var response = new UdpPacket((ushort)Port, (ushort)source.Port);

if(count == buffer.Length)
{
response.PayloadData = buffer;
}
else
{
var newBuffer = new byte[count];
Array.Copy(buffer, 0, newBuffer, 0, count);
response.PayloadData = newBuffer;
}

callbacks[source](source, response);
}

private async Task HandleStream(object caller, TftpGetStreamEventArgs args)
{
this.Log(LogLevel.Noisy, "Searching for file {0}", args.Filename);

var path = await Task.Run(() => FindFile(args.Filename));
if(path == null)
{
this.Log(LogLevel.Warning, "Asked for {0} file, but it does not exist", args.Filename);
return;
}

try
{
using (var stream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
{
var buffer = new byte[stream.Length];
await stream.ReadAsync(buffer, 0, (int)stream.Length);
args.Result = new MemoryStream(buffer);
}
}
catch (Exception e)
{
this.Log(LogLevel.Warning, "There was an error when reading {0} file: {1}", path, e.Message);
}

string FindFile(string filename)
{
// first check list of files
if(!files.TryGetValue(args.Filename, out var result))
{
// if not found, scan all the directories
foreach(var dir in directories)
{
foreach(var file in Directory.GetFiles(dir))
{
if(file.Substring(dir.Length + 1) == args.Filename)
{
result = file;
break;
}
}
}
}
return result;
}
}

private readonly Dictionary<string, string> files;
private readonly List<string> directories;

private readonly Dictionary<IPEndPoint, Action<IPEndPoint, UdpPacket>> callbacks;
private readonly TftpServer server;
}
}
Loading

0 comments on commit a0332d3

Please sign in to comment.