summaryrefslogtreecommitdiff
path: root/src/forward.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/forward.c')
-rw-r--r--src/forward.c171
1 files changed, 135 insertions, 36 deletions
diff --git a/src/forward.c b/src/forward.c
index 6c9f646..1e4412d 100644
--- a/src/forward.c
+++ b/src/forward.c
@@ -234,7 +234,8 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *dst_addr, unsigned int dst_iface,
- struct dns_header *header, size_t plen, time_t now, struct frec *forward)
+ struct dns_header *header, size_t plen, time_t now,
+ struct frec *forward, int hip)
{
char *domain = NULL;
int type = 0, norebind = 0;
@@ -282,8 +283,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->orig_id = ntohs(header->id);
forward->new_id = get_id(crc);
forward->fd = udpfd;
- forward->crc = crc;
forward->forwardall = 0;
+ forward->flags |= hip;
if (norebind)
forward->flags |= FREC_NOREBIND;
if (header->hb4 & HB4_CD)
@@ -318,7 +319,9 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
}
}
-
+
+ forward->crc = crc;
+
/* check for send errors here (no route to host)
if we fail to send to all nameservers, send back an error
packet straight away (helps modem users when offline) */
@@ -436,7 +439,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
}
static size_t process_reply(struct dns_header *header, time_t now,
- struct server *server, size_t n, int check_rebind, int checking_disabled)
+ struct server *server, size_t n, int check_rebind, int flags)
{
unsigned char *pheader, *sizep;
char **sets = 0;
@@ -513,13 +516,24 @@ static size_t process_reply(struct dns_header *header, time_t now,
SET_RCODE(header, NOERROR);
}
- if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, checking_disabled))
+ if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, flags & FREC_CHECKING_DISABLED))
{
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1;
}
}
+#ifdef HAVE_HIP
+ if (flags & (FREC_HIP_V4 | FREC_HIP_V6))
+ {
+ /* By now, there should be enough information in the cache to answer the
+ question. Restore the orignal question, and get the answer. */
+
+ n = hip_restore_request(header, n, flags);
+ return answer_hip(header, ((char *) header) + PACKETSZ, n);
+ }
+#endif
+
/* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */
@@ -573,7 +587,7 @@ void reply_query(int fd, int family, time_t now)
return;
server = forward->sentto;
-
+
if ((RCODE(header) == SERVFAIL || RCODE(header) == REFUSED) &&
!option_bool(OPT_ORDER) &&
forward->forwardall == 0)
@@ -593,11 +607,25 @@ void reply_query(int fd, int family, time_t now)
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->hb3 &= ~(HB3_QR | HB3_TC);
- forward_query(-1, NULL, NULL, 0, header, nn, now, forward);
+ forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0);
return;
}
}
}
+
+#ifdef HAVE_HIP
+ if ((forward->flags & (FREC_HIP_V4 | FREC_HIP_V6)) &&
+ RCODE(header) == NOERROR &&
+ ntohs(header->ancount) == 0 &&
+ (nn = hip_restore_request(header, n, forward->flags)))
+ {
+ /* No HIP records returned, try again for A/AAAA record */
+ forward->flags &= ~(FREC_HIP_V4 | FREC_HIP_V6);
+ header->hb3 &= ~(HB3_QR | HB3_TC);
+ forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0);
+ return;
+ }
+#endif
if ((forward->sentto->flags & SERV_TYPE) == 0)
{
@@ -632,7 +660,7 @@ void reply_query(int fd, int family, time_t now)
if (!option_bool(OPT_NO_REBIND))
check_rebind = 0;
- if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags & FREC_CHECKING_DISABLED)))
+ if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, forward->flags)))
{
header->id = htons(forward->orig_id);
header->hb4 |= HB4_RA; /* recursion if available */
@@ -655,6 +683,7 @@ void receive_query(struct listener *listen, time_t now)
ssize_t n;
int if_index = 0;
int auth_dns = 0;
+ int do_hip = 0;
struct iovec iov[1];
struct msghdr msg;
struct cmsghdr *cmptr;
@@ -837,6 +866,17 @@ void receive_query(struct listener *listen, time_t now)
log_query(F_QUERY | F_IPV6 | F_FORWARD, daemon->namebuff,
(struct all_addr *)&source_addr.in6.sin6_addr, types);
#endif
+
+#ifdef HAVE_HIP
+ if (type == T_A)
+ do_hip = FREC_HIP_V4;
+
+#ifdef HAVE_IPV6
+ if (type == T_AAAA)
+ do_hip = FREC_HIP_V6;
+#endif
+
+#endif
}
#ifdef HAVE_AUTH
@@ -850,8 +890,22 @@ void receive_query(struct listener *listen, time_t now)
else
#endif
{
- m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
- dst_addr_4, netmask, now);
+
+#ifdef HAVE_HIP
+ if (!option_bool(OPT_HIP))
+ do_hip = 0;
+
+ if (do_hip)
+ {
+ m = answer_hip(header, ((char *) header) + PACKETSZ, (size_t)n);
+
+ if (m == 0)
+ hip_make_request(header, (size_t)n);
+ }
+ else
+#endif
+ m = answer_request(header, ((char *) header) + PACKETSZ, (size_t)n,
+ dst_addr_4, netmask, now);
if (m >= 1)
{
@@ -860,7 +914,7 @@ void receive_query(struct listener *listen, time_t now)
daemon->local_answer++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
- header, (size_t)n, now, NULL))
+ header, (size_t)n, now, NULL, do_hip))
daemon->queries_forwarded++;
else
daemon->local_answer++;
@@ -876,6 +930,7 @@ unsigned char *tcp_request(int confd, time_t now,
{
size_t size = 0;
int norebind = 0;
+ int do_hip = 0;
int checking_disabled;
size_t m;
unsigned short qtype;
@@ -907,7 +962,10 @@ unsigned char *tcp_request(int confd, time_t now,
continue;
/* save state of "cd" flag in query */
- checking_disabled = header->hb4 & HB4_CD;
+ if (header->hb4 & HB4_CD)
+ checking_disabled = FREC_CHECKING_DISABLED;
+ else
+ checking_disabled = 0;
/* RFC 4035: sect 4.6 para 2 */
header->hb4 &= ~HB4_AD;
@@ -933,15 +991,41 @@ unsigned char *tcp_request(int confd, time_t now,
else
dst_addr_4.s_addr = 0;
+#ifdef HAVE_HIP
+ if (qtype == T_A)
+ do_hip = FREC_HIP_V4;
+
+#ifdef HAVE_IPV6
+ if (qtype == T_AAAA)
+ do_hip = FREC_HIP_V6;
+#endif
+
+#endif
+
+
#ifdef HAVE_AUTH
if (auth_dns)
m = answer_auth(header, ((char *) header) + 65536, (size_t)size, now, &peer_addr);
else
#endif
{
- /* m > 0 if answered from cache */
- m = answer_request(header, ((char *) header) + 65536, (size_t)size,
- dst_addr_4, netmask, now);
+
+#ifdef HAVE_HIP
+ if (!option_bool(OPT_HIP))
+ do_hip = 0;
+
+ if (do_hip)
+ {
+ m = answer_hip(header, ((char *) header) + 65536, (size_t)size);
+
+ if (m == 0)
+ hip_make_request(header, (size_t)size);
+ }
+ else
+#endif
+ /* m > 0 if answered from cache */
+ m = answer_request(header, ((char *) header) + 65536, (size_t)size,
+ dst_addr_4, netmask, now);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
@@ -967,7 +1051,7 @@ unsigned char *tcp_request(int confd, time_t now,
if (!flags && last_server)
{
struct server *firstsendto = NULL;
- unsigned int crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
+ unsigned int crc;
/* Loop round available servers until we succeed in connecting to one.
Note that this code subtley ensures that consecutive queries on this connection
@@ -1022,7 +1106,9 @@ unsigned char *tcp_request(int confd, time_t now,
#endif
}
+ hip_restart:
*length = htons(size);
+ crc = questions_crc(header, (unsigned int)size, daemon->namebuff);
if (!read_write(last_server->tcpfd, packet, size + sizeof(u16), 0) ||
!read_write(last_server->tcpfd, &c1, 1, 1) ||
@@ -1037,41 +1123,54 @@ unsigned char *tcp_request(int confd, time_t now,
if (!read_write(last_server->tcpfd, payload, m, 1))
return packet;
- if (!gotname)
- strcpy(daemon->namebuff, "query");
- if (last_server->addr.sa.sa_family == AF_INET)
- log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&last_server->addr.in.sin_addr, NULL);
-#ifdef HAVE_IPV6
- else
- log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
- (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
-#endif
-
- /* There's no point in updating the cache, since this process will exit and
- lose the information after a few queries. We make this call for the alias and
- bogus-nxdomain side-effects. */
/* If the crc of the question section doesn't match the crc we sent, then
someone might be attempting to insert bogus values into the cache by
sending replies containing questions and bogus answers. */
if (crc == questions_crc(header, (unsigned int)m, daemon->namebuff))
- m = process_reply(header, now, last_server, (unsigned int)m,
- option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
-
+ {
+ if (do_hip &&
+ RCODE(header) == NOERROR &&
+ ntohs(header->ancount) == 0 &&
+ (size = hip_restore_request(header, size, do_hip)))
+ {
+ /* No HIP records returned, try again for A/AAAA record */
+ do_hip = 0;
+ header->hb3 &= ~(HB3_QR | HB3_TC);
+ goto hip_restart;
+ }
+
+ if (!gotname)
+ strcpy(daemon->namebuff, "query");
+ if (last_server->addr.sa.sa_family == AF_INET)
+ log_query(F_SERVER | F_IPV4 | F_FORWARD, daemon->namebuff,
+ (struct all_addr *)&last_server->addr.in.sin_addr, NULL);
+#ifdef HAVE_IPV6
+ else
+ log_query(F_SERVER | F_IPV6 | F_FORWARD, daemon->namebuff,
+ (struct all_addr *)&last_server->addr.in6.sin6_addr, NULL);
+#endif
+
+ /* There's no point in updating the cache, since this process will exit and
+ lose the information after a few queries. We make this call for the alias and
+ bogus-nxdomain side-effects. */
+
+ m = process_reply(header, now, last_server, (unsigned int)m,
+ option_bool(OPT_NO_REBIND) && !norebind, checking_disabled);
+ }
break;
}
}
-
+
/* In case of local answer or no connections made. */
if (m == 0)
m = setup_reply(header, (unsigned int)size, addrp, flags, daemon->local_ttl);
}
}
-
+
check_log_writer(NULL);
*length = htons(m);
-
+
if (m == 0 || !read_write(confd, packet, m + sizeof(u16), 0))
return packet;
}