diff --git a/CHANGES b/CHANGES index 8d974fa125..49fd8fd062 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,9 @@ +2.1-104 | 2012-11-01 10:37:50 -0700 + + * A new built-in function lookup_hostname_txt() provides support for + DNS TXT queries. (Vlad Grigorescu) + 2.1-101 | 2012-10-31 14:30:26 -0700 * Documentation reorg: The install info has been consolidated into a diff --git a/VERSION b/VERSION index 22f0551932..973961c7a6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1-101 +2.1-104 diff --git a/src/DNS_Mgr.cc b/src/DNS_Mgr.cc index 6b0f18f459..774730384a 100644 --- a/src/DNS_Mgr.cc +++ b/src/DNS_Mgr.cc @@ -46,13 +46,15 @@ extern int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); class DNS_Mgr_Request { public: - DNS_Mgr_Request(const char* h, int af) { host = copy_string(h); fam = af; } + DNS_Mgr_Request(const char* h, int af, bool is_txt) + { host = copy_string(h); fam = af; qtype = is_txt ? 16 : 0; } DNS_Mgr_Request(const IPAddr& a) { addr = a; host = 0; fam = 0; } ~DNS_Mgr_Request() { delete [] host; } // Returns nil if this was an address request. const char* ReqHost() const { return host; } const IPAddr& ReqAddr() const { return addr; } + const bool ReqIsTxt() const { return qtype == 16; } int MakeRequest(nb_dns_info* nb_dns); int RequestPending() const { return request_pending; } @@ -61,7 +63,8 @@ public: protected: char* host; // if non-nil, this is a host request - int fam; // address family query type for host requests + int fam; // address family query type for host requests + int qtype; // Query type IPAddr addr; int request_pending; }; @@ -75,7 +78,7 @@ int DNS_Mgr_Request::MakeRequest(nb_dns_info* nb_dns) char err[NB_DNS_ERRSIZE]; if ( host ) - return nb_dns_host_request2(nb_dns, host, fam, (void*) this, err) >= 0; + return nb_dns_host_request2(nb_dns, host, fam, qtype, (void*) this, err) >= 0; else { const uint32* bytes; @@ -475,8 +478,8 @@ TableVal* DNS_Mgr::LookupHost(const char* name) // Not found, or priming. switch ( mode ) { case DNS_PRIME: - requests.append(new DNS_Mgr_Request(name, AF_INET)); - requests.append(new DNS_Mgr_Request(name, AF_INET6)); + requests.append(new DNS_Mgr_Request(name, AF_INET, false)); + requests.append(new DNS_Mgr_Request(name, AF_INET6, false)); return empty_addr_set(); case DNS_FORCE: @@ -484,8 +487,8 @@ TableVal* DNS_Mgr::LookupHost(const char* name) return 0; case DNS_DEFAULT: - requests.append(new DNS_Mgr_Request(name, AF_INET)); - requests.append(new DNS_Mgr_Request(name, AF_INET6)); + requests.append(new DNS_Mgr_Request(name, AF_INET, false)); + requests.append(new DNS_Mgr_Request(name, AF_INET6, false)); Resolve(); return LookupHost(name); @@ -636,6 +639,7 @@ int DNS_Mgr::Save() Save(f, host_mappings); Save(f, addr_mappings); + // Save(f, text_mappings); // We don't save the TXT mappings (yet?). fclose(f); @@ -704,39 +708,48 @@ void DNS_Mgr::AddResult(DNS_Mgr_Request* dr, struct nb_dns_result* r) new_dm = new DNS_Mapping(dr->ReqHost(), h, ttl); prev_dm = 0; - HostMap::iterator it = host_mappings.find(dr->ReqHost()); - if ( it == host_mappings.end() ) + if ( dr->ReqIsTxt() ) { - host_mappings[dr->ReqHost()].first = - new_dm->Type() == AF_INET ? new_dm : 0; - host_mappings[dr->ReqHost()].second = - new_dm->Type() == AF_INET ? 0 : new_dm; + TextMap::iterator it = text_mappings.find(dr->ReqHost()); + if ( it == text_mappings.end() ) + text_mappings[dr->ReqHost()] = new_dm; } - else { - if ( new_dm->Type() == AF_INET ) + HostMap::iterator it = host_mappings.find(dr->ReqHost()); + if ( it == host_mappings.end() ) { - prev_dm = it->second.first; - it->second.first = new_dm; + host_mappings[dr->ReqHost()].first = + new_dm->Type() == AF_INET ? new_dm : 0; + + host_mappings[dr->ReqHost()].second = + new_dm->Type() == AF_INET ? 0 : new_dm; } else { - prev_dm = it->second.second; - it->second.second = new_dm; + if ( new_dm->Type() == AF_INET ) + { + prev_dm = it->second.first; + it->second.first = new_dm; + } + else + { + prev_dm = it->second.second; + it->second.second = new_dm; + } } - } - if ( new_dm->Failed() && prev_dm && prev_dm->Valid() ) - { - // Put previous, valid entry back - CompareMappings - // will generate a corresponding warning. - if ( prev_dm->Type() == AF_INET ) - host_mappings[dr->ReqHost()].first = prev_dm; - else - host_mappings[dr->ReqHost()].second = prev_dm; + if ( new_dm->Failed() && prev_dm && prev_dm->Valid() ) + { + // Put previous, valid entry back - CompareMappings + // will generate a corresponding warning. + if ( prev_dm->Type() == AF_INET ) + host_mappings[dr->ReqHost()].first = prev_dm; + else + host_mappings[dr->ReqHost()].second = prev_dm; - ++keep_prev; + ++keep_prev; + } } } else @@ -928,7 +941,10 @@ TableVal* DNS_Mgr::LookupNameInCache(string name) { HostMap::iterator it = dns_mgr->host_mappings.find(name); if ( it == dns_mgr->host_mappings.end() ) + { + it = dns_mgr->host_mappings.begin(); return 0; + } DNS_Mapping* d4 = it->second.first; DNS_Mapping* d6 = it->second.second; @@ -951,6 +967,26 @@ TableVal* DNS_Mgr::LookupNameInCache(string name) return tv6; } +const char* DNS_Mgr::LookupTextInCache(string name) + { + TextMap::iterator it = dns_mgr->text_mappings.find(name); + if ( it == dns_mgr->text_mappings.end() ) + return 0; + + DNS_Mapping* d = it->second; + + if ( d->Expired() ) + { + dns_mgr->text_mappings.erase(it); + delete d; + return 0; + } + + // The escapes in the following strings are to avoid having it + // interpreted as a trigraph sequence. + return d->names ? d->names[0] : "<\?\?\?>"; + } + void DNS_Mgr::AsyncLookupAddr(const IPAddr& host, LookupCallback* callback) { if ( ! did_init ) @@ -976,6 +1012,7 @@ void DNS_Mgr::AsyncLookupAddr(const IPAddr& host, LookupCallback* callback) // A new one. req = new AsyncRequest; req->host = host; + req->is_txt = false; asyncs_queued.push_back(req); asyncs_addrs.insert(AsyncRequestAddrMap::value_type(host, req)); } @@ -985,19 +1022,36 @@ void DNS_Mgr::AsyncLookupAddr(const IPAddr& host, LookupCallback* callback) IssueAsyncRequests(); } -void DNS_Mgr::AsyncLookupName(string name, LookupCallback* callback) +void DNS_Mgr::AsyncLookupName(string name, LookupCallback* callback, bool is_txt) { if ( ! did_init ) Init(); // Do we already know the answer? - TableVal* addrs = LookupNameInCache(name); - if ( addrs ) + + if ( is_txt ) { - callback->Resolved(addrs); - Unref(addrs); - delete callback; - return; + const char* text = LookupTextInCache(name); + + if ( text ) + { + callback->Resolved(text); + delete callback; + return; + } + } + + else + { + TableVal* addrs = LookupNameInCache(name); + + if ( addrs ) + { + callback->Resolved(addrs); + Unref(addrs); + delete callback; + return; + } } AsyncRequest* req = 0; @@ -1011,6 +1065,7 @@ void DNS_Mgr::AsyncLookupName(string name, LookupCallback* callback) // A new one. req = new AsyncRequest; req->name = name; + req->is_txt = is_txt; asyncs_queued.push_back(req); asyncs_names.insert(AsyncRequestNameMap::value_type(name, req)); } @@ -1036,8 +1091,9 @@ void DNS_Mgr::IssueAsyncRequests() dr = new DNS_Mgr_Request(req->host); else { - dr = new DNS_Mgr_Request(req->name.c_str(), AF_INET); - dr6 = new DNS_Mgr_Request(req->name.c_str(), AF_INET6); + dr = new DNS_Mgr_Request(req->name.c_str(), AF_INET, req->is_txt); + if ( ! req->is_txt ) + dr6 = new DNS_Mgr_Request(req->name.c_str(), AF_INET6, req->is_txt); } if ( ! dr->MakeRequest(nb_dns) ) @@ -1109,6 +1165,39 @@ void DNS_Mgr::CheckAsyncAddrRequest(const IPAddr& addr, bool timeout) } +void DNS_Mgr::CheckAsyncTextRequest(const char* host, bool timeout) + { + // Note that this code is a mirror of that for CheckAsyncAddrRequest. + + AsyncRequestTextMap::iterator i = asyncs_texts.find(host); + + if ( i != asyncs_texts.end() ) + { + const char* name = LookupTextInCache(host); + if ( name ) + { + ++successful; + i->second->Resolved(name); + } + + else if ( timeout ) + { + AsyncRequestTextMap::iterator it = asyncs_texts.begin(); + ++failed; + i->second->Timeout(); + } + + else + return; + + asyncs_texts.erase(i); + --asyncs_pending; + + // Don't delete the request. That will be done once it + // eventually times out. + } + } + void DNS_Mgr::CheckAsyncHostRequest(const char* host, bool timeout) { // Note that this code is a mirror of that for CheckAsyncAddrRequest. @@ -1157,8 +1246,12 @@ void DNS_Mgr::Flush() for ( AddrMap::iterator it2 = addr_mappings.begin(); it2 != addr_mappings.end(); ++it2 ) delete it2->second; + for ( TextMap::iterator it3 = text_mappings.begin(); it3 != text_mappings.end(); ++it3 ) + delete it3->second; + host_mappings.clear(); addr_mappings.clear(); + text_mappings.clear(); } void DNS_Mgr::Process() @@ -1177,6 +1270,8 @@ void DNS_Mgr::DoProcess(bool flush) if ( req->IsAddrReq() ) CheckAsyncAddrRequest(req->host, true); + else if ( req->is_txt ) + CheckAsyncTextRequest(req->name.c_str(), true); else CheckAsyncHostRequest(req->name.c_str(), true); @@ -1184,7 +1279,7 @@ void DNS_Mgr::DoProcess(bool flush) delete req; } - if ( asyncs_addrs.size() == 0 && asyncs_names.size() == 0 ) + if ( asyncs_addrs.size() == 0 && asyncs_names.size() == 0 && asyncs_texts.size() == 0 ) return; if ( AnswerAvailable(0) <= 0 ) @@ -1217,6 +1312,8 @@ void DNS_Mgr::DoProcess(bool flush) if ( ! dr->ReqHost() ) CheckAsyncAddrRequest(dr->ReqAddr(), true); + else if ( dr->ReqIsTxt() ) + CheckAsyncTextRequest(dr->ReqHost(), do_host_timeout); else CheckAsyncHostRequest(dr->ReqHost(), do_host_timeout); @@ -1271,5 +1368,6 @@ void DNS_Mgr::GetStats(Stats* stats) stats->pending = asyncs_pending; stats->cached_hosts = host_mappings.size(); stats->cached_addresses = addr_mappings.size(); + stats->cached_texts = text_mappings.size(); } diff --git a/src/DNS_Mgr.h b/src/DNS_Mgr.h index 7983a91573..369b2efc53 100644 --- a/src/DNS_Mgr.h +++ b/src/DNS_Mgr.h @@ -63,6 +63,7 @@ public: const char* LookupAddrInCache(const IPAddr& addr); TableVal* LookupNameInCache(string name); + const char* LookupTextInCache(string name); // Support for async lookups. class LookupCallback { @@ -76,7 +77,9 @@ public: }; void AsyncLookupAddr(const IPAddr& host, LookupCallback* callback); - void AsyncLookupName(string name, LookupCallback* callback); + + // If is_txt is true, perform a TXT lookup. + void AsyncLookupName(string name, LookupCallback* callback, bool is_txt = false); struct Stats { unsigned long requests; // These count only async requests. @@ -85,6 +88,7 @@ public: unsigned long pending; unsigned long cached_hosts; unsigned long cached_addresses; + unsigned long cached_texts; }; void GetStats(Stats* stats); @@ -106,6 +110,7 @@ protected: typedef map > HostMap; typedef map AddrMap; + typedef map TextMap; void LoadCache(FILE* f); void Save(FILE* f, const AddrMap& m); void Save(FILE* f, const HostMap& m); @@ -122,6 +127,7 @@ protected: // requested. void CheckAsyncAddrRequest(const IPAddr& addr, bool timeout); void CheckAsyncHostRequest(const char* host, bool timeout); + void CheckAsyncTextRequest(const char* host, bool timeout); // Process outstanding requests. void DoProcess(bool flush); @@ -138,6 +144,7 @@ protected: HostMap host_mappings; AddrMap addr_mappings; + TextMap text_mappings; DNS_mgr_request_list requests; @@ -165,6 +172,7 @@ protected: double time; IPAddr host; string name; + bool is_txt; CallbackList callbacks; bool IsAddrReq() const { return name.length() == 0; } @@ -210,6 +218,9 @@ protected: typedef map AsyncRequestNameMap; AsyncRequestNameMap asyncs_names; + typedef map AsyncRequestTextMap; + AsyncRequestTextMap asyncs_texts; + typedef list QueuedList; QueuedList asyncs_queued; diff --git a/src/bro.bif b/src/bro.bif index 1b1c23950d..b5e45ea5fe 100644 --- a/src/bro.bif +++ b/src/bro.bif @@ -3770,6 +3770,36 @@ function lookup_addr%(host: addr%) : string return 0; %} +## Issues an asynchronous TEXT DNS lookup and delays the function result. +## This function can therefore only be called inside a ``when`` condition, +## e.g., ``when ( local h = lookup_hostname_txt("www.bro-ids.org") ) { f(h); }``. +## +## host: The hostname to lookup. +## +## Returns: A set of DNS A and AAAA records associated with *host*. +## +## .. bro:see:: lookup_hostname +function lookup_hostname_txt%(host: string%) : addr_set + %{ + // FIXME: Is should be easy to adapt the function to synchronous + // lookups if we're reading a trace. + Trigger* trigger = frame->GetTrigger(); + + if ( ! trigger) + { + builtin_error("lookup_hostname_txt() can only be called inside a when-condition"); + return new StringVal(""); + } + + frame->SetDelayed(); + trigger->Hold(); + + dns_mgr->AsyncLookupName(host->CheckString(), + new LookupHostCallback(trigger, frame->GetCall(), false), + true); + return 0; + %} + ## Issues an asynchronous DNS lookup and delays the function result. ## This function can therefore only be called inside a ``when`` condition, ## e.g., ``when ( local h = lookup_hostname("www.bro-ids.org") ) { f(h); }``. diff --git a/src/nb_dns.c b/src/nb_dns.c index 3051be9bc2..e8595e6837 100644 --- a/src/nb_dns.c +++ b/src/nb_dns.c @@ -307,31 +307,32 @@ nb_dns_host_request(register struct nb_dns_info *nd, register const char *name, register void *cookie, register char *errstr) { - return (nb_dns_host_request2(nd, name, AF_INET, cookie, errstr)); + return (nb_dns_host_request2(nd, name, AF_INET, 0, cookie, errstr)); } int nb_dns_host_request2(register struct nb_dns_info *nd, register const char *name, - register int af, register void *cookie, register char *errstr) + register int af, register int qtype, register void *cookie, register char *errstr) { - register int qtype; + if (qtype != 16) { - switch (af) { + switch (af) { - case AF_INET: - qtype = T_A; - break; + case AF_INET: + qtype = T_A; + break; #ifdef AF_INET6 - case AF_INET6: - qtype = T_AAAA; - break; + case AF_INET6: + qtype = T_AAAA; + break; #endif - default: - snprintf(errstr, NB_DNS_ERRSIZE, - "nb_dns_host_request2(): uknown address family %d", af); - return (-1); + default: + snprintf(errstr, NB_DNS_ERRSIZE, + "nb_dns_host_request2(): unknown address family %d", af); + return (-1); + } } return (_nb_dns_mkquery(nd, name, af, qtype, cookie, errstr)); } @@ -579,6 +580,7 @@ nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr) nr->host_errno = NO_RECOVERY; return (-1); } + memcpy(bp, rdata, rdlen); *hap++ = bp; bp += rdlen; @@ -587,6 +589,20 @@ nb_dns_activity(struct nb_dns_info *nd, struct nb_dns_result *nr, char *errstr) /* Keep looking for more A records */ break; + case T_TXT: + if (bp + rdlen >= ep) { + snprintf(errstr, NB_DNS_ERRSIZE, + "nb_dns_activity(): overflow 1 for txt"); + nr->host_errno = NO_RECOVERY; + return (-1); + } + + memcpy(bp, rdata, rdlen); + he->h_name = bp+1; /* First char is a control character. */ + nr->hostent = he; + nr->ttl = rttl; + return (1); + case T_PTR: n = dn_expand((const u_char *)msg, (const u_char *)msg + msglen, rdata, bp, ep - bp); diff --git a/src/nb_dns.h b/src/nb_dns.h index d458f61716..33e09aa226 100644 --- a/src/nb_dns.h +++ b/src/nb_dns.h @@ -22,7 +22,7 @@ void nb_dns_finish(struct nb_dns_info *); int nb_dns_fd(struct nb_dns_info *); int nb_dns_host_request(struct nb_dns_info *, const char *, void *, char *); -int nb_dns_host_request2(struct nb_dns_info *, const char *, int, +int nb_dns_host_request2(struct nb_dns_info *, const char *, int, int, void *, char *); int nb_dns_addr_request(struct nb_dns_info *, nb_uint32_t, void *, char *);