Initial commit: HPR Knowledge Base MCP Server
- MCP server with stdio transport for local use - Search episodes, transcripts, hosts, and series - 4,511 episodes with metadata and transcripts - Data loader with in-memory JSON storage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
337
hpr_transcripts/hpr0862.txt
Normal file
337
hpr_transcripts/hpr0862.txt
Normal file
@@ -0,0 +1,337 @@
|
||||
Episode: 862
|
||||
Title: HPR0862: Breaking Down TFTP
|
||||
Source: https://hub.hackerpublicradio.org/ccdn.php?filename=/eps/hpr0862/hpr0862.mp3
|
||||
Transcribed: 2025-10-08 03:43:45
|
||||
|
||||
---
|
||||
|
||||
Hi, this is Kevin Grenade with the first installment of my series Breaking Down Protocols.
|
||||
I was inspired to do this by Steve Gibson's How the Internet Work Series on Security
|
||||
Now and Klatu's Networking Basic Series here on HPR.
|
||||
Not so much inspired that it could be done, but that it could be interesting.
|
||||
So what I'll be trying to do is describe different protocols and pretty much all the nitty-gritty
|
||||
detail except I'll at the same time be trying to describe why they do the different things
|
||||
they do with the trade-offs they make, etc. to the best of my ability.
|
||||
So even though this is going to be very technical, I'm hoping that it'll be pretty accessible
|
||||
to everyone.
|
||||
In this episode, I'll be describing TFTP trivial file transfer protocol.
|
||||
Before getting to the technical details, I think the most important things are why would
|
||||
you want to use it.
|
||||
Well, obviously for a file transfer protocol, you need to transfer files, but why use TFTP
|
||||
instead of some other file transfer protocol.
|
||||
Well, it's that first word trivial.
|
||||
It's very simple.
|
||||
It's very simple to implement.
|
||||
It takes up a very small memory footprint, etc.
|
||||
What it doesn't do is provide a lot of robustness, a lot of features, or a lot of speed.
|
||||
TFTP derives much of its simplicity from its assumptions about the underlying transport
|
||||
protocols, or rather the lack of assumptions about the underlying transport protocols.
|
||||
All it requires are machine-level addressing, application-level addressing, and fixed-length
|
||||
packets.
|
||||
It was originally designed on top of UDP-IP, which provide these, but it can be implemented
|
||||
on top of any other protocol that provides these features.
|
||||
Two protocols I'm aware of that make use of TFTP are PXC and A-Rink 615A.
|
||||
PXC, sometimes called Pixie Boot, is a protocol that is used to bootstrap a computer off
|
||||
of the network.
|
||||
So you embed PXC, which includes TFTP and DHCP and UDP and IP, into the networking
|
||||
card itself, and it will query for a Pixie Boot server, give the network card an address,
|
||||
and then download files from the Pixie server over TFTP in order to bootstrap the system.
|
||||
So with the Linux system, it will download usually a kernel and a initial RAM disk file,
|
||||
and then it will bootstrap off those.
|
||||
TFTP is a really good match for this scenario because it's very simple and it only relies
|
||||
on UDP.
|
||||
DHCP also relies on UDP, so that has a synergy going where you don't have to implement
|
||||
one transport layer for one protocol and a different transport layer protocol for the
|
||||
other.
|
||||
Not to mention UDP is very simple in the first place compared to, for example, TCP.
|
||||
A-Rink 615A also uses TFTP to provide file transfer services.
|
||||
It's not a bootstrap protocol like PXC, it instead is used more for generic file transfer,
|
||||
it can be used to upload new firmware for remote targets, and it can also be used to retrieve
|
||||
configuration and log data from those targets.
|
||||
In this case, the various targets are actually avionics modules, and they generally have
|
||||
a very small embedded system on them, and in this case, the simple implementation of TFTP
|
||||
is crucial because the resources on these systems are so constrained.
|
||||
The common thread between these two applications is that the resources available are very constrained,
|
||||
and they're also secondary functions of the hardware that they're implemented on.
|
||||
In the case of PXC, the primary function is a network card, and the PXC booting system
|
||||
is an add-on feature, I mean, it's just a bullet point, in most cases.
|
||||
In the case of A-Rink 615A, it also doesn't have anything to do with what the module is actually
|
||||
supposed to be doing.
|
||||
Some of these are monitoring landing gear, some of them are monitoring fuel tanks, things
|
||||
like that, and the ability to upgrade them and retrieve data from them is really a secondary
|
||||
function, so you don't want to spend a lot of time on it.
|
||||
It's not their core competency, to say.
|
||||
The simplicity of TFTP and the simplicity of its requirements really shines here.
|
||||
A side note is that A-Rink 615A is an example of a TFTP implementation that is not built
|
||||
on top of IPUDP.
|
||||
There's actually a special protocol called AFDX that is used on aircraft, and it fulfills
|
||||
all of the same requirements that UDP does.
|
||||
But due to the way that TFTP was designed, you can actually move it on top of another
|
||||
protocol.
|
||||
Now that I've talked a bit about what TFTP is good for, let's dive into how it's actually
|
||||
implemented.
|
||||
The first thing that you want to do is to open a connection to a TFTP server.
|
||||
So the client will format a packet that requests a particular file, I'll go over the format
|
||||
of the packet later, and it will open a port locally with a random port number.
|
||||
It actually doesn't matter what it is.
|
||||
And it will then send that packet to port 69 of the target machine.
|
||||
This is what's called a well-known port.
|
||||
There's actually a registry, a global registry of well-known port numbers that are used
|
||||
and different protocols reserve certain ports, mostly in the 0 to 1000 range, for their
|
||||
sold use.
|
||||
HTTP, for example, has port 80 reserved, it also has other ports reserved for secure communications
|
||||
etc.
|
||||
But anyway, TFTP uses port 69.
|
||||
So you get your packet, you open a local port, and then you send your packet to port 69
|
||||
on the remote machine.
|
||||
And it receives that packet, if everything's okay, it will then open its own random
|
||||
port and respond back to the port that you sent your packet from.
|
||||
So just for example, you open up port 1027 and send a packet to port 69, and then they
|
||||
will open port 1024 and send it back to port 1027 on your computer.
|
||||
So from then on, every message that gets sent back and forth will be addressed to that
|
||||
IP port pair.
|
||||
So on your computer, you're going to use your IP address and port 1027, and on the server
|
||||
they're going to use their IP and whatever port I just said, I actually forgot what it
|
||||
was.
|
||||
But anyway, so that's how you know which packets arriving at the computer are intended
|
||||
for that TFTP conversation.
|
||||
An important point about this setup is that you can have one process running as the server
|
||||
on your computer listening to port 69.
|
||||
And what happens is every time it receives a request, it will actually spawn a new process
|
||||
that will finish the conversation.
|
||||
And what that means is that server will then be free to start more TFTP transactions
|
||||
by continuing to listen to port 69.
|
||||
So your server doesn't get bogged down with trying to start new sessions and handle
|
||||
those sessions at the same time.
|
||||
Now that we know how to start a TFTP file transfer, we can take a closer look at the layout
|
||||
of the packets.
|
||||
The first two bytes of each packet is a number that says what type of packet it is.
|
||||
And actually there's only five different packet types in normal TFTP.
|
||||
There's actually an additional one, but we'll be getting to that at the very end.
|
||||
The packet types are read request, write request, data, act, and error.
|
||||
The read request and write request packets are almost exactly the same.
|
||||
That is that initial message that you send from the client to port 69 on the server.
|
||||
If it's a read request, which means the opcode is one, you want to download a file from
|
||||
the server.
|
||||
It's a write request, which has an opcode of two.
|
||||
It means you want to upload a file to the server.
|
||||
The rest of the message is just made of two strings.
|
||||
The first of which is the name of the file that the client wants to transfer, either as
|
||||
a read or a write.
|
||||
The second string indicates the transfer mode of the request.
|
||||
There are three default options for this, but one of them is not even used anymore.
|
||||
That ASCII indicates that the sender should transmit bytes as defined by the document USASIX3.4-1968 and RFC764.
|
||||
I'm not going to get into the details here, but it's a standard for data interchange between
|
||||
different CPU architectures.
|
||||
The most commonly used mode is octet.
|
||||
This indicates that the sender should transmit bytes in its native representation.
|
||||
This is less portable, but faster since no translation has to happen.
|
||||
It's up to the client to know whether it's safe to use octet mode.
|
||||
Mail mode was part of the original specification as a forwarding method for email, but email
|
||||
ended up being forwarded over more advanced protocols, and it's deprecated for TFTP.
|
||||
Nobody does this.
|
||||
Custom servers are also allowed to implement any additional modes that they want.
|
||||
For example, they could have a UTF-8 mode, but there is no guarantee that other TFTP clients
|
||||
or servers will support these additional modes, so that's basically only going to be used
|
||||
within some kind of a closed system where the implementer is in control of both the client
|
||||
and the server, and then they can do whatever they want.
|
||||
The rest of the packets are just as simple.
|
||||
A data packet has an opcode of 3, a 2-byte block number, and up to 512 bytes of payload.
|
||||
I'll explain what this means later.
|
||||
An app packet has an opcode of 4, and a 2-byte block number, which matches the 2-byte block
|
||||
number in the data packet, and then the last message is an error packet.
|
||||
It has an opcode of 5, a 2-byte error code, and optionally a null-terminated string that
|
||||
should be a human-readable indication of what went wrong.
|
||||
TFTP defines seven error codes to cover the most common errors, such as found, not found,
|
||||
access, violation, and disk full, and provides a catch-all error code for use when none
|
||||
of the common errors apply.
|
||||
The catch-all error code should be supplemented with an ASCII string indicating the cause
|
||||
of the error.
|
||||
That string isn't necessary for the other errors.
|
||||
That's it for the packets themselves, what you might call the syntax of the protocol.
|
||||
Now I'll move on to what is called the control-flow of the protocol, which is a set of rules
|
||||
for how these messages are used, and what they mean.
|
||||
I've covered some of this already, for example, that a write or read request message is used
|
||||
to start a transfer.
|
||||
TFTP is what's called a lockstep protocol.
|
||||
This means that one side sends a message, then listens for a reply before sending the
|
||||
next message.
|
||||
This makes it very simple, but it has some drawbacks.
|
||||
Since only one packet is in flight on the network at a time, it's quite difficult to get
|
||||
very high through put out of TFTP, and this only gets worse at the latency of the connection
|
||||
is high.
|
||||
In normal operation, each message has one other message that can be used to reply to it.
|
||||
A client starts a read by sending a read request, which is replied to with a data packet,
|
||||
which is in turn replied to with an AC.
|
||||
Then the client and server alternate sending data and AC packets until the transfer is done.
|
||||
You can think of a ping pong game or a pendulum of a clock swinging back and forth, it just
|
||||
alternates.
|
||||
To write a file, the client first sends a write request, which is replied to with an AC,
|
||||
which is replied to with a data packet, and so on.
|
||||
The exception to this rigid back and forth is the error packet, which can be used to reply
|
||||
to anything.
|
||||
This principle goes a long way towards making the implementation of the TFTP client or
|
||||
server simple, since there's only one message that they have to expect during the bulk
|
||||
of the transfer.
|
||||
Since TFTP is layered on top of UDP, which provides no delivery guarantees, TFTP has
|
||||
to handle retransmission itself.
|
||||
As you would expect, it does so in the simplest way possible.
|
||||
After sending a message, the center starts a timeout and rescinds the message if the timeout
|
||||
expires.
|
||||
Since retransmission is happening, the packets have to be marked so the client and server
|
||||
can tell them apart.
|
||||
This is done with block counters.
|
||||
Each data and AC packet has a block counter field.
|
||||
The first data packet has a field with the value of 1.
|
||||
Each subsequent data packet has a field 1 higher, and each AC has the same value as the
|
||||
data packet it is acknowledging.
|
||||
The exception is the AC of a right request, which has a value of 0.
|
||||
There are three ways for a transfer to terminate, completing successfully, explicitly airing
|
||||
out, and timing out.
|
||||
The successful end of the transfer is signaled by a short data packet.
|
||||
All data packets except for the last one have a 512 byte payload.
|
||||
The last data packet has either whatever is left of the data, or if the data was a multiple
|
||||
of 512 bytes, an empty data packet is sent.
|
||||
This lets the receiver know the transmission is done, but to let the sender know it was
|
||||
received, the receiver sends one last AC.
|
||||
If either side encounters an error that renders them unable to complete the transaction, they
|
||||
can halt the transfer.
|
||||
They should send an error packet to let the other side know why.
|
||||
Reasons can include user intervention, no disk space, access violation, illegal operation,
|
||||
unknown TID, this one's special, and file exists.
|
||||
Systems are also always coming up with new and exciting ways to make an operation fail,
|
||||
like printer on fire.
|
||||
Regardless of the reason, if the side encountering the error is feeling nice, they can send an
|
||||
error so the other side isn't left hanging.
|
||||
This leads to the third failure mode timeout.
|
||||
If the side encountering an error isn't feeling nice, or if the network connection is interrupted
|
||||
or a powers cut or if there are too many sunspots, one side will just stop responding and the
|
||||
other side should probably give up eventually, or at least ask the user what to do.
|
||||
All RFC 1350 says about this is timeouts must also be used to detect errors.
|
||||
Thanks guys.
|
||||
A TFTP implementation will also retransmit the latest packet if it receives a duplicate
|
||||
of the latest packet received.
|
||||
So for example, if it receives an AC of block 5, it will send a data packet containing
|
||||
block 6.
|
||||
If it later receives another AC with a block counter 5, it assumes block 6 was lost and
|
||||
rescinds it.
|
||||
This can be faster than waiting for the sender's timeout to expire.
|
||||
This actually leads to a serious problem called the Sorcerer's Apprentice Syndrome.
|
||||
Imagine what will happen if a TFTP packet is delayed instead of lost.
|
||||
The sender will timeout and rescind, and the receiver will get the same message twice.
|
||||
The receiver will reply to both messages, and then the original sender will get both
|
||||
replies and in term reply to both of them.
|
||||
This can continue indefinitely doubling the bandwidth used by the transfer.
|
||||
But even worse, it can happen again and again.
|
||||
Which is the reason for calling the bug the Sorcerer's Apprentice Syndrome.
|
||||
The simple TFTP automaton just keeps mindlessly cloning itself which could possibly bring
|
||||
down a network.
|
||||
The fix for this bug is simple, break the chain of retransmitts.
|
||||
TFTP is required to not reply to duplicate AC messages.
|
||||
In other words, it replies to the first AC with a given block counter number, but ignores
|
||||
any subsequent ACs with the same block counter number.
|
||||
This bug was originally addressed by RFC1123 and later the main TFTP RFC was updated to
|
||||
contain the fix.
|
||||
That's the TFTP protocol as defined in RFC1350.
|
||||
It works, but there are a few shortcomings to the protocol which have been addressed
|
||||
by later IFCs.
|
||||
First, the block size of 512 bytes keeps throughput quite low.
|
||||
Second, the receiver of a file can't determine if it has room for the file and can't give
|
||||
feedback to the user about progress since it doesn't know how big the file is.
|
||||
Third, there is no standard timeout period or any way to adjust it.
|
||||
The means used to address these shortcomings is called the TFTP option extension.
|
||||
During initialization, the client specifies options in its reader write request and the
|
||||
server replies with a new message, the OAC, or option acknowledgment which echoes the
|
||||
options back to the client.
|
||||
The transaction then continues as usual, but possibly modified by the options used.
|
||||
The format of the option extension is simple.
|
||||
Each option used adds two strings to the end of a reader write request.
|
||||
The first string identifies the type of option being requested.
|
||||
The second string provides a value associated with the option.
|
||||
An important point is that the extension method is backwards compatible with vanilla TFTP.
|
||||
A TFTP server that doesn't recognize options will just ignore the extra data at the end
|
||||
of the reader write request.
|
||||
And by replying with a data or ACC instead of an OACC packet, we'll signal to the client
|
||||
that it cannot or will not use options and the transfer can proceed as usual.
|
||||
In order to be backwards compatible with servers that may only allocate a 512 byte buffer
|
||||
for receiving messages, read and write requests are limited to 512 bytes.
|
||||
The OAC packet contains just the opcode identifying it as an OAC, which is 6, and any options
|
||||
being acknowledged.
|
||||
Depending on the particular option, the value associated with that option may be different
|
||||
in the OAC than in the reader write request.
|
||||
One last adjustment to the protocol is the addition of a new error code that is used
|
||||
to indicate that a transfer should be terminated due to option negotiation.
|
||||
For example, if the server indicates that it cannot support an option and the client does
|
||||
not wish to continue the transmission unless the option is used.
|
||||
The block size option spelled BLKSIZE allows the client to request that the file being transferred
|
||||
be broken into chunks that aren't 512 bytes in length.
|
||||
The valid range that can be requested is between 8 and 65,464 bytes inclusive.
|
||||
While it allows the client to request a block size smaller than 512 bytes, the usual goal
|
||||
of the block size option is to request a larger block size.
|
||||
Ideally the block size will result in the largest packet that will not be fragmented by intervening
|
||||
routers, but selecting the block size to make this happen is left as an exercise for the
|
||||
implementer.
|
||||
A common choice is 1,428 since this matches the M2U of Ethernet after accounting for
|
||||
the various packet headers, but it may be desirable to adjust this based on system design or
|
||||
even local network conditions.
|
||||
The server may echo a smaller value in its OAC, for example if it has statically sized
|
||||
buffers or special knowledge about the M2U.
|
||||
The timeout option allows the client to request a particular timeout duration before the server
|
||||
retransments TFTP packets.
|
||||
The valid range is between 1 and 255 seconds inclusive.
|
||||
If the server is willing to accept this option, it must reply with an OAC containing a matching
|
||||
timeout value.
|
||||
Generally, the timeout duration should be slightly higher than the round trip time for a packet
|
||||
reaching its destination and the reply returning to the sender.
|
||||
Increasing the value is important if the link being used has a very high latency, and
|
||||
decreasing the value can be helpful when the link being used is somewhat unreliable since
|
||||
retries will be attempted more quickly.
|
||||
The transfer size option, spelled TSIZE, allows the client to provide or request the size
|
||||
of the file being transferred.
|
||||
In a write request, the client sets the transfer size option value to the link to the file
|
||||
in octets, which is echoed back by the server in an OAC.
|
||||
In a read request, the client sets the transfer size option value to 0, and the server sets
|
||||
the transfer size option in the OAC to the size of the requested file.
|
||||
This is primarily intended to allow the client or server to terminate the operation early
|
||||
if the file is too large, but it can also be used by the client to provide progress information
|
||||
to the user.
|
||||
There is one other issue related to the block counter, which is roll over.
|
||||
The block counter is a two-byte unsigned integer, meaning the largest number it can represent
|
||||
is 65,536.
|
||||
The problem is that the TFTP protocol doesn't specify what should happen if the block
|
||||
counter value exceeds this number.
|
||||
It is very likely that most implementations will represent the block counter internally
|
||||
as a 16-bit unsigned integer, and only modify this integer by incrementing it.
|
||||
If so, the counter will reset to 0 after reaching its maximum value, and everything will
|
||||
work smoothly.
|
||||
However, if either implementation uses a different representation of the counter, they may disagree
|
||||
on what the current value for the block counter should be, and therefore be unable to transfer
|
||||
files with a size exceeding 65,536 blocks.
|
||||
That comes out to just a bit under 32 megabytes, so if you don't support roll over that's
|
||||
compatible with the other end, that's the size of the file you'll be limited to.
|
||||
A short note indicating that the block counter should roll over to 0 upon reaching its maximum
|
||||
size would have prevented this problem and allowed the TFTP implementations to confidently
|
||||
transfer files of completely arbitrary sizes.
|
||||
But since 32 megabytes was seen as big enough when TFTP was originally written, this wasn't
|
||||
considered a problem.
|
||||
This is an example of how underspecifying a protocol can lead to problems in the future
|
||||
when unanticipated situations can arise.
|
||||
And there you have it, the TFTP protocol as I know it, while I was doing research for
|
||||
this podcast, I actually discovered an additional TFTPRFC290 for TFTP multicast option.
|
||||
I am not familiar with it, and it looks somewhat complicated, so I'm going to be skipping
|
||||
that one.
|
||||
I'd like to take this opportunity to thank everyone involved in producing HPR for providing
|
||||
this forum for audio casts.
|
||||
And with that, this is Kevin Grenade signing off, and hoping to hear from you.
|
||||
You have been listening to Hacker Public Radio, or Hacker Public Radio does our.
|
||||
We are a community podcast network that releases shows every weekday Monday through Friday.
|
||||
Today's show, like all our shows, was contributed by an HPR listener like yourself.
|
||||
If you ever consider recording a podcast, then visit our website to find out how easy
|
||||
it really is.
|
||||
Hacker Public Radio was founded by the Digital.Pound and the Infonomicom Computer Club.
|
||||
HPR is funded by the binary revolution at binref.com.
|
||||
All binref projects are crowd-responsive by linear pages.
|
||||
From shared hosting to custom private clouds, go to lunarpages.com for all your hosting
|
||||
needs.
|
||||
Oneless otherwise stasis, today's show is released under a creative commons, attribution,
|
||||
share alike, lead us our license.
|
||||
Reference in New Issue
Block a user