xref: /third_party/toybox/toys/pending/tftpd.c (revision 0f66f451)
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