#include "libbb.h"
-
#if ENABLE_FEATURE_TFTP_GET || ENABLE_FEATURE_TFTP_PUT
-#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
-#define TFTP_TIMEOUT 5 /* seconds */
-#define TFTP_NUM_RETRIES 5 /* number of retries */
+#define TFTP_BLOCKSIZE_DEFAULT 512 /* according to RFC 1350, don't change */
+#define TFTP_TIMEOUT_MS 50
+#define TFTP_MAXTIMEOUT_MS 2000
+#define TFTP_NUM_RETRIES 12 /* number of backed-off retries */
/* opcodes we support */
#define TFTP_RRQ 1
const char *remotefile, const int localfd,
unsigned port, int tftp_bufsize)
{
- struct timeval tv;
- fd_set rfds;
- int socketfd;
+ struct pollfd pfd[1];
+#define socketfd (pfd[0].fd)
int len;
int send_len;
USE_FEATURE_TFTP_BLOCKSIZE(smallint want_option_ack = 0;)
uint16_t opcode;
uint16_t block_nr = 1;
uint16_t recv_blk;
- int timeout = TFTP_NUM_RETRIES;
+ int retries, waittime_ms;
char *cp;
unsigned org_port;
send_len = cp - xbuf;
/* NB: send_len value is preserved in code below
* for potential resend */
+
+ retries = TFTP_NUM_RETRIES; /* re-initialize */
+ waittime_ms = TFTP_TIMEOUT_MS;
+
send_again:
#if ENABLE_DEBUG_TFTP
fprintf(stderr, "sending %u bytes\n", send_len);
if (finished && (opcode == TFTP_ACK))
goto ret;
- timeout = TFTP_NUM_RETRIES; /* re-initialize */
recv_again:
/* Receive packet */
- tv.tv_sec = TFTP_TIMEOUT;
- tv.tv_usec = 0;
- FD_ZERO(&rfds);
- FD_SET(socketfd, &rfds);
- switch (select(socketfd + 1, &rfds, NULL, NULL, &tv)) {
+ /*pfd[0].fd = socketfd;*/
+ pfd[0].events = POLLIN;
+ switch (safe_poll(pfd, 1, waittime_ms)) {
unsigned from_port;
case 1:
from->len = peer_lsa->len;
goto recv_again;
goto process_pkt;
case 0:
- timeout--;
- if (timeout == 0) {
- bb_error_msg("last timeout");
+ retries--;
+ if (retries == 0) {
+ bb_error_msg("timeout");
goto ret;
}
- bb_error_msg("last timeout" + 5);
+
+ /* exponential backoff with limit */
+ waittime_ms += waittime_ms/2;
+ if (waittime_ms > TFTP_MAXTIMEOUT_MS) {
+ waittime_ms = TFTP_MAXTIMEOUT_MS;
+ }
+
goto send_again; /* resend last sent pkt */
default:
- bb_perror_msg("select");
+ /*bb_perror_msg("poll"); - done in safe_poll */
goto ret;
}
process_pkt:
"unknown transfer id",
"file already exists",
"no such user",
- "bad option",
+ "bad option"
};
- enum { NUM_ERRCODE = sizeof(errcode_str) / sizeof(errcode_str[0]) };
+
const char *msg = "";
if (rbuf[4] != '\0') {
msg = &rbuf[4];
rbuf[tftp_bufsize - 1] = '\0';
- } else if (recv_blk < NUM_ERRCODE) {
+ } else if (recv_blk < ARRAY_SIZE(errcode_str)) {
msg = errcode_str[recv_blk];
}
bb_error_msg("server error: (%u) %s", recv_blk, msg);
/* htons can be impossible to use in const initializer: */
/*static const uint16_t error_8[2] = { htons(TFTP_ERROR), htons(8) };*/
/* thus we open-code big-endian layout */
- static const char error_8[4] = { 0,TFTP_ERROR, 0,8 };
+ static const uint8_t error_8[4] = { 0,TFTP_ERROR, 0,8 };
xsendto(socketfd, error_8, 4, &peer_lsa->sa, peer_lsa->len);
bb_error_msg("server proposes bad blksize %d, exiting", blksize);
goto ret;
/* Server lost our TFTP_ACK. Resend it */
block_nr = recv_blk;
continue;
- }
+ }
}
if (CMD_PUT(cmd) && (opcode == TFTP_ACK)) {
return finished == 0; /* returns 1 on failure */
}
-int tftp_main(int argc, char **argv);
+int tftp_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int tftp_main(int argc, char **argv)
{
len_and_sockaddr *peer_lsa;
opt_complementary = "" USE_FEATURE_TFTP_GET("g:") USE_FEATURE_TFTP_PUT("p:")
USE_GETPUT("?g--p:p--g");
- USE_GETPUT(cmd =) getopt32(argc, argv,
+ USE_GETPUT(cmd =) getopt32(argv,
USE_FEATURE_TFTP_GET("g") USE_FEATURE_TFTP_PUT("p")
"l:r:" USE_FEATURE_TFTP_BLOCKSIZE("b:"),
&localfile, &remotefile
#if ENABLE_DEBUG_TFTP
fprintf(stderr, "using server '%s', remotefile '%s', localfile '%s'\n",
- xmalloc_sockaddr2dotted(&peer_lsa->sa, peer_lsa->len),
+ xmalloc_sockaddr2dotted(&peer_lsa->sa),
remotefile, localfile);
#endif