HFish/core/protocol/tftp/libs/README.md

181 lines
5.2 KiB
Markdown
Raw Normal View History

2019-10-28 22:25:23 +08:00
TFTP server and client library for Golang
=========================================
[![GoDoc](https://godoc.org/github.com/pin/tftp?status.svg)](https://godoc.org/github.com/pin/tftp)
[![Build Status](https://travis-ci.org/pin/tftp.svg?branch=master)](https://travis-ci.org/pin/tftp)
Implements:
* [RFC 1350](https://tools.ietf.org/html/rfc1350) - The TFTP Protocol (Revision 2)
* [RFC 2347](https://tools.ietf.org/html/rfc2347) - TFTP Option Extension
* [RFC 2348](https://tools.ietf.org/html/rfc2348) - TFTP Blocksize Option
Partially implements (tsize server side only):
* [RFC 2349](https://tools.ietf.org/html/rfc2349) - TFTP Timeout Interval and Transfer Size Options
Set of features is sufficient for PXE boot support.
``` go
import "github.com/pin/tftp"
```
The package is cohesive to Golang `io`. Particularly it implements
`io.ReaderFrom` and `io.WriterTo` interfaces. That allows efficient data
transmission without unnecessary memory copying and allocations.
TFTP Server
-----------
```go
// readHandler is called when client starts file download from server
func readHandler(filename string, rf io.ReaderFrom) error {
file, err := os.Open(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
n, err := rf.ReadFrom(file)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
fmt.Printf("%d bytes sent\n", n)
return nil
}
// writeHandler is called when client starts file upload to server
func writeHandler(filename string, wt io.WriterTo) error {
file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
n, err := wt.WriteTo(file)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return err
}
fmt.Printf("%d bytes received\n", n)
return nil
}
func main() {
// use nil in place of handler to disable read or write operations
s := tftp.NewServer(readHandler, writeHandler)
s.SetTimeout(5 * time.Second) // optional
err := s.ListenAndServe(":69") // blocks until s.Shutdown() is called
if err != nil {
fmt.Fprintf(os.Stdout, "server: %v\n", err)
os.Exit(1)
}
}
```
TFTP Client
-----------
Upload file to server:
```go
c, err := tftp.NewClient("172.16.4.21:69")
file, err := os.Open(path)
c.SetTimeout(5 * time.Second) // optional
rf, err := c.Send("foobar.txt", "octet")
n, err := rf.ReadFrom(file)
fmt.Printf("%d bytes sent\n", n)
```
Download file from server:
```go
c, err := tftp.NewClient("172.16.4.21:69")
wt, err := c.Receive("foobar.txt", "octet")
file, err := os.Create(path)
// Optionally obtain transfer size before actual data.
if n, ok := wt.(IncomingTransfer).Size(); ok {
fmt.Printf("Transfer size: %d\n", n)
}
n, err := wt.WriteTo(file)
fmt.Printf("%d bytes received\n", n)
```
Note: please handle errors better :)
TSize option
------------
PXE boot ROM often expects tsize option support from a server: client
(e.g. computer that boots over the network) wants to know size of a
download before the actual data comes. Server has to obtain stream
size and send it to a client.
Often it will happen automatically because TFTP library tries to check
if `io.Reader` provided to `ReadFrom` method also satisfies
`io.Seeker` interface (`os.File` for instance) and uses `Seek` to
determine file size.
In case `io.Reader` you provide to `ReadFrom` in read handler does not
satisfy `io.Seeker` interface or you do not want TFTP library to call
`Seek` on your reader but still want to respond with tsize option
during outgoing request you can use an `OutgoingTransfer` interface:
```go
func readHandler(filename string, rf io.ReaderFrom) error {
...
// Set transfer size before calling ReadFrom.
rf.(tftp.OutgoingTransfer).SetSize(myFileSize)
...
// ReadFrom ...
```
Similarly, it is possible to obtain size of a file that is about to be
received using `IncomingTransfer` interface (see `Size` method).
Local and Remote Address
------------------------
The `OutgoingTransfer` and `IncomingTransfer` interfaces also provide
the `RemoteAddr` method which returns the peer IP address and port as
a `net.UDPAddr`. The `RequestPacketInfo` interface provides a
`LocalIP` method with returns the local IP address as a `net.IP` that
the request is being handled on. These can be used for detailed
logging in a server handler, among other things.
Note that LocalIP may return nil or an unspecified IP address
if finding that is not supported on a particular operating system by
the Go net libraries, or if you call it as a TFTP client.
```go
func readHandler(filename string, rf io.ReaderFrom) error {
...
raddr := rf.(tftp.OutgoingTransfer).RemoteAddr()
laddr := rf.(tftp.RequestPacketInfo).LocalIP()
log.Println("RRQ from", raddr.String(), "To ",laddr.String())
log.Println("")
...
// ReadFrom ...
```
Backoff
-------
The default backoff before retransmitting an unacknowledged packet is a
random duration between 0 and 1 second. This behavior can be overridden
in clients and servers by providing a custom backoff calculation function.
```go
s := tftp.NewServer(readHandler, writeHandler)
s.SetBackoff(func (attempts int) time.Duration {
return time.Duration(attempts) * time.Second
})
```
or, for no backoff
```go
s.SetBackoff(func (int) time.Duration { return 0 })
```