A Trivial File Transfer Protocol Server written in Ada
by Martyn Pike –
For an upcoming project, I needed a simple way of transferring binary files over an Ethernet connection with minimal (if any at all) user interaction.
A protocol that's particularly appropriate for this kind of usage is the Trivial File Transfer Protocol (TFTP). You can find a high level description on Wikipedia and a more detailed breakdown of the protocol here.
My previous experience with this protocol has mostly been within test rig environments, where a target computer accesses its operational software payload from a TFTP server at boot-time. The beauty of this is approach is that it allows for different payloads to be used between reboots by switching the files being served. That is exactly how this server will be used in my forthcoming project, also to be documented on blog.adacore.com.
The Ada TFTP server will be hosted on Ubuntu Linux and support a subset of the transactions provided by the protocol. For example, the ability for the client to write a file to the server will not be supported.
There are a number of TFTP servers available for Ubuntu Linux. However I wanted to implement my own in Ada, mainly to prove it could be done, but also to test a couple of different options for handling UDP/IP transactions from within Ada applications.
To do this, I needed something to mimic the capabilities of GNAT.Sockets.
To start with, I reviewed the current catalogue of available Ada software on Github and the repository from my good friends over at CodeLabs, who happen to be the developers of the Muen separation kernel which will also figure in my forthcoming project.
The CodeLabs team had exactly what I was looking for: Anet. I highly recommend that you review its code on the CodeLabs repository. That repository is now my go-to for (publicly available and open source) high quality examples of Ada and SPARK software development. Many of my future blog posts about applying the AdaCore tools and techniques will be oriented around examples based on CodeLabs source code.
Back to my TFTP server. Since I had two options for the UDP/IP transaction functionality, I decided to set about creating one version of my server using GNAT.Sockets and another using Anet from CodeLabs.
If you want to get ahead of the game, the code is available on my GitHub repository for all three parts of this blog series.
The first step towards my objective was to obtain Anet, by cloning the repository, building the library and installing it in a location where the GNAT build tools can locate it.
All the code for this blog post can be built with GNAT Community 2019 as well as GNAT Pro.
The TFTP server code has been reviewed by CodePeer for the detection of run-time vulnerabilities that may lead to unexpected code execution paths and by GNATcheck against a suitable coding standard.
Both CodePeer and GNATcheck are available from AdaCore as professionally assured products and can be qualified as TQL-5 review tools for use by DO-178B/C projects.
The following command sequence installs the Anet libraries into ~/sw/adalibs and with a suitable GNAT compiler in my PATH, I would execute the following commands:
git clone https://git.codelabs.ch/anet.git
cd anet
git checkout master
make all
make PREFIX=~/sw/adalibs install
After doing this, there will be a shared library called libanet.so stored in the ~/sw/adalibs/lib directory.
Before writing code that will use this library, the GPR_PROJECT_PATH environment variable needs to identify the ~/sw/adalibs/lib/gnat directory.
This can be done by using the following
export GPR_PROJECT_PATH=~/sw/adalibs/lib/gnat
I encountered a slight learning curve with the Anet API because it's architecture differs from that of GNAT.Sockets. However, the code documentation is very good and before I knew it I had a proof of concept working.
To build the code from Github (with the same GNAT compiler in the path which was used to build Anet), you can use the following:
export GPR_PROJECT_PATH=~/sw/adalibs/lib/gnat
git clone https://github.com/darthmartyn/adatftpd-anet
cd adatftpd-anet
make all
The makefile and GNAT project file are as follows:
# Assumes GPR_PROJECT_PATH includes Anet installation
# Try to use the same GNAT to build adatftpd that was used to
# build Anet.
all:
gprbuild -p -P adatftpd.gpr
check:
gnatcheck -P adatftpd.gpr --show-rule -rules -from=gnatcheck.rules
review:
codepeer -P adatftpd.gpr -level 2 -output-msg
clean:
gprclean -q -P adatftpd.gpr
with "anet.gpr";
project Adatftpd is
for Languages use ("Ada");
for Source_Dirs use ("src/**");
for Object_Dir use "obj";
for Exec_Dir use "test";
for Main use ("main.adb");
package Builder is
for Executable ("main.adb") use "adatftpd-anet";
end Builder;
package Compiler is
for Switches ("ada") use ("-gnata");
end Compiler;
end Adatftpd;
Assuming no errors occurred during this sequence of commands, the 'test' sub-directory will contain the 'adatftpf-anet' executable.
You can also checkout the verification program I wrote for my TFTP server, which is also available on my Github repository.
I'd welcome feedback and collaboration on either of these TFTP related projects.