client.go 2.8 KB

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