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(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(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