summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhpa <hpa>2004-06-13 21:11:24 +0000
committerhpa <hpa>2004-06-13 21:11:24 +0000
commit2cba51056f17f8698dd41327d12661c3b5602276 (patch)
tree9b18409b455c7b8747456def75ea2c09c6a404ca
parent5fbd57b61a4d9646930c706f176218423a17cc6a (diff)
downloadtftp-hpa-0.37.tar.gz
Fix a pathology where a client sending ACKs for the wrongtftp-hpa-0.37
packet can prevent proper retransmission.
-rw-r--r--CHANGES5
-rw-r--r--tftpd/tftpd.c35
2 files changed, 29 insertions, 11 deletions
diff --git a/CHANGES b/CHANGES
index 5aae695..5a7a296 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,5 +1,10 @@
$Id$
+Changes in 0.37:
+ Fix a pathology where a client sending ACKs for the wrong
+ packet can prevent proper retransmission.
+
+
Changes in 0.36:
Portability fixes.
diff --git a/tftpd/tftpd.c b/tftpd/tftpd.c
index af6f253..fbadadf 100644
--- a/tftpd/tftpd.c
+++ b/tftpd/tftpd.c
@@ -207,25 +207,37 @@ set_socket_nonblock(int fd, int flag)
}
/*
- * Receive packet with synchronous timeout
+ * Receive packet with synchronous timeout; timeout is adjusted
+ * to account for time spent waiting.
*/
static int recv_time(int s, void *rbuf, int len, unsigned int flags,
- unsigned long timeout_us)
+ unsigned long *timeout_us_p)
{
fd_set fdset;
- struct timeval tmv;
+ struct timeval tmv, t0, t1;
int rv, err;
+ unsigned long timeout_us = *timeout_us_p;
+ unsigned long timeout_left, dt;
+
+ gettimeofday(&t0, NULL);
+ timeout_left = timeout_us;
for ( ; ; ) {
FD_ZERO(&fdset);
FD_SET(s, &fdset);
- tmv.tv_sec = timeout_us / 1000000;
- tmv.tv_usec = timeout_us % 1000000;
-
do {
+ tmv.tv_sec = timeout_left / 1000000;
+ tmv.tv_usec = timeout_left % 1000000;
+
rv = select(s+1, &fdset, NULL, NULL, &tmv);
- } while ( rv == -1 && errno == EINTR );
+ err = errno;
+
+ gettimeofday(&t1, NULL);
+
+ dt = (t1.tv_sec - t0.tv_sec)*1000000 + (t1.tv_usec - t0.tv_usec);
+ *timeout_us_p = timeout_left = ( dt >= timeout_us ) ? 1 : (timeout_us - dt);
+ } while ( rv == -1 && err == EINTR );
if ( rv == 0 ) {
timer(0); /* Should not return */
@@ -236,6 +248,7 @@ static int recv_time(int s, void *rbuf, int len, unsigned int flags,
rv = recv(s, rbuf, len, flags);
err = errno;
set_socket_nonblock(s, 0);
+
if ( rv < 0 ) {
if ( E_WOULD_BLOCK(err) || err == EINTR ) {
continue; /* Once again, with feeling... */
@@ -1178,7 +1191,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen)
goto abort;
}
for ( ; ; ) {
- n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, timeout);
+ n = recv_time(peer, ackbuf, sizeof(ackbuf), 0, &timeout);
if (n < 0) {
syslog(LOG_WARNING, "tftpd: read: %m\n");
goto abort;
@@ -1219,7 +1232,7 @@ tftp_sendfile(struct formats *pf, struct tftphdr *oap, int oacklen)
}
read_ahead(file, pf->f_convert);
for ( ; ; ) {
- n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, timeout);
+ n = recv_time(peer, ackbuf, sizeof (ackbuf), 0, &timeout);
if (n < 0) {
syslog(LOG_WARNING, "tftpd: read(ack): %m");
goto abort;
@@ -1296,7 +1309,7 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen)
}
write_behind(file, pf->f_convert);
for ( ; ; ) {
- n = recv_time(peer, dp, PKTSIZE, 0, timeout);
+ n = recv_time(peer, dp, PKTSIZE, 0, &timeout);
if (n < 0) { /* really? */
syslog(LOG_WARNING, "tftpd: read: %m");
goto abort;
@@ -1331,7 +1344,7 @@ tftp_recvfile(struct formats *pf, struct tftphdr *oap, int oacklen)
(void) send(peer, ackbuf, 4, 0);
timeout_quit = 1; /* just quit on timeout */
- n = recv_time(peer, buf, sizeof (buf), 0, timeout); /* normally times out and quits */
+ n = recv_time(peer, buf, sizeof (buf), 0, &timeout); /* normally times out and quits */
timeout_quit = 0;
if (n >= 4 && /* if read some data */