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