diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 755bda9..bea57bc 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -956,10 +956,11 @@ int main (int argc, char **argv) { struct tftp_prefix *p; - my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s", + my_syslog(MS_TFTP | LOG_INFO, "TFTP %s%s %s %s", daemon->tftp_prefix ? _("root is ") : _("enabled"), - daemon->tftp_prefix ? daemon->tftp_prefix: "", - option_bool(OPT_TFTP_SECURE) ? _("secure mode") : ""); + daemon->tftp_prefix ? daemon->tftp_prefix : "", + option_bool(OPT_TFTP_SECURE) ? _("secure mode") : "", + option_bool(OPT_SINGLE_PORT) ? _("single port mode") : ""); if (tftp_prefix_missing) my_syslog(MS_TFTP | LOG_WARNING, _("warning: %s inaccessible"), daemon->tftp_prefix); @@ -977,7 +978,7 @@ int main (int argc, char **argv) if (max_fd < 0) max_fd = 5; - else if (max_fd < 100) + else if (max_fd < 100 && !option_bool(OPT_SINGLE_PORT)) max_fd = max_fd/2; else max_fd = max_fd - 20; @@ -1707,6 +1708,7 @@ static int set_dns_listeners(time_t now) } #ifdef HAVE_TFTP + /* tftp == 0 in single-port mode. */ if (tftp <= daemon->tftp_max && listener->tftpfd != -1) poll_listen(listener->tftpfd, POLLIN); #endif diff --git a/src/tftp.c b/src/tftp.c index 92de357..ca6676a 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -242,23 +242,37 @@ void tftp_request(struct listener *listen, time_t now) /* data transfer via server listening socket */ if (option_bool(OPT_SINGLE_PORT)) - for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next) - if (sockaddr_isequal(&peer, &transfer->peer)) + { + int tftp_cnt; + + for (tftp_cnt = 0, transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next) { - if (ntohs(*((unsigned short *)packet)) == OP_RRQ) + tftp_cnt++; + + if (sockaddr_isequal(&peer, &transfer->peer)) { - /* Handle repeated RRQ or abandoned transfer from same host and port - by unlinking and reusing the struct transfer. */ - *up = transfer->next; - break; - } - else - { - handle_tftp(now, transfer, len); - return; + if (ntohs(*((unsigned short *)packet)) == OP_RRQ) + { + /* Handle repeated RRQ or abandoned transfer from same host and port + by unlinking and reusing the struct transfer. */ + *up = transfer->next; + break; + } + else + { + handle_tftp(now, transfer, len); + return; + } } } - + + /* Enforce simultaneous transfer limit. In non-single-port mode + this is doene by not listening on the server socket when + too many transfers are in progress. */ + if (!transfer && tftp_cnt >= daemon->tftp_max) + return; + } + if (name) { /* check for per-interface prefix */ @@ -583,21 +597,28 @@ void check_tftp_listeners(time_t now) ssize_t len; /* timeout, retransmit */ - transfer->timeout += 1 + (1<backoff); + transfer->timeout += 1 + (1<<(transfer->backoff/2)); /* we overwrote the buffer... */ daemon->srv_save = NULL; - if ((len = get_block(daemon->packet, transfer)) == -1) + /* check for wrap of 16 bit block no. */ + if (transfer->block == 0x10000) + { + len = tftp_err(ERR_ILL, daemon->packet, _("too many blocks"), NULL); + endcon = 1; + } + else if ((len = get_block(daemon->packet, transfer)) == -1) { len = tftp_err_oops(daemon->packet, transfer->file->filename); endcon = 1; } - /* don't complain about timeout when we're awaiting the last - ACK, some clients never send it */ - else if (++transfer->backoff > 7 && len != 0) + else if (++transfer->backoff > 7) { - endcon = 1; + /* don't complain about timeout when we're awaiting the last + ACK, some clients never send it */ + if (len == transfer->blocksize + 4) + endcon = 1; len = 0; } @@ -720,7 +741,8 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file) char *errstr = strerror(errno); memset(packet, 0, daemon->packet_buff_sz); - sanitise(file); + if (file) + sanitise(file); mess->op = htons(OP_ERR); mess->err = htons(err);