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
88static 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;
99
100 pollfds[0].fd = TT.sfd;
101
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
113
114
115 if (blksize != TFTPD_BLKSIZE || tsize) {
116 pktopcode = TFTPD_OP_OACK;
117
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) {
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
135 if (opcode == TFTPD_OP_WRQ) blockno = 0;
136
137
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;
154 ptr += len;
155 }
156SEND_PKT:
157
158 *((uint16_t*)spkt) = htons(pktopcode);
159RETRY_SEND:
160 if (sendto(TT.sfd, spkt, (ptr - spkt), 0, dstaddr, socklen) <0)
161 perror_exit("sendto failed");
162
163 if ((pktopcode == TFTPD_OP_ACK) && done) break;
164
165POLL_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
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;
201 }
202
203
204
205 if ((opcode == TFTPD_OP_RRQ) && (pktopcode == TFTPD_OP_ACK)) {
206 if (rblockno == (uint16_t) (blockno - 1)) {
207 if (!done) continue;
208 break;
209 }
210 }
211
212
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 }
228
229CLEAN_APP:
230 if (CFG_TOYBOX_FREE) {
231 free(spkt);
232 free(rpkt);
233 close(fd);
234 }
235}
236
237void 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
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
264 opcode = buf[1];
265 if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ))
266 || ((opcode == TFTPD_OP_WRQ) && (toys.optflags & 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;
279
280 if (buf >= toybuf+recvmsg_len || strcasecmp(buf, "octet")) {
281 send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
282 return;
283 }
284
285
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")) {
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
305 do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr,
306 socklen, toybuf + 2, opcode, tsize, blksize);
307 if (CFG_TOYBOX_FREE) close(STDIN_FILENO);
308}
309