tftp

package module
v2.1.0+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2016 License: MIT Imports: 10 Imported by: 0

README

TFTP server and client library for Golang

GoDoc Build Status

Implements:

Partially implements (tsize server side only):

  • RFC 2349 - TFTP Timeout Interval and Transfer Size Options

Set of features is sufficient for PXE boot support.

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


// 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:

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:

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:


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).

Remote Address

The OutgoingTransfer and IncomingTransfer interfaces also provide the RemoteAddr method which returns the peer IP address and port as a net.UDPAddr. This can be used for detailed logging in a server handler.


func readHandler(filename string, rf io.ReaderFrom) error {
        ...
        raddr := rf.(tftp.OutgoingTransfer).RemoteAddr()
        log.Println("RRQ from", raddr.String())
        ...
        // 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.

	s := tftp.NewServer(readHandler, writeHandler)
	s.SetBackoff(func (attempts int) time.Duration {
		return time.Duration(attempts) * time.Second
	})

or, for no backoff

	s.SetBackoff(func (int) time.Duration { return 0 })

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

func NewClient

func NewClient(addr string) (*Client, error)

NewClient creates TFTP client for server on address provided.

func (Client) Receive

func (c Client) Receive(filename string, mode string) (io.WriterTo, error)

Receive starts incoming file transmission. It returns io.WriterTo or error.

func (Client) Send

func (c Client) Send(filename string, mode string) (io.ReaderFrom, error)

Send starts outgoing file transmission. It returns io.ReaderFrom or error.

func (*Client) SetBackoff

func (c *Client) SetBackoff(h backoffFunc)

SetBackoff sets a user provided function that is called to provide a backoff duration prior to retransmitting an unacknowledged packet.

func (*Client) SetRetries

func (c *Client) SetRetries(count int)

SetRetries sets maximum number of attempts client made to transmit a packet. Default is 5 attempts.

func (*Client) SetTimeout

func (c *Client) SetTimeout(t time.Duration)

SetTimeout sets maximum time client waits for single network round-trip to succeed. Default is 5 seconds.

type IncomingTransfer

type IncomingTransfer interface {
	// Size returns the size of an incoming file if the request included the
	// tsize option (see RFC2349).  To differentiate a zero-sized file transfer
	// from a request without tsize use the second boolean "ok" return value.
	Size() (n int64, ok bool)

	// RemoteAddr returns the remote peer's IP address and port.
	RemoteAddr() net.UDPAddr
}

IncomingTransfer provides methods that expose information associated with an incoming transfer.

type OutgoingTransfer

type OutgoingTransfer interface {
	// SetSize is used to set the outgoing transfer size (tsize option: RFC2349)
	// manually in a server write transfer handler.
	//
	// It is not necessary in most cases; when the io.Reader provided to
	// ReadFrom also satisfies io.Seeker (e.g. os.File) the transfer size will
	// be determined automatically. Seek will not be attempted when the
	// transfer size option is set with SetSize.
	//
	// The value provided will be used only if SetSize is called before ReadFrom
	// and only on in a server read handler.
	SetSize(n int64)

	// RemoteAddr returns the remote peer's IP address and port.
	RemoteAddr() net.UDPAddr
}

OutgoingTransfer provides methods to set the outgoing transfer size and retrieve the remote address of the peer.

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer(readHandler func(filename string, rf io.ReaderFrom) error,
	writeHandler func(filename string, wt io.WriterTo) error) *Server

NewServer creates TFTP server. It requires two functions to handle read and write requests. In case nil is provided for read or write handler the respective operation is disabled.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(addr string) error

ListenAndServe binds to address provided and start the server. ListenAndServe returns when Shutdown is called.

func (*Server) Serve

func (s *Server) Serve(conn *net.UDPConn)

Serve starts server provided already opened UDP connecton. It is useful for the case when you want to run server in separate goroutine but still want to be able to handle any errors opening connection. Serve returns when Shutdown is called or connection is closed.

func (*Server) SetBackoff

func (s *Server) SetBackoff(h backoffFunc)

SetBackoff sets a user provided function that is called to provide a backoff duration prior to retransmitting an unacknowledged packet.

func (*Server) SetRetries

func (s *Server) SetRetries(count int)

SetRetries sets maximum number of attempts server made to transmit a packet. Default is 5 attempts.

func (*Server) SetTimeout

func (s *Server) SetTimeout(t time.Duration)

SetTimeout sets maximum time server waits for single network round-trip to succeed. Default is 5 seconds.

func (*Server) Shutdown

func (s *Server) Shutdown()

Shutdown make server stop listening for new requests, allows server to finish outstanding transfers and stops server.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL