001/* 002 * Copyright 2015-2024 the original author or authors 003 * 004 * This software is licensed under the Apache License, Version 2.0, 005 * the GNU Lesser General Public License version 2 or later ("LGPL") 006 * and the WTFPL. 007 * You may choose either license to govern your use of this software only 008 * upon the condition that you accept all of the terms of either 009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL. 010 */ 011package org.minidns.hla; 012 013import java.io.IOException; 014import java.net.Inet4Address; 015import java.net.Inet6Address; 016import java.net.InetAddress; 017 018import org.minidns.AbstractDnsClient; 019import org.minidns.DnsClient; 020import org.minidns.dnslabel.DnsLabel; 021import org.minidns.dnsmessage.Question; 022import org.minidns.dnsname.DnsName; 023import org.minidns.dnsqueryresult.DnsQueryResult; 024import org.minidns.hla.srv.SrvProto; 025import org.minidns.hla.srv.SrvService; 026import org.minidns.hla.srv.SrvServiceProto; 027import org.minidns.hla.srv.SrvType; 028import org.minidns.iterative.ReliableDnsClient; 029import org.minidns.record.Data; 030import org.minidns.record.PTR; 031import org.minidns.record.SRV; 032import org.minidns.record.Record.TYPE; 033 034/** 035 * The high-level MiniDNS resolving API. It is designed to be easy to use. 036 * <p> 037 * A simple exammple how to resolve the IPv4 address of a given domain: 038 * </p> 039 * <pre> 040 * {@code 041 * ResolverResult<A> result = DnssecResolverApi.INSTANCE.resolve("verteiltesysteme.net", A.class); 042 * if (!result.wasSuccessful()) { 043 * RESPONSE_CODE responseCode = result.getResponseCode(); 044 * // Perform error handling. 045 * … 046 * return; 047 * } 048 * if (!result.isAuthenticData()) { 049 * // Response was not secured with DNSSEC. 050 * … 051 * return; 052 * } 053 * Set<A> answers = result.getAnswers(); 054 * for (A a : answers) { 055 * InetAddress inetAddress = a.getInetAddress(); 056 * // Do someting with the InetAddress, e.g. connect to. 057 * … 058 * } 059 * } 060 * </pre> 061 * <p> 062 * MiniDNS also supports SRV resource records as first class citizens: 063 * </p> 064 * <pre> 065 * {@code 066 * SrvResolverResult result = DnssecResolverApi.INSTANCE.resolveSrv(SrvType.xmpp_client, "example.org") 067 * if (!result.wasSuccessful()) { 068 * RESPONSE_CODE responseCode = result.getResponseCode(); 069 * // Perform error handling. 070 * … 071 * return; 072 * } 073 * if (!result.isAuthenticData()) { 074 * // Response was not secured with DNSSEC. 075 * … 076 * return; 077 * } 078 * List<ResolvedSrvRecord> srvRecords = result.getSortedSrvResolvedAddresses(); 079 * // Loop over the domain names pointed by the SRV RR. MiniDNS will return the list 080 * // correctly sorted by the priority and weight of the related SRV RR. 081 * for (ResolvedSrvRecord srvRecord : srvRecord) { 082 * // Loop over the Internet Address RRs resolved for the SRV RR. The order of 083 * // the list depends on the prefered IP version setting of MiniDNS. 084 * for (InternetAddressRR inetAddressRR : srvRecord.addresses) { 085 * InetAddress inetAddress = inetAddressRR.getInetAddress(); 086 * int port = srvAddresses.port; 087 * // Try to connect to inetAddress at port. 088 * … 089 * } 090 * } 091 * } 092 * </pre> 093 * 094 * @author Florian Schmaus 095 * 096 */ 097public class ResolverApi { 098 099 public static final ResolverApi INSTANCE = new ResolverApi(new ReliableDnsClient()); 100 101 private final AbstractDnsClient dnsClient; 102 103 public ResolverApi(AbstractDnsClient dnsClient) { 104 this.dnsClient = dnsClient; 105 } 106 107 public final <D extends Data> ResolverResult<D> resolve(String name, Class<D> type) throws IOException { 108 return resolve(DnsName.from(name), type); 109 } 110 111 public final <D extends Data> ResolverResult<D> resolve(DnsName name, Class<D> type) throws IOException { 112 TYPE t = TYPE.getType(type); 113 Question q = new Question(name, t); 114 return resolve(q); 115 } 116 117 public <D extends Data> ResolverResult<D> resolve(Question question) throws IOException { 118 DnsQueryResult dnsQueryResult = dnsClient.query(question); 119 120 return new ResolverResult<D>(question, dnsQueryResult, null); 121 } 122 123 public SrvResolverResult resolveSrv(SrvType type, String serviceName) throws IOException { 124 return resolveSrv(type.service, type.proto, DnsName.from(serviceName)); 125 } 126 127 public SrvResolverResult resolveSrv(SrvType type, DnsName serviceName) throws IOException { 128 return resolveSrv(type.service, type.proto, serviceName); 129 } 130 131 public SrvResolverResult resolveSrv(SrvService service, SrvProto proto, String name) throws IOException { 132 return resolveSrv(service.dnsLabel, proto.dnsLabel, DnsName.from(name)); 133 } 134 135 public SrvResolverResult resolveSrv(SrvService service, SrvProto proto, DnsName name) throws IOException { 136 return resolveSrv(service.dnsLabel, proto.dnsLabel, name); 137 } 138 139 public SrvResolverResult resolveSrv(DnsLabel service, DnsLabel proto, DnsName name) throws IOException { 140 SrvServiceProto srvServiceProto = new SrvServiceProto(service, proto); 141 return resolveSrv(name, srvServiceProto); 142 } 143 144 public SrvResolverResult resolveSrv(String name) throws IOException { 145 return resolveSrv(DnsName.from(name)); 146 } 147 148 public ResolverResult<PTR> reverseLookup(CharSequence inetAddressCs) throws IOException { 149 InetAddress inetAddress = InetAddress.getByName(inetAddressCs.toString()); 150 return reverseLookup(inetAddress); 151 } 152 153 public ResolverResult<PTR> reverseLookup(InetAddress inetAddress) throws IOException { 154 if (inetAddress instanceof Inet4Address) { 155 return reverseLookup((Inet4Address) inetAddress); 156 } else if (inetAddress instanceof Inet6Address) { 157 return reverseLookup((Inet6Address) inetAddress); 158 } else { 159 throw new IllegalArgumentException("The given InetAddress '" + inetAddress + "' is neither of type Inet4Address or Inet6Address"); 160 } 161 } 162 163 public ResolverResult<PTR> reverseLookup(Inet4Address inet4Address) throws IOException { 164 Question question = DnsClient.getReverseIpLookupQuestionFor(inet4Address); 165 return resolve(question); 166 } 167 168 public ResolverResult<PTR> reverseLookup(Inet6Address inet6Address) throws IOException { 169 Question question = DnsClient.getReverseIpLookupQuestionFor(inet6Address); 170 return resolve(question); 171 } 172 173 /** 174 * Resolve the {@link SRV} resource record for the given name. After ensuring that the resolution was successful 175 * with {@link SrvResolverResult#wasSuccessful()} , and, if DNSSEC was used, that the results could be verified with 176 * {@link SrvResolverResult#isAuthenticData()}, simply use {@link SrvResolverResult#getSortedSrvResolvedAddresses()} to 177 * retrieve the resolved IP addresses. 178 * <p> 179 * The name of SRV records is "_[service]._[protocol].[serviceDomain]", for example "_xmpp-client._tcp.example.org". 180 * </p> 181 * 182 * @param srvDnsName the name to resolve. 183 * @return a <code>SrvResolverResult</code> instance which can be used to retrieve the IP addresses. 184 * @throws IOException if an IO exception occurs. 185 */ 186 public SrvResolverResult resolveSrv(DnsName srvDnsName) throws IOException { 187 final int labelCount = srvDnsName.getLabelCount(); 188 if (labelCount < 3) { 189 throw new IllegalArgumentException(); 190 } 191 192 DnsLabel service = srvDnsName.getLabel(labelCount - 1); 193 DnsLabel proto = srvDnsName.getLabel(labelCount - 2); 194 DnsName name = srvDnsName.stripToLabels(labelCount - 2); 195 196 SrvServiceProto srvServiceProto = new SrvServiceProto(service, proto); 197 198 return resolveSrv(name, srvServiceProto); 199 } 200 201 /** 202 * Resolve the {@link SRV} resource record for the given service name, service and protcol. After ensuring that the 203 * resolution was successful with {@link SrvResolverResult#wasSuccessful()} , and, if DNSSEC was used, that the 204 * results could be verified with {@link SrvResolverResult#isAuthenticData()}, simply use 205 * {@link SrvResolverResult#getSortedSrvResolvedAddresses()} to retrieve the resolved IP addresses. 206 * 207 * @param name the DNS name of the service. 208 * @param srvServiceProto the service and protocol to lookup. 209 * @return a <code>SrvResolverResult</code> instance which can be used to retrieve the IP addresses. 210 * @throws IOException if an I/O error occurs. 211 */ 212 public SrvResolverResult resolveSrv(DnsName name, SrvServiceProto srvServiceProto) throws IOException { 213 DnsName srvDnsName = DnsName.from(srvServiceProto.service, srvServiceProto.proto, name); 214 ResolverResult<SRV> result = resolve(srvDnsName, SRV.class); 215 216 return new SrvResolverResult(result, srvServiceProto, this); 217 } 218 219 public final AbstractDnsClient getClient() { 220 return dnsClient; 221 } 222}