diff --git a/scripts/base/init-bare.bro b/scripts/base/init-bare.bro index 3f13bd232a..600a507d4f 100644 --- a/scripts/base/init-bare.bro +++ b/scripts/base/init-bare.bro @@ -3656,6 +3656,12 @@ global dns_skip_all_addl = T &redef; ## traffic and do not process it. Set to 0 to turn off this functionality. global dns_max_queries = 25 &redef; +## The address of the DNS resolver to use. If not changed from the +## unspecified address, ``[::]``, the first nameserver from /etc/resolv.conf +## gets used (IPv6 is currently only supported if set via this option, not +## when parsed from the file). +const dns_resolver = [::] &redef; + ## HTTP session statistics. ## ## .. bro:see:: http_stats diff --git a/src/DNS_Mgr.cc b/src/DNS_Mgr.cc index 7f651b5fdd..266217c02a 100644 --- a/src/DNS_Mgr.cc +++ b/src/DNS_Mgr.cc @@ -376,12 +376,6 @@ DNS_Mgr::DNS_Mgr(DNS_MgrMode arg_mode) mode = arg_mode; - char err[NB_DNS_ERRSIZE]; - nb_dns = nb_dns_init(err); - - if ( ! nb_dns ) - reporter->Warning("problem initializing NB-DNS: %s", err); - dns_mapping_valid = dns_mapping_unverified = dns_mapping_new_name = dns_mapping_lost_name = dns_mapping_name_changed = dns_mapping_altered = 0; @@ -410,6 +404,35 @@ void DNS_Mgr::InitPostScript() if ( did_init ) return; + auto dns_resolver_id = global_scope()->Lookup("dns_resolver"); + auto dns_resolver_addr = dns_resolver_id->ID_Val()->AsAddr(); + char err[NB_DNS_ERRSIZE]; + + if ( dns_resolver_addr == IPAddr("::") ) + nb_dns = nb_dns_init(err); + else + { + struct sockaddr_storage ss = {0}; + + if ( dns_resolver_addr.GetFamily() == IPv4 ) + { + struct sockaddr_in* sa = (struct sockaddr_in*)&ss; + sa->sin_family = AF_INET; + dns_resolver_addr.CopyIPv4(&sa->sin_addr); + } + else + { + struct sockaddr_in6* sa = (struct sockaddr_in6*)&ss; + sa->sin6_family = AF_INET6; + dns_resolver_addr.CopyIPv6(&sa->sin6_addr); + } + + nb_dns = nb_dns_init2(err, (struct sockaddr*)&ss); + } + + if ( ! nb_dns ) + reporter->Warning("problem initializing NB-DNS: %s", err); + const char* cache_dir = dir ? dir : "."; if ( mode == DNS_PRIME && ! ensure_dir(cache_dir) ) @@ -471,15 +494,15 @@ static const char* fake_addr_lookup_result(const IPAddr& addr) TableVal* DNS_Mgr::LookupHost(const char* name) { + if ( mode == DNS_FAKE ) + return fake_name_lookup_result(name); + if ( ! nb_dns ) return empty_addr_set(); if ( ! did_init ) Init(); - if ( mode == DNS_FAKE ) - return fake_name_lookup_result(name); - if ( mode != DNS_PRIME ) { HostMap::iterator it = host_mappings.find(name); diff --git a/src/nb_dns.c b/src/nb_dns.c index a1f0d018dd..d5e6d9d3d9 100644 --- a/src/nb_dns.c +++ b/src/nb_dns.c @@ -86,7 +86,7 @@ struct nb_dns_hostent { struct nb_dns_info { int s; /* Resolver file descriptor */ - struct sockaddr_in server; /* server address to bind to */ + struct sockaddr_storage server; /* server address to bind to */ struct nb_dns_entry *list; /* outstanding requests */ struct nb_dns_hostent dns_hostent; }; @@ -109,6 +109,18 @@ my_strerror(int errnum) #endif } +static const char* sa_ntop(struct sockaddr* sa, char* buf, int len) + { + if ( sa->sa_family == AF_INET ) + return inet_ntop(sa->sa_family, + &(((struct sockaddr_in*)sa)->sin_addr), + buf, len); + else + return inet_ntop(sa->sa_family, + &(((struct sockaddr_in6*)sa)->sin6_addr), + buf, len); + } + struct nb_dns_info * nb_dns_init(char *errstr) { @@ -136,13 +148,12 @@ nb_dns_init(char *errstr) for ( i = 0; i < _res.nscount; ++i ) { - nd->server = _res.nsaddr_list[i]; - + memcpy(&nd->server, &_res.nsaddr_list[i], sizeof(struct sockaddr_in)); /* XXX support IPv6 */ - if ( nd->server.sin_family != AF_INET ) + if ( nd->server.ss_family != AF_INET ) continue; - nd->s = socket(nd->server.sin_family, SOCK_DGRAM, 0); + nd->s = socket(nd->server.ss_family, SOCK_DGRAM, 0); if ( nd->s < 0 ) { @@ -153,10 +164,14 @@ nb_dns_init(char *errstr) } if ( connect(nd->s, (struct sockaddr *)&nd->server, - sizeof(struct sockaddr)) < 0 ) + nd->server.ss_family == AF_INET ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)) < 0 ) { - snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", - inet_ntoa(nd->server.sin_addr), my_strerror(errno)); + char s[INET6_ADDRSTRLEN]; + sa_ntop((struct sockaddr*)&nd->server, s, INET6_ADDRSTRLEN); + snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", s, + my_strerror(errno)); close(nd->s); free(nd); return (NULL); @@ -170,6 +185,58 @@ nb_dns_init(char *errstr) return (NULL); } +struct nb_dns_info * +nb_dns_init2(char *errstr, struct sockaddr* sa) +{ + register struct nb_dns_info *nd; + + nd = (struct nb_dns_info *)malloc(sizeof(*nd)); + if (nd == NULL) { + snprintf(errstr, NB_DNS_ERRSIZE, "nb_dns_init: malloc(): %s", + my_strerror(errno)); + return (NULL); + } + memset(nd, 0, sizeof(*nd)); + nd->s = -1; + + if ( sa->sa_family == AF_INET ) + { + memcpy(&nd->server, sa, sizeof(struct sockaddr_in)); + ((struct sockaddr_in*)&nd->server)->sin_port = htons(53); + } + else + { + memcpy(&nd->server, sa, sizeof(struct sockaddr_in6)); + ((struct sockaddr_in6*)&nd->server)->sin6_port = htons(53); + } + + nd->s = socket(nd->server.ss_family, SOCK_DGRAM, 0); + + if ( nd->s < 0 ) + { + snprintf(errstr, NB_DNS_ERRSIZE, "socket(): %s", + my_strerror(errno)); + free(nd); + return (NULL); + } + + if ( connect(nd->s, (struct sockaddr *)&nd->server, + nd->server.ss_family == AF_INET ? + sizeof(struct sockaddr_in) : + sizeof(struct sockaddr_in6)) < 0 ) + { + char s[INET6_ADDRSTRLEN]; + sa_ntop((struct sockaddr*)&nd->server, s, INET6_ADDRSTRLEN); + snprintf(errstr, NB_DNS_ERRSIZE, "connect(%s): %s", s, + my_strerror(errno)); + close(nd->s); + free(nd); + return (NULL); + } + + return (nd); +} + void nb_dns_finish(struct nb_dns_info *nd) { @@ -226,12 +293,12 @@ _nb_dns_cmpsockaddr(register struct sockaddr *sa1, sin6a = (struct sockaddr_in6 *)sa1; sin6b = (struct sockaddr_in6 *)sa2; if (sin6a->sin6_port != sin6b->sin6_port) { - snprintf(errstr, NB_DNS_ERRSIZE, serr, 2); + snprintf(errstr, NB_DNS_ERRSIZE, serr, 62); return (-1); } if (memcmp(&sin6a->sin6_addr, &sin6b->sin6_addr, sizeof(sin6a->sin6_addr)) != 0) { - snprintf(errstr, NB_DNS_ERRSIZE, serr, 3); + snprintf(errstr, NB_DNS_ERRSIZE, serr, 63); return (-1); } break; @@ -446,7 +513,7 @@ nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr) register int msglen, qtype, atype, n, i; register struct nb_dns_entry *ne, *lastne; socklen_t fromlen; - struct sockaddr from; + struct sockaddr_storage from; u_long msg[MAXPACKET / sizeof(u_long)]; register char *bp, *ep; register char **ap, **hap; @@ -460,7 +527,8 @@ nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr) /* This comes from the second half of do_query() */ fromlen = sizeof(from); - msglen = recvfrom(nd->s, (char *)msg, sizeof(msg), 0, &from, &fromlen); + msglen = recvfrom(nd->s, (char *)msg, sizeof(msg), 0, + (struct sockaddr*)&from, &fromlen); if (msglen <= 0) { snprintf(errstr, NB_DNS_ERRSIZE, "recvfrom(): %s", my_strerror(errno)); @@ -479,8 +547,8 @@ nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr) } /* RES_INSECURE1 style check */ - if (_nb_dns_cmpsockaddr((struct sockaddr *)&nd->server, &from, - errstr) < 0) { + if (_nb_dns_cmpsockaddr((struct sockaddr*)&nd->server, + (struct sockaddr*)&from, errstr) < 0) { nr->host_errno = NO_RECOVERY; return (-1); } diff --git a/src/nb_dns.h b/src/nb_dns.h index 33e09aa226..eba9d39084 100644 --- a/src/nb_dns.h +++ b/src/nb_dns.h @@ -17,6 +17,7 @@ typedef unsigned int nb_uint32_t; /* Public routines */ struct nb_dns_info *nb_dns_init(char *); +struct nb_dns_info *nb_dns_init2(char *, struct sockaddr*); void nb_dns_finish(struct nb_dns_info *); int nb_dns_fd(struct nb_dns_info *);