Compare commits

...
12 Commits
Author SHA1 Message Date
Simon Kelley 287d6bc88d Fix 5846f749e5
Not sure how that bug got in there. Very (un)lucky choice
of test data, or last-minute "it'll be fine" modification I guess.

It was late, I can't remember.

Thanks to Dominik Derrigs for spotting the problem.
2025-06-03 22:16:50 +01:00
Simon Kelley c378d2c1de Fix crash in filter_servers().
The bug occurs when we ask lookup_domain() for a server for a domain
which is not a general upstream server, by setting F_DOMAINSRV in the flags.

If there are no possible servers, because there are no upstream servers
defined (for instance, at startup) then the code steps off the end of an
array and SEGVs.

The bug has been latent for some time, but
3e659bd4ec added a new call to lookup_domain()
which can actually trigger the bug if DNSSEC is enabled and a certain
amount of bad luck ensues.

Thanks to the testers extraordinaire at PiHole for reporting this.
2025-06-02 22:37:08 +01:00
Simon Kelley 5846f749e5 Rewrite chunks of inotify.c to remove a memory leak.
The code is much easier to follow now, this shouldn't
happen again.
2025-05-29 22:44:15 +01:00
Matthias Andree c9a4240ec4 Unbreak compilation on non-Linux systems (FreeBSD)
and put the void cast of netlink_warn under the same preprocessor
condition as its definition, #ifdef HAVE_LINUX_NETWORK.
2025-05-25 21:15:41 +01:00
Simon Kelley e7b87dee85 Tftp code tweaks. 2025-05-24 21:15:02 +01:00
Simon Kelley 90b248582c Remove compiler warnings for obscure combinations of build options. 2025-05-24 15:09:53 +01:00
Simon Kelley ebef27f321 Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349). 2025-05-24 14:41:40 +01:00
Matthias Andree 1861a881eb Only define variable "a" if we HAVE_LINUX_NETWORK
Variable unsigned char a is defined unconditionally,
but it is only used if HAVE_LINUX_NETWORK is defined.
This triggers compiler warnings on, say, FreeBSD.

Fix by wrapping the definition in proper #ifdef.
2025-05-21 19:48:39 +01:00
Matthias Andree 96bdb42d40 Fix GCC's -Wunterminated-string-initialization warning in edns0.c.
GCC complains that writing the five-character "ODNS\0" string into
a four-element char magic[4] array truncates the NUL character.
The warning's rationale is that this is incompatible with C++, or
maybe non-intentional.

GCC 8 has added a nonstring variable attribute, clang 20.1 does
not yet support this, but clang's Git head does.

Add an ATTRIBUTE_NONSTRING macro, currently only defined on GCC >= 8
as __attribute__ ((nonstring)).  This successfully suppresses
the warning on Fedora Linux 42's default compiler.

The alternative would be to replace the "ODNS" literal by {0} and
instead memcpy(opt.magic, "ODNS", sizeof(opt.magic)); on the next line,
which is correct, C++ compatible, but also less concise.
2025-05-21 15:06:27 +01:00
Simon Kelley c7a909ad65 Add RRtypes HHIT and BRID to the table. 2025-05-21 15:03:51 +01:00
Simon Kelley baf3c57af5 Fix compiler warnings. 2025-05-18 18:22:48 +01:00
Simon Kelley e48a2af4f5 Overhaul extract_addresses() function.
The proximate cause for doing this is to fix a bug that
causes replies to PTR queries with more than one answer to have the
second and subsequent answers ignored.

The fix turned into a small re-write which removed a very old hack.
When caching reponses which include CNAME records, the cache system
stores the CNAME with a link to the record representing the target of
the CNAME. This isn't possible for PTR records representing IP
addresses since the name stored is the target of the PTR, record and
its name is inferred from the address in the cache record. Such
cache records have the F_REVERSE flag set. To get
around this, long ago, the code which stores such records elided the
CNAME entirely, so
4.3.2.1.in-addr.arpa   CNAME 18/3.2.1.in-addr.arpa
18/3.2.1.in-addr.arpa   PTR   myhost.example.com

would be stored as
4.3.2.1.in-addr.arpa PTR   myhost.example.com

and returned from the cache to subsequent requestor in that form.

Since that hack was committed, dnsmasq has learned to cache arbitrary
RRs. So now we can store the PTR records for all the no-trivial cases.

The means the CNAME chains ending in PTR records don't get mangled,
and we can store PTR records whose name in not w.x.y.x.in-addr.arpa
or the IPv6 equivalent.
2025-05-18 17:24:41 +01:00
13 changed files with 507 additions and 424 deletions

No files matched your search

+5
View File
@@ -56,6 +56,11 @@ version 2.92
Many thanks to JAXPORT, Jacksonville Port Authority for sponsoring
this enhancement to dnsmasq.
Fix failure to cache PTR RRs when a reply contains more than one answer.
Thanks to Dmitry for spotting this.
Add TFTP options windowsize (RFC 7440) and timeout (RFC 2349).
version 2.91
Fix spurious "resource limit exceeded messages". Thanks to
+2
View File
@@ -102,6 +102,8 @@ static const struct {
{ 64, "SVCB" }, /* Service Binding [draft-ietf-dnsop-svcb-https-00] SVCB/svcb-completed-template 2020-06-30*/
{ 65, "HTTPS" }, /* HTTPS Binding [draft-ietf-dnsop-svcb-https-00] HTTPS/https-completed-template 2020-06-30*/
{ 66, "DSYNC" }, /* Endpoint discovery for delegation synchronization [draft-ietf-dnsop-generalized-notify-03] DSYNC/dsync-completed-template 2024-12-10 */
{ 67, "HHIT" }, /* [draft-ietf-drip-registries-28] */
{ 68, "BRID" }, /* [draft-ietf-drip-registries-28] */
{ 99, "SPF" }, /* [RFC7208] */
{ 100, "UINFO" }, /* [IANA-Reserved] */
{ 101, "UID" }, /* [IANA-Reserved] */
+2
View File
@@ -52,6 +52,8 @@
#define CHUSER "nobody"
#define CHGRP "dip"
#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 RANDFILE "/dev/urandom"
#define DNSMASQ_SERVICE "uk.org.thekelleys.dnsmasq" /* Default - may be overridden by config */
+14 -3
View File
@@ -30,7 +30,9 @@ static volatile pid_t pid = 0;
static volatile int pipewrite;
static void set_dns_listeners(void);
#ifdef HAVE_TFTP
static void set_tftp_listeners(void);
#endif
static void check_dns_listeners(time_t now);
static void do_tcp_connection(struct listener *listener, time_t now, int slot);
static void sig_handler(int sig);
@@ -62,14 +64,16 @@ int main (int argc, char **argv)
int need_cap_net_raw = 0;
int need_cap_net_bind_service = 0;
int have_cap_chown = 0;
# ifdef HAVE_DHCP
char *bound_device = NULL;
int did_bind = 0;
# endif
struct server *serv;
char *netlink_warn;
#else
int bind_fallback = 0;
#endif
#if defined(HAVE_DHCP) || defined(HAVE_DHCP6)
#if defined(HAVE_DHCP)
struct dhcp_context *context;
struct dhcp_relay *relay;
#endif
@@ -77,6 +81,10 @@ int main (int argc, char **argv)
int tftp_prefix_missing = 0;
#endif
#ifdef HAVE_LINUX_NETWORK
(void)netlink_warn;
#endif
#if defined(HAVE_IDN) || defined(HAVE_LIBIDN2) || defined(LOCALEDIR)
setlocale(LC_ALL, "");
#endif
@@ -1939,11 +1947,14 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
pid_t p;
union mysockaddr tcp_addr;
socklen_t tcp_len = sizeof(union mysockaddr);
unsigned char a = 0, *buff;
unsigned char *buff;
struct server *s;
int flags, auth_dns;
struct in_addr netmask;
int pipefd[2];
#ifdef HAVE_LINUX_NETWORK
unsigned char a = 0;
#endif
while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);
@@ -2052,7 +2063,7 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
single byte comes back up the pipe, which
is sent by the child after it has closed the
netlink socket. */
read_write(pipefd[0], &a, 1, RW_READ);
#endif
+10 -6
View File
@@ -48,6 +48,12 @@
#define ATTRIBUTE_NORETURN
#endif
#if __GNUC__ + 0 >= 8 // clang 20.1.0 does not yet support this
#define ATTRIBUTE_NONSTRING __attribute__ ((nonstring))
#else
#define ATTRIBUTE_NONSTRING
#endif
/* get these before config.h for IPv6 stuff... */
#include <sys/types.h>
#include <sys/socket.h>
@@ -1111,14 +1117,13 @@ struct tftp_file {
struct tftp_transfer {
int sockfd;
time_t timeout;
int backoff;
unsigned int block, blocksize, expansion;
time_t retransmit, start;
unsigned int lastack, block, blocksize, windowsize, timeout, expansion;
off_t offset;
union mysockaddr peer;
union all_addr source;
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_transfer *next;
};
@@ -1413,7 +1418,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
unsigned short *typep, unsigned short *classp);
void setup_reply(struct dns_header *header, unsigned int flags, int ede);
int extract_addresses(struct dns_header *header, size_t qlen, char *name,
time_t now, struct ipsets *ipsets, struct ipsets *nftsets, int is_sign,
time_t now, struct ipsets *ipsets, struct ipsets *nftsets,
int check_rebind, int no_cache_dnssec, int secure);
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
void report_addresses(struct dns_header *header, size_t len, u32 mark);
@@ -1762,7 +1767,6 @@ void queue_relay_snoop(struct in6_addr *client, int if_index, struct in6_addr *p
/* tftp.c */
#ifdef HAVE_TFTP
void tftp_request(struct listener *listen, time_t now);
void check_tftp_listeners(time_t now);
int do_tftp_script_run(void);
#endif
+1 -3
View File
@@ -343,10 +343,8 @@ int filter_servers(int seed, int flags, int *lowout, int *highout)
else
{
/* If we want a server for a particular domain, and this one isn't, return nothing. */
if ((flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
if (nlow != nhigh && (flags & F_DOMAINSRV) && daemon->serverarray[nlow]->domain_len == 0)
nlow = nhigh;
else
nlow = i;
}
}
}
+1 -1
View File
@@ -494,7 +494,7 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
#define UMBRELLA_DEVICESZ sizeof(daemon->umbrella_device)
struct umbrella_opt {
u8 magic[4];
u8 magic[4] ATTRIBUTE_NONSTRING;
u8 version;
u8 flags;
/* We have 4 possible fields since we'll never send both IPv4 and
+3 -1
View File
@@ -670,6 +670,7 @@ int fast_retry(time_t now)
return ret;
}
#if defined(HAVE_IPSET) || defined(HAVE_NFTSET)
static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domain) {
/* Similar algorithm to search_servers. */
struct ipsets *ipset_pos, *ret = NULL;
@@ -690,6 +691,7 @@ static struct ipsets *domain_find_sets(struct ipsets *setlist, const char *domai
return ret;
}
#endif
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
@@ -819,7 +821,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
else
{
int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, is_sign, check_rebind, no_cache, cache_secure);
int rc = extract_addresses(header, n, daemon->namebuff, now, ipsets, nftsets, check_rebind, no_cache, cache_secure);
if (rc != 0)
{
+106 -85
View File
@@ -133,29 +133,44 @@ void inotify_dnsmasq_init()
}
}
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *path)
static struct hostsfile *dyndir_addhosts(struct dyndir *dd, char *file)
{
/* Check if this file is already known in dd->files */
struct hostsfile *ah = NULL;
for(ah = dd->files; ah; ah = ah->next)
if(ah && ah->fname && strcmp(path, ah->fname) == 0)
struct hostsfile *ah;
size_t dirlen = strlen(dd->dname);
/* ah->fname always starts with the string in dd->dname */
for (ah = dd->files; ah; ah = ah->next)
if (ah->fname[dirlen] == '/' &&
strcmp(&ah->fname[dirlen+1], file) == 0)
return ah;
/* Not known, create new hostsfile record for this dyndir */
struct hostsfile *newah = NULL;
if(!(newah = whine_malloc(sizeof(struct hostsfile))))
return NULL;
if ((ah = whine_malloc(sizeof(struct hostsfile))))
{
char *path;
/* Add this file to the tip of the linked list */
newah->next = dd->files;
dd->files = newah;
/* Copy flags, set index and the full file path */
newah->flags = dd->flags;
newah->index = daemon->host_index++;
newah->fname = path;
return newah;
if (!(path = whine_malloc(dirlen + strlen(file) + 2)))
{
free(ah);
return NULL;
}
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, file);
/* Add this file to the tip of the linked list */
ah->next = dd->files;
dd->files = ah;
/* Copy flags, set index and the full file path */
ah->flags = dd->flags;
ah->index = daemon->host_index++;
ah->fname = path;
}
return ah;
}
@@ -204,10 +219,8 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
while ((ent = readdir(dir_stream)))
{
size_t lendir = strlen(dd->dname);
size_t lenfile = strlen(ent->d_name);
char *path;
/* ignore emacs backups and dotfiles */
if (lenfile == 0 ||
ent->d_name[lenfile - 1] == '~' ||
@@ -215,33 +228,36 @@ void set_dynamic_inotify(int flag, int total_size, struct crec **rhash, int revh
ent->d_name[0] == '.')
continue;
if ((path = whine_malloc(lendir + lenfile + 2)))
if (dd->flags & AH_HOSTS)
{
struct hostsfile *ah;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, ent->d_name);
if (!(ah = dyndir_addhosts(dd, path)))
{
free(path);
continue;
}
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
{
if (dd->flags & AH_HOSTS)
total_size = read_hostsfile(path, ah->index, total_size, rhash, revhashsz);
if ((ah = dyndir_addhosts(dd, ent->d_name)) &&
stat(ah->fname, &buf) != -1 && S_ISREG(buf.st_mode))
total_size = read_hostsfile(ah->fname, ah->index, total_size, rhash, revhashsz);
}
#ifdef HAVE_DHCP
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
else if (dd->flags & (AH_DHCP_HST | AH_DHCP_OPT))
{
char *path;
if ((path = whine_malloc(strlen(dd->dname) + lenfile + 2)))
{
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, ent->d_name);
/* ignore non-regular files */
if (stat(path, &buf) != -1 && S_ISREG(buf.st_mode))
option_read_dynfile(path, dd->flags);
#endif
free(path);
}
}
#endif
}
closedir(dir_stream);
}
}
@@ -251,6 +267,8 @@ int inotify_check(time_t now)
int hit = 0;
struct dyndir *dd;
(void)now;
while (1)
{
int rc;
@@ -283,50 +301,27 @@ int inotify_check(time_t now)
for (dd = daemon->dynamic_dirs; dd; dd = dd->next)
if (dd->wd == in->wd)
{
size_t lendir = strlen(dd->dname);
char *path;
if ((path = whine_malloc(lendir + in->len + 2)))
if (dd->flags & AH_HOSTS)
{
struct hostsfile *ah = NULL;
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, in->name);
/* Is this is a deletion event? */
if (in->mask & IN_DELETE)
my_syslog(LOG_INFO, _("inotify: %s removed"), path);
else
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
if (dd->flags & AH_HOSTS)
struct hostsfile *ah;
if ((ah = dyndir_addhosts(dd, in->name)))
{
if ((ah = dyndir_addhosts(dd, path)))
{
const unsigned int removed = cache_remove_uid(ah->index);
if (removed > 0)
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, path);
const unsigned int removed = cache_remove_uid(ah->index);
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
if (!(in->mask & IN_DELETE))
read_hostsfile(path, ah->index, 0, NULL, 0);
/* Is this is a deletion event? */
if (in->mask & IN_DELETE)
my_syslog(LOG_INFO, _("inotify: %s removed"), ah->fname);
else
my_syslog(LOG_INFO, _("inotify: %s new or modified"), ah->fname);
if (removed > 0)
my_syslog(LOG_INFO, _("inotify: flushed %u names read from %s"), removed, ah->fname);
/* (Re-)load hostsfile only if this event isn't triggered by deletion */
if (!(in->mask & IN_DELETE))
read_hostsfile(ah->fname, ah->index, 0, NULL, 0);
#ifdef HAVE_DHCP
if (daemon->dhcp || daemon->doing_dhcp6)
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
#endif
}
}
#ifdef HAVE_DHCP
else if (dd->flags & AH_DHCP_HST)
{
if (option_read_dynfile(path, AH_DHCP_HST))
if (daemon->dhcp || daemon->doing_dhcp6)
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
@@ -334,17 +329,43 @@ int inotify_check(time_t now)
lease_update_file(now);
lease_update_dns(1);
}
}
else if (dd->flags & AH_DHCP_OPT)
option_read_dynfile(path, AH_DHCP_OPT);
#endif
if (!ah)
free(path);
}
}
#ifdef HAVE_DHCP
else if (!(in->mask & IN_DELETE))
{
char *path;
if ((path = whine_malloc(strlen(dd->dname) + in->len + 2)))
{
strcpy(path, dd->dname);
strcat(path, "/");
strcat(path, in->name);
my_syslog(LOG_INFO, _("inotify: %s new or modified"), path);
if ((dd->flags & AH_DHCP_HST) && option_read_dynfile(path, AH_DHCP_HST))
{
/* Propagate the consequences of loading a new dhcp-host */
dhcp_update_configs(daemon->dhcp_conf);
lease_update_from_configs();
lease_update_file(now);
lease_update_dns(1);
}
if (dd->flags & AH_DHCP_OPT)
option_read_dynfile(path, AH_DHCP_OPT);
free(path);
}
}
#endif
}
}
}
return hit;
}
+5 -1
View File
@@ -1568,6 +1568,8 @@ void check_servers(int no_loop_check)
struct serverfd *sfd, *tmp, **up;
int port = 0, count;
int locals = 0;
(void)no_loop_check;
#ifdef HAVE_LOOP
if (!no_loop_check)
@@ -1775,8 +1777,10 @@ int reload_servers(char *fname)
/* Called when addresses are added or deleted from an interface */
void newaddress(time_t now)
{
#ifdef HAVE_DHCP
struct dhcp_relay *relay;
#endif
(void)now;
if (option_bool(OPT_CLEVERBIND) || option_bool(OPT_LOCAL_SERVICE) ||
+77 -139
View File
@@ -687,12 +687,12 @@ static int log_txt(char *name, unsigned char *p, const int ardlen, int flag)
Return 2 if the packet is malformed.
*/
int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now,
struct ipsets *ipsets, struct ipsets *nftsets, int is_sign, int check_rebind,
struct ipsets *ipsets, struct ipsets *nftsets, int check_rebind,
int no_cache_dnssec, int secure)
{
unsigned char *p, *p1, *endrr, *namep;
int j, qtype, qclass, aqtype, aqclass, ardlen, res;
unsigned long ttl = 0;
unsigned long ttl;
union all_addr addr;
#ifdef HAVE_IPSET
char **ipsets_cur;
@@ -704,13 +704,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
#else
(void)nftsets; /* unused */
#endif
int found = 0, cname_count = CNAME_CHAIN;
struct crec *cpp = NULL;
int name_encoding, found = 0, ptr = 0;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
#ifdef HAVE_DNSSEC
int cname_short = 0;
#endif
unsigned long cttl = ULONG_MAX, attl;
cache_start_insert();
@@ -724,125 +719,73 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (qclass != C_IN)
return 0;
/* PTRs: we chase CNAMEs here, since we have no way to
represent them in the cache. */
if (qtype == T_PTR)
/* If the PTR record encodes an address, store using a name/address record with F_REVERSE set.
Otherwise, it gets stored as an arbitrary RR below. If the query is answerable with
a CNAME, also take the arbitrary-RR route, since the cache can't represent a CNAME
whose target is stored in a F_REVERSE record. */
if (qtype == T_PTR && !(flags & F_NXDOMAIN) && (name_encoding = in_arpa_name_2_addr(name, &addr)))
{
int insert = 1, name_encoding = in_arpa_name_2_addr(name, &addr);
ptr = 1;
if (!(flags & F_NXDOMAIN))
if (!(p1 = skip_questions(header, qlen)))
return 2;
for (j = 0; j < ntohs(header->ancount); j++)
{
cname_loop:
if (!(p1 = skip_questions(header, qlen)))
return 2;
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
return 2; /* bad packet */
for (j = 0; j < ntohs(header->ancount); j++)
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
p = p1;
GETLONG(ttl, p1);
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
if (aqclass == C_IN && res == 1 && aqtype == T_PTR)
{
int secflag = 0;
if (!(res = extract_name(header, qlen, &p1, name, EXTR_NAME_COMPARE, 10)))
return 2; /* bad packet */
found = 1;
if ((daemon->max_ttl != 0) && (ttl > daemon->max_ttl))
ttl = daemon->max_ttl;
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
/* TTL of record is minimum of CNAMES and PTR */
if (attl < cttl)
cttl = attl;
if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == T_PTR))
{
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
{
/* validated RR anywhere in CNAME chain, don't cache. */
if (cname_short || aqtype == T_CNAME)
insert = 0;
secflag = F_DNSSECOK;
/* limit TTL based on signature. */
if (daemon->rr_status[j] < cttl)
cttl = daemon->rr_status[j];
}
#endif
if (aqtype == T_CNAME)
log_query(secflag | F_CNAME | F_FORWARD | F_UPSTREAM, name, NULL, NULL, 0);
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
return 2;
if (aqtype == T_CNAME)
{
if (!cname_count--)
return 0; /* looped CNAMES, we can't cache. */
#ifdef HAVE_DNSSEC
cname_short = 1;
#endif
goto cname_loop;
}
found = 1;
if (!name_encoding)
log_query(secflag | F_FORWARD | F_UPSTREAM, name, NULL, NULL, aqtype);
else
{
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
if (insert)
cache_insert(name, &addr, C_IN, now, cttl, name_encoding | secflag | F_REVERSE);
}
}
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 2; /* bad packet */
}
}
if (!found)
{
flags |= F_NEG | (secure ? F_DNSSECOK : 0);
if (name_encoding)
flags |= F_REVERSE | name_encoding;
log_query(flags | F_UPSTREAM, name, &addr, NULL, 0);
if (name_encoding && !option_bool(OPT_NO_NEG))
{
/* For reverse records, we use the name field to store the SOA name. */
int substring, have_soa = find_soa(header, qlen, name, &substring, &ttl, no_cache_dnssec, now);
if (have_soa || daemon->neg_ttl)
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
{
/* If daemon->neg_ttl is set, we can cache even without an SOA. */
if (!have_soa)
{
flags |= F_NO_RR; /* Marks no SOA found. */
ttl = daemon->neg_ttl;
}
cache_insert(name + substring, &addr, C_IN, now, ttl, flags);
secflag = F_DNSSECOK;
/* limit TTL based on signature. */
if (daemon->rr_status[j] < ttl)
ttl = daemon->rr_status[j];
}
#endif
PUTLONG(ttl, p);
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
return 2;
log_query(name_encoding | secflag | F_REVERSE | F_UPSTREAM, name, &addr, NULL, 0);
cache_insert(name, &addr, C_IN, now, ttl, name_encoding | secflag | F_REVERSE);
/* restore query into name */
p1 = namep;
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
return 2;
}
p1 = endrr;
if (!CHECK_LEN(header, p1, qlen, 0))
return 2; /* bad packet */
}
}
else
if (!ptr || !found)
{
/* everything other than PTR */
struct crec *newc;
int addrlen = 0, insert = 1;
struct crec *newc, *cpp = NULL;
int cname_count = CNAME_CHAIN, addrlen = 0, insert = 1;
if (qtype == T_A)
{
addrlen = INADDRSZ;
@@ -854,12 +797,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
flags |= F_IPV6;
}
else if (qtype != T_CNAME &&
(qtype == T_SRV || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
(qtype == T_SRV || qtype == T_PTR || rr_on_list(daemon->cache_rr, qtype) || rr_on_list(daemon->cache_rr, T_ANY)))
flags |= F_RR;
else
insert = 0; /* NOTE: do not cache data from CNAME queries. */
cname_loop1:
cname_loop:
if (!(p1 = skip_questions(header, qlen)))
return 2;
@@ -872,12 +815,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
GETSHORT(aqtype, p1);
GETSHORT(aqclass, p1);
GETLONG(attl, p1);
if ((daemon->max_ttl != 0) && (attl > daemon->max_ttl) && !is_sign)
{
(p1) -= 4;
PUTLONG(daemon->max_ttl, p1);
}
p = p1;
GETLONG(ttl, p1);
GETSHORT(ardlen, p1);
endrr = p1+ardlen;
@@ -890,6 +829,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
p1 = endrr;
continue;
}
if ((daemon->max_ttl != 0) && (ttl > daemon->max_ttl))
ttl = daemon->max_ttl;
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID) && j < daemon->rr_status_sz && daemon->rr_status[j] != 0)
@@ -897,10 +839,12 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
secflag = F_DNSSECOK;
/* limit TTl based on sig. */
if (daemon->rr_status[j] < attl)
attl = daemon->rr_status[j];
if (daemon->rr_status[j] < ttl)
ttl = daemon->rr_status[j];
}
#endif
PUTLONG(ttl, p);
if (aqtype == T_CNAME)
{
@@ -911,7 +855,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (insert)
{
if ((newc = cache_insert(name, NULL, C_IN, now, attl, F_CNAME | F_FORWARD | secflag)))
if ((newc = cache_insert(name, NULL, C_IN, now, ttl, F_CNAME | F_FORWARD | secflag)))
{
newc->addr.cname.target.cache = NULL;
newc->addr.cname.is_name_ptr = 0;
@@ -924,16 +868,15 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
}
cpp = newc;
if (attl < cttl)
cttl = attl;
}
/* Set the query to the CNAME target and go again unless the query was just for a CNAME. */
namep = p1;
if (!extract_name(header, qlen, &p1, name, EXTR_NAME_EXTRACT, 0))
return 2;
if (qtype != T_CNAME)
goto cname_loop1;
goto cname_loop;
found = 1;
}
@@ -1089,7 +1032,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (insert)
{
newc = cache_insert(name, &addr, C_IN, now, attl, flags | F_FORWARD | secflag);
newc = cache_insert(name, &addr, C_IN, now, ttl, flags | F_FORWARD | secflag);
if (newc && cpp)
{
next_uid(newc);
@@ -1668,7 +1611,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
int rd_bit = (header->hb3 & HB3_RD);
int count = 255; /* catch loops */
/* Suppress cached answers of no_cache set. */
/* Suppress cached answers if no_cache set. */
if (no_cache)
rd_bit = 0;
@@ -1678,14 +1621,12 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (filtered)
*filtered = 0;
/* never answer queries with RD unset, to avoid cache snooping. */
if ( ntohs(header->qdcount) != 1 ||
ntohs(header->ancount) != 0 ||
if (ntohs(header->qdcount) != 1 ||
ntohs(header->ancount) != 0 ||
ntohs(header->nscount) != 0 ||
ntohs(header->qdcount) == 0 ||
OPCODE(header) != QUERY )
return 0;
/* Don't return AD set if checking disabled. */
if (header->hb4 & HB4_CD)
sec_data = 0;
@@ -2348,14 +2289,11 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
For FORWARD NEG records, the addr.rrdata.datalen field of the othewise
empty addr is used to held an offset in to the name which yields the SOA
name. For REVERSE NEG records, the otherwise empty name field holds the
SOA name. If soa_name has zero length, then no SOA is known. soa_lookup
MUST be a neg record here.
name.
If the F_NO_RR flag is set, there was no SOA record supplied with the RR. */
if (soa_lookup && !(soa_lookup->flags & F_NO_RR))
{
char *soa_name = soa_lookup->flags & F_REVERSE ? cache_get_name(soa_lookup) : name + soa_lookup->addr.rrdata.datalen;
char *soa_name = name + soa_lookup->addr.rrdata.datalen;
crecp = NULL;
while ((crecp = cache_find_by_name(crecp, soa_name, now, F_RR)))
+276 -185
View File
@@ -41,11 +41,11 @@ static void sanitise(char *buf);
#define ERR_ILL 4
#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;
char *packet = daemon->packet;
char *filename, *mode, *p, *end, *opt;
char *filename, *mode, *p, *end;
union mysockaddr addr, peer;
struct msghdr msg;
struct iovec iov;
@@ -198,11 +198,11 @@ void tftp_request(struct listener *listen, time_t now)
name = namebuff;
addra.addr4 = addr.in.sin_addr;
if (family == AF_INET6)
addra.addr6 = addr.in6.sin6_addr;
else
addra.addr4 = addr.in.sin_addr;
if (daemon->tftp_interfaces)
{
/* dedicated tftp interface list */
@@ -308,7 +308,9 @@ void tftp_request(struct listener *listen, time_t now)
/* May reuse struct transfer from abandoned transfer in single port mode. */
if (!transfer && !(transfer = whine_malloc(sizeof(struct tftp_transfer))))
return;
memset(transfer, 0, sizeof(struct tftp_transfer));
if (option_bool(OPT_SINGLE_PORT))
transfer->sockfd = listen->tftpfd;
else if ((transfer->sockfd = socket(family, SOCK_DGRAM, 0)) == -1)
@@ -320,15 +322,13 @@ void tftp_request(struct listener *listen, time_t now)
transfer->peer = peer;
transfer->source = addra;
transfer->if_index = if_index;
transfer->timeout = now + 2;
transfer->timeout = 2;
transfer->start = now;
transfer->backoff = 1;
transfer->block = 1;
transfer->blocksize = 512;
transfer->offset = 0;
transfer->file = NULL;
transfer->opt_blocksize = transfer->opt_transize = 0;
transfer->netascii = transfer->carrylf = 0;
transfer->windowsize = 1;
(void)prettyprint_addr(&peer, daemon->addrbuff);
/* if we have a nailed-down range, iterate until we find a free one. */
@@ -361,140 +361,167 @@ void tftp_request(struct listener *listen, time_t now)
p = packet + 2;
end = packet + len;
len = 0;
if (ntohs(*((unsigned short *)packet)) != OP_RRQ ||
!(filename = next(&p, end)) ||
!(mode = next(&p, end)) ||
(strcasecmp(mode, "octet") != 0 && strcasecmp(mode, "netascii") != 0))
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)
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
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 (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 */
int overhead = (family == AF_INET) ? 32 : 52;
transfer->blocksize = atoi(opt);
if (transfer->blocksize < 1)
transfer->blocksize = 1;
if (transfer->blocksize > (unsigned)daemon->packet_buff_sz - 4)
transfer->blocksize = (unsigned)daemon->packet_buff_sz - 4;
if (mtu != 0 && transfer->blocksize > (unsigned)mtu - overhead)
transfer->blocksize = (unsigned)mtu - overhead;
if (val < 1)
val = 1;
if (val > (unsigned)daemon->packet_buff_sz - 4)
val = (unsigned)daemon->packet_buff_sz - 4;
if (mtu != 0 && val > (unsigned)mtu - overhead)
val = (unsigned)mtu - overhead;
transfer->blocksize = val;
transfer->opt_blocksize = 1;
transfer->block = 0;
}
}
else if (strcasecmp(opt, "tsize") == 0 && next(&p, end) && !transfer->netascii)
{
transfer->opt_transize = 1;
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;
else if (strcasecmp(opt, "tsize") == 0 && !transfer->netascii)
{
transfer->opt_transize = 1;
transfer->block = 0;
}
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 (option_bool(OPT_TFTP_APREF_MAC))
/* 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)
{
unsigned char *macaddr = NULL;
unsigned char macbuf[DHCP_CHADDR_MAX];
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));
#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)
{
if (option_bool(OPT_TFTP_APREF_IP))
{
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]);
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
/* 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));
/* Absolute pathnames OK if they match prefix */
if (filename[0] == '/')
/* check permissions and open file */
if ((transfer->file = check_tftp_fileperm(&len, prefix, daemon->addrbuff)))
{
if (strstr(filename, daemon->namebuff) == filename)
daemon->namebuff[0] = 0;
transfer->lastack = transfer->block;
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
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
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
}
if (is_err)
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)
{
#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))
handle_tftp(now, transfer, len);
else
@@ -628,66 +659,90 @@ void check_tftp_listeners(time_t now)
for (transfer = daemon->tftp_trans, up = &daemon->tftp_trans; transfer; transfer = tmp)
{
tmp = transfer->next;
int endcon = 0, error = 0, timeout = 0;
if (difftime(now, transfer->timeout) >= 0.0)
tmp = transfer->next;
/* ->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. */
ssize_t len;
/* timeout, retransmit */
transfer->timeout += 1 + (1<<(transfer->backoff/2));
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if ((len = get_block(daemon->packet, transfer)) == -1)
{
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)
{
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)
transfer->retransmit += transfer->timeout + (1<<(transfer->backoff/2));
transfer->backoff++;
transfer->block = transfer->lastack;
if ((len = get_block(daemon->packet, transfer)) == 0)
endcon = 1; /* got last ACK */
else
{
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
/* send a window'a worth of blocks unless we're retransmitting OACK */
unsigned int i, winsize = transfer->block ? transfer->windowsize : 1;
for (i = 0; i < winsize && !endcon; i++, transfer->block++)
{
/* put on queue to be sent to script and deleted */
transfer->next = daemon->tftp_done_trans;
daemon->tftp_done_trans = transfer;
if (i != 0)
len = get_block(daemon->packet, 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;
}
}
/* packet in daemon->packet as this is called. */
static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
{
@@ -697,13 +752,30 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
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 */
transfer->timeout = now;
transfer->backoff = 0;
if (transfer->block++ != 0)
transfer->offset += transfer->blocksize - transfer->expansion;
/* try and handle 16-bit blockno wrap-around */
unsigned int block = (unsigned short)ntohs(mess->block);
if (block < transfer->lastack)
block |= transfer->block & 0xffff0000;
/* 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)
{
@@ -724,8 +796,7 @@ static void handle_tftp(time_t now, struct tftp_transfer *transfer, ssize_t len)
daemon->addrbuff);
/* Got err, ensure we take abort */
transfer->timeout = now;
transfer->backoff = 100;
transfer->start = 0;
}
}
}
@@ -785,7 +856,10 @@ static ssize_t tftp_err(int err, char *packet, char *message, char *file, char *
char message[];
} *mess = (struct errmess *)packet;
ssize_t len, ret = 4;
/* we overwrote the buffer... */
daemon->srv_save = NULL;
memset(packet, 0, daemon->packet_buff_sz);
if (file)
sanitise(file);
@@ -813,7 +887,10 @@ static ssize_t tftp_err_oops(char *packet, const char *file)
static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
{
memset(packet, 0, daemon->packet_buff_sz);
/* we overwrote the buffer... */
daemon->srv_save = NULL;
if (transfer->block == 0)
{
/* send OACK */
@@ -835,7 +912,17 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
p += (sprintf(p,"tsize") + 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;
}
else
@@ -846,31 +933,35 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
unsigned char data[];
} *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)
return 0; /* finished */
if (size > transfer->blocksize)
if ((size = transfer->file->size - transfer->offset) > transfer->blocksize)
size = transfer->blocksize;
mess->op = htons(OP_DATA);
mess->block = htons((unsigned short)(transfer->block));
if (lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
!read_write(transfer->file->fd, mess->data, size, RW_READ))
if (size != 0 &&
(lseek(transfer->file->fd, transfer->offset, SEEK_SET) == (off_t)-1 ||
!read_write(transfer->file->fd, mess->data, size, RW_READ)))
return -1;
transfer->expansion = 0;
/* Map '\n' to CR-LF in netascii mode */
if (transfer->netascii)
{
size_t i;
int newcarrylf;
transfer->expansion = 0;
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++;
@@ -885,8 +976,8 @@ static ssize_t get_block(char *packet, struct tftp_transfer *transfer)
i++;
}
transfer->carrylf = newcarrylf;
}
return size + 4;
+5
View File
@@ -213,6 +213,11 @@ static int ubus_handle_set_connmark_allowlist(struct ubus_context *ctx, struct u
size_t num_patterns = 0;
struct blob_attr *tb[policy_len];
struct blob_attr *attr;
(void)ctx;
(void)obj;
(void)req;
(void)method;
if (blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)))
return UBUS_STATUS_INVALID_ARGUMENT;