diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-17 09:10:39 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-17 09:10:39 +0000 |
commit | aa9b1828b91bc4b60f1164b7929a6a0ac091419e (patch) | |
tree | 31a2278e3998160a7c40ac5882736a2f48768a2d /networking/tftp.c | |
parent | 1d42665b6b0571b9fa5d3b10fbf2dd03382f0ba2 (diff) | |
download | busybox-w32-aa9b1828b91bc4b60f1164b7929a6a0ac091419e.tar.gz busybox-w32-aa9b1828b91bc4b60f1164b7929a6a0ac091419e.tar.bz2 busybox-w32-aa9b1828b91bc4b60f1164b7929a6a0ac091419e.zip |
tftpd: new applet (mostly using existing code for tftp)
function old new delta
tftp_protocol - 1173 +1173
tftpd_main - 500 +500
tftp_option_get - 102 +102
packed_usage 23650 23662 +12
applet_names 1809 1815 +6
applet_main 1100 1104 +4
applet_nameofs 550 552 +2
tftp_main 302 301 -1
get_nport 32 - -32
tftp 1172 - -1172
------------------------------------------------------------------------------
(add/remove: 3/2 grow/shrink: 4/1 up/down: 1799/-1205) Total: 594 bytes
text data bss dec hex filename
796479 662 7420 804561 c46d1 busybox_old
797153 662 7420 805235 c4973 busybox_unstripped
Diffstat (limited to 'networking/tftp.c')
-rw-r--r-- | networking/tftp.c | 334 |
1 files changed, 242 insertions, 92 deletions
diff --git a/networking/tftp.c b/networking/tftp.c index 187261568..23a24139b 100644 --- a/networking/tftp.c +++ b/networking/tftp.c | |||
@@ -2,7 +2,7 @@ | |||
2 | /* ------------------------------------------------------------------------- | 2 | /* ------------------------------------------------------------------------- |
3 | * tftp.c | 3 | * tftp.c |
4 | * | 4 | * |
5 | * A simple tftp client for busybox. | 5 | * A simple tftp client/server for busybox. |
6 | * Tries to follow RFC1350. | 6 | * Tries to follow RFC1350. |
7 | * Only "octet" mode supported. | 7 | * Only "octet" mode supported. |
8 | * Optional blocksize negotiation (RFC2347 + RFC2348) | 8 | * Optional blocksize negotiation (RFC2347 + RFC2348) |
@@ -16,6 +16,8 @@ | |||
16 | * | 16 | * |
17 | * utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> | 17 | * utftp: Copyright (C) 1999 Uwe Ohse <uwe@ohse.de> |
18 | * | 18 | * |
19 | * tftpd added by Denys Vlasenko | ||
20 | * | ||
19 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | 21 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
20 | * ------------------------------------------------------------------------- */ | 22 | * ------------------------------------------------------------------------- */ |
21 | 23 | ||
@@ -46,7 +48,7 @@ | |||
46 | #define CMD_PUT(cmd) 1 | 48 | #define CMD_PUT(cmd) 1 |
47 | #else | 49 | #else |
48 | #define USE_GETPUT(...) __VA_ARGS__ | 50 | #define USE_GETPUT(...) __VA_ARGS__ |
49 | /* masks coming from getpot32 */ | 51 | /* masks coming from getopt32 */ |
50 | #define CMD_GET(cmd) ((cmd) & 1) | 52 | #define CMD_GET(cmd) ((cmd) & 1) |
51 | #define CMD_PUT(cmd) ((cmd) & 2) | 53 | #define CMD_PUT(cmd) ((cmd) & 2) |
52 | #endif | 54 | #endif |
@@ -64,14 +66,12 @@ static int tftp_blocksize_check(int blocksize, int bufsize) | |||
64 | * but our implementation makes it impossible | 66 | * but our implementation makes it impossible |
65 | * to use blocksizes smaller than 22 octets. | 67 | * to use blocksizes smaller than 22 octets. |
66 | */ | 68 | */ |
67 | |||
68 | if ((bufsize && (blocksize > bufsize)) | 69 | if ((bufsize && (blocksize > bufsize)) |
69 | || (blocksize < 8) || (blocksize > 65564) | 70 | || (blocksize < 24) || (blocksize > 65564) |
70 | ) { | 71 | ) { |
71 | bb_error_msg("bad blocksize"); | 72 | bb_error_msg("bad blocksize"); |
72 | return 0; | 73 | return 0; |
73 | } | 74 | } |
74 | |||
75 | return blocksize; | 75 | return blocksize; |
76 | } | 76 | } |
77 | 77 | ||
@@ -81,8 +81,11 @@ static char *tftp_option_get(char *buf, int len, const char *option) | |||
81 | int opt_found = 0; | 81 | int opt_found = 0; |
82 | int k; | 82 | int k; |
83 | 83 | ||
84 | /* buf points to: | ||
85 | * "opt_name<NUL>opt_val<NUL>opt_name2<NUL>opt_val2<NUL>..." */ | ||
86 | |||
84 | while (len > 0) { | 87 | while (len > 0) { |
85 | /* Make sure the options are terminated correctly */ | 88 | /* Make sure options are terminated correctly */ |
86 | for (k = 0; k < len; k++) { | 89 | for (k = 0; k < len; k++) { |
87 | if (buf[k] == '\0') { | 90 | if (buf[k] == '\0') { |
88 | goto nul_found; | 91 | goto nul_found; |
@@ -90,7 +93,7 @@ static char *tftp_option_get(char *buf, int len, const char *option) | |||
90 | } | 93 | } |
91 | return NULL; | 94 | return NULL; |
92 | nul_found: | 95 | nul_found: |
93 | if (opt_val == 0) { | 96 | if (opt_val == 0) { /* it's "name" part */ |
94 | if (strcasecmp(buf, option) == 0) { | 97 | if (strcasecmp(buf, option) == 0) { |
95 | opt_found = 1; | 98 | opt_found = 1; |
96 | } | 99 | } |
@@ -109,77 +112,116 @@ static char *tftp_option_get(char *buf, int len, const char *option) | |||
109 | 112 | ||
110 | #endif | 113 | #endif |
111 | 114 | ||
112 | static int tftp( USE_GETPUT(const int cmd,) | 115 | static int tftp_protocol( |
116 | USE_GETPUT(int cmd,) | ||
117 | len_and_sockaddr *our_lsa, | ||
113 | len_and_sockaddr *peer_lsa, | 118 | len_and_sockaddr *peer_lsa, |
114 | const char *remotefile, const int localfd, | 119 | USE_TFTP(const char *remote_file,) |
115 | unsigned port, int tftp_bufsize) | 120 | int local_fd, |
121 | int blocksize) | ||
116 | { | 122 | { |
123 | #if !ENABLE_TFTP | ||
124 | #define remote_file NULL | ||
125 | #endif | ||
117 | struct pollfd pfd[1]; | 126 | struct pollfd pfd[1]; |
118 | #define socketfd (pfd[0].fd) | 127 | #define socket_fd (pfd[0].fd) |
119 | int len; | 128 | int len; |
120 | int send_len; | 129 | int send_len; |
121 | USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;) | 130 | USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;) |
122 | smallint finished = 0; | 131 | smallint finished = 0; |
123 | uint16_t opcode; | 132 | uint16_t opcode; |
124 | uint16_t block_nr = 1; | 133 | uint16_t block_nr; |
125 | uint16_t recv_blk; | 134 | uint16_t recv_blk; |
126 | int retries, waittime_ms; | 135 | int retries, waittime_ms; |
136 | int tftp_bufsize = blocksize + 4; | ||
127 | char *cp; | 137 | char *cp; |
128 | |||
129 | unsigned org_port; | ||
130 | len_and_sockaddr *const from = alloca(LSA_LEN_SIZE + peer_lsa->len); | ||
131 | |||
132 | /* Can't use RESERVE_CONFIG_BUFFER here since the allocation | 138 | /* Can't use RESERVE_CONFIG_BUFFER here since the allocation |
133 | * size varies meaning BUFFERS_GO_ON_STACK would fail */ | 139 | * size varies meaning BUFFERS_GO_ON_STACK would fail */ |
134 | /* We must keep the transmit and receive buffers seperate */ | 140 | /* We must keep the transmit and receive buffers seperate */ |
135 | /* In case we rcv a garbage pkt and we need to rexmit the last pkt */ | 141 | /* In case we rcv a garbage pkt and we need to rexmit the last pkt */ |
136 | char *xbuf = xmalloc(tftp_bufsize += 4); | 142 | char *xbuf = xmalloc(tftp_bufsize); |
137 | char *rbuf = xmalloc(tftp_bufsize); | 143 | char *rbuf = xmalloc(tftp_bufsize); |
138 | 144 | ||
139 | port = org_port = htons(port); | 145 | socket_fd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0); |
146 | setsockopt_reuseaddr(socket_fd); | ||
140 | 147 | ||
141 | socketfd = xsocket(peer_lsa->u.sa.sa_family, SOCK_DGRAM, 0); | 148 | if (!ENABLE_TFTP || our_lsa) { |
149 | /* tftpd */ | ||
150 | block_nr = 0; | ||
142 | 151 | ||
143 | /* build opcode */ | 152 | /* Create a socket which is: |
144 | opcode = TFTP_WRQ; | 153 | * 1. bound to IP:port peer sent 1st datagram to, |
145 | if (CMD_GET(cmd)) { | 154 | * 2. connected to peer's IP:port |
146 | opcode = TFTP_RRQ; | 155 | * This way we will answer from the IP:port peer |
147 | } | 156 | * expects, will not get any other packets on |
148 | cp = xbuf + 2; | 157 | * the socket, and also plain read/write will work. */ |
149 | /* add filename and mode */ | 158 | xbind(socket_fd, &our_lsa->u.sa, our_lsa->len); |
150 | /* fill in packet if the filename fits into xbuf */ | 159 | xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len); |
151 | len = strlen(remotefile) + 1; | ||
152 | if (2 + len + sizeof("octet") >= tftp_bufsize) { | ||
153 | bb_error_msg("remote filename is too long"); | ||
154 | goto ret; | ||
155 | } | ||
156 | strcpy(cp, remotefile); | ||
157 | cp += len; | ||
158 | /* add "mode" part of the package */ | ||
159 | strcpy(cp, "octet"); | ||
160 | cp += sizeof("octet"); | ||
161 | 160 | ||
162 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | 161 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE |
163 | len = tftp_bufsize - 4; /* data block size */ | 162 | if (blocksize != TFTP_BLOCKSIZE_DEFAULT) { |
164 | if (len != TFTP_BLOCKSIZE_DEFAULT) { | 163 | /* Create and send OACK packet */ |
165 | /* rfc2348 says that 65464 is a max allowed value */ | 164 | opcode = TFTP_OACK; |
166 | if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) { | 165 | cp = xbuf + 2; |
166 | goto add_blksize_opt; | ||
167 | } | ||
168 | /* else: just fall into while (1) loop below */ | ||
169 | #endif | ||
170 | } else { | ||
171 | /* tftp */ | ||
172 | block_nr = 1; | ||
173 | |||
174 | /* We can't (and don't really need to) bind the socket: | ||
175 | * we don't know from which local IP datagrams will be sent, | ||
176 | * but kernel will pick the same IP every time (unless routing | ||
177 | * table is changed), thus peer will see dgrams consistently | ||
178 | * coming from the same IP. | ||
179 | * We would like to connect the socket, but since peer's | ||
180 | * UDP code can be less perfect than ours, _peer's_ IP:port | ||
181 | * in replies may differ from IP:port we used to send | ||
182 | * our first packet. We can connect() only when we get | ||
183 | * first reply. */ | ||
184 | |||
185 | /* build opcode */ | ||
186 | opcode = TFTP_WRQ; | ||
187 | if (CMD_GET(cmd)) { | ||
188 | opcode = TFTP_RRQ; | ||
189 | } | ||
190 | cp = xbuf + 2; | ||
191 | /* add filename and mode */ | ||
192 | /* fill in packet if the filename fits into xbuf */ | ||
193 | len = strlen(remote_file) + 1; | ||
194 | if (2 + len + sizeof("octet") >= tftp_bufsize) { | ||
167 | bb_error_msg("remote filename is too long"); | 195 | bb_error_msg("remote filename is too long"); |
168 | goto ret; | 196 | goto ret; |
169 | } | 197 | } |
170 | /* add "blksize", <nul>, blocksize */ | 198 | strcpy(cp, remote_file); |
171 | strcpy(cp, "blksize"); | 199 | cp += len; |
172 | cp += sizeof("blksize"); | 200 | /* add "mode" part of the package */ |
173 | cp += snprintf(cp, 6, "%d", len) + 1; | 201 | strcpy(cp, "octet"); |
174 | want_option_ack = 1; | 202 | cp += sizeof("octet"); |
175 | } | 203 | |
204 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
205 | if (blocksize != TFTP_BLOCKSIZE_DEFAULT) { | ||
206 | /* rfc2348 says that 65464 is a max allowed value */ | ||
207 | if ((&xbuf[tftp_bufsize - 1] - cp) < sizeof("blksize NNNNN")) { | ||
208 | bb_error_msg("remote filename is too long"); | ||
209 | goto ret; | ||
210 | } | ||
211 | want_option_ack = 1; | ||
212 | add_blksize_opt: | ||
213 | /* add "blksize", <nul>, blocksize, <nul> */ | ||
214 | strcpy(cp, "blksize"); | ||
215 | cp += sizeof("blksize"); | ||
216 | cp += snprintf(cp, 6, "%d", blocksize) + 1; | ||
217 | } | ||
176 | #endif | 218 | #endif |
177 | /* First packet is built, so skip packet generation */ | 219 | /* First packet is built, so skip packet generation */ |
178 | goto send_pkt; | 220 | goto send_pkt; |
221 | } | ||
179 | 222 | ||
180 | /* Using mostly goto's - continue/break will be less clear | 223 | /* Using mostly goto's - continue/break will be less clear |
181 | * in where we actually jump to */ | 224 | * in where we actually jump to */ |
182 | |||
183 | while (1) { | 225 | while (1) { |
184 | /* Build ACK or DATA */ | 226 | /* Build ACK or DATA */ |
185 | cp = xbuf + 2; | 227 | cp = xbuf + 2; |
@@ -189,7 +231,7 @@ static int tftp( USE_GETPUT(const int cmd,) | |||
189 | opcode = TFTP_ACK; | 231 | opcode = TFTP_ACK; |
190 | if (CMD_PUT(cmd)) { | 232 | if (CMD_PUT(cmd)) { |
191 | opcode = TFTP_DATA; | 233 | opcode = TFTP_DATA; |
192 | len = full_read(localfd, cp, tftp_bufsize - 4); | 234 | len = full_read(local_fd, cp, tftp_bufsize - 4); |
193 | if (len < 0) { | 235 | if (len < 0) { |
194 | bb_perror_msg(bb_msg_read_error); | 236 | bb_perror_msg(bb_msg_read_error); |
195 | goto ret; | 237 | goto ret; |
@@ -216,36 +258,36 @@ static int tftp( USE_GETPUT(const int cmd,) | |||
216 | fprintf(stderr, "%02x ", (unsigned char) *cp); | 258 | fprintf(stderr, "%02x ", (unsigned char) *cp); |
217 | fprintf(stderr, "\n"); | 259 | fprintf(stderr, "\n"); |
218 | #endif | 260 | #endif |
219 | xsendto(socketfd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len); | 261 | xsendto(socket_fd, xbuf, send_len, &peer_lsa->u.sa, peer_lsa->len); |
220 | /* Was it final ACK? then exit */ | 262 | /* Was it final ACK? then exit */ |
221 | if (finished && (opcode == TFTP_ACK)) | 263 | if (finished && (opcode == TFTP_ACK)) |
222 | goto ret; | 264 | goto ret; |
223 | 265 | ||
224 | recv_again: | 266 | recv_again: |
225 | /* Receive packet */ | 267 | /* Receive packet */ |
226 | /*pfd[0].fd = socketfd;*/ | 268 | /*pfd[0].fd = socket_fd;*/ |
227 | pfd[0].events = POLLIN; | 269 | pfd[0].events = POLLIN; |
228 | switch (safe_poll(pfd, 1, waittime_ms)) { | 270 | switch (safe_poll(pfd, 1, waittime_ms)) { |
229 | unsigned from_port; | ||
230 | case 1: | 271 | case 1: |
231 | from->len = peer_lsa->len; | 272 | if (!our_lsa) { |
232 | memset(&from->u.sa, 0, peer_lsa->len); | 273 | /* tftp (not tftpd!) receiving 1st packet */ |
233 | len = recvfrom(socketfd, rbuf, tftp_bufsize, 0, | 274 | our_lsa = ((void*)(ptrdiff_t)-1); /* not NULL */ |
234 | &from->u.sa, &from->len); | 275 | len = recvfrom(socket_fd, rbuf, tftp_bufsize, 0, |
276 | &peer_lsa->u.sa, &peer_lsa->len); | ||
277 | /* Our first dgram went to port 69 | ||
278 | * but reply may come from different one. | ||
279 | * Remember and use this new port (and IP) */ | ||
280 | if (len >= 0) | ||
281 | xconnect(socket_fd, &peer_lsa->u.sa, peer_lsa->len); | ||
282 | } else { | ||
283 | /* tftpd, or not the very first packet: | ||
284 | * socket is connect()ed, can just read from it. */ | ||
285 | len = safe_read(socket_fd, rbuf, tftp_bufsize); | ||
286 | } | ||
235 | if (len < 0) { | 287 | if (len < 0) { |
236 | bb_perror_msg("recvfrom"); | 288 | bb_perror_msg("read"); |
237 | goto ret; | 289 | goto ret; |
238 | } | 290 | } |
239 | from_port = get_nport(&from->u.sa); | ||
240 | if (port == org_port) { | ||
241 | /* Our first query went to port 69 | ||
242 | * but reply will come from different one. | ||
243 | * Remember and use this new port */ | ||
244 | port = from_port; | ||
245 | set_nport(peer_lsa, from_port); | ||
246 | } | ||
247 | if (port != from_port) | ||
248 | goto recv_again; | ||
249 | goto process_pkt; | 291 | goto process_pkt; |
250 | case 0: | 292 | case 0: |
251 | retries--; | 293 | retries--; |
@@ -316,7 +358,7 @@ static int tftp( USE_GETPUT(const int cmd,) | |||
316 | /*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/ | 358 | /*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/ |
317 | /* thus we open-code big-endian layout */ | 359 | /* thus we open-code big-endian layout */ |
318 | static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 }; | 360 | static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 }; |
319 | xsendto(socketfd, error_8, 4, &peer_lsa->u.sa, peer_lsa->len); | 361 | xsendto(socket_fd, error_8, 4, &peer_lsa->u.sa, peer_lsa->len); |
320 | bb_error_msg("server proposes bad blksize %d, exiting", blksize); | 362 | bb_error_msg("server proposes bad blksize %d, exiting", blksize); |
321 | goto ret; | 363 | goto ret; |
322 | } | 364 | } |
@@ -345,7 +387,7 @@ static int tftp( USE_GETPUT(const int cmd,) | |||
345 | 387 | ||
346 | if (CMD_GET(cmd) && (opcode == TFTP_DATA)) { | 388 | if (CMD_GET(cmd) && (opcode == TFTP_DATA)) { |
347 | if (recv_blk == block_nr) { | 389 | if (recv_blk == block_nr) { |
348 | len = full_write(localfd, &rbuf[4], len - 4); | 390 | len = full_write(local_fd, &rbuf[4], len - 4); |
349 | if (len < 0) { | 391 | if (len < 0) { |
350 | bb_perror_msg(bb_msg_write_error); | 392 | bb_perror_msg(bb_msg_write_error); |
351 | goto ret; | 393 | goto ret; |
@@ -363,7 +405,7 @@ static int tftp( USE_GETPUT(const int cmd,) | |||
363 | } | 405 | } |
364 | 406 | ||
365 | if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) { | 407 | if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) { |
366 | /* did server ACK our last DATA pkt? */ | 408 | /* did peer ACK our last DATA pkt? */ |
367 | if (recv_blk == (uint16_t) (block_nr - 1)) { | 409 | if (recv_blk == (uint16_t) (block_nr - 1)) { |
368 | if (finished) | 410 | if (finished) |
369 | goto ret; | 411 | goto ret; |
@@ -381,25 +423,27 @@ static int tftp( USE_GETPUT(const int cmd,) | |||
381 | * See: | 423 | * See: |
382 | * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome | 424 | * http://en.wikipedia.org/wiki/Sorcerer's_Apprentice_Syndrome |
383 | */ | 425 | */ |
384 | } | 426 | } /* end of "while (1)" */ |
385 | ret: | 427 | ret: |
386 | if (ENABLE_FEATURE_CLEAN_UP) { | 428 | if (ENABLE_FEATURE_CLEAN_UP) { |
387 | close(socketfd); | 429 | close(socket_fd); |
388 | free(xbuf); | 430 | free(xbuf); |
389 | free(rbuf); | 431 | free(rbuf); |
390 | } | 432 | } |
391 | return finished == 0; /* returns 1 on failure */ | 433 | return finished == 0; /* returns 1 on failure */ |
392 | } | 434 | } |
393 | 435 | ||
436 | #if ENABLE_TFTP | ||
437 | |||
394 | int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 438 | int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
395 | int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv) | 439 | int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv) |
396 | { | 440 | { |
397 | len_and_sockaddr *peer_lsa; | 441 | len_and_sockaddr *peer_lsa; |
398 | const char *localfile = NULL; | 442 | const char *local_file = NULL; |
399 | const char *remotefile = NULL; | 443 | const char *remote_file = NULL; |
400 | int port; | 444 | int port; |
401 | USE_GETPUT(int cmd;) | 445 | USE_GETPUT(int cmd;) |
402 | int fd = -1; | 446 | int local_fd; |
403 | int flags = 0; | 447 | int flags = 0; |
404 | int result; | 448 | int result; |
405 | int blocksize = TFTP_BLOCKSIZE_DEFAULT; | 449 | int blocksize = TFTP_BLOCKSIZE_DEFAULT; |
@@ -412,7 +456,7 @@ int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv) | |||
412 | USE_GETPUT(cmd =) getopt32(argv, | 456 | USE_GETPUT(cmd =) getopt32(argv, |
413 | USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p") | 457 | USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p") |
414 | "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"), | 458 | "l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"), |
415 | &localfile, &remotefile | 459 | &local_file, &remote_file |
416 | USE_FEATURE_TFTP_BLOCKSIZE(, &blocksize)); | 460 | USE_FEATURE_TFTP_BLOCKSIZE(, &blocksize)); |
417 | argv += optind; | 461 | argv += optind; |
418 | 462 | ||
@@ -425,36 +469,142 @@ int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv) | |||
425 | return EXIT_FAILURE; | 469 | return EXIT_FAILURE; |
426 | #endif | 470 | #endif |
427 | 471 | ||
428 | if (!localfile) | 472 | if (!local_file) |
429 | localfile = remotefile; | 473 | local_file = remote_file; |
430 | if (!remotefile) | 474 | if (!remote_file) |
431 | remotefile = localfile; | 475 | remote_file = local_file; |
432 | /* Error if filename or host is not known */ | 476 | /* Error if filename or host is not known */ |
433 | if (!remotefile || !argv[0]) | 477 | if (!remote_file || !argv[0]) |
434 | bb_show_usage(); | 478 | bb_show_usage(); |
435 | 479 | ||
436 | fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO; | 480 | local_fd = CMD_GET(cmd) ? STDOUT_FILENO : STDIN_FILENO; |
437 | if (!LONE_DASH(localfile)) { | 481 | if (!LONE_DASH(local_file)) { |
438 | fd = xopen(localfile, flags); | 482 | local_fd = xopen(local_file, flags); |
439 | } | 483 | } |
440 | 484 | ||
441 | port = bb_lookup_port(argv[1], "udp", 69); | 485 | port = bb_lookup_port(argv[1], "udp", 69); |
442 | peer_lsa = xhost2sockaddr(argv[0], port); | 486 | peer_lsa = xhost2sockaddr(argv[0], port); |
443 | 487 | ||
444 | #if ENABLE_DEBUG_TFTP | 488 | #if ENABLE_DEBUG_TFTP |
445 | fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n", | 489 | fprintf(stderr, "using server '%s', remote_file '%s', local_file '%s'\n", |
446 | xmalloc_sockaddr2dotted(&peer_lsa->u.sa), | 490 | xmalloc_sockaddr2dotted(&peer_lsa->u.sa), |
447 | remotefile, localfile); | 491 | remote_file, local_file); |
448 | #endif | 492 | #endif |
449 | 493 | ||
450 | result = tftp( USE_GETPUT(cmd,) peer_lsa, remotefile, fd, port, blocksize); | 494 | result = tftp_protocol( |
495 | USE_GETPUT(cmd,) | ||
496 | NULL /* our_lsa*/, | ||
497 | peer_lsa, | ||
498 | remote_file, local_fd, blocksize); | ||
451 | 499 | ||
452 | if (ENABLE_FEATURE_CLEAN_UP) | 500 | if (ENABLE_FEATURE_CLEAN_UP) |
453 | close(fd); | 501 | close(local_fd); |
454 | if (result != EXIT_SUCCESS && !LONE_DASH(localfile) && CMD_GET(cmd)) { | 502 | if (result != EXIT_SUCCESS && !LONE_DASH(local_file) && CMD_GET(cmd)) { |
455 | unlink(localfile); | 503 | unlink(local_file); |
456 | } | 504 | } |
457 | return result; | 505 | return result; |
458 | } | 506 | } |
459 | 507 | ||
508 | #endif /* ENABLE_TFTP */ | ||
509 | |||
510 | #if ENABLE_TFTPD | ||
511 | |||
512 | /* TODO: libbb candidate? */ | ||
513 | static len_and_sockaddr *get_sock_lsa(int s) | ||
514 | { | ||
515 | len_and_sockaddr *lsa; | ||
516 | socklen_t len = 0; | ||
517 | |||
518 | if (getsockname(s, NULL, &len) != 0) | ||
519 | return NULL; | ||
520 | lsa = xzalloc(LSA_LEN_SIZE + len); | ||
521 | lsa->len = len; | ||
522 | getsockname(s, &lsa->u.sa, &lsa->len); | ||
523 | return lsa; | ||
524 | } | ||
525 | |||
526 | int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
527 | int tftpd_main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) | ||
528 | { | ||
529 | struct stat statbuf; | ||
530 | char block_buf[TFTP_BLOCKSIZE_DEFAULT]; | ||
531 | len_and_sockaddr *our_lsa; | ||
532 | len_and_sockaddr *peer_lsa; | ||
533 | char *filename, *mode, *opt_str; | ||
534 | int result, opcode, cmd, req_modebits, open_mode, local_fd, blksize; | ||
535 | |||
536 | our_lsa = get_sock_lsa(STDIN_FILENO); | ||
537 | if (!our_lsa) | ||
538 | bb_perror_msg_and_die("stdin is not a socket"); | ||
539 | peer_lsa = xzalloc(LSA_LEN_SIZE + our_lsa->len); | ||
540 | peer_lsa->len = our_lsa->len; | ||
541 | |||
542 | if (argv[1]) | ||
543 | xchdir(argv[1]); | ||
544 | |||
545 | result = recv_from_to(STDIN_FILENO, block_buf, sizeof(block_buf), | ||
546 | 0 /* flags */, | ||
547 | &peer_lsa->u.sa, &our_lsa->u.sa, our_lsa->len); | ||
548 | |||
549 | opcode = ntohs(*(uint16_t*)block_buf); | ||
550 | if (result < 4 || result >= sizeof(block_buf) | ||
551 | || block_buf[result-1] != '\0' | ||
552 | || (opcode != TFTP_RRQ && opcode != TFTP_WRQ) | ||
553 | ) { | ||
554 | bb_error_msg_and_die("malformed packet"); | ||
555 | } | ||
556 | filename = block_buf + 2; | ||
557 | if (filename[0] == '.' || strstr(filename, "/.")) { | ||
558 | bb_error_msg_and_die("dot in filename"); | ||
559 | } | ||
560 | mode = filename + strlen(filename) + 1; | ||
561 | if (mode >= block_buf + sizeof(block_buf) | ||
562 | || strcmp(mode, "octet") != 0 | ||
563 | ) { | ||
564 | bb_error_msg_and_die("malformed packet"); | ||
565 | } | ||
566 | blksize = TFTP_BLOCKSIZE_DEFAULT; | ||
567 | #if ENABLE_FEATURE_TFTP_BLOCKSIZE | ||
568 | opt_str = mode + 6; | ||
569 | if (opt_str < block_buf + sizeof(block_buf)) { | ||
570 | char *res = tftp_option_get(opt_str, block_buf + sizeof(block_buf) - opt_str, "blksize"); | ||
571 | if (res) { | ||
572 | int sz = xatoi_u(res); | ||
573 | if (tftp_blocksize_check(sz, 0)) | ||
574 | blksize = sz; | ||
575 | } | ||
576 | } | ||
577 | #endif | ||
578 | xstat(filename, &statbuf); | ||
579 | /* if opcode == TFTP_WRQ: */ | ||
580 | cmd = 1; /* CMD_GET: we will receive file's data */ | ||
581 | req_modebits = 0222; /* writable by anyone */ | ||
582 | open_mode = O_WRONLY | O_TRUNC; | ||
583 | if (opcode == TFTP_RRQ) { | ||
584 | cmd = 2; /* CMD_PUT */ | ||
585 | req_modebits = 0444; /* readable by anyone */ | ||
586 | open_mode = O_RDONLY; | ||
587 | } | ||
588 | if (!S_ISREG(statbuf.st_mode) | ||
589 | || (statbuf.st_mode & req_modebits) != req_modebits | ||
590 | ) { | ||
591 | bb_error_msg_and_die("access to '%s' is denied", filename); | ||
592 | } | ||
593 | local_fd = xopen(filename, open_mode); | ||
594 | |||
595 | close(STDIN_FILENO); /* close old, possibly wildcard socket */ | ||
596 | /* tftp_protocol() will create new one, bound to particular local IP */ | ||
597 | result = tftp_protocol( | ||
598 | USE_GETPUT(cmd,) | ||
599 | our_lsa, peer_lsa, | ||
600 | USE_TFTP(NULL /*remote_file*/,) | ||
601 | local_fd, | ||
602 | blksize | ||
603 | ); | ||
604 | |||
605 | return result; | ||
606 | } | ||
607 | |||
608 | #endif /* ENABLE_TFTPD */ | ||
609 | |||
460 | #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */ | 610 | #endif /* ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT */ |