xref: /third_party/toybox/toys/pending/tftp.c (revision 0f66f451)
1/* tftp.c - TFTP client.
2 *
3 * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
4 * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan@gmail.com>
5 *
6 * No Standard.
7
8USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
9
10config TFTP
11  bool "tftp"
12  default n
13  help
14    usage: tftp [OPTIONS] HOST [PORT]
15
16    Transfer file from/to tftp server.
17
18    -l FILE Local FILE
19    -r FILE Remote FILE
20    -g    Get file
21    -p    Put file
22    -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)
23*/
24#define FOR_tftp
25#include "toys.h"
26
27GLOBALS(
28  char *local_file;
29  char *remote_file;
30  long block_size;
31
32  struct sockaddr_storage inaddr;
33  int af;
34)
35
36#define TFTP_BLKSIZE    512
37#define TFTP_RETRIES    3
38#define TFTP_DATAHEADERSIZE 4
39#define TFTP_MAXPACKETSIZE  (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE)
40#define TFTP_PACKETSIZE    TFTP_MAXPACKETSIZE
41#define TFTP_DATASIZE    (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE)
42#define TFTP_IOBUFSIZE    (TFTP_PACKETSIZE+8)
43
44#define TFTP_OP_RRQ      1  /* Read Request      RFC 1350, RFC 2090 */
45#define TFTP_OP_WRQ      2  /* Write Request     RFC 1350 */
46#define TFTP_OP_DATA    3  /* Data chunk      RFC 1350 */
47#define TFTP_OP_ACK      4  /* Acknowledgement     RFC 1350 */
48#define TFTP_OP_ERR      5  /* Error Message     RFC 1350 */
49#define TFTP_OP_OACK    6  /* Option acknowledgment RFC 2347 */
50
51#define TFTP_ER_ILLEGALOP  4  /* Illegal TFTP operation */
52#define TFTP_ER_UNKID    5  /* Unknown transfer ID */
53
54#define TFTP_ES_NOSUCHFILE  "File not found"
55#define TFTP_ES_ACCESS    "Access violation"
56#define TFTP_ES_FULL    "Disk full or allocation exceeded"
57#define TFTP_ES_ILLEGALOP  "Illegal TFTP operation"
58#define TFTP_ES_UNKID    "Unknown transfer ID"
59#define TFTP_ES_EXISTS    "File already exists"
60#define TFTP_ES_UNKUSER    "No such user"
61#define TFTP_ES_NEGOTIATE  "Terminate transfer due to option negotiation"
62
63// Initializes SERVER with ADDR and returns socket.
64static int init_tftp(struct sockaddr_storage *server)
65{
66  struct timeval to = { .tv_sec = 10, //Time out
67                        .tv_usec = 0 };
68  const int set = 1;
69  int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP);
70
71  xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval));
72  xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set));
73
74  if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535);
75  memset(server, 0, sizeof(struct sockaddr_storage));
76  if (TT.af == AF_INET6) {
77      ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6;
78      ((struct sockaddr_in6 *)server)->sin6_addr =
79        ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr;
80      ((struct sockaddr_in6 *)server)->sin6_port = htons(port);
81  }
82  else {
83      ((struct sockaddr_in *)server)->sin_family = AF_INET;
84      ((struct sockaddr_in *)server)->sin_addr.s_addr =
85        ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr;
86      ((struct sockaddr_in *)server)->sin_port = htons(port);
87  }
88  return sd;
89}
90
91/*
92 * Makes a request packet in BUFFER with OPCODE and file PATH of MODE
93 * and returns length of packet.
94 */
95static int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode)
96{
97  buffer[0] = opcode >> 8;
98  buffer[1] = opcode & 0xff;
99  if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long");
100  return sprintf((char*) &buffer[2], "%s%c%s", path, 0,
101    (mode ? "octet" : "netascii")) + 3;
102}
103
104/*
105 * Makes an acknowledgement packet in BUFFER of BLOCNO
106 * and returns packet length.
107 */
108static int mkpkt_ack(uint8_t *buffer, uint16_t blockno)
109{
110  buffer[0] = TFTP_OP_ACK >> 8;
111  buffer[1] = TFTP_OP_ACK & 0xff;
112  buffer[2] = blockno >> 8;
113  buffer[3] = blockno & 0xff;
114  return 4;
115}
116
117/*
118 * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG.
119 * and returns packet length.
120 */
121static int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg)
122{
123  buffer[0] = TFTP_OP_ERR >> 8;
124  buffer[1] = TFTP_OP_ERR & 0xff;
125  buffer[2] = errorcode >> 8;
126  buffer[3] = errorcode & 0xff;
127  strcpy((char*) &buffer[4], errormsg);
128  return strlen(errormsg) + 5;
129}
130
131/*
132 * Recieves data from server in BUFF with socket SD and updates FROM
133 * and returns read length.
134 */
135static int read_server(int sd, void *buf, int len,
136  struct sockaddr_storage *from)
137{
138  socklen_t alen;
139  ssize_t nb;
140
141  for (;;) {
142    memset(buf, 0, len);
143    alen = sizeof(struct sockaddr_storage);
144    nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen);
145    if (nb < 0) {
146      if (errno == EAGAIN) {
147        perror_msg("server read timed out");
148        return nb;
149      }else if (errno != EINTR) {
150        perror_msg("server read failed");
151        return nb;
152      }
153    }else return nb;
154  }
155  return nb;
156}
157
158/*
159 * sends data to server TO from BUFF of length LEN through socket SD
160 * and returns successfully send bytes number.
161 */
162static ssize_t write_server(int sd, void *buf, size_t len,
163  struct sockaddr_storage *to)
164{
165  ssize_t nb;
166
167  for (;;) {
168    nb = sendto(sd, buf, len, 0, (struct sockaddr *)to,
169            sizeof(struct sockaddr_storage));
170    if (nb < 0) {
171      if (errno != EINTR) {
172        perror_msg("server write failed");
173        return nb;
174      }
175    } else return nb;
176  }
177  return nb;
178}
179
180// checks packet for data and updates block no
181static inline int check_data( uint8_t *packet, uint16_t *opcode,
182  uint16_t *blockno)
183{
184  *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
185  if (*opcode == TFTP_OP_DATA) {
186    *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
187    return 0;
188  }
189  return -1;
190}
191
192// Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO
193static int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno)
194{
195  off_t tmp;
196  int nbytesread;
197
198  packet[0] = TFTP_OP_DATA >> 8;
199  packet[1] = TFTP_OP_DATA & 0xff;
200  packet[2] = blockno >> 8;
201  packet[3] = blockno & 0xff;
202  tmp = lseek(fd, offset, SEEK_SET);
203  if (tmp == (off_t) -1) {
204    perror_msg("lseek failed");
205    return -1;
206  }
207  nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE);
208  if (nbytesread < 0) return -1;
209  return nbytesread + TFTP_DATAHEADERSIZE;
210}
211
212// Receives ACK responses from server and updates blockno
213static int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server,
214  uint16_t *port, uint16_t *blockno)
215{
216  struct sockaddr_storage from;
217  int nbytes;
218  uint16_t opcode, rblockno;
219  int packetlen, retry;
220
221  for (retry = 0; retry < TFTP_RETRIES; retry++) {
222    for (;;) {
223      nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
224      if (nbytes < 4) { // Ack headersize = 4
225        if (nbytes == 0) error_msg("Connection lost.");
226        else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes);
227        else error_msg("Server read ACK failure.");
228        break;
229      } else {
230        if (!*port) {
231          *port = ((struct sockaddr_in *)&from)->sin_port;
232          ((struct sockaddr_in *)server)->sin_port =
233                  ((struct sockaddr_in *)&from)->sin_port;
234        }
235        if (((struct sockaddr_in *)server)->sin_addr.s_addr !=
236                ((struct sockaddr_in *)&from)->sin_addr.s_addr) {
237          error_msg("Invalid address in DATA.");
238          continue;
239        }
240        if (*port != ((struct sockaddr_in *)server)->sin_port) {
241          error_msg("Invalid port in DATA.");
242          packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
243          (void) write_server(sd, packet, packetlen, server);
244          continue;
245        }
246        opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
247        rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
248
249        if (opcode != TFTP_OP_ACK) {
250          error_msg("Bad opcode.");
251          if (opcode > 5) {
252            packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
253            (void) write_server(sd, packet, packetlen, server);
254          }
255          break;
256        }
257        if (blockno) *blockno = rblockno;
258        return 0;
259      }
260    }
261  }
262  error_msg("Timeout, Waiting for ACK.");
263  return -1;
264}
265
266// receives file from server.
267static int file_get(void)
268{
269  struct sockaddr_storage server, from;
270  uint8_t *packet;
271  uint16_t blockno = 0, opcode, rblockno = 0;
272  int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1;
273
274  sd = init_tftp(&server);
275
276  packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE);
277  fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
278
279  len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1);
280  ret = write_server(sd, packet, len, &server);
281  if (ret != len){
282    unlink(TT.local_file);
283    goto errout_with_sd;
284  }
285  if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0;
286  else ((struct sockaddr_in *)&server)->sin_port = 0;
287
288  do {
289    blockno++;
290    for (retry = 0 ; retry < TFTP_RETRIES; retry++) {
291      nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
292      if (nbytesrecvd > 0) {
293        if ( ((TT.af == AF_INET) &&
294                memcmp(&((struct sockaddr_in *)&server)->sin_addr,
295                &((struct sockaddr_in *)&from)->sin_addr,
296                sizeof(struct in_addr))) ||
297             ((TT.af == AF_INET6) &&
298                memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr,
299                &((struct sockaddr_in6 *)&from)->sin6_addr,
300                sizeof(struct in6_addr)))) {
301          error_msg("Invalid address in DATA.");
302          retry--;
303          continue;
304        }
305        if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port
306                && (((struct sockaddr_in *)&server)->sin_port !=
307                ((struct sockaddr_in *)&from)->sin_port)) ||
308             ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port
309                && (((struct sockaddr_in6 *)&server)->sin6_port !=
310                ((struct sockaddr_in6 *)&from)->sin6_port))) {
311          error_msg("Invalid port in DATA.");
312          len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
313          ret = write_server(sd, packet, len, &from);
314          retry--;
315          continue;
316        }
317        if (nbytesrecvd < TFTP_DATAHEADERSIZE) {
318          error_msg("Tiny data packet ignored.");
319          continue;
320        }
321        if (check_data(packet, &opcode, &rblockno) != 0
322            || blockno != rblockno) {
323
324        if (opcode == TFTP_OP_ERR) {
325          char *message = "DATA Check failure.";
326            char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS,
327              TFTP_ES_FULL, TFTP_ES_ILLEGALOP,
328              TFTP_ES_UNKID, TFTP_ES_EXISTS,
329              TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE};
330            if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
331            error_msg_raw(message);
332        }
333        else if (blockno == 1 && opcode == TFTP_OP_OACK) {
334          len = mkpkt_ack(packet, 0);
335          ret = write_server(sd, packet, len, &from);
336          if (ret != len){
337            unlink(TT.local_file);
338            goto errout_with_sd;
339          }
340        }
341        else if (opcode > 5) {
342          len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
343          ret = write_server(sd, packet, len, &from);
344        }
345        continue;
346        }
347        if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port)
348          ((struct sockaddr_in6 *)&server)->sin6_port =
349            ((struct sockaddr_in6 *)&from)->sin6_port;
350        else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port)
351          ((struct sockaddr_in *)&server)->sin_port =
352            ((struct sockaddr_in *)&from)->sin_port;
353        break;
354      }
355    }
356    if (retry == TFTP_RETRIES) {
357      error_msg("Retry limit exceeded.");
358      unlink(TT.local_file);
359      goto errout_with_sd;
360    }
361    ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
362    if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
363      unlink(TT.local_file);
364      goto errout_with_sd;
365    }
366    len = mkpkt_ack(packet, blockno);
367    ret = write_server(sd, packet, len, &server);
368    if (ret != len){
369      unlink(TT.local_file);
370      goto errout_with_sd;
371    }
372  } while (ndatabytes >= TFTP_DATASIZE);
373
374  result = 0;
375
376errout_with_sd: xclose(sd);
377  free(packet);
378  return result;
379}
380
381// Sends file to server.
382int file_put(void)
383{
384  struct sockaddr_storage server;
385  uint8_t *packet;
386  off_t offset = 0;
387  uint16_t blockno = 1, rblockno, port = 0;
388  int packetlen, sd, fd, retry = 0, ret, result = -1;
389
390  sd = init_tftp(&server);
391  packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
392  fd = xopenro(TT.local_file);
393
394  for (;;) {  //first loop for request send and confirmation from server.
395    packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
396    ret = write_server(sd, packet, packetlen, &server);
397    if (ret != packetlen) goto errout_with_sd;
398    if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
399    if (++retry > TFTP_RETRIES) {
400      error_msg("Retry count exceeded.");
401      goto errout_with_sd;
402    }
403  }
404  for (;;) {  // loop for data sending and receving ack from server.
405    packetlen = mkpkt_data(fd, offset, packet, blockno);
406    if (packetlen < 0) goto errout_with_sd;
407
408    ret = write_server(sd, packet, packetlen, &server);
409    if (ret != packetlen) goto errout_with_sd;
410
411    if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
412      if (rblockno == blockno) {
413        if (packetlen < TFTP_PACKETSIZE) break;
414        blockno++;
415        offset += TFTP_DATASIZE;
416        retry = 0;
417        continue;
418      }
419    }
420    if (++retry > TFTP_RETRIES) {
421      error_msg("Retry count exceeded.");
422      goto errout_with_sd;
423    }
424  }
425  result = 0;
426
427errout_with_sd: close(sd);
428  free(packet);
429  return result;
430}
431
432void tftp_main(void)
433{
434  struct addrinfo *info, rp, *res=0;
435  int ret;
436
437  if (FLAG(r)) {
438    if (!FLAG(l)) {
439      char *slash = strrchr(TT.remote_file, '/');
440      TT.local_file = (slash) ? slash + 1 : TT.remote_file;
441    }
442  } else if (FLAG(l)) TT.remote_file = TT.local_file;
443  else error_exit("Please provide some files.");
444
445  memset(&rp, 0, sizeof(rp));
446  rp.ai_family = AF_UNSPEC;
447  rp.ai_socktype = SOCK_STREAM;
448  ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info);
449  if (!ret) {
450    for (res = info; res; res = res->ai_next)
451    if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break;
452  }
453  if (!res)
454    error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret));
455  TT.af = info->ai_family;
456
457  memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen);
458  freeaddrinfo(info);
459
460  if (FLAG(g)) file_get();
461  if (FLAG(p)) file_put();
462}
463