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_tftp
25#include "toys.h"
26
27GLOBALS(
28 char *local_file;
29 char *remote_file;
30 long block_size;
31
32 struct sockaddr_storage inaddr;
33 int af;
34)
35
36#define TFTP_BLKSIZE 512
37#define TFTP_RETRIES 3
38#define TFTP_DATAHEADERSIZE 4
39#define TFTP_MAXPACKETSIZE (TFTP_DATAHEADERSIZE + TFTP_BLKSIZE)
40#define TFTP_PACKETSIZE TFTP_MAXPACKETSIZE
41#define TFTP_DATASIZE (TFTP_PACKETSIZE-TFTP_DATAHEADERSIZE)
42#define TFTP_IOBUFSIZE (TFTP_PACKETSIZE+8)
43
44#define TFTP_OP_RRQ 1
45#define TFTP_OP_WRQ 2
46#define TFTP_OP_DATA 3
47#define TFTP_OP_ACK 4
48#define TFTP_OP_ERR 5
49#define TFTP_OP_OACK 6
50
51#define TFTP_ER_ILLEGALOP 4
52#define TFTP_ER_UNKID 5
53
54#define TFTP_ES_NOSUCHFILE "File not found"
55#define TFTP_ES_ACCESS "Access violation"
56#define TFTP_ES_FULL "Disk full or allocation exceeded"
57#define TFTP_ES_ILLEGALOP "Illegal TFTP operation"
58#define TFTP_ES_UNKID "Unknown transfer ID"
59#define TFTP_ES_EXISTS "File already exists"
60#define TFTP_ES_UNKUSER "No such user"
61#define TFTP_ES_NEGOTIATE "Terminate transfer due to option negotiation"
62
63
64static int init_tftp(struct sockaddr_storage *server)
65{
66 struct timeval to = { .tv_sec = 10,
67 .tv_usec = 0 };
68 const int set = 1;
69 int port = 69, sd = xsocket(TT.af, SOCK_DGRAM, IPPROTO_UDP);
70
71 xsetsockopt(sd, SOL_SOCKET, SO_RCVTIMEO, (void *)&to, sizeof(struct timeval));
72 xsetsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (void *)&set, sizeof(set));
73
74 if(toys.optc == 2) port = atolx_range(toys.optargs[1], 1, 65535);
75 memset(server, 0, sizeof(struct sockaddr_storage));
76 if (TT.af == AF_INET6) {
77 ((struct sockaddr_in6 *)server)->sin6_family = AF_INET6;
78 ((struct sockaddr_in6 *)server)->sin6_addr =
79 ((struct sockaddr_in6 *)&TT.inaddr)->sin6_addr;
80 ((struct sockaddr_in6 *)server)->sin6_port = htons(port);
81 }
82 else {
83 ((struct sockaddr_in *)server)->sin_family = AF_INET;
84 ((struct sockaddr_in *)server)->sin_addr.s_addr =
85 ((struct sockaddr_in *)&TT.inaddr)->sin_addr.s_addr;
86 ((struct sockaddr_in *)server)->sin_port = htons(port);
87 }
88 return sd;
89}
90
91
92
93
94
95static int mkpkt_request(uint8_t *buffer, int opcode, char *path, int mode)
96{
97 buffer[0] = opcode >> 8;
98 buffer[1] = opcode & 0xff;
99 if(strlen(path) > TFTP_BLKSIZE) error_exit("path too long");
100 return sprintf((char*) &buffer[2], "%s%c%s", path, 0,
101 (mode ? "octet" : "netascii")) + 3;
102}
103
104
105
106
107
108static int mkpkt_ack(uint8_t *buffer, uint16_t blockno)
109{
110 buffer[0] = TFTP_OP_ACK >> 8;
111 buffer[1] = TFTP_OP_ACK & 0xff;
112 buffer[2] = blockno >> 8;
113 buffer[3] = blockno & 0xff;
114 return 4;
115}
116
117
118
119
120
121static int mkpkt_err(uint8_t *buffer, uint16_t errorcode, char *errormsg)
122{
123 buffer[0] = TFTP_OP_ERR >> 8;
124 buffer[1] = TFTP_OP_ERR & 0xff;
125 buffer[2] = errorcode >> 8;
126 buffer[3] = errorcode & 0xff;
127 strcpy((char*) &buffer[4], errormsg);
128 return strlen(errormsg) + 5;
129}
130
131
132
133
134
135static ssize_t read_server(int sd, void *buf, size_t len,
136 struct sockaddr_storage *from)
137{
138 socklen_t alen;
139 ssize_t nb;
140
141 for (;;) {
142 memset(buf, 0, len);
143 alen = sizeof(struct sockaddr_storage);
144 nb = recvfrom(sd, buf, len, 0, (struct sockaddr *) from, &alen);
145 if (nb < 0) {
146 if (errno == EAGAIN) {
147 perror_msg("server read timed out");
148 return nb;
149 }else if (errno != EINTR) {
150 perror_msg("server read failed");
151 return nb;
152 }
153 }else return nb;
154 }
155 return nb;
156}
157
158
159
160
161
162static ssize_t write_server(int sd, void *buf, size_t len,
163 struct sockaddr_storage *to)
164{
165 ssize_t nb;
166
167 for (;;) {
168 nb = sendto(sd, buf, len, 0, (struct sockaddr *)to,
169 sizeof(struct sockaddr_storage));
170 if (nb < 0) {
171 if (errno != EINTR) {
172 perror_msg("server write failed");
173 return nb;
174 }
175 } else return nb;
176 }
177 return nb;
178}
179
180
181static inline int check_data( uint8_t *packet, uint16_t *opcode,
182 uint16_t *blockno)
183{
184 *opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
185 if (*opcode == TFTP_OP_DATA) {
186 *blockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
187 return 0;
188 }
189 return -1;
190}
191
192
193static int mkpkt_data(int fd, off_t offset, uint8_t *packet, uint16_t blockno)
194{
195 off_t tmp;
196 int nbytesread;
197
198 packet[0] = TFTP_OP_DATA >> 8;
199 packet[1] = TFTP_OP_DATA & 0xff;
200 packet[2] = blockno >> 8;
201 packet[3] = blockno & 0xff;
202 tmp = lseek(fd, offset, SEEK_SET);
203 if (tmp == (off_t) -1) {
204 perror_msg("lseek failed");
205 return -1;
206 }
207 nbytesread = readall(fd, &packet[TFTP_DATAHEADERSIZE], TFTP_DATASIZE);
208 if (nbytesread < 0) return -1;
209 return nbytesread + TFTP_DATAHEADERSIZE;
210}
211
212
213static int read_ack(int sd, uint8_t *packet, struct sockaddr_storage *server,
214 uint16_t *port, uint16_t *blockno)
215{
216 struct sockaddr_storage from;
217 ssize_t nbytes;
218 uint16_t opcode, rblockno;
219 int packetlen, retry;
220
221 for (retry = 0; retry < TFTP_RETRIES; retry++) {
222 for (;;) {
223 nbytes = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
224 if (nbytes < 4) {
225 if (nbytes == 0) error_msg("Connection lost.");
226 else if (nbytes > 0) error_msg("Short packet: %d bytes", nbytes);
227 else error_msg("Server read ACK failure.");
228 break;
229 } else {
230 if (!*port) {
231 *port = ((struct sockaddr_in *)&from)->sin_port;
232 ((struct sockaddr_in *)server)->sin_port =
233 ((struct sockaddr_in *)&from)->sin_port;
234 }
235 if (((struct sockaddr_in *)server)->sin_addr.s_addr !=
236 ((struct sockaddr_in *)&from)->sin_addr.s_addr) {
237 error_msg("Invalid address in DATA.");
238 continue;
239 }
240 if (*port != ((struct sockaddr_in *)server)->sin_port) {
241 error_msg("Invalid port in DATA.");
242 packetlen = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
243 (void) write_server(sd, packet, packetlen, server);
244 continue;
245 }
246 opcode = (uint16_t) packet[0] << 8 | (uint16_t) packet[1];
247 rblockno = (uint16_t) packet[2] << 8 | (uint16_t) packet[3];
248
249 if (opcode != TFTP_OP_ACK) {
250 error_msg("Bad opcode.");
251 if (opcode > 5) {
252 packetlen = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
253 (void) write_server(sd, packet, packetlen, server);
254 }
255 break;
256 }
257 if (blockno) *blockno = rblockno;
258 return 0;
259 }
260 }
261 }
262 error_msg("Timeout, Waiting for ACK.");
263 return -1;
264}
265
266
267static int file_get(void)
268{
269 struct sockaddr_storage server, from;
270 uint8_t *packet;
271 uint16_t blockno = 0, opcode, rblockno = 0;
272 int len, sd, fd, retry, nbytesrecvd = 0, ndatabytes, ret, result = -1;
273
274 sd = init_tftp(&server);
275
276 packet = (uint8_t*) xzalloc(TFTP_IOBUFSIZE);
277 fd = xcreate(TT.local_file, O_WRONLY | O_CREAT | O_TRUNC, 0666);
278
279 len = mkpkt_request(packet, TFTP_OP_RRQ, TT.remote_file, 1);
280 ret = write_server(sd, packet, len, &server);
281 if (ret != len){
282 unlink(TT.local_file);
283 goto errout_with_sd;
284 }
285 if (TT.af == AF_INET6) ((struct sockaddr_in6 *)&server)->sin6_port = 0;
286 else ((struct sockaddr_in *)&server)->sin_port = 0;
287
288 do {
289 blockno++;
290 for (retry = 0 ; retry < TFTP_RETRIES; retry++) {
291 nbytesrecvd = read_server(sd, packet, TFTP_IOBUFSIZE, &from);
292 if (nbytesrecvd > 0) {
293 if ( ((TT.af == AF_INET) &&
294 memcmp(&((struct sockaddr_in *)&server)->sin_addr,
295 &((struct sockaddr_in *)&from)->sin_addr,
296 sizeof(struct in_addr))) ||
297 ((TT.af == AF_INET6) &&
298 memcmp(&((struct sockaddr_in6 *)&server)->sin6_addr,
299 &((struct sockaddr_in6 *)&from)->sin6_addr,
300 sizeof(struct in6_addr)))) {
301 error_msg("Invalid address in DATA.");
302 retry--;
303 continue;
304 }
305 if ( ((TT.af == AF_INET) && ((struct sockaddr_in *)&server)->sin_port
306 && (((struct sockaddr_in *)&server)->sin_port !=
307 ((struct sockaddr_in *)&from)->sin_port)) ||
308 ((TT.af == AF_INET6) && ((struct sockaddr_in6 *)&server)->sin6_port
309 && (((struct sockaddr_in6 *)&server)->sin6_port !=
310 ((struct sockaddr_in6 *)&from)->sin6_port))) {
311 error_msg("Invalid port in DATA.");
312 len = mkpkt_err(packet, TFTP_ER_UNKID, TFTP_ES_UNKID);
313 ret = write_server(sd, packet, len, &from);
314 retry--;
315 continue;
316 }
317 if (nbytesrecvd < TFTP_DATAHEADERSIZE) {
318 error_msg("Tiny data packet ignored.");
319 continue;
320 }
321 if (check_data(packet, &opcode, &rblockno) != 0
322 || blockno != rblockno) {
323
324 if (opcode == TFTP_OP_ERR) {
325 char *message = "DATA Check failure.";
326 char *arr[] = {TFTP_ES_NOSUCHFILE, TFTP_ES_ACCESS,
327 TFTP_ES_FULL, TFTP_ES_ILLEGALOP,
328 TFTP_ES_UNKID, TFTP_ES_EXISTS,
329 TFTP_ES_UNKUSER, TFTP_ES_NEGOTIATE};
330 if (rblockno && (rblockno < 9)) message = arr[rblockno - 1];
331 error_msg_raw(message);
332 }
333 else if (blockno == 1 && opcode == TFTP_OP_OACK) {
334 len = mkpkt_ack(packet, 0);
335 ret = write_server(sd, packet, len, &from);
336 if (ret != len){
337 unlink(TT.local_file);
338 goto errout_with_sd;
339 }
340 }
341 else if (opcode > 5) {
342 len = mkpkt_err(packet, TFTP_ER_ILLEGALOP, TFTP_ES_ILLEGALOP);
343 ret = write_server(sd, packet, len, &from);
344 }
345 continue;
346 }
347 if ((TT.af == AF_INET6) && !((struct sockaddr_in6 *)&server)->sin6_port)
348 ((struct sockaddr_in6 *)&server)->sin6_port =
349 ((struct sockaddr_in6 *)&from)->sin6_port;
350 else if ((TT.af == AF_INET) && !((struct sockaddr_in *)&server)->sin_port)
351 ((struct sockaddr_in *)&server)->sin_port =
352 ((struct sockaddr_in *)&from)->sin_port;
353 break;
354 }
355 }
356 if (retry == TFTP_RETRIES) {
357 error_msg("Retry limit exceeded.");
358 unlink(TT.local_file);
359 goto errout_with_sd;
360 }
361 ndatabytes = nbytesrecvd - TFTP_DATAHEADERSIZE;
362 if (writeall(fd, packet + TFTP_DATAHEADERSIZE, ndatabytes) < 0){
363 unlink(TT.local_file);
364 goto errout_with_sd;
365 }
366 len = mkpkt_ack(packet, blockno);
367 ret = write_server(sd, packet, len, &server);
368 if (ret != len){
369 unlink(TT.local_file);
370 goto errout_with_sd;
371 }
372 } while (ndatabytes >= TFTP_DATASIZE);
373
374 result = 0;
375
376errout_with_sd: xclose(sd);
377 free(packet);
378 return result;
379}
380
381
382int file_put(void)
383{
384 struct sockaddr_storage server;
385 uint8_t *packet;
386 off_t offset = 0;
387 uint16_t blockno = 1, rblockno, port = 0;
388 int packetlen, sd, fd, retry = 0, ret, result = -1;
389
390 sd = init_tftp(&server);
391 packet = (uint8_t*)xzalloc(TFTP_IOBUFSIZE);
392 fd = xopenro(TT.local_file);
393
394 for (;;) {
395 packetlen = mkpkt_request(packet, TFTP_OP_WRQ, TT.remote_file, 1);
396 ret = write_server(sd, packet, packetlen, &server);
397 if (ret != packetlen) goto errout_with_sd;
398 if (read_ack(sd, packet, &server, &port, NULL) == 0) break;
399 if (++retry > TFTP_RETRIES) {
400 error_msg("Retry count exceeded.");
401 goto errout_with_sd;
402 }
403 }
404 for (;;) {
405 packetlen = mkpkt_data(fd, offset, packet, blockno);
406 if (packetlen < 0) goto errout_with_sd;
407
408 ret = write_server(sd, packet, packetlen, &server);
409 if (ret != packetlen) goto errout_with_sd;
410
411 if (read_ack(sd, packet, &server, &port, &rblockno) == 0) {
412 if (rblockno == blockno) {
413 if (packetlen < TFTP_PACKETSIZE) break;
414 blockno++;
415 offset += TFTP_DATASIZE;
416 retry = 0;
417 continue;
418 }
419 }
420 if (++retry > TFTP_RETRIES) {
421 error_msg("Retry count exceeded.");
422 goto errout_with_sd;
423 }
424 }
425 result = 0;
426
427errout_with_sd: close(sd);
428 free(packet);
429 return result;
430}
431
432void tftp_main(void)
433{
434 struct addrinfo *info, rp, *res=0;
435 int ret;
436
437 if (toys.optflags & FLAG_r) {
438 if (!(toys.optflags & FLAG_l)) {
439 char *slash = strrchr(TT.remote_file, '/');
440 TT.local_file = (slash) ? slash + 1 : TT.remote_file;
441 }
442 } else if (toys.optflags & FLAG_l) TT.remote_file = TT.local_file;
443 else error_exit("Please provide some files.");
444
445 memset(&rp, 0, sizeof(rp));
446 rp.ai_family = AF_UNSPEC;
447 rp.ai_socktype = SOCK_STREAM;
448 ret = getaddrinfo(toys.optargs[0], toys.optargs[1], &rp, &info);
449 if (!ret) {
450 for (res = info; res; res = res->ai_next)
451 if ( (res->ai_family == AF_INET) || (res->ai_family == AF_INET6)) break;
452 }
453 if (!res)
454 error_exit("bad address '%s' : %s", toys.optargs[0], gai_strerror(ret));
455 TT.af = info->ai_family;
456
457 memcpy((void *)&TT.inaddr, info->ai_addr, info->ai_addrlen);
458 freeaddrinfo(info);
459
460 if (toys.optflags & FLAG_g) file_get();
461 if (toys.optflags & FLAG_p) file_put();
462}
463