mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349).
This commit is contained in:
@@ -59,6 +59,8 @@ version 2.92
|
|||||||
Fix failure to cache PTR RRs when a reply contains more than one answer.
|
Fix failure to cache PTR RRs when a reply contains more than one answer.
|
||||||
Thanks to Dmitry for spotting this.
|
Thanks to Dmitry for spotting this.
|
||||||
|
|
||||||
|
Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349).
|
||||||
|
|
||||||
|
|
||||||
version 2.91
|
version 2.91
|
||||||
Fix spurious "resource limit exceeded messages". Thanks to
|
Fix spurious "resource limit exceeded messages". Thanks to
|
||||||
|
|||||||
@@ -52,6 +52,8 @@
|
|||||||
#define CHUSER "nobody"
|
#define CHUSER "nobody"
|
||||||
#define CHGRP "dip"
|
#define CHGRP "dip"
|
||||||
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
|
#define TFTP_MAX_CONNECTIONS 50 /* max simultaneous connections */
|
||||||
|
#define TFTP_MAX_WINDOW 32 /* max window size to negotiate */
|
||||||
|
#define TFTP_TRANSFER_TIME 120 /* Abandon TFTP transfers after this long. Two mins. */
|
||||||
#define LOG_MAX 5 /* log-queue length */
|
#define LOG_MAX 5 /* log-queue length */
|
||||||
#define RANDFILE "/dev/urandom"
|
#define RANDFILE "/dev/urandom"
|
||||||
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
|
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
|
||||||
|
|||||||
@@ -30,7 +30,9 @@ static volatile pid_t pid = 0;
|
|||||||
static volatile int pipewrite;
|
static volatile int pipewrite;
|
||||||
|
|
||||||
static void set_dns_listeners(void);
|
static void set_dns_listeners(void);
|
||||||
|
#ifdef HAVE_TFTP
|
||||||
static void set_tftp_listeners(void);
|
static void set_tftp_listeners(void);
|
||||||
|
#endif
|
||||||
static void check_dns_listeners(time_t now);
|
static void check_dns_listeners(time_t now);
|
||||||
static void do_tcp_connection(struct listener *listener, time_t now, int slot);
|
static void do_tcp_connection(struct listener *listener, time_t now, int slot);
|
||||||
static void sig_handler(int sig);
|
static void sig_handler(int sig);
|
||||||
|
|||||||
@@ -1117,14 +1117,13 @@ struct tftp_file {
|
|||||||
|
|
||||||
struct tftp_transfer {
|
struct tftp_transfer {
|
||||||
int sockfd;
|
int sockfd;
|
||||||
time_t timeout;
|
time_t retransmit, start;
|
||||||
int backoff;
|
unsigned int lastack, block, blocksize, windowsize, timeout, expansion;
|
||||||
unsigned int block, blocksize, expansion;
|
|
||||||
off_t offset;
|
off_t offset;
|
||||||
union mysockaddr peer;
|
union mysockaddr peer;
|
||||||
union all_addr source;
|
union all_addr source;
|
||||||
int if_index;
|
int if_index;
|
||||||
char opt_blocksize, opt_transize, netascii, carrylf;
|
unsigned char opt_blocksize, opt_transize, opt_windowsize, opt_timeout, netascii, carrylf, backoff;
|
||||||
struct tftp_file *file;
|
struct tftp_file *file;
|
||||||
struct tftp_transfer *next;
|
struct tftp_transfer *next;
|
||||||
};
|
};
|
||||||
@@ -1768,7 +1767,6 @@ void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *p
|
|||||||
|
|
||||||
/* tftp.c */
|
/* tftp.c */
|
||||||
#ifdef HAVE_TFTP
|
#ifdef HAVE_TFTP
|
||||||
void tftp_request(struct listener *listen, time_t now);
|
|
||||||
void check_tftp_listeners(time_t now);
|
void check_tftp_listeners(time_t now);
|
||||||
int do_tftp_script_run(void);
|
int do_tftp_script_run(void);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
429
src/tftp.c
429
src/tftp.c
@@ -41,11 +41,11 @@ static void sanitise(char *buf);
|
|||||||
#define ERR_ILL 4
|
#define ERR_ILL 4
|
||||||
#define ERR_TID 5
|
#define ERR_TID 5
|
||||||
|
|
||||||
void tftp_request(struct listener *listen, time_t now)
|
static void tftp_request(struct listener *listen, time_t now)
|
||||||
{
|
{
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
char *packet = daemon->packet;
|
char *packet = daemon->packet;
|
||||||
char *filename, *mode, *p, *end, *opt;
|
char *filename, *mode, *p, *end;
|
||||||
union mysockaddr addr, peer;
|
union mysockaddr addr, peer;
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
struct iovec iov;
|
struct iovec iov;
|
||||||
@@ -309,6 +309,8 @@ void tftp_request(struct listener *listen, time_t now)
|
|||||||
if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
|
if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
memset(transfer, 0, sizeof(struct tftp_transfer));
|
||||||
|
|
||||||
if (option_bool(OPT_SINGLE_PORT))
|
if (option_bool(OPT_SINGLE_PORT))
|
||||||
transfer->sockfd = listen->tftpfd;
|
transfer->sockfd = listen->tftpfd;
|
||||||
else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1)
|
else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1)
|
||||||
@@ -320,14 +322,12 @@ void tftp_request(struct listener *listen, time_t now)
|
|||||||
transfer->peer = peer;
|
transfer->peer = peer;
|
||||||
transfer->source = addra;
|
transfer->source = addra;
|
||||||
transfer->if_index = if_index;
|
transfer->if_index = if_index;
|
||||||
transfer->timeout = now + 2;
|
transfer->timeout = 2;
|
||||||
|
transfer->start = now;
|
||||||
transfer->backoff = 1;
|
transfer->backoff = 1;
|
||||||
transfer->block = 1;
|
transfer->block = 1;
|
||||||
transfer->blocksize = 512;
|
transfer->blocksize = 512;
|
||||||
transfer->offset = 0;
|
transfer->windowsize = 1;
|
||||||
transfer->file = NULL;
|
|
||||||
transfer->opt_blocksize = transfer->opt_transize = 0;
|
|
||||||
transfer->netascii = transfer->carrylf = 0;
|
|
||||||
|
|
||||||
(void)prettyprint_addr(&peer, daemon->addrbuff);
|
(void)prettyprint_addr(&peer, daemon->addrbuff);
|
||||||
|
|
||||||
@@ -362,139 +362,166 @@ void tftp_request(struct listener *listen, time_t now)
|
|||||||
p = packet + 2;
|
p = packet + 2;
|
||||||
end = packet + len;
|
end = packet + len;
|
||||||
|
|
||||||
if (!(filename = next(&p, end)) ||
|
len = 0;
|
||||||
!(mode = next(&p, end)) ||
|
|
||||||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0) ||
|
|
||||||
ntohs(*((unsigned short *)packet)) != OP_RRQ)
|
|
||||||
{
|
|
||||||
if (!filename)
|
|
||||||
len = tftp_err(ERR_ILL, packet, _("empty filename in request from %s"), daemon->addrbuff, NULL);
|
|
||||||
else
|
|
||||||
len = tftp_err(ERR_ILL, packet, _("unsupported %srequest from %s"),
|
|
||||||
(ntohs(*((unsigned short *)packet)) == OP_WRQ) ? _("write ") : "", daemon->addrbuff);
|
|
||||||
is_err = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (strcasecmp(mode, "netascii") == 0)
|
|
||||||
transfer->netascii = 1;
|
|
||||||
|
|
||||||
while ((opt = next(&p, end)))
|
if (ntohs(*((unsigned short *)packet)) == OP_WRQ)
|
||||||
|
len = tftp_err(ERR_ILL, packet, _("unsupported write request from %s"),daemon->addrbuff, NULL);
|
||||||
|
else if (ntohs(*((unsigned short *)packet)) == OP_RRQ)
|
||||||
|
{
|
||||||
|
if (!(filename = next(&p, end)))
|
||||||
|
len = tftp_err(ERR_ILL, packet, _("empty filename in request from %s"), daemon->addrbuff, NULL);
|
||||||
|
else if (!(mode = next(&p, end)) || (strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
|
||||||
|
len = tftp_err(ERR_ILL, packet, _("unsupported request from %s"),daemon->addrbuff, NULL);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
if (strcasecmp(opt, "blksize") == 0)
|
char *opt, *arg;
|
||||||
|
|
||||||
|
if (strcasecmp(mode, "netascii") == 0)
|
||||||
|
transfer->netascii = 1;
|
||||||
|
|
||||||
|
while ((opt = next(&p, end)) && (arg = next(&p, end)))
|
||||||
{
|
{
|
||||||
if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK))
|
unsigned int val = atoi(arg);
|
||||||
|
|
||||||
|
if (strcasecmp(opt, "blksize") == 0 && !option_bool(OPT_TFTP_NOBLOCK))
|
||||||
{
|
{
|
||||||
/* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
|
/* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */
|
||||||
int overhead = (family == AF_INET) ? 32 : 52;
|
int overhead = (family == AF_INET) ? 32 : 52;
|
||||||
transfer->blocksize = atoi(opt);
|
if (val < 1)
|
||||||
if (transfer->blocksize < 1)
|
val = 1;
|
||||||
transfer->blocksize = 1;
|
if (val > (unsigned)daemon->packet_buff_sz - 4)
|
||||||
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
|
val = (unsigned)daemon->packet_buff_sz - 4;
|
||||||
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
|
if (mtu != 0 && val > (unsigned)mtu - overhead)
|
||||||
if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
|
val = (unsigned)mtu - overhead;
|
||||||
transfer->blocksize = (unsigned)mtu - overhead;
|
transfer->blocksize = val;
|
||||||
transfer->opt_blocksize = 1;
|
transfer->opt_blocksize = 1;
|
||||||
transfer->block = 0;
|
transfer->block = 0;
|
||||||
}
|
}
|
||||||
}
|
else if (strcasecmp(opt, "tsize") == 0 && !transfer->netascii)
|
||||||
else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
|
{
|
||||||
{
|
transfer->opt_transize = 1;
|
||||||
transfer->opt_transize = 1;
|
transfer->block = 0;
|
||||||
transfer->block = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cope with backslashes from windows boxen. */
|
|
||||||
for (p = filename; *p; p++)
|
|
||||||
if (*p == '\\')
|
|
||||||
*p = '/';
|
|
||||||
else if (option_bool(OPT_TFTP_LC))
|
|
||||||
*p = tolower((unsigned char)*p);
|
|
||||||
|
|
||||||
strcpy(daemon->namebuff, "/");
|
|
||||||
if (prefix)
|
|
||||||
{
|
|
||||||
if (prefix[0] == '/')
|
|
||||||
daemon->namebuff[0] = 0;
|
|
||||||
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
|
|
||||||
if (prefix[strlen(prefix)-1] != '/')
|
|
||||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
|
||||||
|
|
||||||
if (option_bool(OPT_TFTP_APREF_IP))
|
|
||||||
{
|
|
||||||
size_t oldlen = strlen(daemon->namebuff);
|
|
||||||
struct stat statbuf;
|
|
||||||
|
|
||||||
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
|
|
||||||
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
|
||||||
|
|
||||||
/* remove unique-directory if it doesn't exist */
|
|
||||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
|
||||||
daemon->namebuff[oldlen] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (option_bool(OPT_TFTP_APREF_MAC))
|
|
||||||
{
|
|
||||||
unsigned char *macaddr = NULL;
|
|
||||||
unsigned char macbuf[DHCP_CHADDR_MAX];
|
|
||||||
|
|
||||||
#ifdef HAVE_DHCP
|
|
||||||
if (daemon->dhcp && peer.sa.sa_family == AF_INET)
|
|
||||||
{
|
|
||||||
/* Check if the client IP is in our lease database */
|
|
||||||
struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
|
|
||||||
if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
|
|
||||||
macaddr = lease->hwaddr;
|
|
||||||
}
|
}
|
||||||
#endif
|
else if (strcasecmp(opt, "timeout") == 0)
|
||||||
|
{
|
||||||
|
if (val > 255)
|
||||||
|
val = 255;
|
||||||
|
transfer->timeout = val;
|
||||||
|
transfer->opt_timeout = 1;
|
||||||
|
transfer->block = 0;
|
||||||
|
}
|
||||||
|
else if (strcasecmp(opt, "windowsize") == 0 && !transfer->netascii)
|
||||||
|
{
|
||||||
|
/* windowsize option only supported for binary transfers. */
|
||||||
|
if (val < 1)
|
||||||
|
val = 1;
|
||||||
|
if (val > TFTP_MAX_WINDOW)
|
||||||
|
val = TFTP_MAX_WINDOW;
|
||||||
|
transfer->windowsize = val;
|
||||||
|
transfer->opt_windowsize = 1;
|
||||||
|
transfer->block = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
|
/* cope with backslashes from windows boxen. */
|
||||||
if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
|
for (p = filename; *p; p++)
|
||||||
macaddr = macbuf;
|
if (*p == '\\')
|
||||||
|
*p = '/';
|
||||||
|
else if (option_bool(OPT_TFTP_LC))
|
||||||
|
*p = tolower((unsigned char)*p);
|
||||||
|
|
||||||
if (macaddr)
|
strcpy(daemon->namebuff, "/");
|
||||||
{
|
if (prefix)
|
||||||
|
{
|
||||||
|
if (prefix[0] == '/')
|
||||||
|
daemon->namebuff[0] = 0;
|
||||||
|
strncat(daemon->namebuff, prefix, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||||
|
if (prefix[strlen(prefix)-1] != '/')
|
||||||
|
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||||
|
|
||||||
|
if (option_bool(OPT_TFTP_APREF_IP))
|
||||||
|
{
|
||||||
size_t oldlen = strlen(daemon->namebuff);
|
size_t oldlen = strlen(daemon->namebuff);
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
|
|
||||||
snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
|
strncat(daemon->namebuff, daemon->addrbuff, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||||
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
|
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||||
|
|
||||||
/* remove unique-directory if it doesn't exist */
|
/* remove unique-directory if it doesn't exist */
|
||||||
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||||
daemon->namebuff[oldlen] = 0;
|
daemon->namebuff[oldlen] = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Absolute pathnames OK if they match prefix */
|
if (option_bool(OPT_TFTP_APREF_MAC))
|
||||||
if (filename[0] == '/')
|
{
|
||||||
|
unsigned char *macaddr = NULL;
|
||||||
|
unsigned char macbuf[DHCP_CHADDR_MAX];
|
||||||
|
|
||||||
|
#ifdef HAVE_DHCP
|
||||||
|
if (daemon->dhcp && peer.sa.sa_family == AF_INET)
|
||||||
|
{
|
||||||
|
/* Check if the client IP is in our lease database */
|
||||||
|
struct dhcp_lease *lease = lease_find_by_addr(peer.in.sin_addr);
|
||||||
|
if (lease && lease->hwaddr_type == ARPHRD_ETHER && lease->hwaddr_len == ETHER_ADDR_LEN)
|
||||||
|
macaddr = lease->hwaddr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If no luck, try to find in ARP table. This only works if client is in same (V)LAN */
|
||||||
|
if (!macaddr && find_mac(&peer, macbuf, 1, now) > 0)
|
||||||
|
macaddr = macbuf;
|
||||||
|
|
||||||
|
if (macaddr)
|
||||||
|
{
|
||||||
|
size_t oldlen = strlen(daemon->namebuff);
|
||||||
|
struct stat statbuf;
|
||||||
|
|
||||||
|
snprintf(daemon->namebuff + oldlen, (MAXDNAME-1) - oldlen, "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x/",
|
||||||
|
macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]);
|
||||||
|
|
||||||
|
/* remove unique-directory if it doesn't exist */
|
||||||
|
if (stat(daemon->namebuff, &statbuf) == -1 || !S_ISDIR(statbuf.st_mode))
|
||||||
|
daemon->namebuff[oldlen] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Absolute pathnames OK if they match prefix */
|
||||||
|
if (filename[0] == '/')
|
||||||
|
{
|
||||||
|
if (strstr(filename, daemon->namebuff) == filename)
|
||||||
|
daemon->namebuff[0] = 0;
|
||||||
|
else
|
||||||
|
filename++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (filename[0] == '/')
|
||||||
|
daemon->namebuff[0] = 0;
|
||||||
|
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
|
||||||
|
|
||||||
|
/* check permissions and open file */
|
||||||
|
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
|
||||||
{
|
{
|
||||||
if (strstr(filename, daemon->namebuff) == filename)
|
transfer->lastack = transfer->block;
|
||||||
daemon->namebuff[0] = 0;
|
transfer->retransmit = now + transfer->timeout;
|
||||||
|
/* This packet is may be the first data packet, but only if windowsize == 1
|
||||||
|
To get windowsize greater then one requires an option negotiation,
|
||||||
|
in which case this packet is the OACK. */
|
||||||
|
if ((len = get_block(packet, transfer)) == -1)
|
||||||
|
len = tftp_err_oops(packet, daemon->namebuff);
|
||||||
else
|
else
|
||||||
filename++;
|
is_err = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (filename[0] == '/')
|
|
||||||
daemon->namebuff[0] = 0;
|
|
||||||
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff));
|
|
||||||
|
|
||||||
/* check permissions and open file */
|
|
||||||
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
|
|
||||||
{
|
|
||||||
if ((len = get_block(packet, transfer)) == -1)
|
|
||||||
len = tftp_err_oops(packet, daemon->namebuff);
|
|
||||||
else
|
|
||||||
is_err = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
|
if (len)
|
||||||
|
{
|
||||||
|
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index);
|
||||||
|
|
||||||
#ifdef HAVE_DUMPFILE
|
#ifdef HAVE_DUMPFILE
|
||||||
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
dump_packet_udp(DUMP_TFTP, (void *)packet, len, NULL, (union mysockaddr *)&peer, transfer->sockfd);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
if (is_err)
|
if (is_err)
|
||||||
free_transfer(transfer);
|
free_transfer(transfer);
|
||||||
@@ -610,6 +637,10 @@ void check_tftp_listeners(time_t now)
|
|||||||
|
|
||||||
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
|
if ((len = recvfrom(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0, &peer.sa, &addr_len)) > 0)
|
||||||
{
|
{
|
||||||
|
#ifdef HAVE_DUMPFILE
|
||||||
|
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, (union mysockaddr *)&peer, NULL, transfer->sockfd);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (sockaddr_isequal(&peer, &transfer->peer))
|
if (sockaddr_isequal(&peer, &transfer->peer))
|
||||||
handle_tftp(now, transfer, len);
|
handle_tftp(now, transfer, len);
|
||||||
else
|
else
|
||||||
@@ -628,63 +659,91 @@ void check_tftp_listeners(time_t now)
|
|||||||
|
|
||||||
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
|
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
|
||||||
{
|
{
|
||||||
|
int endcon = 0, error = 0, timeout = 0;
|
||||||
|
|
||||||
tmp = transfer->next;
|
tmp = transfer->next;
|
||||||
|
|
||||||
if (difftime(now, transfer->timeout) >= 0.0)
|
/* ->start set to zero in handle_tftp() when we recv an error packet. */
|
||||||
|
if (transfer->start == 0)
|
||||||
|
endcon = error = 1;
|
||||||
|
else if (difftime(now, transfer->start) > TFTP_TRANSFER_TIME)
|
||||||
{
|
{
|
||||||
int endcon = 0;
|
endcon = 1;
|
||||||
|
/* don't complain about timeout when we're awaiting the last
|
||||||
|
ACK, some clients never send it */
|
||||||
|
if (get_block(daemon->packet, transfer) > 0)
|
||||||
|
error = timeout = 1;
|
||||||
|
}
|
||||||
|
else if (difftime(now, transfer->retransmit) >= 0.0)
|
||||||
|
{
|
||||||
|
/* Do transmission or re-transmission. When we get an ACK, the call to handle_tftp()
|
||||||
|
bumps transfer->lastack and trips the retransmit timer so that we send the next block(s)
|
||||||
|
here. */
|
||||||
|
unsigned int i, winsize;
|
||||||
ssize_t len;
|
ssize_t len;
|
||||||
|
|
||||||
/* timeout, retransmit */
|
transfer->retransmit += transfer->timeout + (1<<(transfer->backoff/2));
|
||||||
transfer->timeout += 1 + (1<<(transfer->backoff/2));
|
transfer->backoff++;
|
||||||
|
transfer->block = transfer->lastack;
|
||||||
|
|
||||||
/* we overwrote the buffer... */
|
if ((len = get_block(daemon->packet, transfer)) == 0)
|
||||||
daemon->srv_save = NULL;
|
endcon = 1; /* got last ACK */
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* send a window'a worth of blocks unless we're retransmitting OACK */
|
||||||
|
winsize = transfer->block ? transfer->windowsize : 1;
|
||||||
|
|
||||||
if ((len = get_block(daemon->packet, transfer)) == -1)
|
/* we overwrote the buffer... */
|
||||||
{
|
daemon->srv_save = NULL;
|
||||||
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
|
||||||
endcon = 1;
|
|
||||||
}
|
|
||||||
else if (++transfer->backoff > 7)
|
|
||||||
{
|
|
||||||
/* don't complain about timeout when we're awaiting the last
|
|
||||||
ACK, some clients never send it */
|
|
||||||
if ((unsigned)len == transfer->blocksize + 4)
|
|
||||||
endcon = 1;
|
|
||||||
len = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (len != 0)
|
for (i = 0; i < winsize && !endcon; i++, transfer->block++)
|
||||||
{
|
|
||||||
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
|
||||||
&transfer->peer, &transfer->source, transfer->if_index);
|
|
||||||
#ifdef HAVE_DUMPFILE
|
|
||||||
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (endcon || len == 0)
|
|
||||||
{
|
|
||||||
strcpy(daemon->namebuff, transfer->file->filename);
|
|
||||||
sanitise(daemon->namebuff);
|
|
||||||
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
|
||||||
my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
|
|
||||||
/* unlink */
|
|
||||||
*up = tmp;
|
|
||||||
if (endcon)
|
|
||||||
free_transfer(transfer);
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
/* put on queue to be sent to script and deleted */
|
if (i != 0)
|
||||||
transfer->next = daemon->tftp_done_trans;
|
len = get_block(daemon->packet, transfer);
|
||||||
daemon->tftp_done_trans = transfer;
|
|
||||||
|
if (len == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (len == -1)
|
||||||
|
{
|
||||||
|
len = tftp_err_oops(daemon->packet, transfer->file->filename);
|
||||||
|
endcon = error = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len,
|
||||||
|
&transfer->peer, &transfer->source, transfer->if_index);
|
||||||
|
#ifdef HAVE_DUMPFILE
|
||||||
|
dump_packet_udp(DUMP_TFTP, (void *)daemon->packet, len, NULL, (union mysockaddr *)&transfer->peer, transfer->sockfd);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
up = &transfer->next;
|
if (endcon)
|
||||||
|
{
|
||||||
|
strcpy(daemon->namebuff, transfer->file->filename);
|
||||||
|
sanitise(daemon->namebuff);
|
||||||
|
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff);
|
||||||
|
if (timeout)
|
||||||
|
my_syslog(MS_TFTP | LOG_ERR, _("timeout sending %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||||
|
else if (error)
|
||||||
|
my_syslog(MS_TFTP | LOG_ERR, _("failed sending %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||||
|
else
|
||||||
|
my_syslog(MS_TFTP | LOG_INFO, _("sent %s to %s"), daemon->namebuff, daemon->addrbuff);
|
||||||
|
|
||||||
|
/* unlink */
|
||||||
|
*up = tmp;
|
||||||
|
if (error)
|
||||||
|
free_transfer(transfer);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* put on queue to be sent to script and deleted */
|
||||||
|
transfer->next = daemon->tftp_done_trans;
|
||||||
|
daemon->tftp_done_trans = transfer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
up = &transfer->next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -697,13 +756,30 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
|||||||
|
|
||||||
if (len >= (ssize_t)sizeof(struct ack))
|
if (len >= (ssize_t)sizeof(struct ack))
|
||||||
{
|
{
|
||||||
if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block)
|
if (ntohs(mess->op) == OP_ACK)
|
||||||
{
|
{
|
||||||
/* Got ack, ensure we take the (re)transmit path */
|
/* try and handle 16-bit blockno wrap-around */
|
||||||
transfer->timeout = now;
|
unsigned int block = (unsigned short)ntohs(mess->block);
|
||||||
transfer->backoff = 0;
|
if (block < transfer->lastack)
|
||||||
if (transfer->block++ != 0)
|
block |= transfer->block & 0xffff0000;
|
||||||
transfer->offset += transfer->blocksize - transfer->expansion;
|
|
||||||
|
/* ignore duplicate ACKs and ACKs for blocks we've not yet sent. */
|
||||||
|
if (block >= transfer->lastack &&
|
||||||
|
block <= transfer->block)
|
||||||
|
{
|
||||||
|
/* Got ack, move forward and ensure we take the (re)transmit path */
|
||||||
|
transfer->retransmit = transfer->start = now;
|
||||||
|
transfer->backoff = 0;
|
||||||
|
transfer->lastack = block + 1;
|
||||||
|
|
||||||
|
/* We have no easy function from block no. to file offset when
|
||||||
|
expanding line breaks in netascii mode, so we update the offset here
|
||||||
|
as each block is acknowledged. This explains why the window size must be
|
||||||
|
one for a netascii transfer; to avoid the block no. doing anything
|
||||||
|
other than incrementing by one. */
|
||||||
|
if (transfer->netascii && block != 0)
|
||||||
|
transfer->offset += transfer->blocksize - transfer->expansion;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (ntohs(mess->op) == OP_ERR)
|
else if (ntohs(mess->op) == OP_ERR)
|
||||||
{
|
{
|
||||||
@@ -724,8 +800,7 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
|
|||||||
daemon->addrbuff);
|
daemon->addrbuff);
|
||||||
|
|
||||||
/* Got err, ensure we take abort */
|
/* Got err, ensure we take abort */
|
||||||
transfer->timeout = now;
|
transfer->start = 0;
|
||||||
transfer->backoff = 100;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -835,6 +910,16 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
|||||||
p += (sprintf(p,"tsize") + 1);
|
p += (sprintf(p,"tsize") + 1);
|
||||||
p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
|
p += (sprintf(p, "%u", (unsigned int)transfer->file->size) + 1);
|
||||||
}
|
}
|
||||||
|
if (transfer->opt_timeout)
|
||||||
|
{
|
||||||
|
p += (sprintf(p,"timeout") + 1);
|
||||||
|
p += (sprintf(p, "%u", transfer->timeout) + 1);
|
||||||
|
}
|
||||||
|
if (transfer->opt_windowsize)
|
||||||
|
{
|
||||||
|
p += (sprintf(p,"windowsize") + 1);
|
||||||
|
p += (sprintf(p, "%u", (unsigned int)transfer->windowsize) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
return p - packet;
|
return p - packet;
|
||||||
}
|
}
|
||||||
@@ -846,31 +931,35 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
|||||||
unsigned char data[];
|
unsigned char data[];
|
||||||
} *mess = (struct datamess *)packet;
|
} *mess = (struct datamess *)packet;
|
||||||
|
|
||||||
size_t size = transfer->file->size - transfer->offset;
|
size_t size;
|
||||||
|
|
||||||
|
if (!transfer->netascii)
|
||||||
|
transfer->offset = (transfer->block - 1) * transfer->blocksize;
|
||||||
|
|
||||||
if (transfer->offset > transfer->file->size)
|
if (transfer->offset > transfer->file->size)
|
||||||
return 0; /* finished */
|
return 0; /* finished */
|
||||||
|
|
||||||
if (size > transfer->blocksize)
|
if ((size = transfer->file->size - transfer->offset) > transfer->blocksize)
|
||||||
size = transfer->blocksize;
|
size = transfer->blocksize;
|
||||||
|
|
||||||
mess->op = htons(OP_DATA);
|
mess->op = htons(OP_DATA);
|
||||||
mess->block = htons((unsigned short)(transfer->block));
|
mess->block = htons((unsigned short)(transfer->block));
|
||||||
|
|
||||||
if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
|
if (size != 0 &&
|
||||||
!read_write(transfer->file->fd, mess->data, size, RW_READ))
|
(lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
|
||||||
|
!read_write(transfer->file->fd, mess->data, size, RW_READ)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
transfer->expansion = 0;
|
|
||||||
|
|
||||||
/* Map '\n' to CR-LF in netascii mode */
|
/* Map '\n' to CR-LF in netascii mode */
|
||||||
if (transfer->netascii)
|
if (transfer->netascii)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
int newcarrylf;
|
int newcarrylf;
|
||||||
|
|
||||||
|
transfer->expansion = 0;
|
||||||
|
|
||||||
for (i = 0, newcarrylf = 0; i < size; i++)
|
for (i = 0, newcarrylf = 0; i < size; i++)
|
||||||
if (mess->data[i] == '\n' && ( i != 0 || !transfer->carrylf))
|
if (mess->data[i] == '\n' && (i != 0 || !transfer->carrylf))
|
||||||
{
|
{
|
||||||
transfer->expansion++;
|
transfer->expansion++;
|
||||||
|
|
||||||
@@ -885,8 +974,8 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
|
|||||||
|
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
transfer->carrylf = newcarrylf;
|
|
||||||
|
|
||||||
|
transfer->carrylf = newcarrylf;
|
||||||
}
|
}
|
||||||
|
|
||||||
return size + 4;
|
return size + 4;
|
||||||
|
|||||||
Reference in New Issue
Block a user