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 // 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,
106     FLAG(c) ? (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_raw(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)) 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   xbind(TT.sfd, (void *)&srcaddr, socklen);
256   xconnect(TT.sfd, (void *)&dstaddr, socklen);
257   // Error condition.
258   if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) {
259     send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
260     return;
261   }
262 
263   // request is either upload or Download.
264   opcode = buf[1];
265   if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ))
266       || ((opcode == TFTPD_OP_WRQ) && FLAG(r))) {
267     send_errpkt((struct sockaddr*)&dstaddr, socklen,
268       (opcode == TFTPD_OP_WRQ) ? "write error" : "packet format error");
269     return;
270   }
271 
272   buf += 2;
273   if (*buf == '.' || strstr(buf, "/.")) {
274     send_errpkt((struct sockaddr*)&dstaddr, socklen, "dot in filename");
275     return;
276   }
277 
278   buf += strlen(buf) + 1; //1 '\0'.
279   // As per RFC 1350, mode is case in-sensitive.
280   if (buf >= toybuf+recvmsg_len || strcasecmp(buf, "octet")) {
281     send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
282     return;
283   }
284 
285   //RFC2348. e.g. of size type: "ttype1\0ttype1_val\0...ttypeN\0ttypeN_val\0"
286   buf += strlen(buf) + 1;
287   rbuflen = toybuf + recvmsg_len - buf;
288   if (rbuflen) {
289     int jump = 0, bflag = 0;
290 
291     for (; rbuflen; rbuflen -= jump, buf += jump) {
292       if (!bflag && !strcasecmp(buf, "blksize")) { //get blksize
293         errno = 0;
294         blksize = strtoul(buf, NULL, 10);
295         if (errno || blksize > 65564 || blksize < 8) blksize = TFTPD_BLKSIZE;
296         bflag ^= 1;
297       } else if (!tsize && !strcasecmp(buf, "tsize")) tsize ^= 1;
298 
299       jump += strlen(buf) + 1;
300     }
301     tsize &= (opcode == TFTPD_OP_RRQ);
302   }
303 
304   //do send / receive file.
305   do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr,
306       socklen, toybuf + 2, opcode, tsize, blksize);
307   if (CFG_TOYBOX_FREE) close(0);
308 }
309