diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-19 05:00:05 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-19 05:00:05 +0000 |
commit | dd9228b861e41c816b31035ce9c2dfa3e5b5dc97 (patch) | |
tree | c98c709ef20814eff1ab22d25095dbd13667d0d0 /networking/tftp.c | |
parent | c0183e6e0d0ad0b60b8891cd0a1eeabf3406805c (diff) | |
download | busybox-w32-dd9228b861e41c816b31035ce9c2dfa3e5b5dc97.tar.gz busybox-w32-dd9228b861e41c816b31035ce9c2dfa3e5b5dc97.tar.bz2 busybox-w32-dd9228b861e41c816b31035ce9c2dfa3e5b5dc97.zip |
tftpd: make it emit error packets
telnetd: use login always, not "sometimes login, sometimes shell"
text data bss dec hex filename
797612 641 7380 805633 c4b01 busybox_old
797695 641 7380 805716 c4b54 busybox_unstripped
Diffstat (limited to 'networking/tftp.c')
-rw-r--r-- | networking/tftp.c | 292 |
1 files changed, 178 insertions, 114 deletions
diff --git a/networking/tftp.c b/networking/tftp.c index 3075ab04b..83b0ef36f 100644 --- a/networking/tftp.c +++ b/networking/tftp.c | |||
@@ -25,7 +25,7 @@ | |||
25 | 25 | ||
26 | #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT | 26 | #if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT |
27 | 27 | ||
28 | #define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ | 28 | #define TFTP_BLKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */ |
29 | #define TFTP_TIMEOUT_MS 50 | 29 | #define TFTP_TIMEOUT_MS 50 |
30 | #define TFTP_MAXTIMEOUT_MS 2000 | 30 | #define TFTP_MAXTIMEOUT_MS 2000 |
31 | #define TFTP_NUM_RETRIES 12 /* number of backed-off retries */ | 31 | #define TFTP_NUM_RETRIES 12 /* number of backed-off retries */ |
@@ -38,6 +38,17 @@ | |||
38 | #define TFTP_ERROR 5 | 38 | #define TFTP_ERROR 5 |
39 | #define TFTP_OACK 6 | 39 | #define TFTP_OACK 6 |
40 | 40 | ||
41 | /* error codes sent over network */ | ||
42 | #define ERR_UNSPEC 0 | ||
43 | #define ERR_NOFILE 1 | ||
44 | #define ERR_ACCESS 2 | ||
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 | |||
41 | #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT | 52 | #if ENABLE_FEATURE_TFTP_GET && !ENABLE_FEATURE_TFTP_PUT |
42 | #define USE_GETPUT(...) | 53 | #define USE_GETPUT(...) |
43 | #define CMD_GET(cmd) 1 | 54 | #define CMD_GET(cmd) 1 |
@@ -56,29 +67,48 @@ | |||
56 | * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive | 67 | * CMD_GET(cmd) and CMD_PUT(cmd) are mutually exclusive |
57 | */ | 68 | */ |
58 | 69 | ||
59 | // TODO: emit error packets before dying | 70 | |
71 | struct globals { | ||
72 | /* u16 TFTP_ERROR; u16 reason; both network-endian, then error text: */ | ||
73 | uint8_t error_pkt[4 + 32]; | ||
74 | /* used in tftpd_main(), a bit big fro stack: */ | ||
75 | char block_buf[TFTP_BLKSIZE_DEFAULT]; | ||
76 | }; | ||
77 | #define G (*(struct globals*)&bb_common_bufsiz1) | ||
78 | #define block_buf (G.block_buf ) | ||
79 | #define error_pkt (G.error_pkt ) | ||
80 | #define INIT_G() \ | ||
81 | do { \ | ||
82 | } while (0) | ||
83 | |||
84 | #define error_pkt_reason (error_pkt[3]) | ||
85 | #define error_pkt_str (error_pkt + 4) | ||
60 | 86 | ||
61 | 87 | ||
62 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | 88 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
63 | 89 | ||
64 | static int tftp_blocksize_check(int blocksize, int bufsize) | 90 | static int tftp_blksize_check(const char *blksize_str, int maxsize) |
65 | { | 91 | { |
66 | /* Check if the blocksize is valid: | 92 | /* Check if the blksize is valid: |
67 | * RFC2348 says between 8 and 65464, | 93 | * RFC2348 says between 8 and 65464, |
68 | * but our implementation makes it impossible | 94 | * but our implementation makes it impossible |
69 | * to use blocksizes smaller than 22 octets. | 95 | * to use blksizes smaller than 22 octets. */ |
70 | */ | 96 | unsigned blksize = bb_strtou(blksize_str, NULL, 10); |
71 | if ((bufsize && (blocksize > bufsize)) | 97 | if (errno |
72 | || (blocksize < 24) || (blocksize > 65564) | 98 | || (blksize < 24) || (blksize > maxsize) |
73 | ) { | 99 | ) { |
74 | bb_error_msg("bad blocksize"); | 100 | bb_error_msg("bad blocksize '%s'", blksize_str); |
75 | return 0; | 101 | return -1; |
76 | } | 102 | } |
77 | return blocksize; | 103 | #if ENABLE_DEBUG_TFTP |
104 | bb_error_msg("using blksize %u", blksize); | ||
105 | #endif | ||
106 | return blksize; | ||
78 | } | 107 | } |
79 | 108 | ||
80 | static char *tftp_option_get(char *buf, int len, const char *option) | 109 | static char *tftp_get_blksize(char *buf, int len) |
81 | { | 110 | { |
111 | #define option "blksize" | ||
82 | int opt_val = 0; | 112 | int opt_val = 0; |
83 | int opt_found = 0; | 113 | int opt_found = 0; |
84 | int k; | 114 | int k; |
@@ -110,6 +140,7 @@ static char *tftp_option_get(char *buf, int len, const char *option) | |||
110 | } | 140 | } |
111 | 141 | ||
112 | return NULL; | 142 | return NULL; |
143 | #undef option | ||
113 | } | 144 | } |
114 | 145 | ||
115 | #endif | 146 | #endif |
@@ -120,7 +151,7 @@ static int tftp_protocol( | |||
120 | len_and_sockaddr *peer_lsa, | 151 | len_and_sockaddr *peer_lsa, |
121 | USE_TFTP(const char *remote_file,) | 152 | USE_TFTP(const char *remote_file,) |
122 | int local_fd, | 153 | int local_fd, |
123 | int blocksize) | 154 | int blksize) |
124 | { | 155 | { |
125 | #if !ENABLE_TFTP | 156 | #if !ENABLE_TFTP |
126 | #define remote_file NULL | 157 | #define remote_file NULL |
@@ -135,14 +166,14 @@ static int tftp_protocol( | |||
135 | uint16_t block_nr; | 166 | uint16_t block_nr; |
136 | uint16_t recv_blk; | 167 | uint16_t recv_blk; |
137 | int retries, waittime_ms; | 168 | int retries, waittime_ms; |
138 | int tftp_bufsize = blocksize + 4; | 169 | int io_bufsize = blksize + 4; |
139 | char *cp; | 170 | char *cp; |
140 | /* Can't use RESERVE_CONFIG_BUFFER here since the allocation | 171 | /* Can't use RESERVE_CONFIG_BUFFER here since the allocation |
141 | * size varies meaning BUFFERS_GO_ON_STACK would fail */ | 172 | * size varies meaning BUFFERS_GO_ON_STACK would fail */ |
142 | /* We must keep the transmit and receive buffers seperate */ | 173 | /* We must keep the transmit and receive buffers seperate */ |
143 | /* In case we rcv a garbage pkt and we need to rexmit the last pkt */ | 174 | /* In case we rcv a garbage pkt and we need to rexmit the last pkt */ |
144 | char *xbuf = xmalloc(tftp_bufsize); | 175 | char *xbuf = xmalloc(io_bufsize); |
145 | char *rbuf = xmalloc(tftp_bufsize); | 176 | char *rbuf = xmalloc(io_bufsize); |
146 | 177 | ||
147 | socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0); | 178 | socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0); |
148 | setsockopt_reuseaddr(socket_fd); | 179 | setsockopt_reuseaddr(socket_fd); |
@@ -160,6 +191,10 @@ static int tftp_protocol( | |||
160 | xbind(socket_fd, &our_lsa->u.sa, our_lsa->len); | 191 | xbind(socket_fd, &our_lsa->u.sa, our_lsa->len); |
161 | xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len); | 192 | xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len); |
162 | 193 | ||
194 | /* Is there an error already? Send pkt and bail out */ | ||
195 | if (error_pkt_reason || error_pkt_str[0]) | ||
196 | goto send_err_pkt; | ||
197 | |||
163 | if (CMD_GET(cmd)) { | 198 | if (CMD_GET(cmd)) { |
164 | /* it's upload - we must ACK 1st packet (with filename) | 199 | /* it's upload - we must ACK 1st packet (with filename) |
165 | * as if it's "block 0" */ | 200 | * as if it's "block 0" */ |
@@ -167,10 +202,11 @@ static int tftp_protocol( | |||
167 | } | 202 | } |
168 | 203 | ||
169 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | 204 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
170 | if (blocksize != TFTP_BLOCKSIZE_DEFAULT) { | 205 | if (blksize != TFTP_BLKSIZE_DEFAULT) { |
171 | /* Create and send OACK packet */ | 206 | /* Create and send OACK packet */ |
172 | /* block_nr is still 1, we expect ACK to (block_nr-1), | 207 | /* For the download case, block_nr is still 1 - |
173 | * that is, to "block 0" */ | 208 | * we expect 1st ACK from peer to be for (block_nr-1), |
209 | * that is, for "block 0" which is our OACK pkt */ | ||
174 | opcode = TFTP_OACK; | 210 | opcode = TFTP_OACK; |
175 | cp = xbuf + 2; | 211 | cp = xbuf + 2; |
176 | goto add_blksize_opt; | 212 | goto add_blksize_opt; |
@@ -200,7 +236,7 @@ static int tftp_protocol( | |||
200 | /* add filename and mode */ | 236 | /* add filename and mode */ |
201 | /* fill in packet if the filename fits into xbuf */ | 237 | /* fill in packet if the filename fits into xbuf */ |
202 | len = strlen(remote_file) + 1; | 238 | len = strlen(remote_file) + 1; |
203 | if (2 + len + sizeof("octet") >= tftp_bufsize) { | 239 | if (2 + len + sizeof("octet") >= io_bufsize) { |
204 | bb_error_msg("remote filename is too long"); | 240 | bb_error_msg("remote filename is too long"); |
205 | goto ret; | 241 | goto ret; |
206 | } | 242 | } |
@@ -211,18 +247,18 @@ static int tftp_protocol( | |||
211 | cp += sizeof("octet"); | 247 | cp += sizeof("octet"); |
212 | 248 | ||
213 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | 249 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
214 | if (blocksize != TFTP_BLOCKSIZE_DEFAULT) { | 250 | if (blksize != TFTP_BLKSIZE_DEFAULT) { |
215 | /* rfc2348 says that 65464 is a max allowed value */ | 251 | /* rfc2348 says that 65464 is a max allowed value */ |
216 | if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) { | 252 | if ((&xbuf[io_bufsize - 1] - cp) < sizeof("blksize NNNNN")) { |
217 | bb_error_msg("remote filename is too long"); | 253 | bb_error_msg("remote filename is too long"); |
218 | goto ret; | 254 | goto ret; |
219 | } | 255 | } |
220 | want_option_ack = 1; | 256 | want_option_ack = 1; |
221 | add_blksize_opt: | 257 | add_blksize_opt: |
222 | /* add "blksize", <nul>, blocksize, <nul> */ | 258 | /* add "blksize", <nul>, blksize, <nul> */ |
223 | strcpy(cp, "blksize"); | 259 | strcpy(cp, "blksize"); |
224 | cp += sizeof("blksize"); | 260 | cp += sizeof("blksize"); |
225 | cp += snprintf(cp, 6, "%d", blocksize) + 1; | 261 | cp += snprintf(cp, 6, "%d", blksize) + 1; |
226 | } | 262 | } |
227 | #endif | 263 | #endif |
228 | /* First packet is built, so skip packet generation */ | 264 | /* First packet is built, so skip packet generation */ |
@@ -240,12 +276,11 @@ static int tftp_protocol( | |||
240 | opcode = TFTP_ACK; | 276 | opcode = TFTP_ACK; |
241 | if (CMD_PUT(cmd)) { | 277 | if (CMD_PUT(cmd)) { |
242 | opcode = TFTP_DATA; | 278 | opcode = TFTP_DATA; |
243 | len = full_read(local_fd, cp, tftp_bufsize - 4); | 279 | len = full_read(local_fd, cp, blksize); |
244 | if (len < 0) { | 280 | if (len < 0) { |
245 | bb_perror_msg(bb_msg_read_error); | 281 | goto send_read_err_pkt; |
246 | goto ret; | ||
247 | } | 282 | } |
248 | if (len != (tftp_bufsize - 4)) { | 283 | if (len != blksize) { |
249 | finished = 1; | 284 | finished = 1; |
250 | } | 285 | } |
251 | cp += len; | 286 | cp += len; |
@@ -277,11 +312,28 @@ static int tftp_protocol( | |||
277 | /*pfd[0].fd = socket_fd;*/ | 312 | /*pfd[0].fd = socket_fd;*/ |
278 | pfd[0].events = POLLIN; | 313 | pfd[0].events = POLLIN; |
279 | switch (safe_poll(pfd, 1, waittime_ms)) { | 314 | switch (safe_poll(pfd, 1, waittime_ms)) { |
315 | default: | ||
316 | /*bb_perror_msg("poll"); - done in safe_poll */ | ||
317 | goto ret; | ||
318 | case 0: | ||
319 | retries--; | ||
320 | if (retries == 0) { | ||
321 | bb_error_msg("timeout"); | ||
322 | goto ret; /* no err packet sent */ | ||
323 | } | ||
324 | |||
325 | /* exponential backoff with limit */ | ||
326 | waittime_ms += waittime_ms/2; | ||
327 | if (waittime_ms > TFTP_MAXTIMEOUT_MS) { | ||
328 | waittime_ms = TFTP_MAXTIMEOUT_MS; | ||
329 | } | ||
330 | |||
331 | goto send_again; /* resend last sent pkt */ | ||
280 | case 1: | 332 | case 1: |
281 | if (!our_lsa) { | 333 | if (!our_lsa) { |
282 | /* tftp (not tftpd!) receiving 1st packet */ | 334 | /* tftp (not tftpd!) receiving 1st packet */ |
283 | our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */ | 335 | our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */ |
284 | len = recvfrom(socket_fd, rbuf, tftp_bufsize, 0, | 336 | len = recvfrom(socket_fd, rbuf, io_bufsize, 0, |
285 | &peer_lsa->u.sa, &peer_lsa->len); | 337 | &peer_lsa->u.sa, &peer_lsa->len); |
286 | /* Our first dgram went to port 69 | 338 | /* Our first dgram went to port 69 |
287 | * but reply may come from different one. | 339 | * but reply may come from different one. |
@@ -291,62 +343,44 @@ static int tftp_protocol( | |||
291 | } else { | 343 | } else { |
292 | /* tftpd, or not the very first packet: | 344 | /* tftpd, or not the very first packet: |
293 | * socket is connect()ed, can just read from it. */ | 345 | * socket is connect()ed, can just read from it. */ |
294 | len = safe_read(socket_fd, rbuf, tftp_bufsize); | 346 | /* Don't full_read()! |
347 | * This is not TCP, one read == one pkt! */ | ||
348 | len = safe_read(socket_fd, rbuf, io_bufsize); | ||
295 | } | 349 | } |
296 | if (len < 0) { | 350 | if (len < 0) { |
297 | bb_perror_msg("read"); | 351 | goto send_read_err_pkt; |
298 | goto ret; | ||
299 | } | 352 | } |
300 | if (len < 4) /* too small? */ | 353 | if (len < 4) { /* too small? */ |
301 | goto recv_again; | 354 | goto recv_again; |
302 | goto process_pkt; | ||
303 | case 0: | ||
304 | retries--; | ||
305 | if (retries == 0) { | ||
306 | bb_error_msg("timeout"); | ||
307 | goto ret; | ||
308 | } | 355 | } |
309 | |||
310 | /* exponential backoff with limit */ | ||
311 | waittime_ms += waittime_ms/2; | ||
312 | if (waittime_ms > TFTP_MAXTIMEOUT_MS) { | ||
313 | waittime_ms = TFTP_MAXTIMEOUT_MS; | ||
314 | } | ||
315 | |||
316 | goto send_again; /* resend last sent pkt */ | ||
317 | default: | ||
318 | /*bb_perror_msg("poll"); - done in safe_poll */ | ||
319 | goto ret; | ||
320 | } | 356 | } |
321 | process_pkt: | 357 | |
322 | /* Process recv'ed packet */ | 358 | /* Process recv'ed packet */ |
323 | opcode = ntohs( ((uint16_t*)rbuf)[0] ); | 359 | opcode = ntohs( ((uint16_t*)rbuf)[0] ); |
324 | recv_blk = ntohs( ((uint16_t*)rbuf)[1] ); | 360 | recv_blk = ntohs( ((uint16_t*)rbuf)[1] ); |
325 | |||
326 | #if ENABLE_DEBUG_TFTP | 361 | #if ENABLE_DEBUG_TFTP |
327 | fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk); | 362 | fprintf(stderr, "received %d bytes: %04x %04x\n", len, opcode, recv_blk); |
328 | #endif | 363 | #endif |
329 | 364 | ||
330 | if (opcode == TFTP_ERROR) { | 365 | if (opcode == TFTP_ERROR) { |
331 | static const char *const errcode_str[] = { | 366 | static const char errcode_str[] = |
332 | "", | 367 | "\0" |
333 | "file not found", | 368 | "file not found\0" |
334 | "access violation", | 369 | "access violation\0" |
335 | "disk full", | 370 | "disk full\0" |
336 | "illegal TFTP operation", | 371 | "bad operation\0" |
337 | "unknown transfer id", | 372 | "unknown transfer id\0" |
338 | "file already exists", | 373 | "file already exists\0" |
339 | "no such user", | 374 | "no such user\0" |
340 | "bad option" | 375 | "bad option"; |
341 | }; | ||
342 | 376 | ||
343 | const char *msg = ""; | 377 | const char *msg = ""; |
344 | 378 | ||
345 | if (len > 4 && rbuf[4] != '\0') { | 379 | if (len > 4 && rbuf[4] != '\0') { |
346 | msg = &rbuf[4]; | 380 | msg = &rbuf[4]; |
347 | rbuf[tftp_bufsize - 1] = '\0'; | 381 | rbuf[io_bufsize - 1] = '\0'; /* paranoia */ |
348 | } else if (recv_blk < ARRAY_SIZE(errcode_str)) { | 382 | } else if (recv_blk <= 8) { |
349 | msg = errcode_str[recv_blk]; | 383 | msg = nth_string(errcode_str, recv_blk); |
350 | } | 384 | } |
351 | bb_error_msg("server error: (%u) %s", recv_blk, msg); | 385 | bb_error_msg("server error: (%u) %s", recv_blk, msg); |
352 | goto ret; | 386 | goto ret; |
@@ -355,29 +389,18 @@ static int tftp_protocol( | |||
355 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | 389 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
356 | if (want_option_ack) { | 390 | if (want_option_ack) { |
357 | want_option_ack = 0; | 391 | want_option_ack = 0; |
358 | |||
359 | if (opcode == TFTP_OACK) { | 392 | if (opcode == TFTP_OACK) { |
360 | /* server seems to support options */ | 393 | /* server seems to support options */ |
361 | char *res; | 394 | char *res; |
362 | 395 | ||
363 | res = tftp_option_get(&rbuf[2], len - 2, "blksize"); | 396 | res = tftp_get_blksize(&rbuf[2], len - 2); |
364 | if (res) { | 397 | if (res) { |
365 | int blksize = xatoi_u(res); | 398 | blksize = tftp_blksize_check(res, blksize); |
366 | if (!tftp_blocksize_check(blksize, tftp_bufsize - 4)) { | 399 | if (blksize < 0) { |
367 | /* send ERROR 8 to server... */ | 400 | error_pkt_reason = ERR_BAD_OPT; |
368 | /* htons can be impossible to use in const initializer: */ | 401 | goto send_err_pkt; |
369 | /*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/ | ||
370 | /* thus we open-code big-endian layout */ | ||
371 | static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 }; | ||
372 | xsendto(socket_fd, error_8, 4, &peer_lsa->u.sa, peer_lsa->len); | ||
373 | bb_error_msg("server proposes bad blksize %d, exiting", blksize); | ||
374 | goto ret; | ||
375 | } | 402 | } |
376 | #if ENABLE_DEBUG_TFTP | 403 | io_bufsize = blksize + 4; |
377 | fprintf(stderr, "using blksize %u\n", | ||
378 | blksize); | ||
379 | #endif | ||
380 | tftp_bufsize = blksize + 4; | ||
381 | /* Send ACK for OACK ("block" no: 0) */ | 404 | /* Send ACK for OACK ("block" no: 0) */ |
382 | block_nr = 0; | 405 | block_nr = 0; |
383 | continue; | 406 | continue; |
@@ -387,10 +410,9 @@ static int tftp_protocol( | |||
387 | * must be ignored by the client and server | 410 | * must be ignored by the client and server |
388 | * as if it were never requested." */ | 411 | * as if it were never requested." */ |
389 | } | 412 | } |
390 | 413 | bb_error_msg("server only supports blocksize of 512"); | |
391 | bb_error_msg("blksize is not supported by server" | 414 | blksize = TFTP_BLKSIZE_DEFAULT; |
392 | " - reverting to 512"); | 415 | io_bufsize = TFTP_BLKSIZE_DEFAULT + 4; |
393 | tftp_bufsize = TFTP_BLOCKSIZE_DEFAULT + 4; | ||
394 | } | 416 | } |
395 | #endif | 417 | #endif |
396 | /* block_nr is already advanced to next block# we expect | 418 | /* block_nr is already advanced to next block# we expect |
@@ -401,9 +423,10 @@ static int tftp_protocol( | |||
401 | len = full_write(local_fd, &rbuf[4], len - 4); | 423 | len = full_write(local_fd, &rbuf[4], len - 4); |
402 | if (len < 0) { | 424 | if (len < 0) { |
403 | bb_perror_msg(bb_msg_write_error); | 425 | bb_perror_msg(bb_msg_write_error); |
404 | goto ret; | 426 | error_pkt_reason = ERR_WRITE; |
427 | goto send_err_pkt; | ||
405 | } | 428 | } |
406 | if (len != (tftp_bufsize - 4)) { | 429 | if (len != blksize) { |
407 | finished = 1; | 430 | finished = 1; |
408 | } | 431 | } |
409 | continue; /* send ACK */ | 432 | continue; /* send ACK */ |
@@ -442,6 +465,15 @@ static int tftp_protocol( | |||
442 | free(rbuf); | 465 | free(rbuf); |
443 | } | 466 | } |
444 | return finished == 0; /* returns 1 on failure */ | 467 | return finished == 0; /* returns 1 on failure */ |
468 | |||
469 | send_read_err_pkt: | ||
470 | bb_perror_msg(bb_msg_read_error); | ||
471 | strcpy(error_pkt_str, bb_msg_read_error); | ||
472 | send_err_pkt: | ||
473 | error_pkt[1] = TFTP_ERROR; | ||
474 | xsendto(socket_fd, error_pkt, 4 + 1 + strlen(error_pkt_str), | ||
475 | &peer_lsa->u.sa, peer_lsa->len); | ||
476 | return EXIT_FAILURE; | ||
445 | } | 477 | } |
446 | 478 | ||
447 | #if ENABLE_TFTP | 479 | #if ENABLE_TFTP |
@@ -457,18 +489,20 @@ int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv) | |||
457 | int local_fd; | 489 | int local_fd; |
458 | int flags = 0; | 490 | int flags = 0; |
459 | int result; | 491 | int result; |
460 | int blocksize = TFTP_BLOCKSIZE_DEFAULT; | 492 | int blksize; |
493 | const char *blksize_str = "512"; | ||
494 | |||
495 | INIT_G(); | ||
461 | 496 | ||
462 | /* -p or -g is mandatory, and they are mutually exclusive */ | 497 | /* -p or -g is mandatory, and they are mutually exclusive */ |
463 | opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:") | 498 | opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:") |
464 | USE_GETPUT("g--p:p--g:") | 499 | USE_GETPUT("g--p:p--g:"); |
465 | USE_FEATURE_TFTP_BLOCKSIZE("b+"); | ||
466 | 500 | ||
467 | USE_GETPUT(cmd =) getopt32(argv, | 501 | USE_GETPUT(cmd =) getopt32(argv, |
468 | USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p") | 502 | USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p") |
469 | "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"), | 503 | "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"), |
470 | &local_file, &remote_file | 504 | &local_file, &remote_file |
471 | USE_FEATURE_TFTP_BLOCKSIZE(, &blocksize)); | 505 | USE_FEATURE_TFTP_BLOCKSIZE(, &blksize_str)); |
472 | argv += optind; | 506 | argv += optind; |
473 | 507 | ||
474 | flags = O_RDONLY; | 508 | flags = O_RDONLY; |
@@ -476,8 +510,13 @@ int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv) | |||
476 | flags = O_WRONLY | O_CREAT | O_TRUNC; | 510 | flags = O_WRONLY | O_CREAT | O_TRUNC; |
477 | 511 | ||
478 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | 512 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
479 | if (!tftp_blocksize_check(blocksize, 0)) | 513 | /* Check if the blksize is valid: |
514 | * RFC2348 says between 8 and 65464 */ | ||
515 | blksize = tftp_blksize_check(blksize_str, 65564); | ||
516 | if (blksize < 0) { | ||
517 | //bb_error_msg("bad block size"); | ||
480 | return EXIT_FAILURE; | 518 | return EXIT_FAILURE; |
519 | } | ||
481 | #endif | 520 | #endif |
482 | 521 | ||
483 | if (!local_file) | 522 | if (!local_file) |
@@ -506,7 +545,7 @@ int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv) | |||
506 | USE_GETPUT(cmd,) | 545 | USE_GETPUT(cmd,) |
507 | NULL /* our_lsa*/, | 546 | NULL /* our_lsa*/, |
508 | peer_lsa, | 547 | peer_lsa, |
509 | remote_file, local_fd, blocksize); | 548 | remote_file, local_fd, blksize); |
510 | 549 | ||
511 | if (ENABLE_FEATURE_CLEAN_UP) | 550 | if (ENABLE_FEATURE_CLEAN_UP) |
512 | close(local_fd); | 551 | close(local_fd); |
@@ -537,12 +576,16 @@ static len_and_sockaddr *get_sock_lsa(int s) | |||
537 | int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 576 | int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
538 | int tftpd_main(int argc ATTRIBUTE_UNUSED, char **argv) | 577 | int tftpd_main(int argc ATTRIBUTE_UNUSED, char **argv) |
539 | { | 578 | { |
540 | char block_buf[TFTP_BLOCKSIZE_DEFAULT]; | ||
541 | len_and_sockaddr *our_lsa; | 579 | len_and_sockaddr *our_lsa; |
542 | len_and_sockaddr *peer_lsa; | 580 | len_and_sockaddr *peer_lsa; |
543 | char *filename, *mode, *opt_str; | 581 | char *filename, *mode; |
544 | int opt_r, result, opcode, open_mode, local_fd, blksize; | 582 | const char *error_msg; |
545 | USE_GETPUT(int cmd;) | 583 | int opt_r, result, opcode, open_mode; |
584 | int local_fd = local_fd; /* for compiler */ | ||
585 | int blksize = blksize; | ||
586 | USE_GETPUT(int cmd = cmd;) | ||
587 | |||
588 | INIT_G(); | ||
546 | 589 | ||
547 | our_lsa = get_sock_lsa(STDIN_FILENO); | 590 | our_lsa = get_sock_lsa(STDIN_FILENO); |
548 | if (!our_lsa) | 591 | if (!our_lsa) |
@@ -559,6 +602,7 @@ int tftpd_main(int argc ATTRIBUTE_UNUSED, char **argv) | |||
559 | 0 /* flags */, | 602 | 0 /* flags */, |
560 | &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len); | 603 | &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len); |
561 | 604 | ||
605 | error_msg = "malformed packet"; | ||
562 | opcode = ntohs(*(uint16_t*)block_buf); | 606 | opcode = ntohs(*(uint16_t*)block_buf); |
563 | if (result < 4 || result >= sizeof(block_buf) | 607 | if (result < 4 || result >= sizeof(block_buf) |
564 | || block_buf[result-1] != '\0' | 608 | || block_buf[result-1] != '\0' |
@@ -567,27 +611,35 @@ int tftpd_main(int argc ATTRIBUTE_UNUSED, char **argv) | |||
567 | USE_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */ | 611 | USE_FEATURE_TFTP_GET(opcode != TFTP_WRQ) /* not upload */ |
568 | ) | 612 | ) |
569 | ) { | 613 | ) { |
570 | bb_error_msg_and_die("malformed packet"); | 614 | goto err; |
571 | } | 615 | } |
572 | filename = block_buf + 2; | 616 | filename = block_buf + 2; |
573 | if (filename[0] == '.' || strstr(filename, "/.")) { | 617 | if (filename[0] == '.' || strstr(filename, "/.")) { |
574 | bb_error_msg_and_die("dot in filename"); | 618 | error_msg = "dot in filename"; |
619 | goto err; | ||
575 | } | 620 | } |
576 | mode = filename + strlen(filename) + 1; | 621 | mode = filename + strlen(filename) + 1; |
577 | if (mode >= block_buf + sizeof(block_buf) | 622 | if (mode >= block_buf + result |
578 | || strcmp(mode, "octet") != 0 | 623 | || strcmp(mode, "octet") != 0 |
579 | ) { | 624 | ) { |
580 | bb_error_msg_and_die("malformed packet"); | 625 | goto err; |
581 | } | 626 | } |
582 | blksize = TFTP_BLOCKSIZE_DEFAULT; | 627 | blksize = TFTP_BLKSIZE_DEFAULT; |
583 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | 628 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
584 | opt_str = mode + 6; | 629 | { |
585 | if (opt_str < block_buf + sizeof(block_buf)) { | 630 | char *res; |
586 | char *res = tftp_option_get(opt_str, block_buf + sizeof(block_buf) - opt_str, "blksize"); | 631 | char *opt_str = mode + sizeof("octet"); |
587 | if (res) { | 632 | int opt_len = block_buf + result - opt_str; |
588 | int sz = xatoi_u(res); | 633 | if (opt_len > 0) { |
589 | if (tftp_blocksize_check(sz, 0)) | 634 | res = tftp_get_blksize(opt_str, opt_len); |
590 | blksize = sz; | 635 | if (res) { |
636 | blksize = tftp_blksize_check(res, 65564); | ||
637 | if (blksize < 0) { | ||
638 | error_pkt_reason = ERR_BAD_OPT; | ||
639 | /* will just send error pkt */ | ||
640 | goto do_proto; | ||
641 | } | ||
642 | } | ||
591 | } | 643 | } |
592 | } | 644 | } |
593 | #endif | 645 | #endif |
@@ -599,16 +651,28 @@ int tftpd_main(int argc ATTRIBUTE_UNUSED, char **argv) | |||
599 | #endif | 651 | #endif |
600 | #if ENABLE_FEATURE_TFTP_GET | 652 | #if ENABLE_FEATURE_TFTP_GET |
601 | if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) { | 653 | if (!ENABLE_FEATURE_TFTP_PUT || opcode == TFTP_WRQ) { |
602 | if (opt_r) | 654 | if (opt_r) { |
603 | bb_error_msg_and_die("upload is prohibited"); | 655 | error_pkt_reason = ERR_WRITE; |
656 | /* will just send error pkt */ | ||
657 | goto do_proto; | ||
658 | } | ||
604 | USE_GETPUT(cmd = 1;) /* CMD_GET: we will receive file's data */ | 659 | USE_GETPUT(cmd = 1;) /* CMD_GET: we will receive file's data */ |
605 | open_mode = O_WRONLY | O_TRUNC; | 660 | open_mode = O_WRONLY | O_TRUNC; |
606 | } | 661 | } |
607 | #endif | 662 | #endif |
608 | local_fd = xopen(filename, open_mode); | 663 | local_fd = open(filename, open_mode); |
664 | if (local_fd < 0) { | ||
665 | error_msg = "can't open file"; | ||
666 | err: | ||
667 | strcpy(error_pkt_str, error_msg); | ||
668 | } | ||
609 | 669 | ||
610 | close(STDIN_FILENO); /* close old, possibly wildcard socket */ | 670 | close(STDIN_FILENO); /* close old, possibly wildcard socket */ |
611 | /* tftp_protocol() will create new one, bound to particular local IP */ | 671 | /* tftp_protocol() will create new one, bound to particular local IP */ |
672 | |||
673 | /* NB: if error_pkt_str or error_pkt_reason is set up, | ||
674 | * tftp_protocol() just sends one error pkt and returns */ | ||
675 | do_proto: | ||
612 | result = tftp_protocol( | 676 | result = tftp_protocol( |
613 | USE_GETPUT(cmd,) | 677 | USE_GETPUT(cmd,) |
614 | our_lsa, peer_lsa, | 678 | our_lsa, peer_lsa, |