| /* |
| * 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(); |
| } |
| } |