mirror of
http://thekelleys.org.uk/git/dnsmasq.git
synced 2026-11-03 13:38:32 +00:00
Compare commits
6
Commits
v2.93test1
...
v2.93test2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed2ba5b07a | ||
|
|
729c16a8ac | ||
|
|
80acb76f46 | ||
|
|
f1fa05d754 | ||
|
|
b148ba4752 | ||
|
|
81f619612e |
No files matched your search
+5
-1
@@ -202,11 +202,15 @@ void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
|
||||
{
|
||||
if (len > buff_len)
|
||||
{
|
||||
if (!(new = whine_malloc(len)))
|
||||
blen = len + 1024;
|
||||
if (!(new = whine_malloc(blen)))
|
||||
return NULL;
|
||||
|
||||
if (buff)
|
||||
free(buff);
|
||||
|
||||
buff = new;
|
||||
buff_len = blen;
|
||||
}
|
||||
data = buff;
|
||||
}
|
||||
|
||||
+6
-5
@@ -864,6 +864,9 @@ int main (int argc, char **argv)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Don't start logging malloc before logging is set up. */
|
||||
daemon->log_malloc = option_bool(OPT_LOG_MALLOC);
|
||||
|
||||
if (daemon->port == 0)
|
||||
my_syslog(LOG_INFO, _("started, version %s DNS disabled"), VERSION);
|
||||
else
|
||||
@@ -1977,11 +1980,11 @@ 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 *buff;
|
||||
struct server *s;
|
||||
int flags, auth_dns = 0;
|
||||
struct in_addr netmask;
|
||||
int pipefd[2];
|
||||
struct iovec tcpbuff;
|
||||
#ifdef HAVE_LINUX_NETWORK
|
||||
unsigned char a = 0;
|
||||
#endif
|
||||
@@ -2156,10 +2159,8 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)
|
||||
if ((flags = fcntl(confd, F_GETFL, 0)) != -1)
|
||||
while(retry_send(fcntl(confd, F_SETFL, flags & ~O_NONBLOCK)));
|
||||
|
||||
buff = tcp_request(confd, now, &tcp_addr, netmask, auth_dns);
|
||||
|
||||
if (buff)
|
||||
free(buff);
|
||||
tcp_request(confd, now, &tcpbuff, &tcp_addr, netmask, auth_dns);
|
||||
free(tcpbuff.iov_base);
|
||||
|
||||
for (s = daemon->servers; s; s = s->next)
|
||||
if (s->tcpfd != -1)
|
||||
|
||||
+12
-5
@@ -290,7 +290,8 @@ struct event_desc {
|
||||
#define OPT_AUTH_LOG 76
|
||||
#define OPT_LEASEQUERY 77
|
||||
#define OPT_LOG_ONLY_FAILED 78
|
||||
#define OPT_LAST 79
|
||||
#define OPT_LOG_MALLOC 79
|
||||
#define OPT_LAST 80
|
||||
|
||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||
@@ -1207,6 +1208,7 @@ extern struct daemon {
|
||||
int log_fac; /* log facility */
|
||||
char *log_file; /* optional log file */
|
||||
int max_logs; /* queue limit */
|
||||
int log_malloc; /* log malloc/realloc/free */
|
||||
int randport_limit; /* Maximum number of source ports for query. */
|
||||
int cachesize, ftabsize;
|
||||
int port, query_port, min_port, max_port;
|
||||
@@ -1495,8 +1497,12 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval, char *limit);
|
||||
void *safe_malloc(size_t size);
|
||||
void safe_strncpy(char *dest, const char *src, size_t size);
|
||||
void safe_pipe(int *fd, int read_noblock);
|
||||
void *whine_malloc(size_t size);
|
||||
void *whine_realloc(void *ptr, size_t size);
|
||||
#define whine_malloc(x) whine_malloc_real(__func__, __LINE__, (x))
|
||||
#define whine_realloc(x, y) whine_realloc_real(__func__, __LINE__, (x), (y))
|
||||
#define free(x) free_real(__func__, __LINE__, (x))
|
||||
void free_real(const char *func, unsigned int line, void *ptr);
|
||||
void *whine_malloc_real(const char *func, unsigned int line, size_t size);
|
||||
void *whine_realloc_real(const char *func, unsigned int line, void *ptr, size_t size);
|
||||
int sa_len(union mysockaddr *addr);
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2);
|
||||
int sockaddr_isnull(const union mysockaddr *s);
|
||||
@@ -1521,6 +1527,7 @@ int memcmp_masked(unsigned char *a, unsigned char *b, int len,
|
||||
int expand_buf(struct iovec *iov, size_t size);
|
||||
char *print_mac(char *buff, unsigned char *mac, int len);
|
||||
int read_write(int fd, unsigned char *packet, int size, int rw);
|
||||
int read_writev(int fd, struct iovec *iov, int iovcnt, int rw);
|
||||
void close_fds(long max_fd, int spare1, int spare2, int spare3);
|
||||
int wildcard_match(const char* wildcard, const char* match);
|
||||
int wildcard_matchn(const char* wildcard, const char* match, int num);
|
||||
@@ -1563,8 +1570,8 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *n,
|
||||
int class, char *name, struct server *server,
|
||||
int *keycount, int *validatecount);
|
||||
#endif
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns);
|
||||
void server_gone(struct server *server);
|
||||
int send_from(int fd, int nowild, char *packet, size_t len,
|
||||
union mysockaddr *to, union all_addr *source,
|
||||
|
||||
+136
-135
@@ -135,9 +135,9 @@ static void log_query_mysockaddr(unsigned int flags, char *name, union mysockadd
|
||||
}
|
||||
|
||||
static void server_send(struct server *server, int fd,
|
||||
const void *header, size_t plen, int flags)
|
||||
const void *header, size_t plen)
|
||||
{
|
||||
while (retry_send(sendto(fd, header, plen, flags,
|
||||
while (retry_send(sendto(fd, header, plen, 0,
|
||||
&server->addr.sa,
|
||||
sa_len(&server->addr))));
|
||||
}
|
||||
@@ -1074,7 +1074,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
|
||||
set_outgoing_mark(orig, fd);
|
||||
#endif
|
||||
|
||||
server_send(server, fd, header, nn, 0);
|
||||
server_send(server, fd, header, nn);
|
||||
server->queries++;
|
||||
#ifdef HAVE_DUMPFILE
|
||||
dump_packet_udp(DUMP_SEC_QUERY, (void *)header, (size_t)nn, NULL, &server->addr, fd);
|
||||
@@ -2018,20 +2018,21 @@ void receive_query(struct listener *listen, time_t now)
|
||||
}
|
||||
|
||||
|
||||
/* Send query in packet, qsize to a server determined by first,last,start and
|
||||
get the reply. return reply size. */
|
||||
static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet, size_t qsize,
|
||||
int have_mark, unsigned int mark, struct server **servp)
|
||||
/* Send query in header, qsize to a server determined by first,last,start and
|
||||
get the reply into the buffer passed by outbuff. Return reply size. */
|
||||
static ssize_t tcp_talk(int first, int last, int start, struct dns_header *header, size_t qsize,
|
||||
struct iovec *recvbuff, int have_mark, unsigned int mark, struct server **servp)
|
||||
{
|
||||
int firstsendto = -1;
|
||||
u16 *length = (u16 *)packet;
|
||||
unsigned char *payload = &packet[2];
|
||||
struct dns_header *header = (struct dns_header *)payload;
|
||||
unsigned int rsize;
|
||||
u16 length;
|
||||
unsigned int rsize = 0;
|
||||
int class, rclass, type, rtype;
|
||||
unsigned char *p;
|
||||
struct blockdata *saved_question;
|
||||
struct timeval tv;
|
||||
struct iovec sendio[2];
|
||||
#ifdef MSG_FASTOPEN
|
||||
struct msghdr msg;
|
||||
#endif
|
||||
|
||||
(void)mark;
|
||||
(void)have_mark;
|
||||
@@ -2043,10 +2044,12 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
GETSHORT(type, p);
|
||||
GETSHORT(class, p);
|
||||
|
||||
/* Save question for retry. */
|
||||
if (!(saved_question = blockdata_alloc((char *)header, (size_t)qsize)))
|
||||
return 0;
|
||||
|
||||
length = htons(qsize);
|
||||
sendio[0].iov_base = &length;
|
||||
sendio[0].iov_len = sizeof(length);
|
||||
sendio[1].iov_base = header;
|
||||
sendio[1].iov_len = qsize;
|
||||
|
||||
while (1)
|
||||
{
|
||||
int data_sent = 0, fatal = 0;
|
||||
@@ -2068,10 +2071,6 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
*servp = serv = daemon->serverarray[start];
|
||||
|
||||
retry:
|
||||
blockdata_retrieve(saved_question, qsize, header);
|
||||
|
||||
*length = htons(qsize);
|
||||
|
||||
if (serv->tcpfd == -1)
|
||||
{
|
||||
if ((serv->tcpfd = socket(serv->addr.sa.sa_family, SOCK_STREAM, 0)) == -1)
|
||||
@@ -2102,10 +2101,17 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
tv.tv_sec += TCP_TIMEOUT;
|
||||
setsockopt(serv->tcpfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
#endif
|
||||
|
||||
#ifdef MSG_FASTOPEN
|
||||
server_send(serv, serv->tcpfd, packet, qsize + sizeof(u16), MSG_FASTOPEN);
|
||||
|
||||
msg.msg_name = &serv->addr.sa;
|
||||
msg.msg_namelen = sa_len(&serv->addr);
|
||||
msg.msg_iov = sendio;
|
||||
msg.msg_iovlen = 2;
|
||||
msg.msg_control = NULL;
|
||||
msg.msg_controllen = 0;
|
||||
msg.msg_flags = 0;
|
||||
|
||||
while (retry_send(sendmsg(serv->tcpfd, &msg, MSG_FASTOPEN)));
|
||||
|
||||
if (errno == 0)
|
||||
data_sent = 1;
|
||||
else if (errno == ETIMEDOUT || errno == EHOSTUNREACH || errno == EINPROGRESS || errno == ECONNREFUSED)
|
||||
@@ -2131,12 +2137,13 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
serv->flags &= ~SERV_GOT_TCP;
|
||||
}
|
||||
|
||||
/* We us the _ONCE veriant of read_write() here because we've set a timeout on the tcp socket
|
||||
/* We use the _ONCE variant of read_write() here because we've set a timeout on the tcp socket
|
||||
and wish to abort if the whole data is not read/written within the timeout. */
|
||||
if ((!data_sent && !read_write(serv->tcpfd, (unsigned char *)packet, qsize + sizeof(u16), RW_WRITE_ONCE)) ||
|
||||
!read_write(serv->tcpfd, (unsigned char *)length, sizeof (*length), RW_READ_ONCE) ||
|
||||
!read_write(serv->tcpfd, payload, (rsize = ntohs(*length)), RW_READ_ONCE))
|
||||
{
|
||||
if ((!data_sent && !read_writev(serv->tcpfd, sendio, 2, RW_WRITE_ONCE)) ||
|
||||
!read_write(serv->tcpfd, (unsigned char *)&length, sizeof(length), RW_READ_ONCE) ||
|
||||
!expand_buf(recvbuff, (rsize = ntohs(length))) ||
|
||||
!read_write(serv->tcpfd, recvbuff->iov_base, rsize, RW_READ_ONCE))
|
||||
{
|
||||
/* We get data then EOF, reopen connection to same server,
|
||||
else try next. This avoids DoS from a server which accepts
|
||||
connections and then closes them. */
|
||||
@@ -2149,28 +2156,28 @@ static ssize_t tcp_talk(int first, int last, int start, unsigned char *packet,
|
||||
else
|
||||
goto failed;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* If the question section of the reply doesn't match the question we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers.
|
||||
Try another server, or give up */
|
||||
p = (unsigned char *)(((struct dns_header *)recvbuff->iov_base)+1);
|
||||
if (extract_name(((struct dns_header *)recvbuff->iov_base), rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1)
|
||||
continue;
|
||||
GETSHORT(rtype, p);
|
||||
GETSHORT(rclass, p);
|
||||
|
||||
/* If the question section of the reply doesn't match the question we sent, then
|
||||
someone might be attempting to insert bogus values into the cache by
|
||||
sending replies containing questions and bogus answers.
|
||||
Try another server, or give up */
|
||||
p = (unsigned char *)(header+1);
|
||||
if (extract_name(header, rsize, &p, daemon->namebuff, EXTR_NAME_COMPARE, 4) != 1)
|
||||
continue;
|
||||
GETSHORT(rtype, p);
|
||||
GETSHORT(rclass, p);
|
||||
|
||||
if (type != rtype || class != rclass)
|
||||
continue;
|
||||
if (type != rtype || class != rclass)
|
||||
continue;
|
||||
}
|
||||
|
||||
serv->flags |= SERV_GOT_TCP;
|
||||
|
||||
*servp = serv;
|
||||
blockdata_free(saved_question);
|
||||
return rsize;
|
||||
}
|
||||
|
||||
blockdata_free(saved_question);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2182,19 +2189,16 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
int class, char *name, struct server *server,
|
||||
int *keycount, int *validatecount)
|
||||
{
|
||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
struct dns_header *new_header = (struct dns_header *)&packet[2];
|
||||
int start, first, last, new_status;
|
||||
ssize_t n = *plenp;
|
||||
int log_save = daemon->log_display_id;
|
||||
struct iovec recvbuff;
|
||||
|
||||
recvbuff.iov_len = 0;
|
||||
recvbuff.iov_base = NULL;
|
||||
|
||||
*plenp = 0;
|
||||
|
||||
if (!packet)
|
||||
return STAT_ABANDONED;
|
||||
|
||||
memcpy(new_header, header, n);
|
||||
|
||||
/* Set TCP flag in logs. */
|
||||
daemon->log_display_id = -daemon->log_display_id;
|
||||
|
||||
@@ -2212,10 +2216,11 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC | F_SERVER, name, &server->addr,
|
||||
STAT_ISEQUAL(status, STAT_NEED_KEY) ? "dnssec-query[DNSKEY]" : "dnssec-query[DS]", 0);
|
||||
|
||||
if ((n = tcp_talk(first, last, start, packet, n, 0, 0, &server)) == 0)
|
||||
if ((n = tcp_talk(first, last, start, header, n, &recvbuff, 0, 0, &server)) == 0)
|
||||
new_status = STAT_ABANDONED;
|
||||
else
|
||||
{
|
||||
struct dns_header *new_header = (struct dns_header *)recvbuff.iov_base;
|
||||
new_status = tcp_key_recurse(now, status, new_header, n, class, daemon->namebuff, daemon->keyname, server, 0, 0, keycount, validatecount);
|
||||
|
||||
if (STAT_ISEQUAL(status, STAT_OK))
|
||||
@@ -2252,7 +2257,7 @@ int tcp_from_udp(time_t now, int status, struct dns_header *header, ssize_t *ple
|
||||
}
|
||||
|
||||
daemon->log_display_id = log_save;
|
||||
free(packet);
|
||||
free(recvbuff.iov_base);
|
||||
return new_status;
|
||||
}
|
||||
|
||||
@@ -2262,9 +2267,12 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
int have_mark, unsigned int mark, int *keycount, int *validatecount)
|
||||
{
|
||||
int first, last, start, new_status;
|
||||
unsigned char *packet = NULL;
|
||||
struct dns_header *new_header = NULL;
|
||||
struct iovec new_packet;
|
||||
struct dns_header *query_header = NULL, *new_header;
|
||||
|
||||
new_packet.iov_base = NULL;
|
||||
new_packet.iov_len = 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
size_t m;
|
||||
@@ -2298,34 +2306,21 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
}
|
||||
|
||||
/* Can't validate because we need a key/DS whose name now in keyname.
|
||||
Make query for same, and recurse to validate */
|
||||
if (!packet)
|
||||
{
|
||||
packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
new_header = (struct dns_header *)&packet[2];
|
||||
}
|
||||
|
||||
if (!packet)
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
|
||||
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class, 0,
|
||||
Make query for same in UDP packet buffer, recurse to validate*/
|
||||
query_header = (struct dns_header *)daemon->packet;
|
||||
daemon->srv_save = NULL;
|
||||
|
||||
m = dnssec_generate_query(query_header, ((unsigned char *)query_header) + daemon->edns_pktsz, keyname, class, 0,
|
||||
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS);
|
||||
|
||||
if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1)
|
||||
if ((start = dnssec_server(server, keyname, STAT_ISEQUAL(new_status, STAT_NEED_DS), &first, &last)) == -1 ||
|
||||
(m = tcp_talk(first, last, start, query_header, m, &new_packet, have_mark, mark, &server)) == 0)
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0)
|
||||
{
|
||||
new_status = STAT_ABANDONED;
|
||||
break;
|
||||
}
|
||||
|
||||
new_header = new_packet.iov_base;
|
||||
log_save = daemon->log_display_id;
|
||||
daemon->log_display_id = -(++daemon->log_id);
|
||||
|
||||
@@ -2342,8 +2337,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
break;
|
||||
}
|
||||
|
||||
if (packet)
|
||||
free(packet);
|
||||
free(new_packet.iov_base);
|
||||
|
||||
return new_status;
|
||||
}
|
||||
@@ -2353,11 +2347,11 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
/* The daemon forks before calling this: it should deal with one connection,
|
||||
blocking as necessary, and then return. Note, need to be a bit careful
|
||||
about resources for debug mode, when the fork is suppressed: that's
|
||||
done by the caller. */
|
||||
unsigned char *tcp_request(int confd, time_t now,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
|
||||
done by the caller, which also frees bigbuff. */
|
||||
void tcp_request(int confd, time_t now, struct iovec *bigbuff,
|
||||
union mysockaddr *local_addr, struct in_addr netmask, int auth_dns)
|
||||
{
|
||||
size_t size = 0, saved_size = 0;
|
||||
size_t size = 0;
|
||||
int norebind = 0;
|
||||
#ifdef HAVE_CONNTRACK
|
||||
int allowed = 1;
|
||||
@@ -2366,16 +2360,10 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
int local_auth = 0;
|
||||
#endif
|
||||
int checking_disabled, do_bit = 0, ad_reqd = 0, have_pseudoheader = 0;
|
||||
struct blockdata *saved_question = NULL;
|
||||
unsigned short qtype;
|
||||
unsigned int gotname = 0;
|
||||
/* Max TCP packet + slop + size */
|
||||
unsigned char *packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16));
|
||||
unsigned char *payload = &packet[2];
|
||||
u16 tcp_len;
|
||||
/* largest field in header is 16-bits, so this is still sufficiently aligned */
|
||||
struct dns_header *header = (struct dns_header *)payload;
|
||||
u16 *length = (u16 *)packet;
|
||||
u16 tcp_len, out_len;
|
||||
struct dns_header *header, *out_header;
|
||||
struct server *serv;
|
||||
struct in_addr dst_addr_4;
|
||||
union mysockaddr peer_addr;
|
||||
@@ -2385,9 +2373,13 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
unsigned int mark = 0;
|
||||
int have_mark = 0;
|
||||
int first, last, filtered, do_stale = 0;
|
||||
|
||||
if (!packet || getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
|
||||
return packet;
|
||||
struct iovec out_iov[2];
|
||||
|
||||
bigbuff->iov_base = NULL;
|
||||
bigbuff->iov_len = 0;
|
||||
|
||||
if (getpeername(confd, (struct sockaddr *)&peer_addr, &peer_len) == -1)
|
||||
return;
|
||||
|
||||
#ifdef HAVE_CONNTRACK
|
||||
/* Get connection mark of incoming query to set on outgoing connections. */
|
||||
@@ -2408,7 +2400,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
if (option_bool(OPT_LOCAL_SERVICE))
|
||||
{
|
||||
struct addrlist *addr;
|
||||
|
||||
|
||||
if (peer_addr.sa.sa_family == AF_INET6)
|
||||
{
|
||||
for (addr = daemon->interface_addrs; addr; addr = addr->next)
|
||||
@@ -2431,7 +2423,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
prettyprint_addr(&peer_addr, daemon->addrbuff);
|
||||
my_syslog(LOG_WARNING, _("ignoring query from non-local network %s"), daemon->addrbuff);
|
||||
return packet;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2449,18 +2441,26 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
if (query_count >= TCP_MAX_QUERIES)
|
||||
break;
|
||||
|
||||
/* Now get the query into the normal UDP packet buffer.
|
||||
Ignore queries long than this. If we're answering locally,
|
||||
copy the query into the output buffer, but for forwarding, tcp_talk()
|
||||
wants the query in a a different buffer from the reply.
|
||||
Note that we overwrote any saved UDP query - this onlt matters in debug mode. */
|
||||
daemon->srv_save = NULL;
|
||||
if (!read_write(confd, (unsigned char *)&tcp_len, sizeof(tcp_len), RW_READ) ||
|
||||
!(size = ntohs(tcp_len)) ||
|
||||
!read_write(confd, payload, size, RW_READ))
|
||||
!(size = ntohs(tcp_len)) || size > (size_t)daemon->packet_buff_sz ||
|
||||
!read_write(confd, (unsigned char *)daemon->packet, size, RW_READ))
|
||||
break;
|
||||
|
||||
|
||||
if (size < (int)sizeof(struct dns_header))
|
||||
continue;
|
||||
|
||||
/* Clear buffer beyond request to avoid risk of
|
||||
information disclosure. */
|
||||
memset(payload + size, 0, 65536 - size);
|
||||
/* Make sure we have a buffer big enough for the largest answer. */
|
||||
expand_buf(bigbuff, 65536 + MAXDNAME + RRFIXEDSZ);
|
||||
out_header = bigbuff->iov_base;
|
||||
|
||||
/* header == query */
|
||||
header = (struct dns_header *)daemon->packet;
|
||||
query_count++;
|
||||
|
||||
/* log_query gets called indirectly all over the place, so
|
||||
@@ -2479,9 +2479,6 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
ede = EDE_INVALID_DATA;
|
||||
else
|
||||
{
|
||||
if (saved_question)
|
||||
blockdata_free(saved_question);
|
||||
|
||||
do_bit = 0;
|
||||
|
||||
if (find_pseudoheader(header, (size_t)size, NULL, &pheader, NULL, NULL))
|
||||
@@ -2496,9 +2493,12 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
do_bit = 1; /* do bit */
|
||||
}
|
||||
|
||||
size = add_edns0_config(header, size, ((unsigned char *) header) + 65536, &peer_addr, now, &cacheable);
|
||||
saved_question = blockdata_alloc((char *)header, (size_t)size);
|
||||
saved_size = size;
|
||||
size = add_edns0_config(header, size, ((unsigned char *) header) + daemon->edns_pktsz, &peer_addr, now, &cacheable);
|
||||
|
||||
/* Clear buffer to avoid risk of information disclosure. */
|
||||
memset(bigbuff->iov_base, 0, bigbuff->iov_len);
|
||||
/* Copy query into output buffer for local answering */
|
||||
memcpy(out_header, header, size);
|
||||
|
||||
log_query_mysockaddr((auth_dns ? F_NOERR | F_AUTH : 0) | F_QUERY | F_FORWARD, daemon->namebuff,
|
||||
&peer_addr, NULL, qtype);
|
||||
@@ -2557,10 +2557,10 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#endif
|
||||
#ifdef HAVE_AUTH
|
||||
else if (auth_dns)
|
||||
m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr, local_auth);
|
||||
m = answer_auth(out_header, ((char *) out_header) + 65536, (size_t)size, now, &peer_addr, local_auth);
|
||||
#endif
|
||||
else
|
||||
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
|
||||
m = answer_request(out_header, ((char *) out_header) + 65536, (size_t)size,
|
||||
dst_addr_4, netmask, now, ad_reqd, do_bit, !cacheable, &stale, &filtered);
|
||||
}
|
||||
}
|
||||
@@ -2568,15 +2568,12 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
/* Do this by steam now we're not in the select() loop */
|
||||
check_log_writer(1);
|
||||
|
||||
if (m == 0 && ede == EDE_UNSET && saved_question)
|
||||
if (m == 0 && ede == EDE_UNSET)
|
||||
{
|
||||
struct server *master;
|
||||
int start;
|
||||
int no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
|
||||
|
||||
blockdata_retrieve(saved_question, (size_t)saved_size, header);
|
||||
size = saved_size;
|
||||
|
||||
/* save state of "cd" flag in query */
|
||||
checking_disabled = header->hb4 & HB4_CD;
|
||||
|
||||
@@ -2605,7 +2602,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#ifdef HAVE_DNSSEC
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
{
|
||||
size = add_do_bit(header, size, ((unsigned char *) header) + 65536);
|
||||
size = add_do_bit(header, size, ((unsigned char *) header) + daemon->edns_pktsz);
|
||||
|
||||
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
|
||||
this allows it to select auth servers when one is returning bad data. */
|
||||
@@ -2615,12 +2612,14 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
#endif
|
||||
|
||||
/* Loop round available servers until we succeed in connecting to one. */
|
||||
if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0)
|
||||
if ((m = tcp_talk(first, last, start, header, size, bigbuff, have_mark, mark, &serv)) == 0)
|
||||
ede = EDE_NETERR;
|
||||
else
|
||||
{
|
||||
/* just in case tcp_talk() expanded buffer - should never happen */
|
||||
out_header = bigbuff->iov_base;
|
||||
/* get query name again for logging - may have been overwritten */
|
||||
if (!extract_name(header, (unsigned int)size, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
if (!extract_name(out_header, (unsigned int)size, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
strcpy(daemon->namebuff, "query");
|
||||
log_query_mysockaddr(F_SERVER | F_FORWARD, daemon->namebuff, &serv->addr, NULL, 0);
|
||||
|
||||
@@ -2636,7 +2635,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
{
|
||||
int keycount = daemon->limit[LIMIT_WORK]; /* Limit to number of DNSSEC questions, to catch loops and avoid filling cache. */
|
||||
int validatecount = daemon->limit[LIMIT_CRYPTO];
|
||||
int status = tcp_key_recurse(now, STAT_OK, header, m, 0, daemon->namebuff, daemon->keyname,
|
||||
/* tcp_key_recurse() may overwrite packetbuf, and thuse *header is now invalid */
|
||||
int status = tcp_key_recurse(now, STAT_OK, out_header, m, 0, daemon->namebuff, daemon->keyname,
|
||||
serv, have_mark, mark, &keycount, &validatecount);
|
||||
char *result, *domain = "result";
|
||||
|
||||
@@ -2662,7 +2662,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
no_cache_dnssec = 1;
|
||||
bogusanswer = 1;
|
||||
|
||||
if (extract_name(header, m, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
if (extract_name(out_header, m, NULL, daemon->namebuff, EXTR_NAME_EXTRACT, 0))
|
||||
domain = daemon->namebuff;
|
||||
}
|
||||
|
||||
@@ -2686,18 +2686,18 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
|
||||
/* restore CD bit to the value in the query */
|
||||
if (checking_disabled)
|
||||
header->hb4 |= HB4_CD;
|
||||
out_header->hb4 |= HB4_CD;
|
||||
else
|
||||
header->hb4 &= ~HB4_CD;
|
||||
out_header->hb4 &= ~HB4_CD;
|
||||
|
||||
/* Never cache answers which are contingent on the source or MAC address EDSN0 option,
|
||||
since the cache is ignorant of such things. */
|
||||
if (!cacheable)
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
m = process_reply(header, now, serv, (unsigned int)m,
|
||||
m = process_reply(out_header, now, serv, (unsigned int)m,
|
||||
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
|
||||
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)header) + 65536, ede);
|
||||
ad_reqd, do_bit, !have_pseudoheader, &peer_addr, ((unsigned char *)out_header) + 65536, ede);
|
||||
|
||||
/* process_reply() adds pheader itself */
|
||||
have_pseudoheader = 0;
|
||||
@@ -2712,8 +2712,8 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
/* In case of local answer or no connections made. */
|
||||
if (m == 0)
|
||||
{
|
||||
if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff,
|
||||
((char *) header) + 65536, first, last, ede)))
|
||||
if (!(m = make_local_answer(flags, gotname, size, out_header, daemon->namebuff,
|
||||
((char *) out_header) + 65536, first, last, ede)))
|
||||
break;
|
||||
}
|
||||
else if (ede == EDE_UNSET)
|
||||
@@ -2729,14 +2729,12 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
u16 swap = htons((u16)ede);
|
||||
|
||||
if (ede != EDE_UNSET)
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
|
||||
else
|
||||
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, 0, NULL, 0, do_bit, 0);
|
||||
m = add_pseudoheader(out_header, m, ((unsigned char *) out_header) + 65536, 0, NULL, 0, do_bit, 0);
|
||||
}
|
||||
|
||||
check_log_writer(1);
|
||||
|
||||
*length = htons(m);
|
||||
check_log_writer(1);
|
||||
|
||||
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
|
||||
#ifdef HAVE_AUTH
|
||||
@@ -2746,7 +2744,13 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
report_addresses(header, m, mark);
|
||||
#endif
|
||||
|
||||
if (!read_write(confd, packet, m + sizeof(u16), RW_WRITE))
|
||||
/* use scatter-gather IO so that length doesn't end up in separate packet. */
|
||||
out_len = htons(m);
|
||||
out_iov[0].iov_len = sizeof(out_len);
|
||||
out_iov[0].iov_base = &out_len;
|
||||
out_iov[1].iov_len = m;
|
||||
out_iov[1].iov_base = bigbuff->iov_base;
|
||||
if (!read_writev(confd, out_iov, 2, RW_WRITE))
|
||||
break;
|
||||
|
||||
/* If we answered with stale data, this process will now try and get fresh data into
|
||||
@@ -2762,18 +2766,15 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
daemon->log_source_addr = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we ran once to get fresh data, confd is already closed. */
|
||||
|
||||
/* If we ran once to get fresh data, confd is already closed. */
|
||||
if (!do_stale)
|
||||
{
|
||||
shutdown(confd, SHUT_RDWR);
|
||||
close(confd);
|
||||
}
|
||||
|
||||
blockdata_free(saved_question);
|
||||
|
||||
check_log_writer(1);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/* return a UDP socket bound to a random port, have to cope with straying into
|
||||
@@ -3274,7 +3275,7 @@ void resend_query(void)
|
||||
{
|
||||
if (daemon->srv_save)
|
||||
server_send(daemon->srv_save, daemon->fd_save,
|
||||
daemon->packet, daemon->packet_len, 0);
|
||||
daemon->packet, daemon->packet_len);
|
||||
}
|
||||
|
||||
/* A server record is going away, remove references to it */
|
||||
|
||||
@@ -197,6 +197,7 @@ struct myoption {
|
||||
#define LOPT_DO_ENCODE 388
|
||||
#define LOPT_LEASEQUERY 389
|
||||
#define LOPT_SPLIT_RELAY 390
|
||||
#define LOPT_LOG_MALLOC 391
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -398,6 +399,7 @@ static const struct myoption opts[] =
|
||||
{ "no-ident", 0, 0, LOPT_NO_IDENT },
|
||||
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
|
||||
{ "leasequery", 2, 0, LOPT_LEASEQUERY },
|
||||
{ "log-malloc", 0, 0, LOPT_LOG_MALLOC },
|
||||
{ NULL, 0, 0, 0 }
|
||||
};
|
||||
|
||||
@@ -606,6 +608,7 @@ static struct {
|
||||
{ LOPT_NO_IDENT, OPT_NO_IDENT, NULL, gettext_noop("Do not add CHAOS TXT records."), NULL },
|
||||
{ LOPT_CACHE_RR, ARG_DUP, "<RR-type>", gettext_noop("Cache this DNS resource record type."), NULL },
|
||||
{ LOPT_MAX_PROCS, ARG_ONE, "<integer>", gettext_noop("Maximum number of concurrent tcp connections."), NULL },
|
||||
{ LOPT_LOG_MALLOC, OPT_LOG_MALLOC, NULL, gettext_noop("Log memory allocation for debugging."), NULL },
|
||||
{ 0, 0, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
+79
-43
@@ -341,26 +341,6 @@ void safe_pipe(int *fd, int read_noblock)
|
||||
die(_("cannot create pipe: %s"), NULL, EC_MISC);
|
||||
}
|
||||
|
||||
void *whine_malloc(size_t size)
|
||||
{
|
||||
void *ret = calloc(1, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *whine_realloc(void *ptr, size_t size)
|
||||
{
|
||||
void *ret = realloc(ptr, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to reallocate %d bytes"), (int) size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sockaddr_isequal(const union mysockaddr *s1, const union mysockaddr *s2)
|
||||
{
|
||||
if (s1->sa.sa_family == s2->sa.sa_family)
|
||||
@@ -777,43 +757,60 @@ int retry_send(ssize_t rc)
|
||||
"once" fails on EAGAIN, as this a timeout.
|
||||
This indicates a timeout of a TCP socket.
|
||||
*/
|
||||
int read_write(int fd, unsigned char *packet, int size, int rw)
|
||||
int read_writev(int fd, struct iovec *iov, int iovcnt, int rw)
|
||||
{
|
||||
ssize_t n, done;
|
||||
|
||||
for (done = 0; done < size; done += n)
|
||||
{
|
||||
if (rw & 1)
|
||||
n = read(fd, &packet[done], (size_t)(size - done));
|
||||
else
|
||||
n = write(fd, &packet[done], (size_t)(size - done));
|
||||
|
||||
if (n == 0)
|
||||
return 0;
|
||||
int cur = 0;
|
||||
ssize_t n, done = 0;
|
||||
|
||||
while (cur < iovcnt)
|
||||
{
|
||||
iov[cur].iov_len -= done;
|
||||
iov[cur].iov_base = ((char *)iov[cur].iov_base) + done;
|
||||
|
||||
if (rw & 1)
|
||||
n = readv(fd, &iov[cur], iovcnt - cur);
|
||||
else
|
||||
n = writev(fd, &iov[cur], iovcnt - cur);
|
||||
|
||||
iov[cur].iov_len += done;
|
||||
iov[cur].iov_base = ((char *)iov[cur].iov_base) - done;
|
||||
|
||||
if (n == -1)
|
||||
{
|
||||
n = 0; /* don't mess with counter when we loop. */
|
||||
|
||||
if (errno == EINTR || errno == ENOMEM || errno == ENOBUFS)
|
||||
continue;
|
||||
|
||||
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||
{
|
||||
/* "once" variant */
|
||||
if (rw & 2)
|
||||
return 0;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(rw & 2) && (errno == EAGAIN || errno == EWOULDBLOCK))
|
||||
continue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (n == 0 && (rw & 1))
|
||||
return 0;
|
||||
|
||||
done += n;
|
||||
while ((size_t)done >= iov[cur].iov_len)
|
||||
done -= iov[cur++].iov_len;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int read_write(int fd, unsigned char *packet, int size, int rw)
|
||||
{
|
||||
struct iovec iov;
|
||||
|
||||
/* size == 0 is not an error, just a NOOP. */
|
||||
if (size == 0)
|
||||
return 1;
|
||||
|
||||
iov.iov_len = (size_t)size;
|
||||
iov.iov_base = packet;
|
||||
|
||||
return read_writev(fd, &iov, 1, rw);
|
||||
}
|
||||
|
||||
/* close all fds except STDIN, STDOUT and STDERR, spare1, spare2 and spare3 */
|
||||
void close_fds(long max_fd, int spare1, int spare2, int spare3)
|
||||
{
|
||||
@@ -949,3 +946,42 @@ int kernel_version(void)
|
||||
return version * 256 + (split ? atoi(split) : 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define hash_ptr(x) (((unsigned int)(((char *)(x)) - ((char *)NULL))) & 0xffffff)
|
||||
|
||||
void *whine_malloc_real(const char *func, unsigned int line, size_t size)
|
||||
{
|
||||
void *ret = calloc(1, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to allocate %d bytes"), (int) size);
|
||||
else if (daemon->log_malloc)
|
||||
my_syslog(LOG_INFO, _("malloc: %s:%u %zu bytes at %x"), func, line, size, hash_ptr(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *whine_realloc_real(const char *func, unsigned int line, void *ptr, size_t size)
|
||||
{
|
||||
unsigned int old = hash_ptr(ptr);
|
||||
void *ret = realloc(ptr, size);
|
||||
|
||||
if (!ret)
|
||||
my_syslog(LOG_ERR, _("failed to reallocate %d bytes"), (int) size);
|
||||
else if (daemon->log_malloc)
|
||||
my_syslog(LOG_INFO, _("realloc: %s:%u %zu bytes from %x to %x"), func, line, size, old, hash_ptr(ret));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#undef free
|
||||
void free_real(const char *func, unsigned int line, void *ptr)
|
||||
{
|
||||
if (ptr)
|
||||
{
|
||||
if (daemon->log_malloc)
|
||||
my_syslog(LOG_INFO, _("free: %s:%u block at %x"), func, line, hash_ptr(ptr));
|
||||
|
||||
free(ptr);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user