10f66f451Sopenharmony_ci/* tftp.c - TFTP client.
20f66f451Sopenharmony_ci *
30f66f451Sopenharmony_ci * Copyright 2012 Madhur Verma <mad.flexi@gmail.com>
40f66f451Sopenharmony_ci * Copyright 2015 Sameer Prakash Pradhan <sameer.p.pradhan@gmail.com>
50f66f451Sopenharmony_ci *
60f66f451Sopenharmony_ci * No Standard.
70f66f451Sopenharmony_ci
80f66f451Sopenharmony_ciUSE_TFTP(NEWTOY(tftp, "<1b#<8>65464r:l:g|p|[!gp]", TOYFLAG_USR|TOYFLAG_BIN))
90f66f451Sopenharmony_ci
100f66f451Sopenharmony_ciconfig TFTP
110f66f451Sopenharmony_ci  bool "tftp"
120f66f451Sopenharmony_ci  default n
130f66f451Sopenharmony_ci  help
140f66f451Sopenharmony_ci    usage: tftp [OPTIONS] HOST [PORT]
150f66f451Sopenharmony_ci
160f66f451Sopenharmony_ci    Transfer file from/to tftp server.
170f66f451Sopenharmony_ci
180f66f451Sopenharmony_ci    -l FILE Local FILE
190f66f451Sopenharmony_ci    -r FILE Remote FILE
200f66f451Sopenharmony_ci    -g    Get file
210f66f451Sopenharmony_ci    -p    Put file
220f66f451Sopenharmony_ci    -b SIZE Transfer blocks of SIZE octets(8 <= SIZE <= 65464)
230f66f451Sopenharmony_ci*/
240f66f451Sopenharmony_ci#define FOR_tftp
250f66f451Sopenharmony_ci#include "toys.h"
260f66f451Sopenharmony_ci
270f66f451Sopenharmony_ciGLOBALS(
280f66f451Sopenharmony_ci  char *local_file;
290f66f451Sopenharmony_ci  char *remote_file;
300f66f451Sopenharmony_ci  long block_size;
310f66f451Sopenharmony_ci
320f66f451Sopenharmony_ci  struct sockaddr_storage inaddr;
330f66f451Sopenharmony_ci  int af;
340f66f451Sopenharmony_ci)
350f66f451Sopenharmony_ci
360f66f451Sopenharmony_ci#define TFTP_BLKSIZE    512
370f66f451Sopenharmony_ci#define TFTP_RETRIES    3
380f66f451Sopenharmony_ci#define TFTP_DATAHEADERSIZE 4
390f66f451Sopenharmony_ci#define TFTP_MAXPACKETSIZE  (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE)
400f66f451Sopenharmony_ci#define TFTP_PACKETSIZE    TFTP_MAXPACKETSIZE
410f66f451Sopenharmony_ci#define TFTP_DATASIZE    (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE)
420f66f451Sopenharmony_ci#define TFTP_IOBUFSIZE    (TFTP_PACKETSIZE+8)
430f66f451Sopenharmony_ci
440f66f451Sopenharmony_ci#define TFTP_OP_RRQ      1  /* Read Request      RFC 1350, RFC 2090 */
450f66f451Sopenharmony_ci#define TFTP_OP_WRQ      2  /* Write Request     RFC 1350 */
460f66f451Sopenharmony_ci#define TFTP_OP_DATA    3  /* Data chunk      RFC 1350 */
470f66f451Sopenharmony_ci#define TFTP_OP_ACK      4  /* Acknowledgement     RFC 1350 */
480f66f451Sopenharmony_ci#define TFTP_OP_ERR      5  /* Error Message     RFC 1350 */
490f66f451Sopenharmony_ci#define TFTP_OP_OACK    6  /* Option acknowledgment RFC 2347 */
500f66f451Sopenharmony_ci
510f66f451Sopenharmony_ci#define TFTP_ER_ILLEGALOP  4  /* Illegal TFTP operation */
520f66f451Sopenharmony_ci#define TFTP_ER_UNKID    5  /* Unknown transfer ID */
530f66f451Sopenharmony_ci
540f66f451Sopenharmony_ci#define TFTP_ES_NOSUCHFILE  "File not found"
550f66f451Sopenharmony_ci#define TFTP_ES_ACCESS    "Access violation"
560f66f451Sopenharmony_ci#define TFTP_ES_FULL    "Disk full or allocation exceeded"
570f66f451Sopenharmony_ci#define TFTP_ES_ILLEGALOP  "Illegal TFTP operation"
580f66f451Sopenharmony_ci#define TFTP_ES_UNKID    "Unknown transfer ID"
590f66f451Sopenharmony_ci#define TFTP_ES_EXISTS    "File already exists"
600f66f451Sopenharmony_ci#define TFTP_ES_UNKUSER    "No such user"
610f66f451Sopenharmony_ci#define TFTP_ES_NEGOTIATE  "Terminate transfer due to option negotiation"
620f66f451Sopenharmony_ci
630f66f451Sopenharmony_ci// Initializes SERVER with ADDR and returns socket.
640f66f451Sopenharmony_cistatic int init_tftp(struct sockaddr_storage *server)
650f66f451Sopenharmony_ci{
660f66f451Sopenharmony_ci  struct timeval to = { .tv_sec = 10, //Time out
670f66f451Sopenharmony_ci                        .tv_usec = 0 };
680f66f451Sopenharmony_ci  const int set = 1;
690f66f451Sopenharmony_ci  int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP);
700f66f451Sopenharmony_ci
710f66f451Sopenharmony_ci  xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval));
720f66f451Sopenharmony_ci  xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set));
730f66f451Sopenharmony_ci
740f66f451Sopenharmony_ci  if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535);
750f66f451Sopenharmony_ci  memset(server, 0, sizeof(struct sockaddr_storage));
760f66f451Sopenharmony_ci  if (TT.af == AF_INET6) {
770f66f451Sopenharmony_ci      ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6;
780f66f451Sopenharmony_ci      ((struct sockaddr_in6 *)server)->sin6_addr =
790f66f451Sopenharmony_ci        ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr;
800f66f451Sopenharmony_ci      ((struct sockaddr_in6 *)server)->sin6_port = htons(port);
810f66f451Sopenharmony_ci  }
820f66f451Sopenharmony_ci  else {
830f66f451Sopenharmony_ci      ((struct sockaddr_in *)server)->sin_family = AF_INET;
840f66f451Sopenharmony_ci      ((struct sockaddr_in *)server)->sin_addr.s_addr =
850f66f451Sopenharmony_ci        ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr;
860f66f451Sopenharmony_ci      ((struct sockaddr_in *)server)->sin_port = htons(port);
870f66f451Sopenharmony_ci  }
880f66f451Sopenharmony_ci  return sd;
890f66f451Sopenharmony_ci}
900f66f451Sopenharmony_ci
910f66f451Sopenharmony_ci/*
920f66f451Sopenharmony_ci * Makes a request packet in BUFFER with OPCODE and file PATH of MODE
930f66f451Sopenharmony_ci * and returns length of packet.
940f66f451Sopenharmony_ci */
950f66f451Sopenharmony_cistatic int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode)
960f66f451Sopenharmony_ci{
970f66f451Sopenharmony_ci  buffer[0] = opcode >> 8;
980f66f451Sopenharmony_ci  buffer[1] = opcode & 0xff;
990f66f451Sopenharmony_ci  if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long");
1000f66f451Sopenharmony_ci  return sprintf((char*) &buffer[2], "%s%c%s", path, 0,
1010f66f451Sopenharmony_ci    (mode ? "octet" : "netascii")) + 3;
1020f66f451Sopenharmony_ci}
1030f66f451Sopenharmony_ci
1040f66f451Sopenharmony_ci/*
1050f66f451Sopenharmony_ci * Makes an acknowledgement packet in BUFFER of BLOCNO
1060f66f451Sopenharmony_ci * and returns packet length.
1070f66f451Sopenharmony_ci */
1080f66f451Sopenharmony_cistatic int mkpkt_ack(uint8_t *buffer, uint16_t blockno)
1090f66f451Sopenharmony_ci{
1100f66f451Sopenharmony_ci  buffer[0] = TFTP_OP_ACK >> 8;
1110f66f451Sopenharmony_ci  buffer[1] = TFTP_OP_ACK & 0xff;
1120f66f451Sopenharmony_ci  buffer[2] = blockno >> 8;
1130f66f451Sopenharmony_ci  buffer[3] = blockno & 0xff;
1140f66f451Sopenharmony_ci  return 4;
1150f66f451Sopenharmony_ci}
1160f66f451Sopenharmony_ci
1170f66f451Sopenharmony_ci/*
1180f66f451Sopenharmony_ci * Makes an error packet in BUFFER with ERRORCODE and ERRORMSG.
1190f66f451Sopenharmony_ci * and returns packet length.
1200f66f451Sopenharmony_ci */
1210f66f451Sopenharmony_cistatic int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg)
1220f66f451Sopenharmony_ci{
1230f66f451Sopenharmony_ci  buffer[0] = TFTP_OP_ERR >> 8;
1240f66f451Sopenharmony_ci  buffer[1] = TFTP_OP_ERR & 0xff;
1250f66f451Sopenharmony_ci  buffer[2] = errorcode >> 8;
1260f66f451Sopenharmony_ci  buffer[3] = errorcode & 0xff;
1270f66f451Sopenharmony_ci  strcpy((char*) &buffer[4], errormsg);
1280f66f451Sopenharmony_ci  return strlen(errormsg) + 5;
1290f66f451Sopenharmony_ci}
1300f66f451Sopenharmony_ci
1310f66f451Sopenharmony_ci/*
1320f66f451Sopenharmony_ci * Recieves data from server in BUFF with socket SD and updates FROM
1330f66f451Sopenharmony_ci * and returns read length.
1340f66f451Sopenharmony_ci */
1350f66f451Sopenharmony_cistatic int read_server(int sd, void *buf, int len,
1360f66f451Sopenharmony_ci  struct sockaddr_storage *from)
1370f66f451Sopenharmony_ci{
1380f66f451Sopenharmony_ci  socklen_t alen;
1390f66f451Sopenharmony_ci  ssize_t nb;
1400f66f451Sopenharmony_ci
1410f66f451Sopenharmony_ci  for (;;) {
1420f66f451Sopenharmony_ci    memset(buf, 0, len);
1430f66f451Sopenharmony_ci    alen = sizeof(struct sockaddr_storage);
1440f66f451Sopenharmony_ci    nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen);
1450f66f451Sopenharmony_ci    if (nb < 0) {
1460f66f451Sopenharmony_ci      if (errno == EAGAIN) {
1470f66f451Sopenharmony_ci        perror_msg("server read timed out");
1480f66f451Sopenharmony_ci        return nb;
1490f66f451Sopenharmony_ci      }else if (errno != EINTR) {
1500f66f451Sopenharmony_ci        perror_msg("server read failed");
1510f66f451Sopenharmony_ci        return nb;
1520f66f451Sopenharmony_ci      }
1530f66f451Sopenharmony_ci    }else return nb;
1540f66f451Sopenharmony_ci  }
1550f66f451Sopenharmony_ci  return nb;
1560f66f451Sopenharmony_ci}
1570f66f451Sopenharmony_ci
1580f66f451Sopenharmony_ci/*
1590f66f451Sopenharmony_ci * sends data to server TO from BUFF of length LEN through socket SD
1600f66f451Sopenharmony_ci * and returns successfully send bytes number.
1610f66f451Sopenharmony_ci */
1620f66f451Sopenharmony_cistatic ssize_t write_server(int sd, void *buf, size_t len,
1630f66f451Sopenharmony_ci  struct sockaddr_storage *to)
1640f66f451Sopenharmony_ci{
1650f66f451Sopenharmony_ci  ssize_t nb;
1660f66f451Sopenharmony_ci
1670f66f451Sopenharmony_ci  for (;;) {
1680f66f451Sopenharmony_ci    nb = sendto(sd, buf, len, 0, (struct sockaddr *)to,
1690f66f451Sopenharmony_ci            sizeof(struct sockaddr_storage));
1700f66f451Sopenharmony_ci    if (nb < 0) {
1710f66f451Sopenharmony_ci      if (errno != EINTR) {
1720f66f451Sopenharmony_ci        perror_msg("server write failed");
1730f66f451Sopenharmony_ci        return nb;
1740f66f451Sopenharmony_ci      }
1750f66f451Sopenharmony_ci    } else return nb;
1760f66f451Sopenharmony_ci  }
1770f66f451Sopenharmony_ci  return nb;
1780f66f451Sopenharmony_ci}
1790f66f451Sopenharmony_ci
1800f66f451Sopenharmony_ci// checks packet for data and updates block no
1810f66f451Sopenharmony_cistatic inline int check_data( uint8_t *packet, uint16_t *opcode,
1820f66f451Sopenharmony_ci  uint16_t *blockno)
1830f66f451Sopenharmony_ci{
1840f66f451Sopenharmony_ci  *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
1850f66f451Sopenharmony_ci  if (*opcode == TFTP_OP_DATA) {
1860f66f451Sopenharmony_ci    *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
1870f66f451Sopenharmony_ci    return 0;
1880f66f451Sopenharmony_ci  }
1890f66f451Sopenharmony_ci  return -1;
1900f66f451Sopenharmony_ci}
1910f66f451Sopenharmony_ci
1920f66f451Sopenharmony_ci// Makes data packet through FD from file OFFSET in buffer PACKET of BLOCKNO
1930f66f451Sopenharmony_cistatic int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno)
1940f66f451Sopenharmony_ci{
1950f66f451Sopenharmony_ci  off_t tmp;
1960f66f451Sopenharmony_ci  int nbytesread;
1970f66f451Sopenharmony_ci
1980f66f451Sopenharmony_ci  packet[0] = TFTP_OP_DATA >> 8;
1990f66f451Sopenharmony_ci  packet[1] = TFTP_OP_DATA & 0xff;
2000f66f451Sopenharmony_ci  packet[2] = blockno >> 8;
2010f66f451Sopenharmony_ci  packet[3] = blockno & 0xff;
2020f66f451Sopenharmony_ci  tmp = lseek(fd, offset, SEEK_SET);
2030f66f451Sopenharmony_ci  if (tmp == (off_t) -1) {
2040f66f451Sopenharmony_ci    perror_msg("lseek failed");
2050f66f451Sopenharmony_ci    return -1;
2060f66f451Sopenharmony_ci  }
2070f66f451Sopenharmony_ci  nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE);
2080f66f451Sopenharmony_ci  if (nbytesread < 0) return -1;
2090f66f451Sopenharmony_ci  return nbytesread + TFTP_DATAHEADERSIZE;
2100f66f451Sopenharmony_ci}
2110f66f451Sopenharmony_ci
2120f66f451Sopenharmony_ci// Receives ACK responses from server and updates blockno
2130f66f451Sopenharmony_cistatic int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server,
2140f66f451Sopenharmony_ci  uint16_t *port, uint16_t *blockno)
2150f66f451Sopenharmony_ci{
2160f66f451Sopenharmony_ci  struct sockaddr_storage from;
2170f66f451Sopenharmony_ci  int nbytes;
2180f66f451Sopenharmony_ci  uint16_t opcode, rblockno;
2190f66f451Sopenharmony_ci  int packetlen, retry;
2200f66f451Sopenharmony_ci
2210f66f451Sopenharmony_ci  for (retry = 0; retry < TFTP_RETRIES; retry++) {
2220f66f451Sopenharmony_ci    for (;;) {
2230f66f451Sopenharmony_ci      nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
2240f66f451Sopenharmony_ci      if (nbytes < 4) { // Ack headersize = 4
2250f66f451Sopenharmony_ci        if (nbytes == 0) error_msg("Connection lost.");
2260f66f451Sopenharmony_ci        else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes);
2270f66f451Sopenharmony_ci        else error_msg("Server read ACK failure.");
2280f66f451Sopenharmony_ci        break;
2290f66f451Sopenharmony_ci      } else {
2300f66f451Sopenharmony_ci        if (!*port) {
2310f66f451Sopenharmony_ci          *port = ((struct sockaddr_in *)&from)->sin_port;
2320f66f451Sopenharmony_ci          ((struct sockaddr_in *)server)->sin_port =
2330f66f451Sopenharmony_ci                  ((struct sockaddr_in *)&from)->sin_port;
2340f66f451Sopenharmony_ci        }
2350f66f451Sopenharmony_ci        if (((struct sockaddr_in *)server)->sin_addr.s_addr !=
2360f66f451Sopenharmony_ci                ((struct sockaddr_in *)&from)->sin_addr.s_addr) {
2370f66f451Sopenharmony_ci          error_msg("Invalid address in DATA.");
2380f66f451Sopenharmony_ci          continue;
2390f66f451Sopenharmony_ci        }
2400f66f451Sopenharmony_ci        if (*port != ((struct sockaddr_in *)server)->sin_port) {
2410f66f451Sopenharmony_ci          error_msg("Invalid port in DATA.");
2420f66f451Sopenharmony_ci          packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
2430f66f451Sopenharmony_ci          (void) write_server(sd, packet, packetlen, server);
2440f66f451Sopenharmony_ci          continue;
2450f66f451Sopenharmony_ci        }
2460f66f451Sopenharmony_ci        opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
2470f66f451Sopenharmony_ci        rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
2480f66f451Sopenharmony_ci
2490f66f451Sopenharmony_ci        if (opcode != TFTP_OP_ACK) {
2500f66f451Sopenharmony_ci          error_msg("Bad opcode.");
2510f66f451Sopenharmony_ci          if (opcode > 5) {
2520f66f451Sopenharmony_ci            packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
2530f66f451Sopenharmony_ci            (void) write_server(sd, packet, packetlen, server);
2540f66f451Sopenharmony_ci          }
2550f66f451Sopenharmony_ci          break;
2560f66f451Sopenharmony_ci        }
2570f66f451Sopenharmony_ci        if (blockno) *blockno = rblockno;
2580f66f451Sopenharmony_ci        return 0;
2590f66f451Sopenharmony_ci      }
2600f66f451Sopenharmony_ci    }
2610f66f451Sopenharmony_ci  }
2620f66f451Sopenharmony_ci  error_msg("Timeout, Waiting for ACK.");
2630f66f451Sopenharmony_ci  return -1;
2640f66f451Sopenharmony_ci}
2650f66f451Sopenharmony_ci
2660f66f451Sopenharmony_ci// receives file from server.
2670f66f451Sopenharmony_cistatic int file_get(void)
2680f66f451Sopenharmony_ci{
2690f66f451Sopenharmony_ci  struct sockaddr_storage server, from;
2700f66f451Sopenharmony_ci  uint8_t *packet;
2710f66f451Sopenharmony_ci  uint16_t blockno = 0, opcode, rblockno = 0;
2720f66f451Sopenharmony_ci  int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1;
2730f66f451Sopenharmony_ci
2740f66f451Sopenharmony_ci  sd = init_tftp(&server);
2750f66f451Sopenharmony_ci
2760f66f451Sopenharmony_ci  packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE);
2770f66f451Sopenharmony_ci  fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
2780f66f451Sopenharmony_ci
2790f66f451Sopenharmony_ci  len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1);
2800f66f451Sopenharmony_ci  ret = write_server(sd, packet, len, &server);
2810f66f451Sopenharmony_ci  if (ret != len){
2820f66f451Sopenharmony_ci    unlink(TT.local_file);
2830f66f451Sopenharmony_ci    goto errout_with_sd;
2840f66f451Sopenharmony_ci  }
2850f66f451Sopenharmony_ci  if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0;
2860f66f451Sopenharmony_ci  else ((struct sockaddr_in *)&server)->sin_port = 0;
2870f66f451Sopenharmony_ci
2880f66f451Sopenharmony_ci  do {
2890f66f451Sopenharmony_ci    blockno++;
2900f66f451Sopenharmony_ci    for (retry = 0 ; retry < TFTP_RETRIES; retry++) {
2910f66f451Sopenharmony_ci      nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
2920f66f451Sopenharmony_ci      if (nbytesrecvd > 0) {
2930f66f451Sopenharmony_ci        if ( ((TT.af == AF_INET) &&
2940f66f451Sopenharmony_ci                memcmp(&((struct sockaddr_in *)&server)->sin_addr,
2950f66f451Sopenharmony_ci                &((struct sockaddr_in *)&from)->sin_addr,
2960f66f451Sopenharmony_ci                sizeof(struct in_addr))) ||
2970f66f451Sopenharmony_ci             ((TT.af == AF_INET6) &&
2980f66f451Sopenharmony_ci                memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr,
2990f66f451Sopenharmony_ci                &((struct sockaddr_in6 *)&from)->sin6_addr,
3000f66f451Sopenharmony_ci                sizeof(struct in6_addr)))) {
3010f66f451Sopenharmony_ci          error_msg("Invalid address in DATA.");
3020f66f451Sopenharmony_ci          retry--;
3030f66f451Sopenharmony_ci          continue;
3040f66f451Sopenharmony_ci        }
3050f66f451Sopenharmony_ci        if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port
3060f66f451Sopenharmony_ci                && (((struct sockaddr_in *)&server)->sin_port !=
3070f66f451Sopenharmony_ci                ((struct sockaddr_in *)&from)->sin_port)) ||
3080f66f451Sopenharmony_ci             ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port
3090f66f451Sopenharmony_ci                && (((struct sockaddr_in6 *)&server)->sin6_port !=
3100f66f451Sopenharmony_ci                ((struct sockaddr_in6 *)&from)->sin6_port))) {
3110f66f451Sopenharmony_ci          error_msg("Invalid port in DATA.");
3120f66f451Sopenharmony_ci          len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
3130f66f451Sopenharmony_ci          ret = write_server(sd, packet, len, &from);
3140f66f451Sopenharmony_ci          retry--;
3150f66f451Sopenharmony_ci          continue;
3160f66f451Sopenharmony_ci        }
3170f66f451Sopenharmony_ci        if (nbytesrecvd < TFTP_DATAHEADERSIZE) {
3180f66f451Sopenharmony_ci          error_msg("Tiny data packet ignored.");
3190f66f451Sopenharmony_ci          continue;
3200f66f451Sopenharmony_ci        }
3210f66f451Sopenharmony_ci        if (check_data(packet, &opcode, &rblockno) != 0
3220f66f451Sopenharmony_ci            || blockno != rblockno) {
3230f66f451Sopenharmony_ci
3240f66f451Sopenharmony_ci        if (opcode == TFTP_OP_ERR) {
3250f66f451Sopenharmony_ci          char *message = "DATA Check failure.";
3260f66f451Sopenharmony_ci            char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS,
3270f66f451Sopenharmony_ci              TFTP_ES_FULL, TFTP_ES_ILLEGALOP,
3280f66f451Sopenharmony_ci              TFTP_ES_UNKID, TFTP_ES_EXISTS,
3290f66f451Sopenharmony_ci              TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE};
3300f66f451Sopenharmony_ci            if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
3310f66f451Sopenharmony_ci            error_msg_raw(message);
3320f66f451Sopenharmony_ci        }
3330f66f451Sopenharmony_ci        else if (blockno == 1 && opcode == TFTP_OP_OACK) {
3340f66f451Sopenharmony_ci          len = mkpkt_ack(packet, 0);
3350f66f451Sopenharmony_ci          ret = write_server(sd, packet, len, &from);
3360f66f451Sopenharmony_ci          if (ret != len){
3370f66f451Sopenharmony_ci            unlink(TT.local_file);
3380f66f451Sopenharmony_ci            goto errout_with_sd;
3390f66f451Sopenharmony_ci          }
3400f66f451Sopenharmony_ci        }
3410f66f451Sopenharmony_ci        else if (opcode > 5) {
3420f66f451Sopenharmony_ci          len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
3430f66f451Sopenharmony_ci          ret = write_server(sd, packet, len, &from);
3440f66f451Sopenharmony_ci        }
3450f66f451Sopenharmony_ci        continue;
3460f66f451Sopenharmony_ci        }
3470f66f451Sopenharmony_ci        if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port)
3480f66f451Sopenharmony_ci          ((struct sockaddr_in6 *)&server)->sin6_port =
3490f66f451Sopenharmony_ci            ((struct sockaddr_in6 *)&from)->sin6_port;
3500f66f451Sopenharmony_ci        else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port)
3510f66f451Sopenharmony_ci          ((struct sockaddr_in *)&server)->sin_port =
3520f66f451Sopenharmony_ci            ((struct sockaddr_in *)&from)->sin_port;
3530f66f451Sopenharmony_ci        break;
3540f66f451Sopenharmony_ci      }
3550f66f451Sopenharmony_ci    }
3560f66f451Sopenharmony_ci    if (retry == TFTP_RETRIES) {
3570f66f451Sopenharmony_ci      error_msg("Retry limit exceeded.");
3580f66f451Sopenharmony_ci      unlink(TT.local_file);
3590f66f451Sopenharmony_ci      goto errout_with_sd;
3600f66f451Sopenharmony_ci    }
3610f66f451Sopenharmony_ci    ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
3620f66f451Sopenharmony_ci    if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
3630f66f451Sopenharmony_ci      unlink(TT.local_file);
3640f66f451Sopenharmony_ci      goto errout_with_sd;
3650f66f451Sopenharmony_ci    }
3660f66f451Sopenharmony_ci    len = mkpkt_ack(packet, blockno);
3670f66f451Sopenharmony_ci    ret = write_server(sd, packet, len, &server);
3680f66f451Sopenharmony_ci    if (ret != len){
3690f66f451Sopenharmony_ci      unlink(TT.local_file);
3700f66f451Sopenharmony_ci      goto errout_with_sd;
3710f66f451Sopenharmony_ci    }
3720f66f451Sopenharmony_ci  } while (ndatabytes >= TFTP_DATASIZE);
3730f66f451Sopenharmony_ci
3740f66f451Sopenharmony_ci  result = 0;
3750f66f451Sopenharmony_ci
3760f66f451Sopenharmony_cierrout_with_sd: xclose(sd);
3770f66f451Sopenharmony_ci  free(packet);
3780f66f451Sopenharmony_ci  return result;
3790f66f451Sopenharmony_ci}
3800f66f451Sopenharmony_ci
3810f66f451Sopenharmony_ci// Sends file to server.
3820f66f451Sopenharmony_ciint file_put(void)
3830f66f451Sopenharmony_ci{
3840f66f451Sopenharmony_ci  struct sockaddr_storage server;
3850f66f451Sopenharmony_ci  uint8_t *packet;
3860f66f451Sopenharmony_ci  off_t offset = 0;
3870f66f451Sopenharmony_ci  uint16_t blockno = 1, rblockno, port = 0;
3880f66f451Sopenharmony_ci  int packetlen, sd, fd, retry = 0, ret, result = -1;
3890f66f451Sopenharmony_ci
3900f66f451Sopenharmony_ci  sd = init_tftp(&server);
3910f66f451Sopenharmony_ci  packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
3920f66f451Sopenharmony_ci  fd = xopenro(TT.local_file);
3930f66f451Sopenharmony_ci
3940f66f451Sopenharmony_ci  for (;;) {  //first loop for request send and confirmation from server.
3950f66f451Sopenharmony_ci    packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
3960f66f451Sopenharmony_ci    ret = write_server(sd, packet, packetlen, &server);
3970f66f451Sopenharmony_ci    if (ret != packetlen) goto errout_with_sd;
3980f66f451Sopenharmony_ci    if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
3990f66f451Sopenharmony_ci    if (++retry > TFTP_RETRIES) {
4000f66f451Sopenharmony_ci      error_msg("Retry count exceeded.");
4010f66f451Sopenharmony_ci      goto errout_with_sd;
4020f66f451Sopenharmony_ci    }
4030f66f451Sopenharmony_ci  }
4040f66f451Sopenharmony_ci  for (;;) {  // loop for data sending and receving ack from server.
4050f66f451Sopenharmony_ci    packetlen = mkpkt_data(fd, offset, packet, blockno);
4060f66f451Sopenharmony_ci    if (packetlen < 0) goto errout_with_sd;
4070f66f451Sopenharmony_ci
4080f66f451Sopenharmony_ci    ret = write_server(sd, packet, packetlen, &server);
4090f66f451Sopenharmony_ci    if (ret != packetlen) goto errout_with_sd;
4100f66f451Sopenharmony_ci
4110f66f451Sopenharmony_ci    if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
4120f66f451Sopenharmony_ci      if (rblockno == blockno) {
4130f66f451Sopenharmony_ci        if (packetlen < TFTP_PACKETSIZE) break;
4140f66f451Sopenharmony_ci        blockno++;
4150f66f451Sopenharmony_ci        offset += TFTP_DATASIZE;
4160f66f451Sopenharmony_ci        retry = 0;
4170f66f451Sopenharmony_ci        continue;
4180f66f451Sopenharmony_ci      }
4190f66f451Sopenharmony_ci    }
4200f66f451Sopenharmony_ci    if (++retry > TFTP_RETRIES) {
4210f66f451Sopenharmony_ci      error_msg("Retry count exceeded.");
4220f66f451Sopenharmony_ci      goto errout_with_sd;
4230f66f451Sopenharmony_ci    }
4240f66f451Sopenharmony_ci  }
4250f66f451Sopenharmony_ci  result = 0;
4260f66f451Sopenharmony_ci
4270f66f451Sopenharmony_cierrout_with_sd: close(sd);
4280f66f451Sopenharmony_ci  free(packet);
4290f66f451Sopenharmony_ci  return result;
4300f66f451Sopenharmony_ci}
4310f66f451Sopenharmony_ci
4320f66f451Sopenharmony_civoid tftp_main(void)
4330f66f451Sopenharmony_ci{
4340f66f451Sopenharmony_ci  struct addrinfo *info, rp, *res=0;
4350f66f451Sopenharmony_ci  int ret;
4360f66f451Sopenharmony_ci
4370f66f451Sopenharmony_ci  if (FLAG(r)) {
4380f66f451Sopenharmony_ci    if (!FLAG(l)) {
4390f66f451Sopenharmony_ci      char *slash = strrchr(TT.remote_file, '/');
4400f66f451Sopenharmony_ci      TT.local_file = (slash) ? slash + 1 : TT.remote_file;
4410f66f451Sopenharmony_ci    }
4420f66f451Sopenharmony_ci  } else if (FLAG(l)) TT.remote_file = TT.local_file;
4430f66f451Sopenharmony_ci  else error_exit("Please provide some files.");
4440f66f451Sopenharmony_ci
4450f66f451Sopenharmony_ci  memset(&rp, 0, sizeof(rp));
4460f66f451Sopenharmony_ci  rp.ai_family = AF_UNSPEC;
4470f66f451Sopenharmony_ci  rp.ai_socktype = SOCK_STREAM;
4480f66f451Sopenharmony_ci  ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info);
4490f66f451Sopenharmony_ci  if (!ret) {
4500f66f451Sopenharmony_ci    for (res = info; res; res = res->ai_next)
4510f66f451Sopenharmony_ci    if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break;
4520f66f451Sopenharmony_ci  }
4530f66f451Sopenharmony_ci  if (!res)
4540f66f451Sopenharmony_ci    error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret));
4550f66f451Sopenharmony_ci  TT.af = info->ai_family;
4560f66f451Sopenharmony_ci
4570f66f451Sopenharmony_ci  memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen);
4580f66f451Sopenharmony_ci  freeaddrinfo(info);
4590f66f451Sopenharmony_ci
4600f66f451Sopenharmony_ci  if (FLAG(g)) file_get();
4610f66f451Sopenharmony_ci  if (FLAG(p)) file_put();
4620f66f451Sopenharmony_ci}
463