Home | History | Annotate | Download | only in pending
      1 /* tftpd.c - TFTP server.
      2  *
      3  * Copyright 2013 Ranjan Kumar <ranjankumar.bth (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com>
      5  *
      6  * No Standard.
      7 
      8 USE_TFTPD(NEWTOY(tftpd, "rcu:l", TOYFLAG_BIN))
      9 
     10 config 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 
     27 GLOBALS(
     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 
     74 static char *g_errpkt = toybuf + TFTPD_BLKSIZE;
     75 
     76 // Create and send error packet.
     77 static void send_errpkt(struct sockaddr *dstaddr,
     78     socklen_t socklen, char *errmsg)
     79 {
     80   error_msg(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.
     88 static 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     }
    156 SEND_PKT:
    157     // 1st ACK will be from dst, which will have blockno-=1
    158     *((uint16_t*)spkt) = htons(pktopcode); //append send pkt's opcode.
    159 RETRY_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 
    165 POLL_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(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 
    229 CLEAN_APP:
    230   if (CFG_TOYBOX_FREE) {
    231     free(spkt);
    232     free(rpkt);
    233     close(fd);
    234   }
    235 }
    236 
    237 void 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