POSTS

In-memory tftp server in golang

I tried my hand at writing an in-memory tftp server from scratch on top of UDP, in golang. Why? Well, it’s been a long time since I’ve implemented a network protocol from scratch, probably college I think, and I never have in golang, which I adore programming in. I also wanted to learn more about writing network protocols in golang, and check my hypothesis about implementing concurrency in a basic server I wrote myself. My goal was to end with a tftp server that worked with known tftp clients (on mac/linux), and could handle many simultaneous transfers without data corruption. Finally, I wanted to write the UDP server code in a way that made it possible to re-use with other protocol implementations in the future.

The first task was to define the TFTP logic in broad strokes, and write tests against how I interpreted the tftp spec. Once complete, I moved on to handling retries if the client didn’t respond, context cancellation, and making sure a transfer always closed correctly.

A transferWorker is spawned in a goroutine for every read or write operation which handles all per-transfer logic. The response for each recieved packet is determined via processPacket(), and then sent to the output packet channel.

File storage is in memory, stored in a map, that is concurrent-safe via use of a mutex. As currently implemented, only one write transfer to a particular file is allowed at a given time, and an error is returned if a second write transfer is started for the same file while one is already in progress.

The UDP server, that knows nothing of the underlying business logic of the TFTP protocol, reads incoming packets on the main server connection, and puts them into an incoming packet channel, which is consumed by whatever ProtocolHandler it is initialized with. Outgoing packets to clients can be sent to the responses channel.

Next steps

If I were to continue developing this solution into something more interesting, here are some things I would do:

  • Make the number of retries, and seconds until a retry is sent configurable.
  • Many more tests around tftp protocol handler behavior and the udp server.
  • Add interfaces for file storage and write different file storage adapters. Local filesystem, AWS S3, and other blob-storage backends are easy targets.
comments powered by Disqus