Contents of /tags/mkinitrd-6_2_3/busybox/networking/tftp.c
Parent Directory | Revision Log
Revision 1054 -
(show annotations)
(download)
Mon May 31 20:54:44 2010 UTC (14 years, 3 months ago) by niro
File MIME type: text/plain
File size: 23491 byte(s)
Mon May 31 20:54:44 2010 UTC (14 years, 3 months ago) by niro
File MIME type: text/plain
File size: 23491 byte(s)
tagged 'mkinitrd-6_2_3'
1 | /* vi: set sw=4 ts=4: */ |
2 | /* |
3 | * A simple tftp client/server for busybox. |
4 | * Tries to follow RFC1350. |
5 | * Only "octet" mode supported. |
6 | * Optional blocksize negotiation (RFC2347 + RFC2348) |
7 | * |
8 | * Copyright (C) 2001 Magnus Damm <damm@opensource.se> |
9 | * |
10 | * Parts of the code based on: |
11 | * |
12 | * atftp: Copyright (C) 2000 Jean-Pierre Lefebvre <helix@step.polymtl.ca> |
13 | * and Remi Lefebvre <remi@debian.org> |
14 | * |
15 | * utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> |
16 | * |
17 | * tftpd added by Denys Vlasenko & Vladimir Dronnikov |
18 | * |
19 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
20 | */ |
21 | #include "libbb.h" |
22 | |
23 | #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT |
24 | |
25 | #define TFTP_BLKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ |
26 | #define TFTP_BLKSIZE_DEFAULT_STR "512" |
27 | #define TFTP_TIMEOUT_MS 50 |
28 | #define TFTP_MAXTIMEOUT_MS 2000 |
29 | #define TFTP_NUM_RETRIES 12 /* number of backed-off retries */ |
30 | |
31 | /* opcodes we support */ |
32 | #define TFTP_RRQ 1 |
33 | #define TFTP_WRQ 2 |
34 | #define TFTP_DATA 3 |
35 | #define TFTP_ACK 4 |
36 | #define TFTP_ERROR 5 |
37 | #define TFTP_OACK 6 |
38 | |
39 | /* error codes sent over network (we use only 0, 1, 3 and 8) */ |
40 | /* generic (error message is included in the packet) */ |
41 | #define ERR_UNSPEC 0 |
42 | #define ERR_NOFILE 1 |
43 | #define ERR_ACCESS 2 |
44 | /* disk full or allocation exceeded */ |
45 | #define ERR_WRITE 3 |
46 | #define ERR_OP 4 |
47 | #define ERR_BAD_ID 5 |
48 | #define ERR_EXIST 6 |
49 | #define ERR_BAD_USER 7 |
50 | #define ERR_BAD_OPT 8 |
51 | |
52 | /* masks coming from getopt32 */ |
53 | enum { |
54 | TFTP_OPT_GET = (1 << 0), |
55 | TFTP_OPT_PUT = (1 << 1), |
56 | /* pseudo option: if set, it's tftpd */ |
57 | TFTPD_OPT = (1 << 7) * ENABLE_TFTPD, |
58 | TFTPD_OPT_r = (1 << 8) * ENABLE_TFTPD, |
59 | TFTPD_OPT_c = (1 << 9) * ENABLE_TFTPD, |
60 | TFTPD_OPT_u = (1 << 10) * ENABLE_TFTPD, |
61 | }; |
62 | |
63 | #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT |
64 | #define IF_GETPUT(...) |
65 | #define CMD_GET(cmd) 1 |
66 | #define CMD_PUT(cmd) 0 |
67 | #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT |
68 | #define IF_GETPUT(...) |
69 | #define CMD_GET(cmd) 0 |
70 | #define CMD_PUT(cmd) 1 |
71 | #else |
72 | #define IF_GETPUT(...) __VA_ARGS__ |
73 | #define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET) |
74 | #define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT) |
75 | #endif |
76 | /* NB: in the code below |
77 | * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive |
78 | */ |
79 | |
80 | |
81 | struct globals { |
82 | /* u16 TFTP_ERROR; u16 reason; both network-endian, then error text: */ |
83 | uint8_t error_pkt[4 + 32]; |
84 | char *user_opt; |
85 | /* used in tftpd_main(), a bit big for stack: */ |
86 | char block_buf[TFTP_BLKSIZE_DEFAULT]; |
87 | #if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
88 | off_t pos; |
89 | off_t size; |
90 | const char *file; |
91 | bb_progress_t pmt; |
92 | #endif |
93 | }; |
94 | #define G (*(struct globals*)&bb_common_bufsiz1) |
95 | struct BUG_G_too_big { |
96 | char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; |
97 | }; |
98 | #define block_buf (G.block_buf ) |
99 | #define user_opt (G.user_opt ) |
100 | #define error_pkt (G.error_pkt ) |
101 | #define INIT_G() do { } while (0) |
102 | |
103 | #define error_pkt_reason (error_pkt[3]) |
104 | #define error_pkt_str (error_pkt + 4) |
105 | |
106 | #if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
107 | /* SIGALRM logic nicked from the wget applet */ |
108 | static void progress_meter(int flag) |
109 | { |
110 | /* We can be called from signal handler */ |
111 | int save_errno = errno; |
112 | |
113 | if (flag == -1) { /* first call to progress_meter */ |
114 | bb_progress_init(&G.pmt); |
115 | } |
116 | |
117 | bb_progress_update(&G.pmt, G.file, 0, G.pos, G.size); |
118 | |
119 | if (flag == 0) { |
120 | /* last call to progress_meter */ |
121 | alarm(0); |
122 | fputc('\n', stderr); |
123 | } else { |
124 | if (flag == -1) { /* first call to progress_meter */ |
125 | signal_SA_RESTART_empty_mask(SIGALRM, progress_meter); |
126 | } |
127 | alarm(1); |
128 | } |
129 | |
130 | errno = save_errno; |
131 | } |
132 | static void tftp_progress_init(void) |
133 | { |
134 | progress_meter(-1); |
135 | } |
136 | static void tftp_progress_done(void) |
137 | { |
138 | progress_meter(0); |
139 | } |
140 | #else |
141 | # define tftp_progress_init() ((void)0) |
142 | # define tftp_progress_done() ((void)0) |
143 | #endif |
144 | |
145 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
146 | |
147 | static int tftp_blksize_check(const char *blksize_str, int maxsize) |
148 | { |
149 | /* Check if the blksize is valid: |
150 | * RFC2348 says between 8 and 65464, |
151 | * but our implementation makes it impossible |
152 | * to use blksizes smaller than 22 octets. */ |
153 | unsigned blksize = bb_strtou(blksize_str, NULL, 10); |
154 | if (errno |
155 | || (blksize < 24) || (blksize > maxsize) |
156 | ) { |
157 | bb_error_msg("bad blocksize '%s'", blksize_str); |
158 | return -1; |
159 | } |
160 | # if ENABLE_TFTP_DEBUG |
161 | bb_error_msg("using blksize %u", blksize); |
162 | # endif |
163 | return blksize; |
164 | } |
165 | |
166 | static char *tftp_get_option(const char *option, char *buf, int len) |
167 | { |
168 | int opt_val = 0; |
169 | int opt_found = 0; |
170 | int k; |
171 | |
172 | /* buf points to: |
173 | * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */ |
174 | |
175 | while (len > 0) { |
176 | /* Make sure options are terminated correctly */ |
177 | for (k = 0; k < len; k++) { |
178 | if (buf[k] == '\0') { |
179 | goto nul_found; |
180 | } |
181 | } |
182 | return NULL; |
183 | nul_found: |
184 | if (opt_val == 0) { /* it's "name" part */ |
185 | if (strcasecmp(buf, option) == 0) { |
186 | opt_found = 1; |
187 | } |
188 | } else if (opt_found) { |
189 | return buf; |
190 | } |
191 | |
192 | k++; |
193 | buf += k; |
194 | len -= k; |
195 | opt_val ^= 1; |
196 | } |
197 | |
198 | return NULL; |
199 | } |
200 | |
201 | #endif |
202 | |
203 | static int tftp_protocol( |
204 | /* NULL if tftp, !NULL if tftpd: */ |
205 | len_and_sockaddr *our_lsa, |
206 | len_and_sockaddr *peer_lsa, |
207 | const char *local_file |
208 | IF_TFTP(, const char *remote_file) |
209 | #if !ENABLE_TFTP |
210 | # define remote_file NULL |
211 | #endif |
212 | /* 1 for tftp; 1/0 for tftpd depending whether client asked about it: */ |
213 | IF_FEATURE_TFTP_BLOCKSIZE(, int want_transfer_size) |
214 | IF_FEATURE_TFTP_BLOCKSIZE(, int blksize)) |
215 | { |
216 | #if !ENABLE_FEATURE_TFTP_BLOCKSIZE |
217 | enum { blksize = TFTP_BLKSIZE_DEFAULT }; |
218 | #endif |
219 | |
220 | struct pollfd pfd[1]; |
221 | #define socket_fd (pfd[0].fd) |
222 | int len; |
223 | int send_len; |
224 | IF_FEATURE_TFTP_BLOCKSIZE(smallint expect_OACK = 0;) |
225 | smallint finished = 0; |
226 | uint16_t opcode; |
227 | uint16_t block_nr; |
228 | uint16_t recv_blk; |
229 | int open_mode, local_fd; |
230 | int retries, waittime_ms; |
231 | int io_bufsize = blksize + 4; |
232 | char *cp; |
233 | /* Can't use RESERVE_CONFIG_BUFFER here since the allocation |
234 | * size varies meaning BUFFERS_GO_ON_STACK would fail. |
235 | * |
236 | * We must keep the transmit and receive buffers separate |
237 | * in case we rcv a garbage pkt - we need to rexmit the last pkt. |
238 | */ |
239 | char *xbuf = xmalloc(io_bufsize); |
240 | char *rbuf = xmalloc(io_bufsize); |
241 | |
242 | socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0); |
243 | setsockopt_reuseaddr(socket_fd); |
244 | |
245 | if (!ENABLE_TFTP || our_lsa) { /* tftpd */ |
246 | /* Create a socket which is: |
247 | * 1. bound to IP:port peer sent 1st datagram to, |
248 | * 2. connected to peer's IP:port |
249 | * This way we will answer from the IP:port peer |
250 | * expects, will not get any other packets on |
251 | * the socket, and also plain read/write will work. */ |
252 | xbind(socket_fd, &our_lsa->u.sa, our_lsa->len); |
253 | xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len); |
254 | |
255 | /* Is there an error already? Send pkt and bail out */ |
256 | if (error_pkt_reason || error_pkt_str[0]) |
257 | goto send_err_pkt; |
258 | |
259 | if (user_opt) { |
260 | struct passwd *pw = xgetpwnam(user_opt); |
261 | change_identity(pw); /* initgroups, setgid, setuid */ |
262 | } |
263 | } |
264 | |
265 | /* Prepare open mode */ |
266 | if (CMD_PUT(option_mask32)) { |
267 | open_mode = O_RDONLY; |
268 | } else { |
269 | open_mode = O_WRONLY | O_TRUNC | O_CREAT; |
270 | #if ENABLE_TFTPD |
271 | if ((option_mask32 & (TFTPD_OPT+TFTPD_OPT_c)) == TFTPD_OPT) { |
272 | /* tftpd without -c */ |
273 | open_mode = O_WRONLY | O_TRUNC; |
274 | } |
275 | #endif |
276 | } |
277 | |
278 | /* Examples of network traffic. |
279 | * Note two cases when ACKs with block# of 0 are sent. |
280 | * |
281 | * Download without options: |
282 | * tftp -> "\0\1FILENAME\0octet\0" |
283 | * "\0\3\0\1FILEDATA..." <- tftpd |
284 | * tftp -> "\0\4\0\1" |
285 | * ... |
286 | * Download with option of blksize 16384: |
287 | * tftp -> "\0\1FILENAME\0octet\0blksize\00016384\0" |
288 | * "\0\6blksize\00016384\0" <- tftpd |
289 | * tftp -> "\0\4\0\0" |
290 | * "\0\3\0\1FILEDATA..." <- tftpd |
291 | * tftp -> "\0\4\0\1" |
292 | * ... |
293 | * Upload without options: |
294 | * tftp -> "\0\2FILENAME\0octet\0" |
295 | * "\0\4\0\0" <- tftpd |
296 | * tftp -> "\0\3\0\1FILEDATA..." |
297 | * "\0\4\0\1" <- tftpd |
298 | * ... |
299 | * Upload with option of blksize 16384: |
300 | * tftp -> "\0\2FILENAME\0octet\0blksize\00016384\0" |
301 | * "\0\6blksize\00016384\0" <- tftpd |
302 | * tftp -> "\0\3\0\1FILEDATA..." |
303 | * "\0\4\0\1" <- tftpd |
304 | * ... |
305 | */ |
306 | block_nr = 1; |
307 | cp = xbuf + 2; |
308 | |
309 | if (!ENABLE_TFTP || our_lsa) { /* tftpd */ |
310 | /* Open file (must be after changing user) */ |
311 | local_fd = open(local_file, open_mode, 0666); |
312 | if (local_fd < 0) { |
313 | error_pkt_reason = ERR_NOFILE; |
314 | strcpy((char*)error_pkt_str, "can't open file"); |
315 | goto send_err_pkt; |
316 | } |
317 | /* gcc 4.3.1 would NOT optimize it out as it should! */ |
318 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
319 | if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) { |
320 | /* Create and send OACK packet. */ |
321 | /* For the download case, block_nr is still 1 - |
322 | * we expect 1st ACK from peer to be for (block_nr-1), |
323 | * that is, for "block 0" which is our OACK pkt */ |
324 | opcode = TFTP_OACK; |
325 | goto add_blksize_opt; |
326 | } |
327 | #endif |
328 | if (CMD_GET(option_mask32)) { |
329 | /* It's upload and we don't send OACK. |
330 | * We must ACK 1st packet (with filename) |
331 | * as if it is "block 0" */ |
332 | block_nr = 0; |
333 | } |
334 | |
335 | } else { /* tftp */ |
336 | /* Open file (must be after changing user) */ |
337 | local_fd = CMD_GET(option_mask32) ? STDOUT_FILENO : STDIN_FILENO; |
338 | if (NOT_LONE_DASH(local_file)) |
339 | local_fd = xopen(local_file, open_mode); |
340 | /* Removing #if, or using if() statement instead of #if may lead to |
341 | * "warning: null argument where non-null required": */ |
342 | #if ENABLE_TFTP |
343 | /* tftp */ |
344 | |
345 | /* We can't (and don't really need to) bind the socket: |
346 | * we don't know from which local IP datagrams will be sent, |
347 | * but kernel will pick the same IP every time (unless routing |
348 | * table is changed), thus peer will see dgrams consistently |
349 | * coming from the same IP. |
350 | * We would like to connect the socket, but since peer's |
351 | * UDP code can be less perfect than ours, _peer's_ IP:port |
352 | * in replies may differ from IP:port we used to send |
353 | * our first packet. We can connect() only when we get |
354 | * first reply. */ |
355 | |
356 | /* build opcode */ |
357 | opcode = TFTP_WRQ; |
358 | if (CMD_GET(option_mask32)) { |
359 | opcode = TFTP_RRQ; |
360 | } |
361 | /* add filename and mode */ |
362 | /* fill in packet if the filename fits into xbuf */ |
363 | len = strlen(remote_file) + 1; |
364 | if (2 + len + sizeof("octet") >= io_bufsize) { |
365 | bb_error_msg("remote filename is too long"); |
366 | goto ret; |
367 | } |
368 | strcpy(cp, remote_file); |
369 | cp += len; |
370 | /* add "mode" part of the packet */ |
371 | strcpy(cp, "octet"); |
372 | cp += sizeof("octet"); |
373 | |
374 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
375 | if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size) |
376 | goto send_pkt; |
377 | |
378 | /* Need to add option to pkt */ |
379 | if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN tsize ") + sizeof(off_t)*3) { |
380 | bb_error_msg("remote filename is too long"); |
381 | goto ret; |
382 | } |
383 | expect_OACK = 1; |
384 | # endif |
385 | #endif /* ENABLE_TFTP */ |
386 | |
387 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
388 | add_blksize_opt: |
389 | if (blksize != TFTP_BLKSIZE_DEFAULT) { |
390 | /* add "blksize", <nul>, blksize, <nul> */ |
391 | strcpy(cp, "blksize"); |
392 | cp += sizeof("blksize"); |
393 | cp += snprintf(cp, 6, "%d", blksize) + 1; |
394 | } |
395 | if (want_transfer_size) { |
396 | /* add "tsize", <nul>, size, <nul> (see RFC2349) */ |
397 | /* if tftp and downloading, we send "0" (since we opened local_fd with O_TRUNC) |
398 | * and this makes server to send "tsize" option with the size */ |
399 | /* if tftp and uploading, we send file size (maybe dont, to not confuse old servers???) */ |
400 | /* if tftpd and downloading, we are answering to client's request */ |
401 | /* if tftpd and uploading: !want_transfer_size, this code is not executed */ |
402 | struct stat st; |
403 | strcpy(cp, "tsize"); |
404 | cp += sizeof("tsize"); |
405 | st.st_size = 0; |
406 | fstat(local_fd, &st); |
407 | cp += sprintf(cp, "%"OFF_FMT"u", (off_t)st.st_size) + 1; |
408 | # if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
409 | /* Save for progress bar. If 0 (tftp downloading), |
410 | * we look at server's reply later */ |
411 | G.size = st.st_size; |
412 | if (remote_file && st.st_size) |
413 | tftp_progress_init(); |
414 | # endif |
415 | } |
416 | #endif |
417 | /* First packet is built, so skip packet generation */ |
418 | goto send_pkt; |
419 | } |
420 | |
421 | /* Using mostly goto's - continue/break will be less clear |
422 | * in where we actually jump to */ |
423 | while (1) { |
424 | /* Build ACK or DATA */ |
425 | cp = xbuf + 2; |
426 | *((uint16_t*)cp) = htons(block_nr); |
427 | cp += 2; |
428 | block_nr++; |
429 | opcode = TFTP_ACK; |
430 | if (CMD_PUT(option_mask32)) { |
431 | opcode = TFTP_DATA; |
432 | len = full_read(local_fd, cp, blksize); |
433 | if (len < 0) { |
434 | goto send_read_err_pkt; |
435 | } |
436 | if (len != blksize) { |
437 | finished = 1; |
438 | } |
439 | cp += len; |
440 | } |
441 | send_pkt: |
442 | /* Send packet */ |
443 | *((uint16_t*)xbuf) = htons(opcode); /* fill in opcode part */ |
444 | send_len = cp - xbuf; |
445 | /* NB: send_len value is preserved in code below |
446 | * for potential resend */ |
447 | |
448 | retries = TFTP_NUM_RETRIES; /* re-initialize */ |
449 | waittime_ms = TFTP_TIMEOUT_MS; |
450 | |
451 | send_again: |
452 | #if ENABLE_TFTP_DEBUG |
453 | fprintf(stderr, "sending %u bytes\n", send_len); |
454 | for (cp = xbuf; cp < &xbuf[send_len]; cp++) |
455 | fprintf(stderr, "%02x ", (unsigned char) *cp); |
456 | fprintf(stderr, "\n"); |
457 | #endif |
458 | xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len); |
459 | |
460 | #if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
461 | if (ENABLE_TFTP && remote_file) { /* tftp */ |
462 | G.pos = (block_nr - 1) * (uoff_t)blksize; |
463 | } |
464 | #endif |
465 | /* Was it final ACK? then exit */ |
466 | if (finished && (opcode == TFTP_ACK)) |
467 | goto ret; |
468 | |
469 | recv_again: |
470 | /* Receive packet */ |
471 | /*pfd[0].fd = socket_fd;*/ |
472 | pfd[0].events = POLLIN; |
473 | switch (safe_poll(pfd, 1, waittime_ms)) { |
474 | default: |
475 | /*bb_perror_msg("poll"); - done in safe_poll */ |
476 | goto ret; |
477 | case 0: |
478 | retries--; |
479 | if (retries == 0) { |
480 | bb_error_msg("timeout"); |
481 | goto ret; /* no err packet sent */ |
482 | } |
483 | |
484 | /* exponential backoff with limit */ |
485 | waittime_ms += waittime_ms/2; |
486 | if (waittime_ms > TFTP_MAXTIMEOUT_MS) { |
487 | waittime_ms = TFTP_MAXTIMEOUT_MS; |
488 | } |
489 | |
490 | goto send_again; /* resend last sent pkt */ |
491 | case 1: |
492 | if (!our_lsa) { |
493 | /* tftp (not tftpd!) receiving 1st packet */ |
494 | our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */ |
495 | len = recvfrom(socket_fd, rbuf, io_bufsize, 0, |
496 | &peer_lsa->u.sa, &peer_lsa->len); |
497 | /* Our first dgram went to port 69 |
498 | * but reply may come from different one. |
499 | * Remember and use this new port (and IP) */ |
500 | if (len >= 0) |
501 | xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len); |
502 | } else { |
503 | /* tftpd, or not the very first packet: |
504 | * socket is connect()ed, can just read from it. */ |
505 | /* Don't full_read()! |
506 | * This is not TCP, one read == one pkt! */ |
507 | len = safe_read(socket_fd, rbuf, io_bufsize); |
508 | } |
509 | if (len < 0) { |
510 | goto send_read_err_pkt; |
511 | } |
512 | if (len < 4) { /* too small? */ |
513 | goto recv_again; |
514 | } |
515 | } |
516 | |
517 | /* Process recv'ed packet */ |
518 | opcode = ntohs( ((uint16_t*)rbuf)[0] ); |
519 | recv_blk = ntohs( ((uint16_t*)rbuf)[1] ); |
520 | #if ENABLE_TFTP_DEBUG |
521 | fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk); |
522 | #endif |
523 | if (opcode == TFTP_ERROR) { |
524 | static const char errcode_str[] ALIGN1 = |
525 | "\0" |
526 | "file not found\0" |
527 | "access violation\0" |
528 | "disk full\0" |
529 | "bad operation\0" |
530 | "unknown transfer id\0" |
531 | "file already exists\0" |
532 | "no such user\0" |
533 | "bad option"; |
534 | |
535 | const char *msg = ""; |
536 | |
537 | if (len > 4 && rbuf[4] != '\0') { |
538 | msg = &rbuf[4]; |
539 | rbuf[io_bufsize - 1] = '\0'; /* paranoia */ |
540 | } else if (recv_blk <= 8) { |
541 | msg = nth_string(errcode_str, recv_blk); |
542 | } |
543 | bb_error_msg("server error: (%u) %s", recv_blk, msg); |
544 | goto ret; |
545 | } |
546 | |
547 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
548 | if (expect_OACK) { |
549 | expect_OACK = 0; |
550 | if (opcode == TFTP_OACK) { |
551 | /* server seems to support options */ |
552 | char *res; |
553 | |
554 | res = tftp_get_option("blksize", &rbuf[2], len - 2); |
555 | if (res) { |
556 | blksize = tftp_blksize_check(res, blksize); |
557 | if (blksize < 0) { |
558 | error_pkt_reason = ERR_BAD_OPT; |
559 | goto send_err_pkt; |
560 | } |
561 | io_bufsize = blksize + 4; |
562 | } |
563 | # if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
564 | if (remote_file && G.size == 0) { /* if we don't know it yet */ |
565 | res = tftp_get_option("tsize", &rbuf[2], len - 2); |
566 | if (res) { |
567 | G.size = bb_strtoull(res, NULL, 10); |
568 | if (G.size) |
569 | tftp_progress_init(); |
570 | } |
571 | } |
572 | # endif |
573 | if (CMD_GET(option_mask32)) { |
574 | /* We'll send ACK for OACK, |
575 | * such ACK has "block no" of 0 */ |
576 | block_nr = 0; |
577 | } |
578 | continue; |
579 | } |
580 | /* rfc2347: |
581 | * "An option not acknowledged by the server |
582 | * must be ignored by the client and server |
583 | * as if it were never requested." */ |
584 | bb_error_msg("server only supports blocksize of 512"); |
585 | blksize = TFTP_BLKSIZE_DEFAULT; |
586 | io_bufsize = TFTP_BLKSIZE_DEFAULT + 4; |
587 | } |
588 | #endif |
589 | /* block_nr is already advanced to next block# we expect |
590 | * to get / block# we are about to send next time */ |
591 | |
592 | if (CMD_GET(option_mask32) && (opcode == TFTP_DATA)) { |
593 | if (recv_blk == block_nr) { |
594 | int sz = full_write(local_fd, &rbuf[4], len - 4); |
595 | if (sz != len - 4) { |
596 | strcpy((char*)error_pkt_str, bb_msg_write_error); |
597 | error_pkt_reason = ERR_WRITE; |
598 | goto send_err_pkt; |
599 | } |
600 | if (sz != blksize) { |
601 | finished = 1; |
602 | } |
603 | continue; /* send ACK */ |
604 | } |
605 | /* Disabled to cope with servers with Sorcerer's Apprentice Syndrome */ |
606 | #if 0 |
607 | if (recv_blk == (block_nr - 1)) { |
608 | /* Server lost our TFTP_ACK. Resend it */ |
609 | block_nr = recv_blk; |
610 | continue; |
611 | } |
612 | #endif |
613 | } |
614 | |
615 | if (CMD_PUT(option_mask32) && (opcode == TFTP_ACK)) { |
616 | /* did peer ACK our last DATA pkt? */ |
617 | if (recv_blk == (uint16_t) (block_nr - 1)) { |
618 | if (finished) |
619 | goto ret; |
620 | continue; /* send next block */ |
621 | } |
622 | } |
623 | /* Awww... recv'd packet is not recognized! */ |
624 | goto recv_again; |
625 | /* why recv_again? - rfc1123 says: |
626 | * "The sender (i.e., the side originating the DATA packets) |
627 | * must never resend the current DATA packet on receipt |
628 | * of a duplicate ACK". |
629 | * DATA pkts are resent ONLY on timeout. |
630 | * Thus "goto send_again" will ba a bad mistake above. |
631 | * See: |
632 | * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome |
633 | */ |
634 | } /* end of "while (1)" */ |
635 | ret: |
636 | if (ENABLE_FEATURE_CLEAN_UP) { |
637 | close(local_fd); |
638 | close(socket_fd); |
639 | free(xbuf); |
640 | free(rbuf); |
641 | } |
642 | return finished == 0; /* returns 1 on failure */ |
643 | |
644 | send_read_err_pkt: |
645 | strcpy((char*)error_pkt_str, bb_msg_read_error); |
646 | send_err_pkt: |
647 | if (error_pkt_str[0]) |
648 | bb_error_msg("%s", (char*)error_pkt_str); |
649 | error_pkt[1] = TFTP_ERROR; |
650 | xsendto(socket_fd, error_pkt, 4 + 1 + strlen((char*)error_pkt_str), |
651 | &peer_lsa->u.sa, peer_lsa->len); |
652 | return EXIT_FAILURE; |
653 | #undef remote_file |
654 | } |
655 | |
656 | #if ENABLE_TFTP |
657 | |
658 | int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
659 | int tftp_main(int argc UNUSED_PARAM, char **argv) |
660 | { |
661 | len_and_sockaddr *peer_lsa; |
662 | const char *local_file = NULL; |
663 | const char *remote_file = NULL; |
664 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
665 | const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR; |
666 | int blksize; |
667 | # endif |
668 | int result; |
669 | int port; |
670 | IF_GETPUT(int opt;) |
671 | |
672 | INIT_G(); |
673 | |
674 | /* -p or -g is mandatory, and they are mutually exclusive */ |
675 | opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:") |
676 | IF_GETPUT("g--p:p--g:"); |
677 | |
678 | IF_GETPUT(opt =) getopt32(argv, |
679 | IF_FEATURE_TFTP_GET("g") IF_FEATURE_TFTP_PUT("p") |
680 | "l:r:" IF_FEATURE_TFTP_BLOCKSIZE("b:"), |
681 | &local_file, &remote_file |
682 | IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str)); |
683 | argv += optind; |
684 | |
685 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
686 | /* Check if the blksize is valid: |
687 | * RFC2348 says between 8 and 65464 */ |
688 | blksize = tftp_blksize_check(blksize_str, 65564); |
689 | if (blksize < 0) { |
690 | //bb_error_msg("bad block size"); |
691 | return EXIT_FAILURE; |
692 | } |
693 | # endif |
694 | |
695 | if (remote_file) { |
696 | if (!local_file) { |
697 | const char *slash = strrchr(remote_file, '/'); |
698 | local_file = slash ? slash + 1 : remote_file; |
699 | } |
700 | } else { |
701 | remote_file = local_file; |
702 | } |
703 | |
704 | /* Error if filename or host is not known */ |
705 | if (!remote_file || !argv[0]) |
706 | bb_show_usage(); |
707 | |
708 | port = bb_lookup_port(argv[1], "udp", 69); |
709 | peer_lsa = xhost2sockaddr(argv[0], port); |
710 | |
711 | # if ENABLE_TFTP_DEBUG |
712 | fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n", |
713 | xmalloc_sockaddr2dotted(&peer_lsa->u.sa), |
714 | remote_file, local_file); |
715 | # endif |
716 | |
717 | # if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
718 | G.file = remote_file; |
719 | # endif |
720 | result = tftp_protocol( |
721 | NULL /*our_lsa*/, peer_lsa, |
722 | local_file, remote_file |
723 | IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */) |
724 | IF_FEATURE_TFTP_BLOCKSIZE(, blksize) |
725 | ); |
726 | tftp_progress_done(); |
727 | |
728 | if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) { |
729 | unlink(local_file); |
730 | } |
731 | return result; |
732 | } |
733 | |
734 | #endif /* ENABLE_TFTP */ |
735 | |
736 | #if ENABLE_TFTPD |
737 | int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
738 | int tftpd_main(int argc UNUSED_PARAM, char **argv) |
739 | { |
740 | len_and_sockaddr *our_lsa; |
741 | len_and_sockaddr *peer_lsa; |
742 | char *local_file, *mode; |
743 | const char *error_msg; |
744 | int opt, result, opcode; |
745 | IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;) |
746 | IF_FEATURE_TFTP_BLOCKSIZE(int want_transfer_size = 0;) |
747 | |
748 | INIT_G(); |
749 | |
750 | our_lsa = get_sock_lsa(STDIN_FILENO); |
751 | if (!our_lsa) { |
752 | /* This is confusing: |
753 | *bb_error_msg_and_die("stdin is not a socket"); |
754 | * Better: */ |
755 | bb_show_usage(); |
756 | /* Help text says that tftpd must be used as inetd service, |
757 | * which is by far the most usual cause of get_sock_lsa |
758 | * failure */ |
759 | } |
760 | peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len); |
761 | peer_lsa->len = our_lsa->len; |
762 | |
763 | /* Shifting to not collide with TFTP_OPTs */ |
764 | opt = option_mask32 = TFTPD_OPT | (getopt32(argv, "rcu:", &user_opt) << 8); |
765 | argv += optind; |
766 | if (argv[0]) |
767 | xchdir(argv[0]); |
768 | |
769 | result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf), |
770 | 0 /* flags */, |
771 | &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len); |
772 | |
773 | error_msg = "malformed packet"; |
774 | opcode = ntohs(*(uint16_t*)block_buf); |
775 | if (result < 4 || result >= sizeof(block_buf) |
776 | || block_buf[result-1] != '\0' |
777 | || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */ |
778 | IF_GETPUT(&&) |
779 | IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */ |
780 | ) |
781 | ) { |
782 | goto err; |
783 | } |
784 | local_file = block_buf + 2; |
785 | if (local_file[0] == '.' || strstr(local_file, "/.")) { |
786 | error_msg = "dot in file name"; |
787 | goto err; |
788 | } |
789 | mode = local_file + strlen(local_file) + 1; |
790 | if (mode >= block_buf + result || strcmp(mode, "octet") != 0) { |
791 | goto err; |
792 | } |
793 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
794 | { |
795 | char *res; |
796 | char *opt_str = mode + sizeof("octet"); |
797 | int opt_len = block_buf + result - opt_str; |
798 | if (opt_len > 0) { |
799 | res = tftp_get_option("blksize", opt_str, opt_len); |
800 | if (res) { |
801 | blksize = tftp_blksize_check(res, 65564); |
802 | if (blksize < 0) { |
803 | error_pkt_reason = ERR_BAD_OPT; |
804 | /* will just send error pkt */ |
805 | goto do_proto; |
806 | } |
807 | } |
808 | if (opcode != TFTP_WRQ /* download? */ |
809 | /* did client ask us about file size? */ |
810 | && tftp_get_option("tsize", opt_str, opt_len) |
811 | ) { |
812 | want_transfer_size = 1; |
813 | } |
814 | } |
815 | } |
816 | # endif |
817 | |
818 | if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) { |
819 | if (opt & TFTPD_OPT_r) { |
820 | /* This would mean "disk full" - not true */ |
821 | /*error_pkt_reason = ERR_WRITE;*/ |
822 | error_msg = bb_msg_write_error; |
823 | goto err; |
824 | } |
825 | IF_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */ |
826 | } else { |
827 | IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */ |
828 | } |
829 | |
830 | /* NB: if error_pkt_str or error_pkt_reason is set up, |
831 | * tftp_protocol() just sends one error pkt and returns */ |
832 | |
833 | do_proto: |
834 | close(STDIN_FILENO); /* close old, possibly wildcard socket */ |
835 | /* tftp_protocol() will create new one, bound to particular local IP */ |
836 | result = tftp_protocol( |
837 | our_lsa, peer_lsa, |
838 | local_file IF_TFTP(, NULL /*remote_file*/) |
839 | IF_FEATURE_TFTP_BLOCKSIZE(, want_transfer_size) |
840 | IF_FEATURE_TFTP_BLOCKSIZE(, blksize) |
841 | ); |
842 | |
843 | return result; |
844 | err: |
845 | strcpy((char*)error_pkt_str, error_msg); |
846 | goto do_proto; |
847 | } |
848 | |
849 | #endif /* ENABLE_TFTPD */ |
850 | |
851 | #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */ |