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  xbind(TT.sfd, (void *)&srcaddr, socklen);
 256  xconnect(TT.sfd, (void *)&dstaddr, socklen);
 257  // Error condition.
 258  if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) {
 259    send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
 260    return;
 261  }
 262
 263  // request is either upload or Download.
 264  opcode = buf[1];
 265  if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ))
 266      || ((opcode == TFTPD_OP_WRQ) && (toys.optflags & FLAG_r))) {
 267    send_errpkt((struct sockaddr*)&dstaddr, socklen,
 268        (opcode == TFTPD_OP_WRQ) ? "write error" : "packet format error");
 269    return;
 270  }
 271
 272  buf += 2;
 273  if (*buf == '.' || strstr(buf, "/.")) {
 274    send_errpkt((struct sockaddr*)&dstaddr, socklen, "dot in filename");
 275    return;
 276  }
 277
 278  buf += strlen(buf) + 1; //1 '\0'.
 279  // As per RFC 1350, mode is case in-sensitive.
 280  if (buf >= toybuf+recvmsg_len || strcasecmp(buf, "octet")) {
 281    send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
 282    return;
 283  }
 284
 285  //RFC2348. e.g. of size type: "ttype1\0ttype1_val\0...ttypeN\0ttypeN_val\0"
 286  buf += strlen(buf) + 1;
 287  rbuflen = toybuf + recvmsg_len - buf;
 288  if (rbuflen) {
 289    int jump = 0, bflag = 0;
 290
 291    for (; rbuflen; rbuflen -= jump, buf += jump) {
 292      if (!bflag && !strcasecmp(buf, "blksize")) { //get blksize
 293        errno = 0;
 294        blksize = strtoul(buf, NULL, 10);
 295        if (errno || blksize > 65564 || blksize < 8) blksize = TFTPD_BLKSIZE;
 296        bflag ^= 1;
 297      } else if (!tsize && !strcasecmp(buf, "tsize")) tsize ^= 1;
 298      
 299      jump += strlen(buf) + 1;
 300    }
 301    tsize &= (opcode == TFTPD_OP_RRQ);
 302  }
 303
 304  //do send / receive file.
 305  do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr,
 306      socklen, toybuf + 2, opcode, tsize, blksize);
 307  if (CFG_TOYBOX_FREE) close(STDIN_FILENO);
 308}
 309