summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2013-06-19 20:42:04 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2013-08-20 15:29:08 +0100
commit81d79a22012ba6823282e98aaede982127ee9125 (patch)
tree8e18bdb40ebd73cd71c32b921437f3389f4d7a10
parent6692a1a53f294f3043f9539a5b0453b4ccc32fd6 (diff)
downloaddnsmasq-hip.tar.gz
Initial checking, HIP query scafolding done. Not yet functional.hip
-rw-r--r--Makefile3
-rw-r--r--bld/Android.mk2
-rw-r--r--src/config.h8
-rw-r--r--src/dns-protocol.h1
-rw-r--r--src/dnsmasq.h12
-rw-r--r--src/forward.c171
-rw-r--r--src/hip.c71
-rw-r--r--src/option.c3
8 files changed, 232 insertions, 39 deletions
diff --git a/Makefile b/Makefile
index fe63aee..69931d3 100644
--- a/Makefile
+++ b/Makefile
@@ -65,7 +65,8 @@ version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
- dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o domain.o
+ dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
+ domain.o hip.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h
diff --git a/bld/Android.mk b/bld/Android.mk
index 46e4d03..9d179c2 100644
--- a/bld/Android.mk
+++ b/bld/Android.mk
@@ -8,7 +8,7 @@ LOCAL_SRC_FILES := bpf.c cache.c dbus.c dhcp.c dnsmasq.c \
netlink.c network.c option.c rfc1035.c \
rfc2131.c tftp.c util.c conntrack.c \
dhcp6.c rfc3315.c dhcp-common.c outpacket.c \
- radv.c slaac.c auth.c ipset.c domain.c
+ radv.c slaac.c auth.c ipset.c domain.c hip.c
LOCAL_MODULE := dnsmasq
diff --git a/src/config.h b/src/config.h
index 5224adf..e3cf598 100644
--- a/src/config.h
+++ b/src/config.h
@@ -105,6 +105,8 @@ HAVE_AUTH
define this to include the facility to act as an authoritative DNS
server for one or more zones.
+HAVE_HIP
+ define this to include Host information Protocol DNS-proxy functions.
NO_IPV6
NO_TFTP
@@ -113,6 +115,7 @@ NO_DHCP6
NO_SCRIPT
NO_LARGEFILE
NO_AUTH
+NO_HIP
these are avilable to explictly disable compile time options which would
otherwise be enabled automatically (HAVE_IPV6, >2Gb file sizes) or
which are enabled by default in the distributed source tree. Building dnsmasq
@@ -136,6 +139,7 @@ RESOLVFILE
#define HAVE_SCRIPT
#define HAVE_AUTH
#define HAVE_IPSET
+#define HAVE_HIP
/* #define HAVE_LUASCRIPT */
/* #define HAVE_BROKEN_RTC */
/* #define HAVE_DBUS */
@@ -327,6 +331,10 @@ HAVE_SOCKADDR_SA_LEN
#undef HAVE_AUTH
#endif
+#ifdef NO_HIP
+#undef HAVE_HIP
+#endif
+
#if defined(NO_IPSET) || !defined(HAVE_LINUX_NETWORK)
#undef HAVE_IPSET
#endif
diff --git a/src/dns-protocol.h b/src/dns-protocol.h
index 80eff72..eead79b 100644
--- a/src/dns-protocol.h
+++ b/src/dns-protocol.h
@@ -50,6 +50,7 @@
#define T_SRV 33
#define T_NAPTR 35
#define T_OPT 41
+#define T_HIP 55
#define T_TKEY 249
#define T_TSIG 250
#define T_AXFR 252
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
index f37034d..08a22f8 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -222,7 +222,8 @@ struct event_desc {
#define OPT_CLEVERBIND 39
#define OPT_TFTP 40
#define OPT_FAST_RA 41
-#define OPT_LAST 42
+#define OPT_HIP 42
+#define OPT_LAST 43
/* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */
@@ -487,6 +488,8 @@ struct hostsfile {
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
+#define FREC_HIP_V4 4
+#define FREC_HIP_V6 8
struct frec {
union mysockaddr source;
@@ -1272,3 +1275,10 @@ void slaac_add_addrs(struct dhcp_lease *lease, time_t now, int force);
time_t periodic_slaac(time_t now, struct dhcp_lease *leases);
void slaac_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface, struct dhcp_lease *leases);
#endif
+
+/* hip.c */
+#ifdef HAVE_HIP
+size_t answer_hip(struct dns_header *header, char *limit, size_t qlen);
+void hip_make_request(struct dns_header *header, size_t qlen);
+size_t hip_restore_request(struct dns_header *header, size_t qlen, int flags);
+#endif
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;
}
diff --git a/src/hip.c b/src/hip.c
new file mode 100644
index 0000000..58b3041
--- /dev/null
+++ b/src/hip.c
@@ -0,0 +1,71 @@
+/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley
+
+ 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
+ the Free Software Foundation; version 2 dated June, 1991, or
+ (at your option) version 3 dated 29 June, 2007.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "dnsmasq.h"
+
+#ifdef HAVE_HIP
+
+size_t answer_hip(struct dns_header *header, char *limit, size_t qlen)
+{
+ /* Answer A or AAAA request using cached HIT */
+ return 0;
+}
+
+void hip_make_request(struct dns_header *header, size_t qlen)
+{
+ unsigned char *p = (unsigned char *)(header+1);
+
+ if (ntohs(header->qdcount) != 1 || OPCODE(header) != QUERY)
+ return; /* must be exactly one query. */
+
+ if (!extract_name(header, qlen, &p, daemon->namebuff, 1, 4))
+ return; /* bad packet */
+
+ PUTSHORT(T_HIP, p);
+}
+
+size_t hip_restore_request(struct dns_header *header, size_t qlen, int flags)
+{
+ unsigned char *p = (unsigned char *)(header+1);
+ unsigned short type = T_A;
+ size_t nn, hlen;
+ unsigned char *pheader;
+
+#ifdef HAVE_IPV6
+ if (flags & FREC_HIP_V6)
+ type = T_AAAA;
+#endif
+
+ if (ntohs(header->qdcount) != 1)
+ return 0; /* must be exactly one query. */
+
+ if (!extract_name(header, qlen, &p, daemon->namebuff, 1, 4))
+ return 0; /* bad packet */
+
+ PUTSHORT(type, p);
+
+ /* and remove answers */
+ pheader = find_pseudoheader(header, qlen, &hlen, NULL, NULL);
+ header->ancount = htons(0);
+ header->nscount = htons(0);
+ header->arcount = htons(0);
+ nn = resize_packet(header, qlen, pheader, hlen);
+
+ return nn;
+}
+
+
+#endif
diff --git a/src/option.c b/src/option.c
index b03388a..98ba5d5 100644
--- a/src/option.c
+++ b/src/option.c
@@ -133,6 +133,7 @@ struct myoption {
#define LOPT_PREF_CLSS 321
#endif
#define LOPT_FAST_RA 322
+#define LOPT_HIP 323
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -271,6 +272,7 @@ static const struct myoption opts[] =
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
{ "force-fast-ra", 0, 0, LOPT_FAST_RA },
+ { "proxy-hip", 0, 0, LOPT_HIP },
{ NULL, 0, 0, 0 }
};
@@ -415,6 +417,7 @@ static struct {
#ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif
+ { LOPT_HIP, OPT_HIP, NULL, gettext_noop("Rewrite DNS requests for HIP"), NULL },
{ 0, 0, NULL, NULL, NULL }
};