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