001/** 002 * 003 * Copyright 2013-2020 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smack.util.dns; 018 019import java.net.InetAddress; 020import java.net.UnknownHostException; 021import java.util.Arrays; 022import java.util.Collection; 023import java.util.List; 024import java.util.logging.Level; 025import java.util.logging.Logger; 026 027import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode; 028import org.jivesoftware.smack.util.rce.RemoteConnectionEndpointLookupFailure; 029 030import org.minidns.dnsname.DnsName; 031import org.minidns.record.SRV; 032 033/** 034 * Implementations of this interface define a class that is capable of resolving DNS addresses. 035 * 036 */ 037public abstract class DNSResolver { 038 039 protected static final Logger LOGGER = Logger.getLogger(DNSResolver.class.getName()); 040 041 private final boolean supportsDnssec; 042 043 protected DNSResolver(boolean supportsDnssec) { 044 this.supportsDnssec = supportsDnssec; 045 } 046 047 /** 048 * Gets a list of service records for the specified service. 049 * 050 * @param name The symbolic name of the service. 051 * @param lookupFailures list of exceptions that occurred during lookup. 052 * @param dnssecMode security mode. 053 * @return The list of SRV records mapped to the service name. 054 */ 055 public final Collection<SRV> lookupSrvRecords(DnsName name, 056 List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode) { 057 checkIfDnssecRequestedAndSupported(dnssecMode); 058 return lookupSrvRecords0(name, lookupFailures, dnssecMode); 059 } 060 061 protected abstract Collection<SRV> lookupSrvRecords0(DnsName name, 062 List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode); 063 064 public final List<InetAddress> lookupHostAddress(DnsName name, 065 List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode) { 066 checkIfDnssecRequestedAndSupported(dnssecMode); 067 return lookupHostAddress0(name, lookupFailures, dnssecMode); 068 } 069 070 /** 071 * Lookup the IP addresses of a given host name. Returns <code>null</code> if there was an error, in which the error 072 * reason will be added in form of a <code>HostAddress</code> to <code>failedAddresses</code>. Returns a empty list 073 * in case the DNS name exists but has no associated A or AAAA resource records. Otherwise, if the resolution was 074 * successful <em>and</em> there is at least one A or AAAA resource record, then a non-empty list will be returned. 075 * <p> 076 * Concrete DNS resolver implementations are free to overwrite this, but have to stick to the interface contract. 077 * </p> 078 * 079 * @param name the DNS name to lookup 080 * @param lookupFailures list of exceptions that occurred during lookup. 081 * @param dnssecMode the selected DNSSEC mode 082 * @return A list, either empty or non-empty, or <code>null</code> 083 */ 084 protected List<InetAddress> lookupHostAddress0(DnsName name, List<RemoteConnectionEndpointLookupFailure> lookupFailures, DnssecMode dnssecMode) { 085 // Default implementation of a DNS name lookup for A/AAAA records. It is assumed that this method does never 086 // support DNSSEC. Subclasses are free to override this method. 087 if (dnssecMode != DnssecMode.disabled) { 088 throw new UnsupportedOperationException("This resolver does not support DNSSEC"); 089 } 090 091 InetAddress[] inetAddressArray; 092 try { 093 inetAddressArray = InetAddress.getAllByName(name.toString()); 094 } catch (UnknownHostException e) { 095 lookupFailures.add(new RemoteConnectionEndpointLookupFailure.DnsLookupFailure(name, e)); 096 return null; 097 } 098 099 return Arrays.asList(inetAddressArray); 100 } 101 102 protected static boolean shouldContinue(CharSequence name, CharSequence hostname, List<InetAddress> hostAddresses) { 103 if (hostAddresses == null) { 104 return true; 105 } 106 107 // If hostAddresses is not null but empty, then the DNS resolution was successful but the domain did not 108 // have any A or AAAA resource records. 109 if (hostAddresses.isEmpty()) { 110 LOGGER.log(Level.INFO, "The DNS name " + name + ", points to a hostname (" + hostname 111 + ") which has neither A or AAAA resource records. This is an indication of a broken DNS setup."); 112 return true; 113 } 114 115 return false; 116 } 117 118 private void checkIfDnssecRequestedAndSupported(DnssecMode dnssecMode) { 119 if (dnssecMode != DnssecMode.disabled && !supportsDnssec) { 120 throw new UnsupportedOperationException("This resolver does not support DNSSEC"); 121 } 122 } 123}