Annotation of /trunk/mkinitrd-magellan/busybox/networking/tftp.c
Parent Directory | Revision Log
Revision 1123 -
(hide annotations)
(download)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 23634 byte(s)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 23634 byte(s)
-updated to busybox-1.17.1
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 | niro | 1123 | /* Was 50 ms but users asked to bump it up a bit */ |
28 | #define TFTP_TIMEOUT_MS 100 | ||
29 | niro | 816 | #define TFTP_MAXTIMEOUT_MS 2000 |
30 | #define TFTP_NUM_RETRIES 12 /* number of backed-off retries */ | ||
31 | niro | 532 | |
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 | niro | 816 | /* 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 | niro | 532 | }; |
63 | |||
64 | #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT | ||
65 | niro | 984 | #define IF_GETPUT(...) |
66 | niro | 532 | #define CMD_GET(cmd) 1 |
67 | #define CMD_PUT(cmd) 0 | ||
68 | #elif !ENABLE_FEATURE_TFTP_GET && ENABLE_FEATURE_TFTP_PUT | ||
69 | niro | 984 | #define IF_GETPUT(...) |
70 | niro | 532 | #define CMD_GET(cmd) 0 |
71 | #define CMD_PUT(cmd) 1 | ||
72 | #else | ||
73 | niro | 984 | #define IF_GETPUT(...) __VA_ARGS__ |
74 | niro | 816 | #define CMD_GET(cmd) ((cmd) & TFTP_OPT_GET) |
75 | #define CMD_PUT(cmd) ((cmd) & TFTP_OPT_PUT) | ||
76 | niro | 532 | #endif |
77 | /* NB: in the code below | ||
78 | niro | 816 | * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive |
79 | niro | 532 | */ |
80 | |||
81 | |||
82 | niro | 816 | 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 | niro | 984 | #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 | niro | 1123 | } FIX_ALIASING; |
95 | niro | 816 | #define G (*(struct globals*)&bb_common_bufsiz1) |
96 | niro | 984 | struct BUG_G_too_big { |
97 | char BUG_G_too_big[sizeof(G) <= COMMON_BUFSIZE ? 1 : -1]; | ||
98 | }; | ||
99 | niro | 816 | #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 | niro | 984 | #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 | niro | 816 | |
114 | niro | 984 | 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 | niro | 1123 | bb_putchar_stderr('\n'); |
124 | niro | 984 | } 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 | niro | 1123 | if (G.pmt.inited) |
140 | progress_meter(0); | ||
141 | niro | 984 | } |
142 | #else | ||
143 | # define tftp_progress_init() ((void)0) | ||
144 | # define tftp_progress_done() ((void)0) | ||
145 | #endif | ||
146 | |||
147 | niro | 532 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
148 | |||
149 | niro | 816 | static int tftp_blksize_check(const char *blksize_str, int maxsize) |
150 | niro | 532 | { |
151 | niro | 816 | /* Check if the blksize is valid: |
152 | niro | 532 | * RFC2348 says between 8 and 65464, |
153 | * but our implementation makes it impossible | ||
154 | niro | 816 | * 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 | niro | 532 | ) { |
159 | niro | 816 | bb_error_msg("bad blocksize '%s'", blksize_str); |
160 | return -1; | ||
161 | niro | 532 | } |
162 | niro | 984 | # if ENABLE_TFTP_DEBUG |
163 | niro | 816 | bb_error_msg("using blksize %u", blksize); |
164 | niro | 984 | # endif |
165 | niro | 816 | return blksize; |
166 | niro | 532 | } |
167 | |||
168 | niro | 816 | static char *tftp_get_option(const char *option, char *buf, int len) |
169 | niro | 532 | { |
170 | int opt_val = 0; | ||
171 | int opt_found = 0; | ||
172 | int k; | ||
173 | |||
174 | niro | 816 | /* buf points to: |
175 | * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */ | ||
176 | |||
177 | niro | 532 | while (len > 0) { |
178 | niro | 816 | /* Make sure options are terminated correctly */ |
179 | niro | 532 | for (k = 0; k < len; k++) { |
180 | if (buf[k] == '\0') { | ||
181 | niro | 816 | goto nul_found; |
182 | niro | 532 | } |
183 | } | ||
184 | niro | 816 | return NULL; |
185 | nul_found: | ||
186 | if (opt_val == 0) { /* it's "name" part */ | ||
187 | niro | 532 | if (strcasecmp(buf, option) == 0) { |
188 | opt_found = 1; | ||
189 | } | ||
190 | niro | 816 | } else if (opt_found) { |
191 | return buf; | ||
192 | niro | 532 | } |
193 | |||
194 | k++; | ||
195 | buf += k; | ||
196 | len -= k; | ||
197 | opt_val ^= 1; | ||
198 | } | ||
199 | |||
200 | return NULL; | ||
201 | } | ||
202 | |||
203 | #endif | ||
204 | |||
205 | niro | 816 | static int tftp_protocol( |
206 | niro | 984 | /* NULL if tftp, !NULL if tftpd: */ |
207 | niro | 816 | len_and_sockaddr *our_lsa, |
208 | niro | 532 | len_and_sockaddr *peer_lsa, |
209 | niro | 816 | const char *local_file |
210 | niro | 984 | IF_TFTP(, const char *remote_file) |
211 | niro | 816 | #if !ENABLE_TFTP |
212 | niro | 984 | # define remote_file NULL |
213 | niro | 816 | #endif |
214 | niro | 984 | /* 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 | niro | 816 | #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 | niro | 532 | int len; |
225 | niro | 816 | int send_len; |
226 | niro | 984 | IF_FEATURE_TFTP_BLOCKSIZE(smallint expect_OACK = 0;) |
227 | niro | 816 | 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 | niro | 532 | char *cp; |
235 | /* Can't use RESERVE_CONFIG_BUFFER here since the allocation | ||
236 | niro | 984 | * 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 | niro | 816 | char *xbuf = xmalloc(io_bufsize); |
242 | char *rbuf = xmalloc(io_bufsize); | ||
243 | niro | 532 | |
244 | niro | 816 | socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0); |
245 | setsockopt_reuseaddr(socket_fd); | ||
246 | niro | 532 | |
247 | niro | 984 | if (!ENABLE_TFTP || our_lsa) { /* tftpd */ |
248 | niro | 816 | /* 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 | niro | 532 | |
257 | niro | 816 | /* 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 | niro | 532 | |
261 | niro | 816 | if (user_opt) { |
262 | niro | 984 | struct passwd *pw = xgetpwnam(user_opt); |
263 | niro | 816 | change_identity(pw); /* initgroups, setgid, setuid */ |
264 | } | ||
265 | } | ||
266 | niro | 532 | |
267 | niro | 984 | /* Prepare open mode */ |
268 | niro | 816 | 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 | niro | 984 | |
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 | niro | 816 | 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 | niro | 532 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
321 | niro | 984 | if (blksize != TFTP_BLKSIZE_DEFAULT || want_transfer_size) { |
322 | niro | 816 | /* 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 | niro | 984 | 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 | niro | 816 | * "warning: null argument where non-null required": */ |
344 | #if ENABLE_TFTP | ||
345 | /* tftp */ | ||
346 | niro | 532 | |
347 | niro | 816 | /* 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 | niro | 532 | |
358 | niro | 816 | /* 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 | niro | 984 | /* add "mode" part of the packet */ |
373 | niro | 816 | strcpy(cp, "octet"); |
374 | cp += sizeof("octet"); | ||
375 | niro | 532 | |
376 | niro | 984 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
377 | if (blksize == TFTP_BLKSIZE_DEFAULT && !want_transfer_size) | ||
378 | niro | 816 | goto send_pkt; |
379 | niro | 532 | |
380 | niro | 984 | /* Need to add option to pkt */ |
381 | if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN tsize ") + sizeof(off_t)*3) { | ||
382 | niro | 816 | bb_error_msg("remote filename is too long"); |
383 | goto ret; | ||
384 | } | ||
385 | niro | 984 | expect_OACK = 1; |
386 | # endif | ||
387 | niro | 816 | #endif /* ENABLE_TFTP */ |
388 | niro | 532 | |
389 | niro | 816 | #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 | niro | 532 | } |
397 | niro | 984 | 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 | niro | 816 | #endif |
419 | /* First packet is built, so skip packet generation */ | ||
420 | goto send_pkt; | ||
421 | } | ||
422 | niro | 532 | |
423 | niro | 816 | /* 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 | niro | 532 | } |
438 | niro | 816 | if (len != blksize) { |
439 | finished = 1; | ||
440 | } | ||
441 | cp += len; | ||
442 | niro | 532 | } |
443 | niro | 816 | 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 | niro | 532 | |
450 | niro | 816 | retries = TFTP_NUM_RETRIES; /* re-initialize */ |
451 | waittime_ms = TFTP_TIMEOUT_MS; | ||
452 | niro | 532 | |
453 | niro | 816 | 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 | niro | 532 | #endif |
460 | niro | 816 | xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len); |
461 | niro | 984 | |
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 | niro | 816 | /* Was it final ACK? then exit */ |
468 | if (finished && (opcode == TFTP_ACK)) | ||
469 | goto ret; | ||
470 | niro | 532 | |
471 | recv_again: | ||
472 | niro | 816 | /* 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 | niro | 532 | bb_error_msg("timeout"); |
483 | niro | 816 | goto ret; /* no err packet sent */ |
484 | niro | 532 | } |
485 | |||
486 | niro | 816 | /* 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 | niro | 532 | |
492 | niro | 816 | 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 | niro | 532 | } |
518 | |||
519 | niro | 816 | /* Process recv'ed packet */ |
520 | niro | 532 | opcode = ntohs( ((uint16_t*)rbuf)[0] ); |
521 | niro | 816 | 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 | niro | 532 | #endif |
525 | if (opcode == TFTP_ERROR) { | ||
526 | niro | 816 | 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 | niro | 532 | |
537 | niro | 816 | const char *msg = ""; |
538 | |||
539 | if (len > 4 && rbuf[4] != '\0') { | ||
540 | niro | 532 | msg = &rbuf[4]; |
541 | niro | 816 | rbuf[io_bufsize - 1] = '\0'; /* paranoia */ |
542 | } else if (recv_blk <= 8) { | ||
543 | msg = nth_string(errcode_str, recv_blk); | ||
544 | niro | 532 | } |
545 | niro | 816 | bb_error_msg("server error: (%u) %s", recv_blk, msg); |
546 | goto ret; | ||
547 | } | ||
548 | niro | 532 | |
549 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
550 | niro | 984 | if (expect_OACK) { |
551 | expect_OACK = 0; | ||
552 | niro | 532 | if (opcode == TFTP_OACK) { |
553 | /* server seems to support options */ | ||
554 | char *res; | ||
555 | |||
556 | niro | 816 | res = tftp_get_option("blksize", &rbuf[2], len - 2); |
557 | niro | 532 | if (res) { |
558 | niro | 816 | blksize = tftp_blksize_check(res, blksize); |
559 | if (blksize < 0) { | ||
560 | error_pkt_reason = ERR_BAD_OPT; | ||
561 | goto send_err_pkt; | ||
562 | niro | 532 | } |
563 | niro | 816 | io_bufsize = blksize + 4; |
564 | niro | 984 | } |
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 | niro | 816 | block_nr = 0; |
579 | niro | 532 | } |
580 | niro | 984 | continue; |
581 | niro | 532 | } |
582 | niro | 984 | /* 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 | niro | 1123 | if (blksize != TFTP_BLKSIZE_DEFAULT) |
587 | bb_error_msg("falling back to blocksize "TFTP_BLKSIZE_DEFAULT_STR); | ||
588 | niro | 816 | blksize = TFTP_BLKSIZE_DEFAULT; |
589 | io_bufsize = TFTP_BLKSIZE_DEFAULT + 4; | ||
590 | niro | 532 | } |
591 | #endif | ||
592 | niro | 816 | /* block_nr is already advanced to next block# we expect |
593 | * to get / block# we are about to send next time */ | ||
594 | niro | 532 | |
595 | niro | 816 | 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 | niro | 532 | } |
603 | niro | 816 | if (sz != blksize) { |
604 | finished = 1; | ||
605 | niro | 532 | } |
606 | niro | 816 | continue; /* send ACK */ |
607 | niro | 532 | } |
608 | niro | 984 | /* Disabled to cope with servers with Sorcerer's Apprentice Syndrome */ |
609 | #if 0 | ||
610 | niro | 816 | if (recv_blk == (block_nr - 1)) { |
611 | niro | 532 | /* Server lost our TFTP_ACK. Resend it */ |
612 | niro | 816 | block_nr = recv_blk; |
613 | niro | 532 | continue; |
614 | } | ||
615 | niro | 984 | #endif |
616 | niro | 532 | } |
617 | |||
618 | niro | 816 | 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 | niro | 532 | } |
625 | } | ||
626 | niro | 816 | /* 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 | niro | 532 | if (ENABLE_FEATURE_CLEAN_UP) { |
640 | niro | 816 | close(local_fd); |
641 | close(socket_fd); | ||
642 | niro | 532 | free(xbuf); |
643 | free(rbuf); | ||
644 | } | ||
645 | niro | 816 | return finished == 0; /* returns 1 on failure */ |
646 | niro | 532 | |
647 | niro | 816 | 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 | niro | 984 | bb_error_msg("%s", (char*)error_pkt_str); |
652 | niro | 816 | 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 | niro | 532 | } |
658 | |||
659 | niro | 816 | #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 | niro | 532 | { |
664 | len_and_sockaddr *peer_lsa; | ||
665 | niro | 816 | const char *local_file = NULL; |
666 | const char *remote_file = NULL; | ||
667 | niro | 984 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
668 | niro | 816 | const char *blksize_str = TFTP_BLKSIZE_DEFAULT_STR; |
669 | int blksize; | ||
670 | niro | 984 | # endif |
671 | niro | 816 | int result; |
672 | niro | 532 | int port; |
673 | niro | 984 | IF_GETPUT(int opt;) |
674 | niro | 532 | |
675 | niro | 816 | INIT_G(); |
676 | |||
677 | niro | 532 | /* -p or -g is mandatory, and they are mutually exclusive */ |
678 | niro | 984 | opt_complementary = "" IF_FEATURE_TFTP_GET("g:") IF_FEATURE_TFTP_PUT("p:") |
679 | IF_GETPUT("g--p:p--g:"); | ||
680 | niro | 532 | |
681 | niro | 984 | 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 | niro | 816 | &local_file, &remote_file |
685 | niro | 984 | IF_FEATURE_TFTP_BLOCKSIZE(, &blksize_str)); |
686 | niro | 816 | argv += optind; |
687 | niro | 532 | |
688 | niro | 984 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
689 | niro | 816 | /* 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 | niro | 532 | } |
696 | niro | 984 | # endif |
697 | niro | 532 | |
698 | niro | 984 | 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 | niro | 816 | remote_file = local_file; |
705 | niro | 984 | } |
706 | |||
707 | niro | 816 | /* Error if filename or host is not known */ |
708 | if (!remote_file || !argv[0]) | ||
709 | niro | 532 | bb_show_usage(); |
710 | |||
711 | niro | 816 | port = bb_lookup_port(argv[1], "udp", 69); |
712 | peer_lsa = xhost2sockaddr(argv[0], port); | ||
713 | |||
714 | niro | 984 | # if ENABLE_TFTP_DEBUG |
715 | niro | 816 | 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 | niro | 984 | # endif |
719 | niro | 816 | |
720 | niro | 984 | # if ENABLE_FEATURE_TFTP_PROGRESS_BAR |
721 | G.file = remote_file; | ||
722 | # endif | ||
723 | niro | 816 | result = tftp_protocol( |
724 | NULL /*our_lsa*/, peer_lsa, | ||
725 | local_file, remote_file | ||
726 | niro | 984 | IF_FEATURE_TFTP_BLOCKSIZE(, 1 /* want_transfer_size */) |
727 | IF_FEATURE_TFTP_BLOCKSIZE(, blksize) | ||
728 | niro | 816 | ); |
729 | niro | 984 | tftp_progress_done(); |
730 | niro | 816 | |
731 | if (result != EXIT_SUCCESS && NOT_LONE_DASH(local_file) && CMD_GET(opt)) { | ||
732 | unlink(local_file); | ||
733 | niro | 532 | } |
734 | niro | 816 | return result; |
735 | } | ||
736 | niro | 532 | |
737 | niro | 816 | #endif /* ENABLE_TFTP */ |
738 | niro | 532 | |
739 | niro | 816 | #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 | niro | 984 | IF_FEATURE_TFTP_BLOCKSIZE(int blksize = TFTP_BLKSIZE_DEFAULT;) |
749 | IF_FEATURE_TFTP_BLOCKSIZE(int want_transfer_size = 0;) | ||
750 | niro | 816 | |
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 | niro | 984 | || (IF_FEATURE_TFTP_PUT(opcode != TFTP_RRQ) /* not download */ |
781 | IF_GETPUT(&&) | ||
782 | IF_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */ | ||
783 | niro | 816 | ) |
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 | niro | 984 | # if ENABLE_FEATURE_TFTP_BLOCKSIZE |
797 | niro | 816 | { |
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 | niro | 984 | if (opcode != TFTP_WRQ /* download? */ |
812 | niro | 816 | /* did client ask us about file size? */ |
813 | niro | 984 | && tftp_get_option("tsize", opt_str, opt_len) |
814 | ) { | ||
815 | want_transfer_size = 1; | ||
816 | } | ||
817 | niro | 816 | } |
818 | } | ||
819 | niro | 984 | # endif |
820 | niro | 532 | |
821 | niro | 816 | 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 | niro | 984 | IF_GETPUT(option_mask32 |= TFTP_OPT_GET;) /* will receive file's data */ |
829 | niro | 816 | } else { |
830 | niro | 984 | IF_GETPUT(option_mask32 |= TFTP_OPT_PUT;) /* will send file's data */ |
831 | niro | 532 | } |
832 | niro | 816 | |
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 | niro | 984 | local_file IF_TFTP(, NULL /*remote_file*/) |
842 | IF_FEATURE_TFTP_BLOCKSIZE(, want_transfer_size) | ||
843 | IF_FEATURE_TFTP_BLOCKSIZE(, blksize) | ||
844 | niro | 816 | ); |
845 | |||
846 | niro | 532 | return result; |
847 | niro | 816 | err: |
848 | strcpy((char*)error_pkt_str, error_msg); | ||
849 | goto do_proto; | ||
850 | niro | 532 | } |
851 | |||
852 | niro | 816 | #endif /* ENABLE_TFTPD */ |
853 | |||
854 | niro | 532 | #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */ |