001/*
002 * Copyright 2015-2018 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.dnsmessage.DnsMessage;
020import org.minidns.dnsmessage.Question;
021import org.minidns.dnsname.DnsName;
022import org.minidns.iterative.ReliableDnsClient;
023import org.minidns.record.Data;
024import org.minidns.record.PTR;
025import org.minidns.record.SRV;
026import org.minidns.record.Record.TYPE;
027import org.minidns.util.InetAddressUtil;
028
029/**
030 * The high-level MiniDNS resolving API. It is designed to be easy to use.
031 * <p>
032 * A simple exammple how to resolve the IPv4 address of a given domain:
033 * </p>
034 * <pre>
035 * {@code
036 * ResolverResult<A> result = DnssecResolverApi.INSTANCE.resolve("verteiltesysteme.net", A.class);
037 * if (!result.wasSuccessful()) {
038 *   RESPONSE_CODE responseCode = result.getResponseCode();
039 *   // Perform error handling.
040 *   …
041 *   return;
042 * }
043 * if (!result.isAuthenticData()) {
044 *   // Response was not secured with DNSSEC.
045 *   …
046 *   return;
047 * }
048 * Set<A> answers = result.getAnswers();
049 * for (A a : answers) {
050 *   InetAddress inetAddress = a.getInetAddress();
051 *   // Do someting with the InetAddress, e.g. connect to.
052 *   …
053 * }
054 * }
055 * </pre>
056 * <p>
057 * MiniDNS also supports SRV resource records as first class citizens:
058 * </p>
059 * <pre>
060 * {@code
061 * SrvResolverResult result = DnssecResolverApi.INSTANCE.resolveSrv(SrvType.xmpp_client, "example.org")
062 * if (!result.wasSuccessful()) {
063 *   RESPONSE_CODE responseCode = result.getResponseCode();
064 *   // Perform error handling.
065 *   …
066 *   return;
067 * }
068 * if (!result.isAuthenticData()) {
069 *   // Response was not secured with DNSSEC.
070 *   …
071 *   return;
072 * }
073 * List<ResolvedSrvRecord> srvRecords = result.getSortedSrvResolvedAddresses();
074 * // Loop over the domain names pointed by the SRV RR. MiniDNS will return the list
075 * // correctly sorted by the priority and weight of the related SRV RR.
076 * for (ResolvedSrvRecord srvRecord : srvRecord) {
077 *   // Loop over the Internet Address RRs resolved for the SRV RR. The order of
078 *   // the list depends on the prefered IP version setting of MiniDNS.
079 *   for (InternetAddressRR inetAddressRR : srvRecord.addresses) {
080 *     InetAddress inetAddress = inetAddressRR.getInetAddress();
081 *     int port = srvAddresses.port;
082 *     // Try to connect to inetAddress at port.
083 *     …
084 *   }
085 * }
086 * }
087 * </pre>
088 *
089 * @author Florian Schmaus
090 *
091 */
092public class ResolverApi {
093
094    public static final ResolverApi INSTANCE = new ResolverApi(new ReliableDnsClient());
095
096    private final AbstractDnsClient dnsClient;
097
098    public ResolverApi(AbstractDnsClient dnsClient) {
099        this.dnsClient = dnsClient;
100    }
101
102    public final <D extends Data> ResolverResult<D> resolve(String name, Class<D> type) throws IOException {
103        return resolve(DnsName.from(name), type);
104    }
105
106    public final <D extends Data> ResolverResult<D> resolve(DnsName name, Class<D> type) throws IOException {
107        TYPE t = TYPE.getType(type);
108        Question q = new Question(name, t);
109        return resolve(q);
110    }
111
112    public <D extends Data> ResolverResult<D> resolve(Question question) throws IOException {
113        DnsMessage dnsMessage = dnsClient.query(question);
114
115        return new ResolverResult<D>(question, dnsMessage, null);
116    }
117
118    public SrvResolverResult resolveSrv(SrvType type, String serviceName) throws IOException {
119        return resolveSrv(type.service, type.proto, DnsName.from(serviceName));
120    }
121
122    public SrvResolverResult resolveSrv(SrvType type, DnsName serviceName) throws IOException {
123        return resolveSrv(type.service, type.proto, serviceName);
124    }
125
126    public SrvResolverResult resolveSrv(SrvService service, SrvProto proto, String name) throws IOException {
127        return resolveSrv(service.dnsName, proto.dnsName, DnsName.from(name));
128    }
129
130    public SrvResolverResult resolveSrv(SrvService service, SrvProto proto, DnsName name) throws IOException {
131        return resolveSrv(service.dnsName, proto.dnsName, name);
132    }
133
134    public SrvResolverResult resolveSrv(DnsName service, DnsName proto, DnsName name) throws IOException {
135        DnsName srvRrName = DnsName.from(service, proto, name);
136        return resolveSrv(srvRrName);
137    }
138
139    public SrvResolverResult resolveSrv(String name) throws IOException {
140        return resolveSrv(DnsName.from(name));
141    }
142
143    public ResolverResult<PTR> reverseLookup(CharSequence inetAddressCs) throws IOException {
144        InetAddress inetAddress = InetAddress.getByName(inetAddressCs.toString());
145        return reverseLookup(inetAddress);
146    }
147
148    public ResolverResult<PTR> reverseLookup(InetAddress inetAddress) throws IOException {
149        if (inetAddress instanceof Inet4Address) {
150            return reverseLookup((Inet4Address) inetAddress);
151        } else if (inetAddress instanceof Inet6Address) {
152            return reverseLookup((Inet6Address) inetAddress);
153        } else {
154            throw new IllegalArgumentException("The given InetAddress '" + inetAddress + "' is neither of type Inet4Address or Inet6Address");
155        }
156    }
157
158    public ResolverResult<PTR> reverseLookup(Inet4Address inet4Address) throws IOException {
159        DnsName reversedIpAddress = InetAddressUtil.reverseIpAddressOf(inet4Address);
160        DnsName dnsName = DnsName.from(reversedIpAddress, DnsName.IN_ADDR_ARPA);
161        return resolve(dnsName, PTR.class);
162    }
163
164    public ResolverResult<PTR> reverseLookup(Inet6Address inet6Address) throws IOException {
165        DnsName reversedIpAddress = InetAddressUtil.reverseIpAddressOf(inet6Address);
166        DnsName dnsName = DnsName.from(reversedIpAddress, DnsName.IP6_ARPA);
167        return resolve(dnsName, PTR.class);
168    }
169
170    /**
171     * Resolve the {@link SRV} resource record for the given name. After ensuring that the resolution was successful
172     * with {@link SrvResolverResult#wasSuccessful()} , and, if DNSSEC was used, that the results could be verified with
173     * {@link SrvResolverResult#isAuthenticData()}, simply use {@link SrvResolverResult#getSortedSrvResolvedAddresses()} to
174     * retrieve the resolved IP addresses.
175     * <p>
176     * The name of SRV records is "_[service]._[protocol].[serviceDomain]", for example "_xmpp-client._tcp.example.org".
177     * </p>
178     *
179     * @param name the name to resolve.
180     * @return a <code>SrvResolverResult</code> instance which can be used to retrieve the addresses.
181     * @throws IOException if an IO exception occurs.
182     */
183    public SrvResolverResult resolveSrv(DnsName name) throws IOException {
184        ResolverResult<SRV> result = resolve(name, SRV.class);
185        return new SrvResolverResult(result, this);
186
187    }
188
189    public final AbstractDnsClient getClient() {
190        return dnsClient;
191    }
192}