001/** 002 * 003 * Copyright 2013-2018 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.List; 023import java.util.logging.Level; 024import java.util.logging.Logger; 025 026import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode; 027 028import org.minidns.dnsname.DnsName; 029 030/** 031 * Implementations of this interface define a class that is capable of resolving DNS addresses. 032 * 033 */ 034public abstract class DNSResolver { 035 036 protected static final Logger LOGGER = Logger.getLogger(DNSResolver.class.getName()); 037 038 private final boolean supportsDnssec; 039 040 protected DNSResolver(boolean supportsDnssec) { 041 this.supportsDnssec = supportsDnssec; 042 } 043 044 /** 045 * Gets a list of service records for the specified service. 046 * @param name The symbolic name of the service. 047 * @param failedAddresses list of failed addresses. 048 * @param dnssecMode security mode. 049 * @return The list of SRV records mapped to the service name. 050 */ 051 public final List<SRVRecord> lookupSRVRecords(DnsName name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) { 052 checkIfDnssecRequestedAndSupported(dnssecMode); 053 return lookupSRVRecords0(name, failedAddresses, dnssecMode); 054 } 055 056 protected abstract List<SRVRecord> lookupSRVRecords0(DnsName name, List<HostAddress> failedAddresses, DnssecMode dnssecMode); 057 058 public final HostAddress lookupHostAddress(DnsName name, int port, List<HostAddress> failedAddresses, DnssecMode dnssecMode) { 059 checkIfDnssecRequestedAndSupported(dnssecMode); 060 List<InetAddress> inetAddresses = lookupHostAddress0(name, failedAddresses, dnssecMode); 061 if (inetAddresses == null || inetAddresses.isEmpty()) { 062 return null; 063 } 064 return new HostAddress(name, port, inetAddresses); 065 } 066 067 /** 068 * Lookup the IP addresses of a given host name. Returns <code>null</code> if there was an error, in which the error 069 * reason will be added in form of a <code>HostAddress</code> to <code>failedAddresses</code>. Returns a empty list 070 * in case the DNS name exists but has no associated A or AAAA resource records. Otherwise, if the resolution was 071 * successful <em>and</em> there is at least one A or AAAA resource record, then a non-empty list will be returned. 072 * <p> 073 * Concrete DNS resolver implementations are free to overwrite this, but have to stick to the interface contract. 074 * </p> 075 * 076 * @param name the DNS name to lookup 077 * @param failedAddresses a list with the failed addresses 078 * @param dnssecMode the selected DNSSEC mode 079 * @return A list, either empty or non-empty, or <code>null</code> 080 */ 081 protected List<InetAddress> lookupHostAddress0(DnsName name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) { 082 // Default implementation of a DNS name lookup for A/AAAA records. It is assumed that this method does never 083 // support DNSSEC. Subclasses are free to override this method. 084 if (dnssecMode != DnssecMode.disabled) { 085 throw new UnsupportedOperationException("This resolver does not support DNSSEC"); 086 } 087 088 InetAddress[] inetAddressArray; 089 try { 090 inetAddressArray = InetAddress.getAllByName(name.toString()); 091 } catch (UnknownHostException e) { 092 failedAddresses.add(new HostAddress(name, e)); 093 return null; 094 } 095 096 return Arrays.asList(inetAddressArray); 097 } 098 099 protected final boolean shouldContinue(CharSequence name, CharSequence hostname, List<InetAddress> hostAddresses) { 100 if (hostAddresses == null) { 101 return true; 102 } 103 104 // If hostAddresses is not null but empty, then the DNS resolution was successful but the domain did not 105 // have any A or AAAA resource records. 106 if (hostAddresses.isEmpty()) { 107 LOGGER.log(Level.INFO, "The DNS name " + name + ", points to a hostname (" + hostname 108 + ") which has neither A or AAAA resource records. This is an indication of a broken DNS setup."); 109 return true; 110 } 111 112 return false; 113 } 114 115 private void checkIfDnssecRequestedAndSupported(DnssecMode dnssecMode) { 116 if (dnssecMode != DnssecMode.disabled && !supportsDnssec) { 117 throw new UnsupportedOperationException("This resolver does not support DNSSEC"); 118 } 119 } 120}