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// Advance to the next option or value. Returns NULL if there are no
  88// more options.
  89static char *next_token(char *at, char *end)
  90{
  91  if (at == NULL) return NULL;
  92
  93  for (; at < end; at++) {
  94    if (*at == '\0') {
  95      at++;
  96      break;
  97    }
  98  }
  99  return (at < end) ? at : NULL;
 100}
 101
 102// Used to send / receive packets.
 103static void do_action(struct sockaddr *srcaddr, struct sockaddr *dstaddr,
 104    socklen_t socklen, char *file, int opcode, int tsize, int blksize)
 105{
 106  int fd, done = 0, retry_count = 12, timeout = 100, len;
 107  uint16_t blockno = 1, pktopcode, rblockno;
 108  char *ptr, *spkt, *rpkt;
 109  struct pollfd pollfds[1];
 110
 111  spkt = xzalloc(blksize + 4);
 112  rpkt = xzalloc(blksize + 4);
 113  ptr = spkt+2; //point after opcode.
 114
 115  pollfds[0].fd = TT.sfd;
 116  // initialize groups, setgid and setuid
 117  if (TT.pw) xsetuser(TT.pw);
 118
 119  if (opcode == TFTPD_OP_RRQ) fd = open(file, O_RDONLY, 0666);
 120  else fd = open(file,
 121    FLAG(c) ? (O_WRONLY|O_TRUNC|O_CREAT) : (O_WRONLY|O_TRUNC), 0666);
 122  if (fd < 0) {
 123    g_errpkt[3] = TFTPD_ER_NOSUCHFILE;
 124    send_errpkt(dstaddr, socklen, "can't open file");
 125    goto CLEAN_APP;
 126  }
 127  // For download -> blockno will be 1.
 128  // 1st ACK will be from dst,which will have blockno-=1
 129  // Create and send ACK packet.
 130  if (blksize != TFTPD_BLKSIZE || tsize) {
 131    pktopcode = TFTPD_OP_OACK;
 132    // add "blksize\000blksize_val\000" in send buffer.
 133    if (blksize != TFTPD_BLKSIZE) {
 134      strcpy(ptr, "blksize");
 135      ptr += strlen("blksize") + 1;
 136      ptr += snprintf(ptr, 6, "%d", blksize) + 1;
 137    }
 138    if (tsize) {// add "tsize\000tsize_val\000" in send buffer.
 139      struct stat sb;
 140
 141      sb.st_size = 0;
 142      fstat(fd, &sb);
 143      strcpy(ptr, "tsize");
 144      ptr += strlen("tsize") + 1;
 145      ptr += sprintf(ptr, "%lu", (unsigned long)sb.st_size)+1;
 146    }
 147    goto SEND_PKT;
 148  }
 149  // upload ->  ACK 1st packet with filename, as it has blockno 0.
 150  if (opcode == TFTPD_OP_WRQ) blockno = 0;
 151
 152  // Prepare DATA and/or ACK pkt and send it.
 153  for (;;) {
 154    int poll_ret;
 155
 156    retry_count = 12, timeout = 100, pktopcode = TFTPD_OP_ACK;
 157    ptr = spkt+2;
 158    *((uint16_t*)ptr) = htons(blockno);
 159    blockno++;
 160    ptr += 2;
 161    if (opcode == TFTPD_OP_RRQ) {
 162      pktopcode = TFTPD_OP_DATA;
 163      len = readall(fd, ptr, blksize);
 164      if (len < 0) {
 165        send_errpkt(dstaddr, socklen, "read-error");
 166        break;
 167      }
 168      if (len != blksize) done = 1; //last pkt.
 169      ptr += len;
 170    }
 171SEND_PKT:
 172    // 1st ACK will be from dst, which will have blockno-=1
 173    *((uint16_t*)spkt) = htons(pktopcode); //append send pkt's opcode.
 174RETRY_SEND:
 175    if (sendto(TT.sfd, spkt, (ptr - spkt), 0, dstaddr, socklen) <0)
 176      perror_exit("sendto failed");
 177    // if "block size < 512", send ACK and exit.
 178    if ((pktopcode == TFTPD_OP_ACK) && done) break;
 179
 180POLL_INPUT:
 181    pollfds[0].events = POLLIN;
 182    pollfds[0].fd = TT.sfd;
 183    poll_ret = poll(pollfds, 1, timeout);
 184    if (poll_ret < 0 && (errno == EINTR || errno == ENOMEM)) goto POLL_INPUT;
 185    if (!poll_ret) {
 186      if (!--retry_count) {
 187        error_msg("timeout");
 188        break;
 189      }
 190      timeout += 150;
 191      goto RETRY_SEND;
 192    } else if (poll_ret == 1) {
 193      len = read(pollfds[0].fd, rpkt, blksize + 4);
 194      if (len < 0) {
 195        send_errpkt(dstaddr, socklen, "read-error");
 196        break;
 197      }
 198      if (len < 4) goto POLL_INPUT;
 199    } else {
 200      perror_msg("poll");
 201      break;
 202    }
 203    // Validate receive packet.
 204    pktopcode = ntohs(((uint16_t*)rpkt)[0]);
 205    rblockno = ntohs(((uint16_t*)rpkt)[1]);
 206    if (pktopcode == TFTPD_OP_ERR) {
 207      char *message = "DATA Check failure.";
 208      char *arr[] = {"File not found", "Access violation",
 209        "Disk full or allocation exceeded", "Illegal TFTP operation",
 210        "Unknown transfer ID", "File already exists",
 211        "No such user", "Terminate transfer due to option negotiation"};
 212
 213      if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
 214      error_msg_raw(message);
 215      break; // Break the for loop.
 216    }
 217
 218    // if download requested by client,
 219    // server will send data pkt and will receive ACK pkt from client.
 220    if ((opcode == TFTPD_OP_RRQ) && (pktopcode == TFTPD_OP_ACK)) {
 221      if (rblockno == (uint16_t) (blockno - 1)) {
 222        if (!done) continue; // Send next chunk of data.
 223        break;
 224      }
 225    }
 226
 227    // server will receive DATA pkt and write the data.
 228    if ((opcode == TFTPD_OP_WRQ) && (pktopcode == TFTPD_OP_DATA)) {
 229      if (rblockno == blockno) {
 230        int nw = writeall(fd, &rpkt[4], len-4);
 231        if (nw != len-4) {
 232          g_errpkt[3] = TFTPD_ER_FULL;
 233          send_errpkt(dstaddr, socklen, "write error");
 234          break;
 235        }
 236
 237        if (nw != blksize) done = 1;
 238      }
 239      continue;
 240    }
 241    goto POLL_INPUT;
 242  } // end of loop
 243
 244CLEAN_APP:
 245  if (CFG_TOYBOX_FREE) {
 246    free(spkt);
 247    free(rpkt);
 248    close(fd);
 249  }
 250}
 251
 252void tftpd_main(void)
 253{
 254  int fd = 0, recvmsg_len, opcode, blksize = TFTPD_BLKSIZE, tsize = 0, set =1, bflag = 0;
 255  struct sockaddr_storage srcaddr, dstaddr;
 256  socklen_t socklen = sizeof(struct sockaddr_storage);
 257  char *buf = toybuf;
 258  char *end;
 259
 260  memset(&srcaddr, 0, sizeof(srcaddr));
 261  if (getsockname(0, (struct sockaddr *)&srcaddr, &socklen)) help_exit(0);
 262
 263  if (TT.user) TT.pw = xgetpwnam(TT.user);
 264  if (*toys.optargs) xchroot(*toys.optargs);
 265
 266  recvmsg_len = recvfrom(fd, toybuf, blksize, 0, (void *)&dstaddr, &socklen);
 267  end = toybuf + recvmsg_len;
 268
 269  TT.sfd = xsocket(dstaddr.ss_family, SOCK_DGRAM, 0);
 270  if (setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&set,
 271        sizeof(set)) < 0) perror_exit("setsockopt failed");
 272  xbind(TT.sfd, (void *)&srcaddr, socklen);
 273  xconnect(TT.sfd, (void *)&dstaddr, socklen);
 274  // Error condition.
 275  if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) {
 276    send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
 277    return;
 278  }
 279
 280  // request is either upload or Download.
 281  opcode = buf[1];
 282  if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ))
 283      || ((opcode == TFTPD_OP_WRQ) && FLAG(r))) {
 284    send_errpkt((struct sockaddr*)&dstaddr, socklen,
 285      (opcode == TFTPD_OP_WRQ) ? "write error" : "packet format error");
 286    return;
 287  }
 288
 289  buf += 2;
 290  if (*buf == '.' || strstr(buf, "/.")) {
 291    send_errpkt((struct sockaddr*)&dstaddr, socklen, "dot in filename");
 292    return;
 293  }
 294
 295  buf = next_token(buf, end);
 296  // As per RFC 1350, mode is case in-sensitive.
 297  if (buf == NULL || strcasecmp(buf, "octet")) {
 298    send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
 299    return;
 300  }
 301
 302  //RFC2348. e.g. of size type: "ttype1\0ttype1_val\0...ttypeN\0ttypeN_val\0"
 303  for (buf = next_token(buf, end); buf != NULL; buf = next_token(buf, end)) {
 304    char *opt = buf;
 305    buf = next_token(buf, end);
 306    if (buf == NULL) break; // Missing value.
 307
 308    if (!bflag && !strcasecmp(opt, "blksize")) {
 309      errno = 0;
 310      blksize = strtoul(buf, NULL, 10);
 311      if (errno || blksize > 65564 || blksize < 8) blksize = TFTPD_BLKSIZE;
 312      bflag ^= 1;
 313    } else if (!tsize && !strcasecmp(opt, "tsize")) tsize ^= 1;
 314  }
 315
 316  tsize &= (opcode == TFTPD_OP_RRQ);
 317
 318  //do send / receive file.
 319  do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr,
 320      socklen, toybuf + 2, opcode, tsize, blksize);
 321  if (CFG_TOYBOX_FREE) close(0);
 322}
 323