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(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.
do_action(struct sockaddr * srcaddr,struct sockaddr * dstaddr,socklen_t socklen,char * file,int opcode,int tsize,int blksize)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 
tftpd_main(void)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)) {
246     toys.exithelp = 1;
247     error_exit(NULL);
248   }
249 
250   if (TT.user) TT.pw = xgetpwnam(TT.user);
251   if (*toys.optargs) xchroot(*toys.optargs);
252 
253   recvmsg_len = recvfrom(fd, toybuf, blksize, 0, (void *)&dstaddr, &socklen);
254 
255   TT.sfd = xsocket(dstaddr.ss_family, SOCK_DGRAM, 0);
256   if (setsockopt(TT.sfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&set,
257         sizeof(set)) < 0) perror_exit("setsockopt failed");
258   if (bind(TT.sfd, (void *)&srcaddr, socklen)) perror_exit("bind");
259   if (connect(TT.sfd, (void *)&dstaddr, socklen) < 0)
260     perror_exit("can't connect to remote host");
261   // Error condition.
262   if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) {
263     send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
264     return;
265   }
266 
267   // request is either upload or Download.
268   opcode = buf[1];
269   if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ))
270       || ((opcode == TFTPD_OP_WRQ) && (toys.optflags & FLAG_r))) {
271     send_errpkt((struct sockaddr*)&dstaddr, socklen,
272     	(opcode == TFTPD_OP_WRQ) ? "write error" : "packet format error");
273     return;
274   }
275 
276   buf += 2;
277   if (*buf == '.' || strstr(buf, "/.")) {
278     send_errpkt((struct sockaddr*)&dstaddr, socklen, "dot in filename");
279     return;
280   }
281 
282   buf += strlen(buf) + 1; //1 '\0'.
283   // As per RFC 1350, mode is case in-sensitive.
284   if (buf >= toybuf+recvmsg_len || strcasecmp(buf, "octet")) {
285     send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
286     return;
287   }
288 
289   //RFC2348. e.g. of size type: "ttype1\0ttype1_val\0...ttypeN\0ttypeN_val\0"
290   buf += strlen(buf) + 1;
291   rbuflen = toybuf + recvmsg_len - buf;
292   if (rbuflen) {
293     int jump = 0, bflag = 0;
294 
295     for (; rbuflen; rbuflen -= jump, buf += jump) {
296       if (!bflag && !strcasecmp(buf, "blksize")) { //get blksize
297         errno = 0;
298         blksize = strtoul(buf, NULL, 10);
299         if (errno || blksize > 65564 || blksize < 8) blksize = TFTPD_BLKSIZE;
300         bflag ^= 1;
301       } else if (!tsize && !strcasecmp(buf, "tsize")) tsize ^= 1;
302 
303       jump += strlen(buf) + 1;
304     }
305     tsize &= (opcode == TFTPD_OP_RRQ);
306   }
307 
308   //do send / receive file.
309   do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr,
310       socklen, toybuf + 2, opcode, tsize, blksize);
311   if (CFG_TOYBOX_FREE) close(STDIN_FILENO);
312 }
313