|
version 1.1.1.2, 2014/06/15 16:31:38
|
version 1.1.1.4, 2021/03/17 00:56:46
|
|
Line 1
|
Line 1
|
| /* dnsmasq is Copyright (c) 2000-2014 Simon Kelley | /* dnsmasq is Copyright (c) 2000-2021 Simon Kelley |
| |
|
| This program is free software; you can redistribute it and/or modify |
This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
it under the terms of the GNU General Public License as published by |
|
Line 18
|
Line 18
|
| |
|
| #ifdef HAVE_TFTP |
#ifdef HAVE_TFTP |
| |
|
| |
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len); |
| static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix); |
static struct tftp_file *check_tftp_fileperm(ssize_t *len, char *prefix); |
| static void free_transfer(struct tftp_transfer *transfer); |
static void free_transfer(struct tftp_transfer *transfer); |
| static ssize_t tftp_err(int err, char *packet, char *mess, char *file); | static ssize_t tftp_err(int err, char *packet, char *message, char *file); |
| static ssize_t tftp_err_oops(char *packet, char *file); |
static ssize_t tftp_err_oops(char *packet, char *file); |
| static ssize_t get_block(char *packet, struct tftp_transfer *transfer); |
static ssize_t get_block(char *packet, struct tftp_transfer *transfer); |
| static char *next(char **p, char *end); |
static char *next(char **p, char *end); |
|
Line 50 void tftp_request(struct listener *listen, time_t now)
|
Line 51 void tftp_request(struct listener *listen, time_t now)
|
| struct ifreq ifr; |
struct ifreq ifr; |
| int is_err = 1, if_index = 0, mtu = 0; |
int is_err = 1, if_index = 0, mtu = 0; |
| struct iname *tmp; |
struct iname *tmp; |
| struct tftp_transfer *transfer; | struct tftp_transfer *transfer = NULL, **up; |
| int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */ |
int port = daemon->start_tftp_port; /* may be zero to use ephemeral port */ |
| #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
| int mtuflag = IP_PMTUDISC_DONT; |
int mtuflag = IP_PMTUDISC_DONT; |
|
Line 59 void tftp_request(struct listener *listen, time_t now)
|
Line 60 void tftp_request(struct listener *listen, time_t now)
|
| char *name = NULL; |
char *name = NULL; |
| char *prefix = daemon->tftp_prefix; |
char *prefix = daemon->tftp_prefix; |
| struct tftp_prefix *pref; |
struct tftp_prefix *pref; |
| struct all_addr addra; | union all_addr addra; |
| #ifdef HAVE_IPV6 | int family = listen->addr.sa.sa_family; |
| /* Can always get recvd interface for IPv6 */ |
/* Can always get recvd interface for IPv6 */ |
| int check_dest = !option_bool(OPT_NOWILD) || listen->family == AF_INET6; | int check_dest = !option_bool(OPT_NOWILD) || family == AF_INET6; |
| #else | |
| int check_dest = !option_bool(OPT_NOWILD); | |
| #endif | |
| union { |
union { |
| struct cmsghdr align; /* this ensures alignment */ |
struct cmsghdr align; /* this ensures alignment */ |
| #ifdef HAVE_IPV6 |
|
| char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
char control6[CMSG_SPACE(sizeof(struct in6_pktinfo))]; |
| #endif |
|
| #if defined(HAVE_LINUX_NETWORK) |
#if defined(HAVE_LINUX_NETWORK) |
| char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
char control[CMSG_SPACE(sizeof(struct in_pktinfo))]; |
| #elif defined(HAVE_SOLARIS_NETWORK) |
#elif defined(HAVE_SOLARIS_NETWORK) |
| char control[CMSG_SPACE(sizeof(unsigned int))]; | char control[CMSG_SPACE(sizeof(struct in_addr)) + |
| | CMSG_SPACE(sizeof(unsigned int))]; |
| #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
| char control[CMSG_SPACE(sizeof(struct sockaddr_dl))]; | char control[CMSG_SPACE(sizeof(struct in_addr)) + |
| | CMSG_SPACE(sizeof(struct sockaddr_dl))]; |
| #endif |
#endif |
| } control_u; |
} control_u; |
| |
|
|
Line 103 void tftp_request(struct listener *listen, time_t now)
|
Line 101 void tftp_request(struct listener *listen, time_t now)
|
| if (listen->iface) |
if (listen->iface) |
| { |
{ |
| addr = listen->iface->addr; |
addr = listen->iface->addr; |
| mtu = listen->iface->mtu; |
|
| name = listen->iface->name; |
name = listen->iface->name; |
| |
mtu = listen->iface->mtu; |
| |
if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu) |
| |
mtu = daemon->tftp_mtu; |
| } |
} |
| else |
else |
| { |
{ |
|
Line 122 void tftp_request(struct listener *listen, time_t now)
|
Line 122 void tftp_request(struct listener *listen, time_t now)
|
| if (msg.msg_controllen < sizeof(struct cmsghdr)) |
if (msg.msg_controllen < sizeof(struct cmsghdr)) |
| return; |
return; |
| |
|
| addr.sa.sa_family = listen->family; | addr.sa.sa_family = family; |
| |
|
| #if defined(HAVE_LINUX_NETWORK) |
#if defined(HAVE_LINUX_NETWORK) |
| if (listen->family == AF_INET) | if (family == AF_INET) |
| for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
| if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) |
if (cmptr->cmsg_level == IPPROTO_IP && cmptr->cmsg_type == IP_PKTINFO) |
| { |
{ |
|
Line 139 void tftp_request(struct listener *listen, time_t now)
|
Line 139 void tftp_request(struct listener *listen, time_t now)
|
| } |
} |
| |
|
| #elif defined(HAVE_SOLARIS_NETWORK) |
#elif defined(HAVE_SOLARIS_NETWORK) |
| if (listen->family == AF_INET) | if (family == AF_INET) |
| for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
| { |
{ |
| union { |
union { |
|
Line 155 void tftp_request(struct listener *listen, time_t now)
|
Line 155 void tftp_request(struct listener *listen, time_t now)
|
| } |
} |
| |
|
| #elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
#elif defined(IP_RECVDSTADDR) && defined(IP_RECVIF) |
| if (listen->family == AF_INET) | if (family == AF_INET) |
| for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
| { |
{ |
| union { |
union { |
|
Line 172 void tftp_request(struct listener *listen, time_t now)
|
Line 172 void tftp_request(struct listener *listen, time_t now)
|
| |
|
| #endif |
#endif |
| |
|
| #ifdef HAVE_IPV6 | if (family == AF_INET6) |
| if (listen->family == AF_INET6) | |
| { |
{ |
| for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr)) |
| if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) |
if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo) |
|
Line 188 void tftp_request(struct listener *listen, time_t now)
|
Line 187 void tftp_request(struct listener *listen, time_t now)
|
| if_index = p.p->ipi6_ifindex; |
if_index = p.p->ipi6_ifindex; |
| } |
} |
| } |
} |
| #endif |
|
| |
|
| if (!indextoname(listen->tftpfd, if_index, namebuff)) |
if (!indextoname(listen->tftpfd, if_index, namebuff)) |
| return; |
return; |
| |
|
| name = namebuff; |
name = namebuff; |
| |
|
| addra.addr.addr4 = addr.in.sin_addr; | addra.addr4 = addr.in.sin_addr; |
| |
|
| #ifdef HAVE_IPV6 | if (family == AF_INET6) |
| if (listen->family == AF_INET6) | addra.addr6 = addr.in6.sin6_addr; |
| addra.addr.addr6 = addr.in6.sin6_addr; | |
| #endif | |
| |
|
| if (daemon->tftp_interfaces) |
if (daemon->tftp_interfaces) |
| { |
{ |
|
Line 215 void tftp_request(struct listener *listen, time_t now)
|
Line 211 void tftp_request(struct listener *listen, time_t now)
|
| else |
else |
| { |
{ |
| /* Do the same as DHCP */ |
/* Do the same as DHCP */ |
| if (!iface_check(listen->family, &addra, name, NULL)) | if (!iface_check(family, &addra, name, NULL)) |
| { |
{ |
| if (!option_bool(OPT_CLEVERBIND)) |
if (!option_bool(OPT_CLEVERBIND)) |
| enumerate_interfaces(0); |
enumerate_interfaces(0); |
| if (!loopback_exception(listen->tftpfd, listen->family, &addra, name) && | if (!loopback_exception(listen->tftpfd, family, &addra, name) && |
| !label_exception(if_index, listen->family, &addra) ) | !label_exception(if_index, family, &addra)) |
| return; |
return; |
| } |
} |
| |
|
|
Line 232 void tftp_request(struct listener *listen, time_t now)
|
Line 228 void tftp_request(struct listener *listen, time_t now)
|
| #endif |
#endif |
| } |
} |
| |
|
| strncpy(ifr.ifr_name, name, IF_NAMESIZE); | safe_strncpy(ifr.ifr_name, name, IF_NAMESIZE); |
| if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) |
if (ioctl(listen->tftpfd, SIOCGIFMTU, &ifr) != -1) |
| mtu = ifr.ifr_mtu; | { |
| | mtu = ifr.ifr_mtu; |
| | if (daemon->tftp_mtu != 0 && daemon->tftp_mtu < mtu) |
| | mtu = daemon->tftp_mtu; |
| | } |
| } |
} |
| |
|
| |
/* Failed to get interface mtu - can use configured value. */ |
| |
if (mtu == 0) |
| |
mtu = daemon->tftp_mtu; |
| |
|
| |
/* data transfer via server listening socket */ |
| |
if (option_bool(OPT_SINGLE_PORT)) |
| |
{ |
| |
int tftp_cnt; |
| |
|
| |
for (tftp_cnt = 0, transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; up = &transfer->next, transfer = transfer->next) |
| |
{ |
| |
tftp_cnt++; |
| |
|
| |
if (sockaddr_isequal(&peer, &transfer->peer)) |
| |
{ |
| |
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) |
if (name) |
| { |
{ |
|
Line 245 void tftp_request(struct listener *listen, time_t now)
|
Line 282 void tftp_request(struct listener *listen, time_t now)
|
| prefix = pref->prefix; |
prefix = pref->prefix; |
| } |
} |
| |
|
| if (listen->family == AF_INET) | if (family == AF_INET) |
| { |
{ |
| addr.in.sin_port = htons(port); |
addr.in.sin_port = htons(port); |
| #ifdef HAVE_SOCKADDR_SA_LEN |
#ifdef HAVE_SOCKADDR_SA_LEN |
| addr.in.sin_len = sizeof(addr.in); |
addr.in.sin_len = sizeof(addr.in); |
| #endif |
#endif |
| } |
} |
| #ifdef HAVE_IPV6 |
|
| else |
else |
| { |
{ |
| addr.in6.sin6_port = htons(port); |
addr.in6.sin6_port = htons(port); |
|
Line 262 void tftp_request(struct listener *listen, time_t now)
|
Line 298 void tftp_request(struct listener *listen, time_t now)
|
| addr.in6.sin6_len = sizeof(addr.in6); |
addr.in6.sin6_len = sizeof(addr.in6); |
| #endif |
#endif |
| } |
} |
| #endif |
|
| |
|
| if (!(transfer = whine_malloc(sizeof(struct tftp_transfer)))) | /* May reuse struct transfer from abandoned transfer in single port mode. */ |
| | if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer)))) |
| return; |
return; |
| |
|
| if ((transfer->sockfd = socket(listen->family, SOCK_DGRAM, 0)) == -1) | if (option_bool(OPT_SINGLE_PORT)) |
| | transfer->sockfd = listen->tftpfd; |
| | else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1) |
| { |
{ |
| free(transfer); |
free(transfer); |
| return; |
return; |
| } |
} |
| |
|
| transfer->peer = peer; |
transfer->peer = peer; |
| |
transfer->source = addra; |
| |
transfer->if_index = if_index; |
| transfer->timeout = now + 2; |
transfer->timeout = now + 2; |
| transfer->backoff = 1; |
transfer->backoff = 1; |
| transfer->block = 1; |
transfer->block = 1; |
|
Line 283 void tftp_request(struct listener *listen, time_t now)
|
Line 323 void tftp_request(struct listener *listen, time_t now)
|
| transfer->opt_blocksize = transfer->opt_transize = 0; |
transfer->opt_blocksize = transfer->opt_transize = 0; |
| transfer->netascii = transfer->carrylf = 0; |
transfer->netascii = transfer->carrylf = 0; |
| |
|
| prettyprint_addr(&peer, daemon->addrbuff); | (void)prettyprint_addr(&peer, daemon->addrbuff); |
| |
|
| /* if we have a nailed-down range, iterate until we find a free one. */ |
/* if we have a nailed-down range, iterate until we find a free one. */ |
| while (1) | while (!option_bool(OPT_SINGLE_PORT)) |
| { |
{ |
| if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 || |
if (bind(transfer->sockfd, &addr.sa, sa_len(&addr)) == -1 || |
| #if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT) |
|
Line 298 void tftp_request(struct listener *listen, time_t now)
|
Line 338 void tftp_request(struct listener *listen, time_t now)
|
| { |
{ |
| if (++port <= daemon->end_tftp_port) |
if (++port <= daemon->end_tftp_port) |
| { |
{ |
| if (listen->family == AF_INET) | if (family == AF_INET) |
| addr.in.sin_port = htons(port); |
addr.in.sin_port = htons(port); |
| #ifdef HAVE_IPV6 |
|
| else |
else |
| addr.in6.sin6_port = htons(port); | addr.in6.sin6_port = htons(port); |
| #endif | |
| continue; |
continue; |
| } |
} |
| my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP")); |
my_syslog(MS_TFTP | LOG_ERR, _("unable to get free port for TFTP")); |
|
Line 316 void tftp_request(struct listener *listen, time_t now)
|
Line 355 void tftp_request(struct listener *listen, time_t now)
|
| |
|
| p = packet + 2; |
p = packet + 2; |
| end = packet + len; |
end = packet + len; |
| |
| if (ntohs(*((unsigned short *)packet)) != OP_RRQ || |
if (ntohs(*((unsigned short *)packet)) != OP_RRQ || |
| !(filename = next(&p, end)) || |
!(filename = next(&p, end)) || |
| !(mode = next(&p, end)) || |
!(mode = next(&p, end)) || |
|
Line 336 void tftp_request(struct listener *listen, time_t now)
|
Line 375 void tftp_request(struct listener *listen, time_t now)
|
| { |
{ |
| if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK)) |
if ((opt = next(&p, end)) && !option_bool(OPT_TFTP_NOBLOCK)) |
| { |
{ |
| |
/* 32 bytes for IP, UDP and TFTP headers, 52 bytes for IPv6 */ |
| |
int overhead = (family == AF_INET) ? 32 : 52; |
| transfer->blocksize = atoi(opt); |
transfer->blocksize = atoi(opt); |
| if (transfer->blocksize < 1) |
if (transfer->blocksize < 1) |
| transfer->blocksize = 1; |
transfer->blocksize = 1; |
| if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4) |
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4) |
| transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4; |
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4; |
| /* 32 bytes for IP, UDP and TFTP headers */ | if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead) |
| if (mtu != 0 && transfer->blocksize > (unsigned)mtu - 32) | transfer->blocksize = (unsigned)mtu - overhead; |
| transfer->blocksize = (unsigned)mtu - 32; | |
| transfer->opt_blocksize = 1; |
transfer->opt_blocksize = 1; |
| transfer->block = 0; |
transfer->block = 0; |
| } |
} |
|
Line 371 void tftp_request(struct listener *listen, time_t now)
|
Line 411 void tftp_request(struct listener *listen, time_t now)
|
| if (prefix[strlen(prefix)-1] != '/') |
if (prefix[strlen(prefix)-1] != '/') |
| strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); |
strncat(daemon->namebuff, "/", (MAXDNAME-1) - strlen(daemon->namebuff)); |
| |
|
| if (option_bool(OPT_TFTP_APREF)) | 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; |
|
Line 383 void tftp_request(struct listener *listen, time_t now)
|
Line 423 void tftp_request(struct listener *listen, time_t now)
|
| 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; |
| } |
} |
| | |
| | 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 |
| | |
| | /* 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 */ |
/* Absolute pathnames OK if they match prefix */ |
| if (filename[0] == '/') |
if (filename[0] == '/') |
| { |
{ |
|
Line 396 void tftp_request(struct listener *listen, time_t now)
|
Line 469 void tftp_request(struct listener *listen, time_t now)
|
| else if (filename[0] == '/') |
else if (filename[0] == '/') |
| daemon->namebuff[0] = 0; |
daemon->namebuff[0] = 0; |
| strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); |
strncat(daemon->namebuff, filename, (MAXDNAME-1) - strlen(daemon->namebuff)); |
| |
| /* check permissions and open file */ |
/* check permissions and open file */ |
| if ((transfer->file = check_tftp_fileperm(&len, prefix))) |
if ((transfer->file = check_tftp_fileperm(&len, prefix))) |
| { |
{ |
|
Line 406 void tftp_request(struct listener *listen, time_t now)
|
Line 479 void tftp_request(struct listener *listen, time_t now)
|
| is_err = 0; |
is_err = 0; |
| } |
} |
| } |
} |
| |
|
| |
send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), packet, len, &peer, &addra, if_index); |
| |
|
| while (sendto(transfer->sockfd, packet, len, 0, |
|
| (struct sockaddr *)&peer, sa_len(&peer)) == -1 && errno == EINTR); |
|
| |
|
| if (is_err) |
if (is_err) |
| free_transfer(transfer); |
free_transfer(transfer); |
| else |
else |
|
Line 459 static struct tftp_file *check_tftp_fileperm(ssize_t *
|
Line 531 static struct tftp_file *check_tftp_fileperm(ssize_t *
|
| else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid) |
else if (option_bool(OPT_TFTP_SECURE) && uid != statbuf.st_uid) |
| goto perm; |
goto perm; |
| |
|
| /* If we're doing many tranfers from the same file, only | /* If we're doing many transfers from the same file, only |
| open it once this saves lots of file descriptors |
open it once this saves lots of file descriptors |
| when mass-booting a big cluster, for instance. |
when mass-booting a big cluster, for instance. |
| Be conservative and only share when inode and name match |
Be conservative and only share when inode and name match |
|
Line 502 static struct tftp_file *check_tftp_fileperm(ssize_t *
|
Line 574 static struct tftp_file *check_tftp_fileperm(ssize_t *
|
| return NULL; |
return NULL; |
| } |
} |
| |
|
| void check_tftp_listeners(fd_set *rset, time_t now) | void check_tftp_listeners(time_t now) |
| { |
{ |
| struct tftp_transfer *transfer, *tmp, **up; |
struct tftp_transfer *transfer, *tmp, **up; |
| ssize_t len; |
|
| |
|
| struct ack { | /* In single port mode, all packets come via port 69 and tftp_request() */ |
| unsigned short op, block; | if (!option_bool(OPT_SINGLE_PORT)) |
| } *mess = (struct ack *)daemon->packet; | for (transfer = daemon->tftp_trans; transfer; transfer = transfer->next) |
| | if (poll_check(transfer->sockfd, POLLIN)) |
| /* Check for activity on any existing transfers */ | |
| for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) | |
| { | |
| tmp = transfer->next; | |
| | |
| prettyprint_addr(&transfer->peer, daemon->addrbuff); | |
| | |
| if (FD_ISSET(transfer->sockfd, rset)) | |
| { |
{ |
| /* we overwrote the buffer... */ |
/* we overwrote the buffer... */ |
| daemon->srv_save = NULL; |
daemon->srv_save = NULL; |
| | handle_tftp(now, transfer, recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)); |
| if ((len = recv(transfer->sockfd, daemon->packet, daemon->packet_buff_sz, 0)) >= (ssize_t)sizeof(struct ack)) | |
| { | |
| if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) | |
| { | |
| /* Got ack, ensure we take the (re)transmit path */ | |
| transfer->timeout = now; | |
| transfer->backoff = 0; | |
| if (transfer->block++ != 0) | |
| transfer->offset += transfer->blocksize - transfer->expansion; | |
| } | |
| else if (ntohs(mess->op) == OP_ERR) | |
| { | |
| char *p = daemon->packet + sizeof(struct ack); | |
| char *end = daemon->packet + len; | |
| char *err = next(&p, end); | |
| | |
| /* Sanitise error message */ | |
| if (!err) | |
| err = ""; | |
| else | |
| sanitise(err); | |
| | |
| my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"), | |
| (int)ntohs(mess->block), err, | |
| daemon->addrbuff); | |
| | |
| /* Got err, ensure we take abort */ | |
| transfer->timeout = now; | |
| transfer->backoff = 100; | |
| } | |
| } | |
| } |
} |
| |
|
| |
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp) |
| |
{ |
| |
tmp = transfer->next; |
| |
|
| if (difftime(now, transfer->timeout) >= 0.0) |
if (difftime(now, transfer->timeout) >= 0.0) |
| { |
{ |
| int endcon = 0; |
int endcon = 0; |
| |
ssize_t len; |
| |
|
| /* timeout, retransmit */ |
/* timeout, retransmit */ |
| transfer->timeout += 1 + (1<<transfer->backoff); | transfer->timeout += 1 + (1<<(transfer->backoff/2)); |
| |
|
| /* we overwrote the buffer... */ |
/* we overwrote the buffer... */ |
| daemon->srv_save = NULL; |
daemon->srv_save = NULL; |
|
Line 571 void check_tftp_listeners(fd_set *rset, time_t now)
|
Line 608 void check_tftp_listeners(fd_set *rset, time_t now)
|
| len = tftp_err_oops(daemon->packet, transfer->file->filename); |
len = tftp_err_oops(daemon->packet, transfer->file->filename); |
| endcon = 1; |
endcon = 1; |
| } |
} |
| /* don't complain about timeout when we're awaiting the last | else if (++transfer->backoff > 7) |
| ACK, some clients never send it */ | |
| else if (++transfer->backoff > 7 && len != 0) | |
| { |
{ |
| endcon = 1; | /* 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; |
len = 0; |
| } |
} |
| |
|
| if (len != 0) |
if (len != 0) |
| while(sendto(transfer->sockfd, daemon->packet, len, 0, | send_from(transfer->sockfd, !option_bool(OPT_SINGLE_PORT), daemon->packet, len, |
| (struct sockaddr *)&transfer->peer, sa_len(&transfer->peer)) == -1 && errno == EINTR); | &transfer->peer, &transfer->source, transfer->if_index); |
| | |
| if (endcon || len == 0) |
if (endcon || len == 0) |
| { |
{ |
| strcpy(daemon->namebuff, transfer->file->filename); |
strcpy(daemon->namebuff, transfer->file->filename); |
| sanitise(daemon->namebuff); |
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); |
my_syslog(MS_TFTP | LOG_INFO, endcon ? _("failed sending %s to %s") : _("sent %s to %s"), daemon->namebuff, daemon->addrbuff); |
| /* unlink */ |
/* unlink */ |
| *up = tmp; |
*up = tmp; |
|
Line 605 void check_tftp_listeners(fd_set *rset, time_t now)
|
Line 644 void check_tftp_listeners(fd_set *rset, time_t now)
|
| up = &transfer->next; |
up = &transfer->next; |
| } |
} |
| } |
} |
| |
|
| |
/* packet in daemon->packet as this is called. */ |
| |
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len) |
| |
{ |
| |
struct ack { |
| |
unsigned short op, block; |
| |
} *mess = (struct ack *)daemon->packet; |
| |
|
| |
if (len >= (ssize_t)sizeof(struct ack)) |
| |
{ |
| |
if (ntohs(mess->op) == OP_ACK && ntohs(mess->block) == (unsigned short)transfer->block) |
| |
{ |
| |
/* Got ack, ensure we take the (re)transmit path */ |
| |
transfer->timeout = now; |
| |
transfer->backoff = 0; |
| |
if (transfer->block++ != 0) |
| |
transfer->offset += transfer->blocksize - transfer->expansion; |
| |
} |
| |
else if (ntohs(mess->op) == OP_ERR) |
| |
{ |
| |
char *p = daemon->packet + sizeof(struct ack); |
| |
char *end = daemon->packet + len; |
| |
char *err = next(&p, end); |
| |
|
| |
(void)prettyprint_addr(&transfer->peer, daemon->addrbuff); |
| |
|
| |
/* Sanitise error message */ |
| |
if (!err) |
| |
err = ""; |
| |
else |
| |
sanitise(err); |
| |
|
| |
my_syslog(MS_TFTP | LOG_ERR, _("error %d %s received from %s"), |
| |
(int)ntohs(mess->block), err, |
| |
daemon->addrbuff); |
| |
|
| |
/* Got err, ensure we take abort */ |
| |
transfer->timeout = now; |
| |
transfer->backoff = 100; |
| |
} |
| |
} |
| |
} |
| |
|
| static void free_transfer(struct tftp_transfer *transfer) |
static void free_transfer(struct tftp_transfer *transfer) |
| { |
{ |
| close(transfer->sockfd); | if (!option_bool(OPT_SINGLE_PORT)) |
| | close(transfer->sockfd); |
| | |
| if (transfer->file && (--transfer->file->refcount) == 0) |
if (transfer->file && (--transfer->file->refcount) == 0) |
| { |
{ |
| close(transfer->file->fd); |
close(transfer->file->fd); |
| free(transfer->file); |
free(transfer->file); |
| } |
} |
| |
|
| free(transfer); |
free(transfer); |
| } |
} |
| |
|
|
Line 641 static void sanitise(char *buf)
|
Line 725 static void sanitise(char *buf)
|
| |
|
| } |
} |
| |
|
| |
#define MAXMESSAGE 500 /* limit to make packet < 512 bytes and definitely smaller than buffer */ |
| static ssize_t tftp_err(int err, char *packet, char *message, char *file) |
static ssize_t tftp_err(int err, char *packet, char *message, char *file) |
| { |
{ |
| struct errmess { |
struct errmess { |
| unsigned short op, err; |
unsigned short op, err; |
| char message[]; |
char message[]; |
| } *mess = (struct errmess *)packet; |
} *mess = (struct errmess *)packet; |
| ssize_t ret = 4; | ssize_t len, ret = 4; |
| char *errstr = strerror(errno); |
char *errstr = strerror(errno); |
| |
|
| |
memset(packet, 0, daemon->packet_buff_sz); |
| sanitise(file); |
sanitise(file); |
| |
| mess->op = htons(OP_ERR); |
mess->op = htons(OP_ERR); |
| mess->err = htons(err); |
mess->err = htons(err); |
| ret += (snprintf(mess->message, 500, message, file, errstr) + 1); | len = snprintf(mess->message, MAXMESSAGE, message, file, errstr); |
| | ret += (len < MAXMESSAGE) ? len + 1 : MAXMESSAGE; /* include terminating zero */ |
| | |
| my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message); |
my_syslog(MS_TFTP | LOG_ERR, "%s", mess->message); |
| |
|
| return ret; |
return ret; |
|
Line 670 static ssize_t tftp_err_oops(char *packet, char *file)
|
Line 758 static ssize_t tftp_err_oops(char *packet, char *file)
|
| /* return -1 for error, zero for done. */ |
/* return -1 for error, zero for done. */ |
| static ssize_t get_block(char *packet, struct tftp_transfer *transfer) |
static ssize_t get_block(char *packet, struct tftp_transfer *transfer) |
| { |
{ |
| |
memset(packet, 0, daemon->packet_buff_sz); |
| |
|
| if (transfer->block == 0) |
if (transfer->block == 0) |
| { |
{ |
| /* send OACK */ |
/* send OACK */ |
|
Line 684 static ssize_t get_block(char *packet, struct tftp_tra
|
Line 774 static ssize_t get_block(char *packet, struct tftp_tra
|
| if (transfer->opt_blocksize) |
if (transfer->opt_blocksize) |
| { |
{ |
| p += (sprintf(p, "blksize") + 1); |
p += (sprintf(p, "blksize") + 1); |
| p += (sprintf(p, "%d", transfer->blocksize) + 1); | p += (sprintf(p, "%u", transfer->blocksize) + 1); |
| } |
} |
| if (transfer->opt_transize) |
if (transfer->opt_transize) |
| { |
{ |