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 if (bind(TT.sfd, (void *)&srcaddr, socklen)) perror_exit("bind");
256 if (connect(TT.sfd, (void *)&dstaddr, socklen) < 0)
257 perror_exit("can't connect to remote host");
258
259 if (recvmsg_len<4 || recvmsg_len>TFTPD_BLKSIZE || toybuf[recvmsg_len-1]) {
260 send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
261 return;
262 }
263
264
265 opcode = buf[1];
266 if (((opcode != TFTPD_OP_RRQ) && (opcode != TFTPD_OP_WRQ))
267 || ((opcode == TFTPD_OP_WRQ) && (toys.optflags & FLAG_r))) {
268 send_errpkt((struct sockaddr*)&dstaddr, socklen,
269 (opcode == TFTPD_OP_WRQ) ? "write error" : "packet format error");
270 return;
271 }
272
273 buf += 2;
274 if (*buf == '.' || strstr(buf, "/.")) {
275 send_errpkt((struct sockaddr*)&dstaddr, socklen, "dot in filename");
276 return;
277 }
278
279 buf += strlen(buf) + 1;
280
281 if (buf >= toybuf+recvmsg_len || strcasecmp(buf, "octet")) {
282 send_errpkt((struct sockaddr*)&dstaddr, socklen, "packet format error");
283 return;
284 }
285
286
287 buf += strlen(buf) + 1;
288 rbuflen = toybuf + recvmsg_len - buf;
289 if (rbuflen) {
290 int jump = 0, bflag = 0;
291
292 for (; rbuflen; rbuflen -= jump, buf += jump) {
293 if (!bflag && !strcasecmp(buf, "blksize")) {
294 errno = 0;
295 blksize = strtoul(buf, NULL, 10);
296 if (errno || blksize > 65564 || blksize < 8) blksize = TFTPD_BLKSIZE;
297 bflag ^= 1;
298 } else if (!tsize && !strcasecmp(buf, "tsize")) tsize ^= 1;
299
300 jump += strlen(buf) + 1;
301 }
302 tsize &= (opcode == TFTPD_OP_RRQ);
303 }
304
305
306 do_action((struct sockaddr*)&srcaddr, (struct sockaddr*)&dstaddr,
307 socklen, toybuf + 2, opcode, tsize, blksize);
308 if (CFG_TOYBOX_FREE) close(STDIN_FILENO);
309}
310