aboutsummaryrefslogtreecommitdiff
path: root/networking/tftp.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2008-03-17 09:10:39 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2008-03-17 09:10:39 +0000
commitaa9b1828b91bc4b60f1164b7929a6a0ac091419e (patch)
tree31a2278e3998160a7c40ac5882736a2f48768a2d /networking/tftp.c
parent1d42665b6b0571b9fa5d3b10fbf2dd03382f0ba2 (diff)
downloadbusybox-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.c334
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
112static int tftp( USE_GETPUT(const int cmd,) 115static 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
394int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 438int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
395int tftp_main(int argc ATTRIBUTE_UNUSED, char **argv) 439int 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? */
513static 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
526int tftpd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
527int 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 */