use of io.netty.handler.codec.dns.DnsRecordType in project netty by netty.
the class DnsNameResolverContext method onResponseAorAAAA.
private void onResponseAorAAAA(DnsRecordType qType, DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope, Promise<T> promise) {
// We often get a bunch of CNAMES as well when we asked for A/AAAA.
final DnsResponse response = envelope.content();
final Map<String, String> cnames = buildAliasMap(response);
final int answerCount = response.count(DnsSection.ANSWER);
boolean found = false;
for (int i = 0; i < answerCount; i++) {
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
final DnsRecordType type = r.type();
if (type != DnsRecordType.A && type != DnsRecordType.AAAA) {
continue;
}
final String questionName = question.name().toLowerCase(Locale.US);
final String recordName = r.name().toLowerCase(Locale.US);
// Make sure the record is for the questioned domain.
if (!recordName.equals(questionName)) {
// Even if the record's name is not exactly same, it might be an alias defined in the CNAME records.
String resolved = questionName;
do {
resolved = cnames.get(resolved);
if (recordName.equals(resolved)) {
break;
}
} while (resolved != null);
if (resolved == null) {
continue;
}
}
InetAddress resolved = parseAddress(r, hostname);
if (resolved == null) {
continue;
}
if (resolvedEntries == null) {
resolvedEntries = new ArrayList<DnsCacheEntry>(8);
}
final DnsCacheEntry e = new DnsCacheEntry(hostname, resolved);
resolveCache.cache(hostname, additionals, resolved, r.timeToLive(), parent.ch.eventLoop());
resolvedEntries.add(e);
found = true;
// Note that we do not break from the loop here, so we decode/cache all A/AAAA records.
}
if (found) {
return;
}
if (traceEnabled) {
addTrace(envelope.sender(), "no matching " + qType + " record found");
}
// We aked for A/AAAA but we got only CNAME.
if (!cnames.isEmpty()) {
onResponseCNAME(question, envelope, cnames, false, promise);
}
}
use of io.netty.handler.codec.dns.DnsRecordType in project netty by netty.
the class DnsNameResolverContext method onResponse.
void onResponse(final DnsServerAddressStream nameServerAddrStream, final DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope, Promise<T> promise) {
try {
final DnsResponse res = envelope.content();
final DnsResponseCode code = res.code();
if (code == DnsResponseCode.NOERROR) {
if (handleRedirect(question, envelope, promise)) {
// Was a redirect so return here as everything else is handled in handleRedirect(...)
return;
}
final DnsRecordType type = question.type();
if (type == DnsRecordType.A || type == DnsRecordType.AAAA) {
onResponseAorAAAA(type, question, envelope, promise);
} else if (type == DnsRecordType.CNAME) {
onResponseCNAME(question, envelope, promise);
}
return;
}
if (traceEnabled) {
addTrace(envelope.sender(), "response code: " + code + " with " + res.count(DnsSection.ANSWER) + " answer(s) and " + res.count(DnsSection.AUTHORITY) + " authority resource(s)");
}
// Retry with the next server if the server did not tell us that the domain does not exist.
if (code != DnsResponseCode.NXDOMAIN) {
query(nameServerAddrStream, question, promise);
}
} finally {
ReferenceCountUtil.safeRelease(envelope);
}
}
use of io.netty.handler.codec.dns.DnsRecordType in project netty by netty.
the class DnsNameResolver method resolveAll.
private Future<List<DnsRecord>> resolveAll(DnsQuestion question, DnsRecord[] additionals, Promise<List<DnsRecord>> promise) {
checkNotNull(question, "question");
checkNotNull(promise, "promise");
// Respect /etc/hosts as well if the record type is A or AAAA.
final DnsRecordType type = question.type();
final String hostname = question.name();
if (type == DnsRecordType.A || type == DnsRecordType.AAAA) {
final List<InetAddress> hostsFileEntries = resolveHostsFileEntries(hostname);
if (hostsFileEntries != null) {
List<DnsRecord> result = new ArrayList<DnsRecord>();
for (InetAddress hostsFileEntry : hostsFileEntries) {
ByteBuf content = null;
if (hostsFileEntry instanceof Inet4Address) {
if (type == DnsRecordType.A) {
content = Unpooled.wrappedBuffer(hostsFileEntry.getAddress());
}
} else if (hostsFileEntry instanceof Inet6Address) {
if (type == DnsRecordType.AAAA) {
content = Unpooled.wrappedBuffer(hostsFileEntry.getAddress());
}
}
if (content != null) {
// Our current implementation does not support reloading the hosts file,
// so use a fairly large TTL (1 day, i.e. 86400 seconds).
result.add(new DefaultDnsRawRecord(hostname, type, 86400, content));
}
}
if (!result.isEmpty()) {
trySuccess(promise, result);
return promise;
}
}
}
// It was not A/AAAA question or there was no entry in /etc/hosts.
final DnsServerAddressStream nameServerAddrs = dnsServerAddressStreamProvider.nameServerAddressStream(hostname);
new DnsRecordResolveContext(this, promise, question, additionals, nameServerAddrs, maxQueriesPerResolve).resolve(promise);
return promise;
}
use of io.netty.handler.codec.dns.DnsRecordType in project netty by netty.
the class DnsResolveContext method onExpectedResponse.
private void onExpectedResponse(DnsQuestion question, AddressedEnvelope<DnsResponse, InetSocketAddress> envelope, final DnsQueryLifecycleObserver queryLifecycleObserver, Promise<List<T>> promise) {
// We often get a bunch of CNAMES as well when we asked for A/AAAA.
final DnsResponse response = envelope.content();
final Map<String, String> cnames = buildAliasMap(response, cnameCache(), parent.executor());
final int answerCount = response.count(DnsSection.ANSWER);
boolean found = false;
boolean completeEarly = this.completeEarly;
for (int i = 0; i < answerCount; i++) {
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
final DnsRecordType type = r.type();
boolean matches = false;
for (DnsRecordType expectedType : expectedTypes) {
if (type == expectedType) {
matches = true;
break;
}
}
if (!matches) {
continue;
}
final String questionName = question.name().toLowerCase(Locale.US);
final String recordName = r.name().toLowerCase(Locale.US);
// Make sure the record is for the questioned domain.
if (!recordName.equals(questionName)) {
Map<String, String> cnamesCopy = new HashMap<String, String>(cnames);
// Even if the record's name is not exactly same, it might be an alias defined in the CNAME records.
String resolved = questionName;
do {
resolved = cnamesCopy.remove(resolved);
if (recordName.equals(resolved)) {
break;
}
} while (resolved != null);
if (resolved == null) {
assert questionName.isEmpty() || questionName.charAt(questionName.length() - 1) == '.';
for (String searchDomain : parent.searchDomains()) {
if (searchDomain.isEmpty()) {
continue;
}
final String fqdn;
if (searchDomain.charAt(searchDomain.length() - 1) == '.') {
fqdn = questionName + searchDomain;
} else {
fqdn = questionName + searchDomain + '.';
}
if (recordName.equals(fqdn)) {
resolved = recordName;
break;
}
}
if (resolved == null) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring record {} as it contains a different name than the " + "question name [{}]. Cnames: {}, Search domains: {}", r.toString(), questionName, cnames, parent.searchDomains());
}
continue;
}
}
}
final T converted = convertRecord(r, hostname, additionals, parent.executor());
if (converted == null) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring record {} as the converted record is null. hostname [{}], Additionals: {}", r.toString(), hostname, additionals);
}
continue;
}
boolean shouldRelease = false;
// include the result
if (!completeEarly) {
completeEarly = isCompleteEarly(converted);
}
// for the extra memory copy and allocations in this cases later on.
if (finalResult == null) {
finalResult = new ArrayList<T>(8);
finalResult.add(converted);
} else if (isDuplicateAllowed() || !finalResult.contains(converted)) {
finalResult.add(converted);
} else {
shouldRelease = true;
}
cache(hostname, additionals, r, converted);
found = true;
if (shouldRelease) {
ReferenceCountUtil.release(converted);
}
// Note that we do not break from the loop here, so we decode/cache all A/AAAA records.
}
if (cnames.isEmpty()) {
if (found) {
if (completeEarly) {
this.completeEarly = true;
}
queryLifecycleObserver.querySucceed();
return;
}
queryLifecycleObserver.queryFailed(NO_MATCHING_RECORD_QUERY_FAILED_EXCEPTION);
} else {
queryLifecycleObserver.querySucceed();
// We also got a CNAME so we need to ensure we also query it.
onResponseCNAME(question, cnames, newDnsQueryLifecycleObserver(question), promise);
}
}
use of io.netty.handler.codec.dns.DnsRecordType in project netty by netty.
the class DnsResolveContext method buildAliasMap.
private static Map<String, String> buildAliasMap(DnsResponse response, DnsCnameCache cache, EventLoop loop) {
final int answerCount = response.count(DnsSection.ANSWER);
Map<String, String> cnames = null;
for (int i = 0; i < answerCount; i++) {
final DnsRecord r = response.recordAt(DnsSection.ANSWER, i);
final DnsRecordType type = r.type();
if (type != DnsRecordType.CNAME) {
continue;
}
if (!(r instanceof DnsRawRecord)) {
continue;
}
final ByteBuf recordContent = ((ByteBufHolder) r).content();
final String domainName = decodeDomainName(recordContent);
if (domainName == null) {
continue;
}
if (cnames == null) {
cnames = new HashMap<String, String>(min(8, answerCount));
}
String name = r.name().toLowerCase(Locale.US);
String mapping = domainName.toLowerCase(Locale.US);
// Cache the CNAME as well.
String nameWithDot = hostnameWithDot(name);
String mappingWithDot = hostnameWithDot(mapping);
if (!nameWithDot.equalsIgnoreCase(mappingWithDot)) {
cache.cache(nameWithDot, mappingWithDot, r.timeToLive(), loop);
cnames.put(name, mapping);
}
}
return cnames != null ? cnames : Collections.<String, String>emptyMap();
}
Aggregations