Annotation of /trunk/mkinitrd-magellan/busybox/networking/tftp.c
Parent Directory | Revision Log
Revision 984 -
(hide annotations)
(download)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 23491 byte(s)
Sun May 30 11:32:42 2010 UTC (14 years ago) by niro
File MIME type: text/plain
File size: 23491 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 | niro | 532 | /* vi: set sw=4 ts=4: */ |
2 | niro | 984 | /* |
3 | niro | 816 | * A simple tftp client/server for busybox. |
4 | niro | 532 | * 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 | niro | 816 | * tftpd added by Denys Vlasenko & Vladimir Dronnikov |
18 | * | ||
19 | niro | 532 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
20 | niro | 984 | */ |
21 | niro | 816 | #include "libbb.h" |
22 | niro | 532 | |
23 | #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT | ||
24 | |||
25 | niro | 816 | #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 | niro | 532 | |
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 | niro | 816 | /* 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 | niro | 532 | }; |
62 | |||
63 | #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT | ||
64 | niro | 984 | #define IF_GETPUT(...) |
65 | niro | 532 | #define CMD_GET(cmd) 1 |
66 | #define CMD_PUT(cmd) 0 | ||
67 | #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT | ||
68 | niro | 984 | #define IF_GETPUT(...) |
69 | niro | 532 | #define CMD_GET(cmd) 0 |
70 | #define CMD_PUT(cmd) 1 | ||
71 | #else | ||
72 | niro | 984 | #define IF_GETPUT(...) __VA_ARGS__ |
73 | niro | 816 | #define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET) |
74 | #define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT) | ||
75 | niro | 532 | #endif |
76 | /* NB: in the code below | ||
77 | niro | 816 | * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive |
78 | niro | 532 | */ |
79 | |||
80 | |||
81 | niro | 816 | 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 | niro | 984 | #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 | niro | 816 | }; |
94 | #define G (*(struct globals*)&bb_common_bufsiz1) | ||
95 | niro | 984 | struct BUG_G_too_big { |
96 | char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; | ||
97 | }; | ||
98 | niro | 816 | #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 | niro | 984 | #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 | niro | 816 | |
113 | niro | 984 | 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 | niro | 532 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
146 | |||
147 | niro | 816 | static int tftp_blksize_check(const char *blksize_str, int maxsize) |
148 | niro | 532 | { |
149 | niro | 816 | /* Check if the blksize is valid: |
150 | niro | 532 | * RFC2348 says between 8 and 65464, |
151 | * but our implementation makes it impossible | ||
152 | niro | 816 | * 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 | niro | 532 | ) { |
157 | niro | 816 | bb_error_msg("bad blocksize '%s'", blksize_str); |
158 | return -1; | ||
159 | niro | 532 | } |
160 | niro | 984 | # if ENABLE_TFTP_DEBUG |
161 | niro | 816 | bb_error_msg("using blksize %u", blksize); |
162 | niro | 984 | # endif |
163 | niro | 816 | return blksize; |
164 | niro | 532 | } |
165 | |||
166 | niro | 816 | static char *tftp_get_option(const char *option, char *buf, int len) |
167 | niro | 532 | { |
168 | int opt_val = 0; | ||
169 | int opt_found = 0; | ||
170 | int k; | ||
171 | |||
172 | niro | 816 | /* buf points to: |
173 | * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */ | ||
174 | |||
175 | niro | 532 | while (len > 0) { |
176 | niro | 816 | /* Make sure options are terminated correctly */ |
177 | niro | 532 | for (k = 0; k < len; k++) { |
178 | if (buf[k] == '\0') { | ||
179 | niro | 816 | goto nul_found; |
180 | niro | 532 | } |
181 | } | ||
182 | niro | 816 | return NULL; |
183 | nul_found: | ||
184 | if (opt_val == 0) { /* it's "name" part */ | ||
185 | niro | 532 | if (strcasecmp(buf, option) == 0) { |
186 | opt_found = 1; | ||
187 | } | ||
188 | niro | 816 | } else if (opt_found) { |
189 | return buf; | ||
190 | niro | 532 | } |
191 | |||
192 | k++; | ||
193 | buf += k; | ||
194 | len -= k; | ||
195 | opt_val ^= 1; | ||
196 | } | ||
197 | |||
198 | return NULL; | ||
199 | } | ||
200 | |||
201 | #endif | ||
202 | |||
203 | niro | 816 | static int tftp_protocol( |
204 | niro | 984 | /* NULL if tftp, !NULL if tftpd: */ |
205 | niro | 816 | len_and_sockaddr *our_lsa, |
206 | niro | 532 | len_and_sockaddr *peer_lsa, |
207 | niro | 816 | const char *local_file |
208 | niro | 984 | IF_TFTP(, const char *remote_file) |
209 | niro | 816 | #if !ENABLE_TFTP |
210 | niro | 984 | # define remote_file NULL |
211 | niro | 816 | #endif |
212 | niro | 984 | /* 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 | niro | 816 | #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 | niro | 532 | int len; |
223 | niro | 816 | int send_len; |
224 | niro | 984 | IF_FEATURE_TFTP_BLOCKSIZE(smallint expect_OACK = 0;) |
225 | niro | 816 | 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 | niro | 532 | char *cp; |
233 | /* Can't use RESERVE_CONFIG_BUFFER here since the allocation | ||
234 | niro | 984 | * 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 | niro | 816 | char *xbuf = xmalloc(io_bufsize); |
240 | char *rbuf = xmalloc(io_bufsize); | ||
241 | niro | 532 | |
242 | niro | 816 | socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0); |
243 | setsockopt_reuseaddr(socket_fd); | ||
244 | niro | 532 | |
245 | niro | 984 | if (!ENABLE_TFTP || our_lsa) { /* tftpd */ |
246 | niro | 816 | /* 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 | niro | 532 | |
255 | niro | 816 | /* 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 | niro | 532 | |
259 | niro | 816 | if (user_opt) { |
260 | niro | 984 | struct passwd *pw = xgetpwnam(user_opt); |
261 | niro | 816 | change_identity(pw); /* initgroups, setgid, setuid */ |
262 | } | ||
263 | } | ||
264 | niro | 532 | |
265 | niro | 984 | /* Prepare open mode */ |
266 | niro | 816 | 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 | niro | 984 | |
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 | niro | 816 | 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 | niro | 532 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
319 | niro | 984 | if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) { |
320 | niro | 816 | /* 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 | niro | 984 | 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 | niro | 816 | * "warning: null argument where non-null required": */ |
342 | #if ENABLE_TFTP | ||
343 | /* tftp */ | ||
344 | niro | 532 | |
345 | niro | 816 | /* 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 | niro | 532 | |
356 | niro | 816 | /* 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 | niro | 984 | /* add "mode" part of the packet */ |
371 | niro | 816 | strcpy(cp, "octet"); |
372 | cp += sizeof("octet"); | ||
373 | niro | 532 | |
374 | niro | 984 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
375 | if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size) | ||
376 | niro | 816 | goto send_pkt; |
377 | niro | 532 | |
378 | niro | 984 | /* Need to add option to pkt */ |
379 | if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN tsize ") + sizeof(off_t)*3) { | ||
380 | niro | 816 | bb_error_msg("remote filename is too long"); |
381 | goto ret; | ||
382 | } | ||
383 | niro | 984 | expect_OACK = 1; |
384 | # endif | ||
385 | niro | 816 | #endif /* ENABLE_TFTP */ |
386 | niro | 532 | |
387 | niro | 816 | #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 | niro | 532 | } |
395 | niro | 984 | 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 | niro | 816 | #endif |
417 | /* First packet is built, so skip packet generation */ | ||
418 | goto send_pkt; | ||
419 | } | ||
420 | niro | 532 | |
421 | niro | 816 | /* 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 | niro | 532 | } |
436 | niro | 816 | if (len != blksize) { |
437 | finished = 1; | ||
438 | } | ||
439 | cp += len; | ||
440 | niro | 532 | } |
441 | niro | 816 | 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 | niro | 532 | |
448 | niro | 816 | retries = TFTP_NUM_RETRIES; /* re-initialize */ |
449 | waittime_ms = TFTP_TIMEOUT_MS; | ||
450 | niro | 532 | |
451 | niro | 816 | 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 | niro | 532 | #endif |
458 | niro | 816 | xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len); |
459 | niro | 984 | |
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 | niro | 816 | /* Was it final ACK? then exit */ |
466 | if (finished && (opcode == TFTP_ACK)) | ||
467 | goto ret; | ||
468 | niro | 532 | |
469 | recv_again: | ||
470 | niro | 816 | /* 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 | niro | 532 | bb_error_msg("timeout"); |
481 | niro | 816 | goto ret; /* no err packet sent */ |
482 | niro | 532 | } |
483 | |||
484 | niro | 816 | /* 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 | niro | 532 | |
490 | niro | 816 | 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 | niro | 532 | } |
516 | |||
517 | niro | 816 | /* Process recv'ed packet */ |
518 | niro | 532 | opcode = ntohs( ((uint16_t*)rbuf)[0] ); |
519 | niro | 816 | 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 | niro | 532 | #endif |
523 | if (opcode == TFTP_ERROR) { | ||
524 | niro | 816 | 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 | niro | 532 | |
535 | niro | 816 | const char *msg = ""; |
536 | |||
537 | if (len > 4 && rbuf[4] != '\0') { | ||
538 | niro | 532 | msg = &rbuf[4]; |
539 | niro | 816 | rbuf[io_bufsize - 1] = '\0'; /* paranoia */ |
540 | } else if (recv_blk <= 8) { | ||
541 | msg = nth_string(errcode_str, recv_blk); | ||
542 | niro | 532 | } |
543 | niro | 816 | bb_error_msg("server error: (%u) %s", recv_blk, msg); |
544 | goto ret; | ||
545 | } | ||
546 | niro | 532 | |
547 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
548 | niro | 984 | if (expect_OACK) { |
549 | expect_OACK = 0; | ||
550 | niro | 532 | if (opcode == TFTP_OACK) { |
551 | /* server seems to support options */ | ||
552 | char *res; | ||
553 | |||
554 | niro | 816 | res = tftp_get_option("blksize", &rbuf[2], len - 2); |
555 | niro | 532 | if (res) { |
556 | niro | 816 | blksize = tftp_blksize_check(res, blksize); |
557 | if (blksize < 0) { | ||
558 | error_pkt_reason = ERR_BAD_OPT; | ||
559 | goto send_err_pkt; | ||
560 | niro | 532 | } |
561 | niro | 816 | io_bufsize = blksize + 4; |
562 | niro | 984 | } |
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 | niro | 816 | block_nr = 0; |
577 | niro | 532 | } |
578 | niro | 984 | continue; |
579 | niro | 532 | } |
580 | niro | 984 | /* 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 | niro | 816 | bb_error_msg("server only supports blocksize of 512"); |
585 | blksize = TFTP_BLKSIZE_DEFAULT; | ||
586 | io_bufsize = TFTP_BLKSIZE_DEFAULT + 4; | ||
587 | niro | 532 | } |
588 | #endif | ||
589 | niro | 816 | /* block_nr is already advanced to next block# we expect |
590 | * to get / block# we are about to send next time */ | ||
591 | niro | 532 | |
592 | niro | 816 | 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 | niro | 532 | } |
600 | niro | 816 | if (sz != blksize) { |
601 | finished = 1; | ||
602 | niro | 532 | } |
603 | niro | 816 | continue; /* send ACK */ |
604 | niro | 532 | } |
605 | niro | 984 | /* Disabled to cope with servers with Sorcerer's Apprentice Syndrome */ |
606 | #if 0 | ||
607 | niro | 816 | if (recv_blk == (block_nr - 1)) { |
608 | niro | 532 | /* Server lost our TFTP_ACK. Resend it */ |
609 | niro | 816 | block_nr = recv_blk; |
610 | niro | 532 | continue; |
611 | } | ||
612 | niro | 984 | #endif |
613 | niro | 532 | } |
614 | |||
615 | niro | 816 | 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 | niro | 532 | } |
622 | } | ||
623 | niro | 816 | /* 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 | niro | 532 | if (ENABLE_FEATURE_CLEAN_UP) { |
637 | niro | 816 | close(local_fd); |
638 | close(socket_fd); | ||
639 | niro | 532 | free(xbuf); |
640 | free(rbuf); | ||
641 | } | ||
642 | niro | 816 | return finished == 0; /* returns 1 on failure */ |
643 | niro | 532 | |
644 | niro | 816 | 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 | niro | 984 | bb_error_msg("%s", (char*)error_pkt_str); |
649 | niro | 816 | 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 | niro | 532 | } |
655 | |||
656 | niro | 816 | #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 | niro | 532 | { |
661 | len_and_sockaddr *peer_lsa; | ||
662 | niro | 816 | const char *local_file = NULL; |
663 | const char *remote_file = NULL; | ||
664 | niro | 984 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
665 | niro | 816 | const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR; |
666 | int blksize; | ||
667 | niro | 984 | # endif |
668 | niro | 816 | int result; |
669 | niro | 532 | int port; |
670 | niro | 984 | IF_GETPUT(int opt;) |
671 | niro | 532 | |
672 | niro | 816 | INIT_G(); |
673 | |||
674 | niro | 532 | /* -p or -g is mandatory, and they are mutually exclusive */ |
675 | niro | 984 | opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:") |
676 | IF_GETPUT("g--p:p--g:"); | ||
677 | niro | 532 | |
678 | niro | 984 | 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 | niro | 816 | &local_file, &remote_file |
682 | niro | 984 | IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str)); |
683 | niro | 816 | argv += optind; |
684 | niro | 532 | |
685 | niro | 984 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
686 | niro | 816 | /* 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 | niro | 532 | } |
693 | niro | 984 | # endif |
694 | niro | 532 | |
695 | niro | 984 | 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 | niro | 816 | remote_file = local_file; |
702 | niro | 984 | } |
703 | |||
704 | niro | 816 | /* Error if filename or host is not known */ |
705 | if (!remote_file || !argv[0]) | ||
706 | niro | 532 | bb_show_usage(); |
707 | |||
708 | niro | 816 | port = bb_lookup_port(argv[1], "udp", 69); |
709 | peer_lsa = xhost2sockaddr(argv[0], port); | ||
710 | |||
711 | niro | 984 | # if ENABLE_TFTP_DEBUG |
712 | niro | 816 | 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 | niro | 984 | # endif |
716 | niro | 816 | |
717 | niro | 984 | # if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
718 | G.file = remote_file; | ||
719 | # endif | ||
720 | niro | 816 | result = tftp_protocol( |
721 | NULL /*our_lsa*/, peer_lsa, | ||
722 | local_file, remote_file | ||
723 | niro | 984 | IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */) |
724 | IF_FEATURE_TFTP_BLOCKSIZE(, blksize) | ||
725 | niro | 816 | ); |
726 | niro | 984 | tftp_progress_done(); |
727 | niro | 816 | |
728 | if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) { | ||
729 | unlink(local_file); | ||
730 | niro | 532 | } |
731 | niro | 816 | return result; |
732 | } | ||
733 | niro | 532 | |
734 | niro | 816 | #endif /* ENABLE_TFTP */ |
735 | niro | 532 | |
736 | niro | 816 | #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 | niro | 984 | IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;) |
746 | IF_FEATURE_TFTP_BLOCKSIZE(int want_transfer_size = 0;) | ||
747 | niro | 816 | |
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 | niro | 984 | || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */ |
778 | IF_GETPUT(&&) | ||
779 | IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */ | ||
780 | niro | 816 | ) |
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 | niro | 984 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
794 | niro | 816 | { |
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 | niro | 984 | if (opcode != TFTP_WRQ /* download? */ |
809 | niro | 816 | /* did client ask us about file size? */ |
810 | niro | 984 | && tftp_get_option("tsize", opt_str, opt_len) |
811 | ) { | ||
812 | want_transfer_size = 1; | ||
813 | } | ||
814 | niro | 816 | } |
815 | } | ||
816 | niro | 984 | # endif |
817 | niro | 532 | |
818 | niro | 816 | 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 | niro | 984 | IF_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */ |
826 | niro | 816 | } else { |
827 | niro | 984 | IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */ |
828 | niro | 532 | } |
829 | niro | 816 | |
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 | niro | 984 | local_file IF_TFTP(, NULL /*remote_file*/) |
839 | IF_FEATURE_TFTP_BLOCKSIZE(, want_transfer_size) | ||
840 | IF_FEATURE_TFTP_BLOCKSIZE(, blksize) | ||
841 | niro | 816 | ); |
842 | |||
843 | niro | 532 | return result; |
844 | niro | 816 | err: |
845 | strcpy((char*)error_pkt_str, error_msg); | ||
846 | goto do_proto; | ||
847 | niro | 532 | } |
848 | |||
849 | niro | 816 | #endif /* ENABLE_TFTPD */ |
850 | |||
851 | niro | 532 | #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */ |