blob: eb520d22b151073dc03f66577c12e4213ba25ccb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.dubbo.registry.dns.util;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import io.netty.buffer.ByteBuf;
import io.netty.channel.AddressedEnvelope;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.handler.codec.dns.DefaultDnsQuestion;
import io.netty.handler.codec.dns.DnsRawRecord;
import io.netty.handler.codec.dns.DnsRecordType;
import io.netty.handler.codec.dns.DnsResponse;
import io.netty.handler.codec.dns.DnsSection;
import io.netty.resolver.ResolvedAddressTypes;
import io.netty.resolver.dns.DnsNameResolver;
import io.netty.resolver.dns.DnsNameResolverBuilder;
import io.netty.util.concurrent.Future;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.stream.Collectors;
import static io.netty.resolver.dns.DnsServerAddresses.sequential;
public class DNSResolver {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final DnsNameResolver resolver;
private static final EventLoopGroup GROUP = new NioEventLoopGroup(1);
public DNSResolver(String nameserver, int port, int maxQueriesPerResolve) {
this.resolver = newResolver(nameserver, port, maxQueriesPerResolve);
}
public ResolveResult resolve(String path) {
ResolveResult recordList = new ResolveResult();
try {
Future<List<InetAddress>> hostFuture = resolver.resolveAll(path);
Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> srvFuture =
resolver.query(new DefaultDnsQuestion(path, DnsRecordType.SRV));
try {
recordList.getHostnameList()
.addAll(hostFuture
.sync().getNow()
.stream()
.map(InetAddress::getHostAddress)
.collect(Collectors.toList()));
DnsResponse srvResponse = srvFuture.sync().getNow().content();
for (int i = 0; i < srvResponse.count(DnsSection.ANSWER); i++) {
DnsRawRecord record = srvResponse.recordAt(DnsSection.ANSWER, i);
ByteBuf buf = record.content();
// Priority
buf.readUnsignedShort();
// Weight
buf.readUnsignedShort();
// Port
int port = buf.readUnsignedShort();
recordList.getPort().add(port);
}
} catch (InterruptedException e) {
logger.warn("Waiting DNS resolve interrupted. " + e.getLocalizedMessage());
}
} catch (Throwable t) {
if (t instanceof UnknownHostException) {
if (logger.isInfoEnabled()) {
logger.info(t.getLocalizedMessage());
}
} else {
logger.error(t.getLocalizedMessage());
}
}
return recordList;
}
public void destroy() {
resolver.close();
}
private static DnsNameResolver newResolver(String nameserver, int port, int maxQueriesPerResolve) {
return new DnsNameResolverBuilder(GROUP.next())
.channelType(NioDatagramChannel.class)
.maxQueriesPerResolve(maxQueriesPerResolve)
.decodeIdn(true)
.optResourceEnabled(false)
.ndots(1)
.resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED)
// ignore cache
.ttl(0, 1)
.nameServerProvider((hostname) -> sequential(new InetSocketAddress(nameserver, port)).stream())
.build();
}
}