toybox/toys/pending/tftpd.c
<<
>>
Prefs
   1/* tftpd.c - TFTP server.
   2 *
   3 * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com>
   4 * Copyright 2013 Kyungwan Han <asura321@gmail.com>
   5 *
   6 * No Standard.
   7
   8USE_TFTPD(NEWTOY(tftpd, "rcu:l", TOYFLAG_BIN))
   9
  10config TFTPD
  11  bool "tftpd"
  12  default n
  13  help
  14    usage: tftpd [-cr] [-u USER] [DIR]
  15
  16    Transfer file from/to tftp server.
  17
  18    -r  read only
  19    -c  Allow file creation via upload
  20    -u  run as USER
  21    -l  Log to syslog (inetd mode requires this)
  22*/
  23
  24#define FOR_tftpd
  25#include "toys.h"
  26
  27GLOBALS(
  28  char *user;
  29
  30  long sfd;
  31  struct passwd *pw;
  32)
  33
  34#define TFTPD_BLKSIZE 512  // as per RFC 1350.
  35
  36// opcodes
  37#define TFTPD_OP_RRQ  1  // Read Request          RFC 1350, RFC 2090
  38#define TFTPD_OP_WRQ  2  // Write Request         RFC 1350
  39#define TFTPD_OP_DATA 3  // Data chunk            RFC 1350
  40#define TFTPD_OP_ACK  4  // Acknowledgement       RFC 1350
  41#define TFTPD_OP_ERR  5  // Error Message         RFC 1350
  42#define TFTPD_OP_OACK 6  // Option acknowledgment RFC 2347
  43
  44// Error Codes:
  45#define TFTPD_ER_NOSUCHFILE  1 // File not found
  46#define TFTPD_ER_ACCESS      2 // Access violation
  47#define TFTPD_ER_FULL        3 // Disk full or allocation exceeded
  48#define TFTPD_ER_ILLEGALOP   4 // Illegal TFTP operation
  49#define TFTPD_ER_UNKID       5 // Unknown transfer ID
  50#define TFTPD_ER_EXISTS      6 // File already exists
  51#define TFTPD_ER_UNKUSER     7 // No such user
  52#define TFTPD_ER_NEGOTIATE   8 // Terminate transfer due to option negotiation
  53
  54/* TFTP Packet Formats
  55 *  Type   Op #     Format without header
  56 *         2 bytes    string    1 byte    string    1 byte
  57 *         -----------------------------------------------
  58 *  RRQ/  | 01/02 |  Filename  |   0  |    Mode    |   0  |
  59 *  WRQ    -----------------------------------------------
  60 *         2 bytes    2 bytes      n bytes
  61 *         ---------------------------------
  62 *  DATA  | 03    |   Block #  |    Data    |
  63 *         ---------------------------------
  64 *         2 bytes    2 bytes
  65 *         -------------------
  66 *  ACK   | 04    |   Block #  |
  67 *         --------------------
  68 *         2 bytes  2 bytes       string     1 byte
  69 *         ----------------------------------------
  70 *  ERROR | 05    |  ErrorCode |   ErrMsg   |   0  |
  71 *         ----------------------------------------
  72 */
  73
  74static char *g_errpkt = toybuf + TFTPD_BLKSIZE;
  75
  76// Create and send error packet.
  77static void send_errpkt(struct sockaddr *dstaddr,
  78    socklen_t socklen, char *errmsg)
  79{
  80  error_msg_raw(errmsg);
  81  g_errpkt[1] = TFTPD_OP_ERR;
  82  strcpy(g_errpkt + 4, errmsg);
  83  if (sendto(TT.sfd, g_errpkt, strlen(errmsg)+5, 0, dstaddr, socklen) < 0)
  84    perror_exit("sendto failed");
  85}
  86
  87// Used to send / receive packets.
  88static void do_action(struct sockaddr *srcaddr, struct sockaddr *dstaddr,
  89    socklen_t socklen, char *file, int opcode, int tsize, int blksize)
  90{
  91  int fd, done = 0, retry_count = 12, timeout = 100, len;
  92  uint16_t blockno = 1, pktopcode, rblockno;
  93  char *ptr, *spkt, *rpkt;
  94  struct pollfd pollfds[1];
  95
  96  spkt = xzalloc(blksize + 4);
  97  rpkt = xzalloc(blksize + 4);
  98  ptr = spkt+2; //point after opcode.
  99
 100  pollfds[0].fd = TT.sfd;
 101  // initialize groups, setgid and setuid
 102  if (TT.pw) xsetuser(TT.pw);
 103
 104  if (opcode == TFTPD_OP_RRQ) fd = open(file, O_RDONLY, 0666);
 105  else fd = open(file, ((toys.optflags & FLAG_c) ?
 106        (O_WRONLY|O_TRUNC|O_CREAT) : (O_WRONLY|O_TRUNC)) , 0666);
 107  if (fd < 0) {
 108    g_errpkt[3] = TFTPD_ER_NOSUCHFILE;
 109    send_errpkt(dstaddr, socklen, "can't open file");
 110    goto CLEAN_APP;
 111  }
 112  // For download -> blockno will be 1.
 113  // 1st ACK will be from dst,which will have blockno-=1
 114  // Create and send ACK packet.
 115  if (blksize != TFTPD_BLKSIZE || tsize) {
 116    pktopcode = TFTPD_OP_OACK;
 117    // add "blksize\000blksize_val\000" in send buffer.
 118    if (blksize != TFTPD_BLKSIZE) {
 119      strcpy(ptr, "blksize");
 120      ptr += strlen("blksize") + 1;
 121      ptr += snprintf(ptr, 6, "%d", blksize) + 1;
 122    }
 123    if (tsize) {// add "tsize\000tsize_val\000" in send buffer.
 124      struct stat sb;
 125
 126      sb.st_size = 0;
 127      fstat(fd, &sb);
 128      strcpy(ptr, "tsize");
 129      ptr += strlen("tsize") + 1;
 130      ptr += sprintf(ptr, "%lu", (unsigned long)sb.st_size)+1;
 131    }
 132    goto SEND_PKT;
 133  }
 134  // upload ->  ACK 1st packet with filename, as it has blockno 0.
 135  if (opcode == TFTPD_OP_WRQ) blockno = 0;
 136
 137  // Prepare DATA and/or ACK pkt and send it.
 138  for (;;) {
 139    int poll_ret;
 140
 141    retry_count = 12, timeout = 100, pktopcode = TFTPD_OP_ACK;
 142    ptr = spkt+2;
 143    *((uint16_t*)ptr) = htons(blockno);
 144    blockno++;
 145    ptr += 2;
 146    if (opcode == TFTPD_OP_RRQ) {
 147      pktopcode = TFTPD_OP_DATA;
 148      len = readall(fd, ptr, blksize);
 149      if (len < 0) {
 150        send_errpkt(dstaddr, socklen, "read-error");
 151        break;
 152      }
 153      if (len != blksize) done = 1; //last pkt.
 154      ptr += len;
 155    }
 156SEND_PKT:
 157    // 1st ACK will be from dst, which will have blockno-=1
 158    *((uint16_t*)spkt) = htons(pktopcode); //append send pkt's opcode.
 159RETRY_SEND:
 160    if (sendto(TT.sfd, spkt, (ptr - spkt), 0, dstaddr, socklen) <0)
 161      perror_exit("sendto failed");
 162    // if "block size < 512", send ACK and exit.
 163    if ((pktopcode == TFTPD_OP_ACK) && done) break;
 164
 165POLL_INPUT:
 166    pollfds[0].events = POLLIN;
 167    pollfds[0].fd = TT.sfd;
 168    poll_ret = poll(pollfds, 1, timeout);
 169    if (poll_ret < 0 && (errno == EINTR || errno == ENOMEM)) goto POLL_INPUT;
 170    if (!poll_ret) {
 171      if (!--retry_count) {
 172        error_msg("timeout");
 173        break;
 174      }
 175      timeout += 150;
 176      goto RETRY_SEND;
 177    } else if (poll_ret == 1) {
 178      len = read(pollfds[0].fd, rpkt, blksize + 4);
 179      if (len < 0) {
 180        send_errpkt(dstaddr, socklen, "read-error");
 181        break;
 182      }
 183      if (len < 4) goto POLL_INPUT;
 184    } else {
 185      perror_msg("poll");
 186      break;
 187    }
 188    // Validate receive packet.
 189    pktopcode = ntohs(((uint16_t*)rpkt)[0]);
 190    rblockno = ntohs(((uint16_t*)rpkt)[1]);
 191    if (pktopcode == TFTPD_OP_ERR) {
 192      char *message = "DATA Check failure.";
 193      char *arr[] = {"File not found", "Access violation",
 194        "Disk full or allocation exceeded", "Illegal TFTP operation",
 195        "Unknown transfer ID", "File already exists",
 196        "No such user", "Terminate transfer due to option negotiation"};
 197
 198      if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
 199      error_msg_raw(message);
 200      break; // Break the for loop.
 201    }
 202
 203    // if download requested by client,
 204    // server will send data pkt and will receive ACK pkt from client.
 205    if ((opcode == TFTPD_OP_RRQ) && (pktopcode == TFTPD_OP_ACK)) {
 206      if (rblockno == (uint16_t) (blockno - 1)) {
 207        if (!done) continue; // Send next chunk of data.
 208        break;
 209      }
 210    }
 211    
 212    // server will receive DATA pkt and write the data.
 213    if ((opcode == TFTPD_OP_WRQ) && (pktopcode == TFTPD_OP_DATA)) {
 214      if (rblockno == blockno) {
 215        int nw = writeall(fd, &rpkt[4], len-4);
 216        if (nw != len-4) {
 217          g_errpkt[3] = TFTPD_ER_FULL;
 218          send_errpkt(dstaddr, socklen, "write error");
 219          break;
 220        }
 221      
 222        if (nw != blksize) done = 1;
 223      }
 224      continue;
 225    }
 226    goto POLL_INPUT;
 227  } // end of loop
 228
 229CLEAN_APP:
 230  if (CFG_TOYBOX_FREE) {
 231    free(spkt);
 232    free(rpkt);
 233    close(fd);
 234  }
 235}
 236
 237void tftpd_main(void)
 238{
 239  int fd = 0, recvmsg_len, rbuflen, opcode, blksize = TFTPD_BLKSIZE, tsize = 0, set =1;
 240  struct sockaddr_storage srcaddr, dstaddr;
 241  socklen_t socklen = sizeof(struct sockaddr_storage);
 242  char *buf = toybuf;
 243
 244  memset(&srcaddr, 0, sizeof(srcaddr));
 245  if (getsockname(0, (struct sockaddr *)&srcaddr, &socklen)) help_exit(0);
 246
 247  if (TT.user) TT.pw = xgetpwnam(TT.user);
 248  if (*toys.optargs) xchroot(*toys.optargs);
 249
 250  recvmsg_len = recvfrom(fd, toybuf, blksize, 0, (void *)&dstaddr, &socklen);
 251
 252  TT.sfd = xsocket(dstaddr.ss_family, SOCK_DGRAM, 0);
 253  if (setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&set,
 254        sizeof(set)) < 0) perror_exit("setsockopt failed");
 255  if (bind(TT.sfd, (void *)&srcaddr, socklen)) perror_exit("bind");
 256  if (connect(TT.sfd, (void *)&dstaddr, socklen) < 0)
 257    perror_exit("can't connect to remote host");
 258  // Error condition.
 259  if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) {
 260    send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
 261    return;
 262  }
 263
 264  // request is either upload or Download.
 265  opcode = buf[1];
 266  if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ))
 267      || ((opcode == TFTPD_OP_WRQ) && (toys.optflags & FLAG_r))) {
 268    send_errpkt((struct sockaddr*)&dstaddr, socklen,
 269        (opcode == TFTPD_OP_WRQ) ? "write error" : "packet format error");
 270    return;
 271  }
 272
 273  buf += 2;
 274  if (*buf == '.' || strstr(buf, "/.")) {
 275    send_errpkt((struct sockaddr*)&dstaddr, socklen, "dot in filename");
 276    return;
 277  }
 278
 279  buf += strlen(buf) + 1; //1 '\0'.
 280  // As per RFC 1350, mode is case in-sensitive.
 281  if (buf >= toybuf+recvmsg_len || strcasecmp(buf, "octet")) {
 282    send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
 283    return;
 284  }
 285
 286  //RFC2348. e.g. of size type: "ttype1\0ttype1_val\0...ttypeN\0ttypeN_val\0"
 287  buf += strlen(buf) + 1;
 288  rbuflen = toybuf + recvmsg_len - buf;
 289  if (rbuflen) {
 290    int jump = 0, bflag = 0;
 291
 292    for (; rbuflen; rbuflen -= jump, buf += jump) {
 293      if (!bflag && !strcasecmp(buf, "blksize")) { //get blksize
 294        errno = 0;
 295        blksize = strtoul(buf, NULL, 10);
 296        if (errno || blksize > 65564 || blksize < 8) blksize = TFTPD_BLKSIZE;
 297        bflag ^= 1;
 298      } else if (!tsize && !strcasecmp(buf, "tsize")) tsize ^= 1;
 299      
 300      jump += strlen(buf) + 1;
 301    }
 302    tsize &= (opcode == TFTPD_OP_RRQ);
 303  }
 304
 305  //do send / receive file.
 306  do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr,
 307      socklen, toybuf + 2, opcode, tsize, blksize);
 308  if (CFG_TOYBOX_FREE) close(STDIN_FILENO);
 309}
 310