void assert_forget_negative(DNSCache *cache, gint cache_size) { gint i; const gchar *hn = NULL; gsize hn_len; gboolean positive; gint positive_limit = cache_size / 2; for (i = 0; i < cache_size; i++) { guint32 ni = htonl(i); hn = NULL; positive = FALSE; if (i < positive_limit) { cr_assert(dns_cache_lookup(cache, AF_INET, (void *) &ni, &hn, &hn_len, &positive) || !positive, "hmmm cache forgot positive entries too early, i=%d\n", i); } else { cr_assert_not(dns_cache_lookup(cache, AF_INET, (void *) &ni, &hn, &hn_len, &positive) || positive, "hmmm cache didn't forget negative entries in time, i=%d\n", i); } } }
void assert_no_forget(DNSCache *cache, gint cache_size) { gint i; const gchar *hn = NULL; gsize hn_len; gboolean positive; gint positive_limit = cache_size / 2; for (i = 0; i < cache_size; i++) { guint32 ni = htonl(i); hn = NULL; positive = FALSE; cr_assert(dns_cache_lookup(cache, AF_INET, (void *) &ni, &hn, &hn_len, &positive), "hmmm cache forgot the cache entry too early, i=%d, hn=%s\n", i, hn); if (i < positive_limit) { cr_assert(positive && strcmp(hn, "hostname") == 0, "hmm, cached returned an positive match, but cached name invalid, i=%d, hn=%s\n", i, hn); } else { cr_assert(!positive && strcmp(hn, "negative") == 0, "hmm, cache returned a positive match, where a negative match was expected, i=%d, hn=%s\n", i, hn); } } }
/** * See if unbound's internal cache can answer the query */ static int cachedb_intcache_lookup(struct module_qstate* qstate) { struct dns_msg* msg; msg = dns_cache_lookup(qstate->env, qstate->qinfo.qname, qstate->qinfo.qname_len, qstate->qinfo.qtype, qstate->qinfo.qclass, qstate->query_flags, qstate->region, qstate->env->scratch, 1 /* no partial messages with only a CNAME */ ); if(!msg && qstate->env->neg_cache && iter_qname_indicates_dnssec(qstate->env, &qstate->qinfo)) { /* lookup in negative cache; may result in * NOERROR/NODATA or NXDOMAIN answers that need validation */ msg = val_neg_getmsg(qstate->env->neg_cache, &qstate->qinfo, qstate->region, qstate->env->rrset_cache, qstate->env->scratch_buffer, *qstate->env->now, 1/*add SOA*/, NULL, qstate->env->cfg); } if(!msg) return 0; /* this is the returned msg */ qstate->return_rcode = LDNS_RCODE_NOERROR; qstate->return_msg = msg; return 1; }
int main() { int i; app_startup(); dns_cache_init(); dns_cache_set_params(50000, 2, 1, NULL); for (i = 0; i < 10000; i++) { guint32 ni = htonl(i); dns_cache_store(FALSE, AF_INET, (void *) &ni, "hostname"); } for (i = 0; i < 10000; i++) { guint32 ni = htonl(i); const gchar *hn = NULL; if (!dns_cache_lookup(AF_INET, (void *) &ni, &hn) || strcmp(hn, "hostname") != 0) { fprintf(stderr, "hmmm cache forgot the hostname, i=%d, hn=%s\n", i, hn); return 1; } } sleep(3); for (i = 0; i < 10000; i++) { guint32 ni = htonl(i); const gchar *hn = NULL; if (dns_cache_lookup(AF_INET, (void *) &ni, &hn)) { fprintf(stderr, "hmmm cache did not forget the hostname, i=%d\n", i); return 1; } } app_shutdown(); return 0; }
void assert_forget_all(DNSCache *cache, gint cache_size) { gint i; const gchar *hn = NULL; gsize hn_len; gboolean positive; for (i = 0; i < cache_size; i++) { guint32 ni = htonl(i); hn = NULL; positive = FALSE; cr_assert_not(dns_cache_lookup(cache, AF_INET, (void *) &ni, &hn, &hn_len, &positive), "hmmm cache did not forget an expired entry, i=%d\n", i); } }
static const gchar * resolve_sockaddr_to_inet_or_inet6_hostname(gsize *result_len, GSockAddr *saddr, const HostResolveOptions *host_resolve_options) { const gchar *hname; gsize hname_len; gboolean positive; void *dnscache_key; dnscache_key = sockaddr_to_dnscache_key(saddr); hname = NULL; positive = FALSE; if (host_resolve_options->use_dns_cache) { if (dns_cache_lookup(saddr->sa.sa_family, dnscache_key, (const gchar **) &hname, &hname_len, &positive)) return hostname_apply_options_fqdn(hname_len, result_len, hname, positive, host_resolve_options); } if (!hname && host_resolve_options->use_dns && host_resolve_options->use_dns != 2) { #ifdef HAVE_GETNAMEINFO hname = resolve_address_using_getnameinfo(saddr, hostname_buffer, sizeof(hostname_buffer)); #else hname = resolve_address_using_gethostbyaddr(saddr, hostname_buffer, sizeof(hostname_buffer)); #endif positive = (hname != NULL); } if (!hname) { hname = g_sockaddr_format(saddr, hostname_buffer, sizeof(hostname_buffer), GSA_ADDRESS_ONLY); positive = FALSE; } if (host_resolve_options->use_dns_cache) dns_cache_store_dynamic(saddr->sa.sa_family, dnscache_key, hname, positive); return hostname_apply_options_fqdn(-1, result_len, hname, positive, host_resolve_options); }
void resolve_sockaddr(gchar *result, gsize *result_len, GSockAddr *saddr, gboolean usedns, gboolean usefqdn, gboolean use_dns_cache, gboolean normalize_hostnames) { gchar *hname; gboolean positive; gchar *p, buf[256]; if (saddr && saddr->sa.sa_family != AF_UNIX) { if (saddr->sa.sa_family == AF_INET #if ENABLE_IPV6 || saddr->sa.sa_family == AF_INET6 #endif ) { void *addr; socklen_t addr_len G_GNUC_UNUSED; if (saddr->sa.sa_family == AF_INET) { addr = &((struct sockaddr_in *) &saddr->sa)->sin_addr; addr_len = sizeof(struct in_addr); } #if ENABLE_IPV6 else { addr = &((struct sockaddr_in6 *) &saddr->sa)->sin6_addr; addr_len = sizeof(struct in6_addr); } #endif hname = NULL; if (usedns) { if ((!use_dns_cache || !dns_cache_lookup(saddr->sa.sa_family, addr, (const gchar **) &hname, &positive)) && usedns != 2) { #ifdef HAVE_GETNAMEINFO if (getnameinfo(&saddr->sa, saddr->salen, buf, sizeof(buf), NULL, 0, 0) == 0) hname = buf; #else struct hostent *hp; G_LOCK(resolv_lock); hp = gethostbyaddr(addr, addr_len, saddr->sa.sa_family); G_UNLOCK(resolv_lock); hname = (hp && hp->h_name) ? hp->h_name : NULL; #endif if (hname) positive = TRUE; if (use_dns_cache && hname) { /* resolution success, store this as a positive match in the cache */ dns_cache_store(FALSE, saddr->sa.sa_family, addr, hname, TRUE); } } } if (!hname) { inet_ntop(saddr->sa.sa_family, addr, buf, sizeof(buf)); hname = buf; if (use_dns_cache) dns_cache_store(FALSE, saddr->sa.sa_family, addr, hname, FALSE); } else { if (!usefqdn && positive) { /* we only truncate hostnames if they were positive * matches (e.g. real hostnames and not IP * addresses) */ p = strchr(hname, '.'); if (p) { if (p - hname > sizeof(buf)) p = &hname[sizeof(buf)] - 1; memcpy(buf, hname, p - hname); buf[p - hname] = 0; hname = buf; } } } } else { g_assert_not_reached(); } } else { if (!local_hostname_fqdn[0]) reset_cached_hostname(); if (usefqdn) { /* avoid copy */ hname = local_hostname_fqdn; } else { hname = local_hostname_short; } } if (normalize_hostnames) { gint i; for (i = 0; hname[i] && i < ((*result_len) - 1); i++) { result[i] = g_ascii_tolower(hname[i]); } result[i] = '\0'; /* the closing \0 is not copied by the previous loop */ *result_len = i; } else { gsize len = strlen(hname); if (*result_len < len - 1) len = *result_len - 1; memcpy(result, hname, len); result[len] = 0; *result_len = len; } }
int dns_transaction_go(DnsTransaction *t) { bool had_stream; usec_t ts; int r; assert(t); had_stream = !!t->stream; dns_transaction_stop(t); log_debug("Excercising transaction on scope %s on %s/%s", dns_protocol_to_string(t->scope->protocol), t->scope->link ? t->scope->link->name : "*", t->scope->family == AF_UNSPEC ? "*" : af_to_name(t->scope->family)); if (t->n_attempts >= TRANSACTION_ATTEMPTS_MAX(t->scope->protocol)) { dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED); return 0; } if (t->scope->protocol == DNS_PROTOCOL_LLMNR && had_stream) { /* If we already tried via a stream, then we don't * retry on LLMNR. See RFC 4795, Section 2.7. */ dns_transaction_complete(t, DNS_TRANSACTION_ATTEMPTS_MAX_REACHED); return 0; } assert_se(sd_event_now(t->scope->manager->event, clock_boottime_or_monotonic(), &ts) >= 0); t->n_attempts++; t->start_usec = ts; t->received = dns_packet_unref(t->received); t->cached = dns_answer_unref(t->cached); t->cached_rcode = 0; /* Check the cache, but only if this transaction is not used * for probing or verifying a zone item. */ if (set_isempty(t->zone_items)) { /* Before trying the cache, let's make sure we figured out a * server to use. Should this cause a change of server this * might flush the cache. */ dns_scope_get_dns_server(t->scope); /* Let's then prune all outdated entries */ dns_cache_prune(&t->scope->cache); r = dns_cache_lookup(&t->scope->cache, t->key, &t->cached_rcode, &t->cached); if (r < 0) return r; if (r > 0) { if (t->cached_rcode == DNS_RCODE_SUCCESS) dns_transaction_complete(t, DNS_TRANSACTION_SUCCESS); else dns_transaction_complete(t, DNS_TRANSACTION_FAILURE); return 0; } } if (t->scope->protocol == DNS_PROTOCOL_LLMNR && !t->initial_jitter) { usec_t jitter; /* RFC 4795 Section 2.7 suggests all queries should be * delayed by a random time from 0 to JITTER_INTERVAL. */ t->initial_jitter = true; random_bytes(&jitter, sizeof(jitter)); jitter %= LLMNR_JITTER_INTERVAL_USEC; r = sd_event_add_time( t->scope->manager->event, &t->timeout_event_source, clock_boottime_or_monotonic(), ts + jitter, LLMNR_JITTER_INTERVAL_USEC, on_transaction_timeout, t); if (r < 0) return r; t->n_attempts = 0; t->state = DNS_TRANSACTION_PENDING; log_debug("Delaying LLMNR transaction for " USEC_FMT "us.", jitter); return 0; } /* Otherwise, we need to ask the network */ r = dns_transaction_make_packet(t); if (r == -EDOM) { /* Not the right request to make on this network? * (i.e. an A request made on IPv6 or an AAAA request * made on IPv4, on LLMNR or mDNS.) */ dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS); return 0; } if (r < 0) return r; if (t->scope->protocol == DNS_PROTOCOL_LLMNR && (dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "in-addr.arpa") > 0 || dns_name_endswith(DNS_RESOURCE_KEY_NAME(t->key), "ip6.arpa") > 0)) { /* RFC 4795, Section 2.4. says reverse lookups shall * always be made via TCP on LLMNR */ r = dns_transaction_open_tcp(t); } else { /* Try via UDP, and if that fails due to large size try via TCP */ r = dns_transaction_emit(t); if (r == -EMSGSIZE) r = dns_transaction_open_tcp(t); } if (r == -ESRCH) { /* No servers to send this to? */ dns_transaction_complete(t, DNS_TRANSACTION_NO_SERVERS); return 0; } else if (r < 0) { if (t->scope->protocol != DNS_PROTOCOL_DNS) { dns_transaction_complete(t, DNS_TRANSACTION_RESOURCES); return 0; } /* Couldn't send? Try immediately again, with a new server */ dns_transaction_next_dns_server(t); return dns_transaction_go(t); } r = sd_event_add_time( t->scope->manager->event, &t->timeout_event_source, clock_boottime_or_monotonic(), ts + transaction_get_resend_timeout(t), 0, on_transaction_timeout, t); if (r < 0) return r; t->state = DNS_TRANSACTION_PENDING; return 1; }