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