1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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
35
36
37#define TFTPD_OP_RRQ 1
38#define TFTPD_OP_WRQ 2
39#define TFTPD_OP_DATA 3
40#define TFTPD_OP_ACK 4
41#define TFTPD_OP_ERR 5
42#define TFTPD_OP_OACK 6
43
44
45#define TFTPD_ER_NOSUCHFILE 1
46#define TFTPD_ER_ACCESS 2
47#define TFTPD_ER_FULL 3
48#define TFTPD_ER_ILLEGALOP 4
49#define TFTPD_ER_UNKID 5
50#define TFTPD_ER_EXISTS 6
51#define TFTPD_ER_UNKUSER 7
52#define TFTPD_ER_NEGOTIATE 8
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74static char *g_errpkt = toybuf + TFTPD_BLKSIZE;
75
76
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
88
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
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;
114
115 pollfds[0].fd = TT.sfd;
116
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
128
129
130 if (blksize != TFTPD_BLKSIZE || tsize) {
131 pktopcode = TFTPD_OP_OACK;
132
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) {
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
150 if (opcode == TFTPD_OP_WRQ) blockno = 0;
151
152
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;
169 ptr += len;
170 }
171SEND_PKT:
172
173 *((uint16_t*)spkt) = htons(pktopcode);
174RETRY_SEND:
175 if (sendto(TT.sfd, spkt, (ptr - spkt), 0, dstaddr, socklen) <0)
176 perror_exit("sendto failed");
177
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
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;
216 }
217
218
219
220 if ((opcode == TFTPD_OP_RRQ) && (pktopcode == TFTPD_OP_ACK)) {
221 if (rblockno == (uint16_t) (blockno - 1)) {
222 if (!done) continue;
223 break;
224 }
225 }
226
227
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 }
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
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
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
297 if (buf == NULL || strcasecmp(buf, "octet")) {
298 send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
299 return;
300 }
301
302
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;
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
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