toybox/toys/pending/tftp.c
<<
>>
Prefs
   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