pin / tftp

TFTP server and client library for Golang
MIT License
286 stars 71 forks source link

read udp [::]:57470: i/o timeout #41

Open 860git opened 6 years ago

860git commented 6 years ago

it is passed when the server and the client are all on the same machine. but other situation,such as between the real machine and the virtual machine, it can't work, and the error returned are:

the client(when send file): read udp [::]:57470: i/o timeout the server:

read udp [::]:38673: i/o timeout
read udp [::]:40223: i/o timeout
read udp [::]:34300: i/o timeout
...

and the other question what's the effect of the parameter filename of func (c Client) Send(filename string, mode string) (io.ReaderFrom, error)

pin commented 5 years ago

Hi 860git!

Sorry for the long delay with response. Do you still need help debugging your setup?

rutvikhp commented 4 years ago

Hey Pin,

I am getting this error. I am getting tftp files from many devices but for some I am getting this error.

Any idea how can I solve it?

purpleidea commented 4 years ago

Hiya,

I recently build this tftp lib into an https://github.com/purpleidea/mgmt/ resource. The code for that is here: https://github.com/purpleidea/mgmt/blob/master/engine/resources/tftp.go

When using this to provision the firmware for some grandstream GXP1625 VOIP phones, it seems to work, but then hang... I eventually get this error:

read udp [::]:38505: i/o timeout

The server is blocking here https://github.com/pin/tftp/blob/1fd60d617f12fd47b8fc4f9e3072857971fb9711/server.go#L303 (the waitgroup) and I believe the wait group in question is this one:

https://github.com/pin/tftp/blob/1fd60d617f12fd47b8fc4f9e3072857971fb9711/server.go#L409

I get the above error inside my readhandler (it looks very much like the example in your README) and in particular, the error comes from this line:

    n, err := rf.ReadFrom(handle)

Unfortunately, it seems that for whatever reason (a TFTP protocol misunderstanding or perhaps a bad TFTP client implementation, or ???) something happens which causes us to block at that ReadFrom line. The BAD part about this is that it's not cancellable AFAICT. We need a variant that takes a https://golang.org/pkg/context/ and lets you kill it, since the Shutdown() method of server isn't causing it to unblock.

HTH, and please LMK

vpaprots commented 1 year ago

If somebody else stumbles upon this, as I did.. mine hung on a large file (~100mb). s.SetBlockSize(65456) appears to have fixed it. (This might be 'per-spec', I didn't dig all that much.. I think dnsmasq-tftp has a similar issue with block size..)

hxing commented 4 months ago

The issue occurs when tftp use ramdom port as src port instead of default tftp server port 69. The UDP package is dropped by host machine, then the client in VM/docker container can't get the udp package. The solution is set the tftp client use the static port when do receive like following func (c Client) Receive(filename string, mode string) (io.WriterTo, error) { conn, err := net.ListenUDP("udp", &net.UDPAddr{IP: net.IPv4zero, Port: 33333}) ... } then open 33333 map outside for NTP.

hxing commented 2 months ago

@purpleidea I quote the TFTP protocol for ports. Normally bi-direction udp connection uses fixed ports, but TFTP is not, The reply package from TFTP server use random port as src port.

"In order to create a connection, each end of the connection chooses a TID for itself, to be used for the duration of that connection. The TID's chosen for a connection should be randomly chosen, so that the probability that the same number is chosen twice in immediate Every packet has associated with it the two succession is very low. TID's of the ends of the connection, the source TID and the destination TID. These TID's are handed to the supporting UDp (or other datagram protocol)as the source and destination ports. Are questing host chooses its source TID as described above, and sends its initial request to the known TID 69 decimal(105 octal)on the serving host. The response to the request, under normal operation,uses a TID chosen by the server as its source TID and the TID chosen for the previous message by the requestor as its destination TID. The two chosen TID's are then used for the remainder of the transfer."