Home | History | Annotate | Download | only in pending
      1 /* tftp.c - TFTP client.
      2  *
      3  * Copyright 2012 Madhur Verma <mad.flexi (at) gmail.com>
      4  * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan (at) gmail.com>
      5  *
      6  * No Standard.
      7 
      8 USE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
      9 
     10 config 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 
     27 GLOBALS(
     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.
     64 static 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  */
     95 static 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  */
    108 static 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  */
    121 static 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  */
    135 static ssize_t read_server(int sd, void *buf, size_t 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  */
    162 static 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
    181 static 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
    193 static 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
    213 static 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   ssize_t 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.
    267 static 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(message);
    332         }
    333         if (opcode > 5) {
    334           len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
    335           ret = write_server(sd, packet, len, &from);
    336         }
    337         continue;
    338         }
    339         if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port)
    340           ((struct sockaddr_in6 *)&server)->sin6_port =
    341             ((struct sockaddr_in6 *)&from)->sin6_port;
    342         else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port)
    343           ((struct sockaddr_in *)&server)->sin_port =
    344             ((struct sockaddr_in *)&from)->sin_port;
    345         break;
    346       }
    347     }
    348     if (retry == TFTP_RETRIES) {
    349       error_msg("Retry limit exceeded.");
    350       unlink(TT.local_file);
    351       goto errout_with_sd;
    352     }
    353     ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
    354     if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
    355       unlink(TT.local_file);
    356       goto errout_with_sd;
    357     }
    358     len = mkpkt_ack(packet, blockno);
    359     ret = write_server(sd, packet, len, &server);
    360     if (ret != len){
    361       unlink(TT.local_file);
    362       goto errout_with_sd;
    363     }
    364   } while (ndatabytes >= TFTP_DATASIZE);
    365 
    366   result = 0;
    367 
    368 errout_with_sd: xclose(sd);
    369   free(packet);
    370   return result;
    371 }
    372 
    373 // Sends file to server.
    374 int file_put(void)
    375 {
    376   struct sockaddr_storage server;
    377   uint8_t *packet;
    378   off_t offset = 0;
    379   uint16_t blockno = 1, rblockno, port = 0;
    380   int packetlen, sd, fd, retry = 0, ret, result = -1;
    381 
    382   sd = init_tftp(&server);
    383   packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
    384   fd = xopenro(TT.local_file);
    385 
    386   for (;;) {  //first loop for request send and confirmation from server.
    387     packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
    388     ret = write_server(sd, packet, packetlen, &server);
    389     if (ret != packetlen) goto errout_with_sd;
    390     if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
    391     if (++retry > TFTP_RETRIES) {
    392       error_msg("Retry count exceeded.");
    393       goto errout_with_sd;
    394     }
    395   }
    396   for (;;) {  // loop for data sending and receving ack from server.
    397     packetlen = mkpkt_data(fd, offset, packet, blockno);
    398     if (packetlen < 0) goto errout_with_sd;
    399 
    400     ret = write_server(sd, packet, packetlen, &server);
    401     if (ret != packetlen) goto errout_with_sd;
    402 
    403     if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
    404       if (rblockno == blockno) {
    405         if (packetlen < TFTP_PACKETSIZE) break;
    406         blockno++;
    407         offset += TFTP_DATASIZE;
    408         retry = 0;
    409         continue;
    410       }
    411     }
    412     if (++retry > TFTP_RETRIES) {
    413       error_msg("Retry count exceeded.");
    414       goto errout_with_sd;
    415     }
    416   }
    417   result = 0;
    418 
    419 errout_with_sd: close(sd);
    420   free(packet);
    421   return result;
    422 }
    423 
    424 void tftp_main(void)
    425 {
    426   struct addrinfo *info, rp, *res=0;
    427   int ret;
    428 
    429   if (toys.optflags & FLAG_r) {
    430     if (!(toys.optflags & FLAG_l)) {
    431       char *slash = strrchr(TT.remote_file, '/');
    432       TT.local_file = (slash) ? slash + 1 : TT.remote_file;
    433     }
    434   } else if (toys.optflags & FLAG_l) TT.remote_file = TT.local_file;
    435   else error_exit("Please provide some files.");
    436 
    437   memset(&rp, 0, sizeof(rp));
    438   rp.ai_family = AF_UNSPEC;
    439   rp.ai_socktype = SOCK_STREAM;
    440   ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info);
    441   if (!ret) {
    442     for (res = info; res; res = res->ai_next)
    443     if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break;
    444   }
    445   if (!res)
    446     error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret));
    447   TT.af = info->ai_family;
    448 
    449   memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen);
    450   freeaddrinfo(info);
    451 
    452   if (toys.optflags & FLAG_g) file_get();
    453   if (toys.optflags & FLAG_p) file_put();
    454 }
    455