001/** 002 * 003 * Copyright 2014-2017 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.minidns; 018 019import java.io.IOException; 020import java.net.InetAddress; 021import java.net.UnknownHostException; 022import java.util.ArrayList; 023import java.util.Collections; 024import java.util.LinkedList; 025import java.util.List; 026import java.util.Set; 027 028import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode; 029import org.jivesoftware.smack.initializer.SmackInitializer; 030import org.jivesoftware.smack.util.DNSUtil; 031import org.jivesoftware.smack.util.dns.DNSResolver; 032import org.jivesoftware.smack.util.dns.HostAddress; 033import org.jivesoftware.smack.util.dns.SRVRecord; 034 035import org.minidns.dnsmessage.DnsMessage.RESPONSE_CODE; 036import org.minidns.dnsmessage.Question; 037import org.minidns.dnsname.DnsName; 038import org.minidns.hla.DnssecResolverApi; 039import org.minidns.hla.ResolutionUnsuccessfulException; 040import org.minidns.hla.ResolverApi; 041import org.minidns.hla.ResolverResult; 042import org.minidns.hla.SrvResolverResult; 043import org.minidns.record.A; 044import org.minidns.record.AAAA; 045import org.minidns.record.SRV; 046 047 048/** 049 * This implementation uses the <a href="https://github.com/rtreffer/minidns/">MiniDNS</a> implementation for 050 * resolving DNS addresses. 051 */ 052public class MiniDnsResolver extends DNSResolver implements SmackInitializer { 053 054 private static final MiniDnsResolver INSTANCE = new MiniDnsResolver(); 055 056 private static final ResolverApi DNSSEC_RESOLVER = DnssecResolverApi.INSTANCE; 057 058 private static final ResolverApi NON_DNSSEC_RESOLVER = ResolverApi.INSTANCE; 059 060 public static DNSResolver getInstance() { 061 return INSTANCE; 062 } 063 064 public MiniDnsResolver() { 065 super(true); 066 } 067 068 @Override 069 protected List<SRVRecord> lookupSRVRecords0(final DnsName name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) { 070 final ResolverApi resolver = getResolver(dnssecMode); 071 072 SrvResolverResult result; 073 try { 074 result = resolver.resolveSrv(name); 075 } catch (IOException e) { 076 failedAddresses.add(new HostAddress(name, e)); 077 return null; 078 } 079 080 ResolutionUnsuccessfulException resolutionUnsuccessfulException = result.getResolutionUnsuccessfulException(); 081 if (resolutionUnsuccessfulException != null) { 082 failedAddresses.add(new HostAddress(name, resolutionUnsuccessfulException)); 083 return null; 084 } 085 086 if (shouldAbortIfNotAuthentic(name, dnssecMode, result, failedAddresses)) { 087 return null; 088 } 089 090 List<SRVRecord> res = new LinkedList<>(); 091 for (SRV srv : result.getAnswers()) { 092 DnsName hostname = srv.target; 093 List<InetAddress> hostAddresses = lookupHostAddress0(hostname, failedAddresses, dnssecMode); 094 if (shouldContinue(name, hostname, hostAddresses)) { 095 continue; 096 } 097 098 SRVRecord srvRecord = new SRVRecord(hostname, srv.port, srv.priority, srv.weight, hostAddresses); 099 res.add(srvRecord); 100 } 101 102 return res; 103 } 104 105 @Override 106 protected List<InetAddress> lookupHostAddress0(final DnsName name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) { 107 final ResolverApi resolver = getResolver(dnssecMode); 108 109 final ResolverResult<A> aResult; 110 final ResolverResult<AAAA> aaaaResult; 111 112 try { 113 aResult = resolver.resolve(name, A.class); 114 aaaaResult = resolver.resolve(name, AAAA.class); 115 } catch (IOException e) { 116 failedAddresses.add(new HostAddress(name, e)); 117 return null; 118 } 119 120 if (!aResult.wasSuccessful() && !aaaaResult.wasSuccessful()) { 121 // Both results where not successful. 122 failedAddresses.add(new HostAddress(name, getExceptionFrom(aResult))); 123 failedAddresses.add(new HostAddress(name, getExceptionFrom(aaaaResult))); 124 return null; 125 } 126 127 if (shouldAbortIfNotAuthentic(name, dnssecMode, aResult, failedAddresses) 128 || shouldAbortIfNotAuthentic(name, dnssecMode, aaaaResult, failedAddresses)) { 129 return null; 130 } 131 132 // TODO: Use ResolverResult.getAnswersOrEmptySet() once we updated MiniDNS. 133 Set<A> aResults; 134 if (aResult.wasSuccessful()) { 135 aResults = aResult.getAnswers(); 136 } 137 else { 138 aResults = Collections.emptySet(); 139 } 140 141 // TODO: Use ResolverResult.getAnswersOrEmptySet() once we updated MiniDNS. 142 Set<AAAA> aaaaResults; 143 if (aaaaResult.wasSuccessful()) { 144 aaaaResults = aaaaResult.getAnswers(); 145 } 146 else { 147 aaaaResults = Collections.emptySet(); 148 } 149 150 List<InetAddress> inetAddresses = new ArrayList<>(aResults.size() 151 + aaaaResults.size()); 152 153 for (A a : aResults) { 154 InetAddress inetAddress; 155 try { 156 inetAddress = InetAddress.getByAddress(a.getIp()); 157 } 158 catch (UnknownHostException e) { 159 continue; 160 } 161 inetAddresses.add(inetAddress); 162 } 163 for (AAAA aaaa : aaaaResults) { 164 InetAddress inetAddress; 165 try { 166 inetAddress = InetAddress.getByAddress(name.ace, aaaa.getIp()); 167 } 168 catch (UnknownHostException e) { 169 continue; 170 } 171 inetAddresses.add(inetAddress); 172 } 173 174 return inetAddresses; 175 } 176 177 public static void setup() { 178 DNSUtil.setDNSResolver(getInstance()); 179 } 180 181 @Override 182 public List<Exception> initialize() { 183 setup(); 184 MiniDnsDane.setup(); 185 return null; 186 } 187 188 private static ResolverApi getResolver(DnssecMode dnssecMode) { 189 if (dnssecMode == DnssecMode.disabled) { 190 return NON_DNSSEC_RESOLVER; 191 } else { 192 return DNSSEC_RESOLVER; 193 } 194 } 195 196 private static boolean shouldAbortIfNotAuthentic(DnsName name, DnssecMode dnssecMode, 197 ResolverResult<?> result, List<HostAddress> failedAddresses) { 198 switch (dnssecMode) { 199 case needsDnssec: 200 case needsDnssecAndDane: 201 // Check if the result is authentic data, i.e. there a no reasons the result is unverified. 202 // TODO: Use ResolverResult.getDnssecResultNotAuthenticException() of newer MiniDNS versions. 203 if (!result.isAuthenticData()) { 204 Exception exception = new Exception("DNSSEC verification failed: " + result.getUnverifiedReasons().iterator().next().getReasonString()); 205 failedAddresses.add(new HostAddress(name, exception)); 206 return true; 207 } 208 break; 209 case disabled: 210 break; 211 default: 212 throw new IllegalStateException("Unknown DnssecMode: " + dnssecMode); 213 } 214 return false; 215 } 216 217 private static ResolutionUnsuccessfulException getExceptionFrom(ResolverResult<?> result) { 218 Question question = result.getQuestion(); 219 RESPONSE_CODE responseCode = result.getResponseCode(); 220 ResolutionUnsuccessfulException resolutionUnsuccessfulException = new ResolutionUnsuccessfulException(question, responseCode); 221 return resolutionUnsuccessfulException; 222 } 223}