• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
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.
send_errpkt(struct sockaddr * dstaddr,socklen_t socklen,char * errmsg)77 static 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.
next_token(char * at,char * end)89 static 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.
do_action(struct sockaddr * srcaddr,struct sockaddr * dstaddr,socklen_t socklen,char * file,int opcode,int tsize,int blksize)103 static 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     }
171 SEND_PKT:
172     // 1st ACK will be from dst, which will have blockno-=1
173     *((uint16_t*)spkt) = htons(pktopcode); //append send pkt's opcode.
174 RETRY_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 
180 POLL_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 
244 CLEAN_APP:
245   if (CFG_TOYBOX_FREE) {
246     free(spkt);
247     free(rpkt);
248     close(fd);
249   }
250 }
251 
tftpd_main(void)252 void 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