1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
|
package tftp
import (
"fmt"
"io"
"net"
"strconv"
"time"
)
// NewClient creates TFTP client for server on address provided.
func NewClient(addr string) (*Client, error) {
a, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, fmt.Errorf("resolving address %s: %v", addr, err)
}
return &Client{
addr: a,
timeout: defaultTimeout,
retries: defaultRetries,
}, nil
}
// SetTimeout sets maximum time client waits for single network round-trip to succeed.
// Default is 5 seconds.
func (c *Client) SetTimeout(t time.Duration) {
if t <= 0 {
c.timeout = defaultTimeout
}
c.timeout = t
}
// SetRetries sets maximum number of attempts client made to transmit a packet.
// Default is 5 attempts.
func (c *Client) SetRetries(count int) {
if count < 1 {
c.retries = defaultRetries
}
c.retries = count
}
// SetBackoff sets a user provided function that is called to provide a
// backoff duration prior to retransmitting an unacknowledged packet.
func (c *Client) SetBackoff(h backoffFunc) {
c.backoff = h
}
// SetBlockSize sets a custom block size used in the transmission.
func (c *Client) SetBlockSize(s int) {
c.blksize = s
}
// RequestTSize sets flag to indicate if tsize should be requested.
func (c *Client) RequestTSize(s bool) {
c.tsize = s
}
// Client stores data about a single TFTP client
type Client struct {
addr *net.UDPAddr
timeout time.Duration
retries int
backoff backoffFunc
blksize int
tsize bool
}
// Send starts outgoing file transmission. It returns io.ReaderFrom or error.
func (c Client) Send(filename string, mode string) (io.ReaderFrom, error) {
conn, err := net.ListenUDP("udp", &net.UDPAddr{})
if err != nil {
return nil, err
}
s := &sender{
send: make([]byte, datagramLength),
receive: make([]byte, datagramLength),
conn: &connConnection{conn: conn},
retry: &backoff{handler: c.backoff},
timeout: c.timeout,
retries: c.retries,
addr: c.addr,
mode: mode,
}
if c.blksize != 0 {
s.opts = make(options)
s.opts["blksize"] = strconv.Itoa(c.blksize)
}
n := packRQ(s.send, opWRQ, filename, mode, s.opts)
addr, err := s.sendWithRetry(n)
if err != nil {
return nil, err
}
s.addr = addr
s.opts = nil
return s, nil
}
// Receive starts incoming file transmission. It returns io.WriterTo or error.
func (c Client) Receive(filename string, mode string) (io.WriterTo, error) {
conn, err := net.ListenUDP("udp", &net.UDPAddr{})
if err != nil {
return nil, err
}
if c.timeout == 0 {
c.timeout = defaultTimeout
}
r := &receiver{
send: make([]byte, datagramLength),
receive: make([]byte, datagramLength),
conn: &connConnection{conn: conn},
retry: &backoff{handler: c.backoff},
timeout: c.timeout,
retries: c.retries,
addr: c.addr,
autoTerm: true,
block: 1,
mode: mode,
}
if c.blksize != 0 || c.tsize {
r.opts = make(options)
}
if c.blksize != 0 {
r.opts["blksize"] = strconv.Itoa(c.blksize)
// Clean it up so we don't send options twice
defer func() { delete(r.opts, "blksize") }()
}
if c.tsize {
r.opts["tsize"] = "0"
}
n := packRQ(r.send, opRRQ, filename, mode, r.opts)
l, addr, err := r.receiveWithRetry(n)
if err != nil {
return nil, err
}
r.l = l
r.addr = addr
return r, nil
}
|