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.util.ArrayList;
015import java.util.Collections;
016import java.util.List;
017import java.util.Set;
018
019import org.minidns.AbstractDnsClient.IpVersionSetting;
020import org.minidns.MiniDnsException.NullResultException;
021import org.minidns.dnsname.DnsName;
022import org.minidns.record.A;
023import org.minidns.record.AAAA;
024import org.minidns.record.InternetAddressRR;
025import org.minidns.record.SRV;
026import org.minidns.util.SrvUtil;
027
028public class SrvResolverResult extends ResolverResult<SRV> {
029
030    private final ResolverApi resolver;
031    private final IpVersionSetting ipVersion;
032
033    private List<ResolvedSrvRecord> sortedSrvResolvedAddresses;
034
035    SrvResolverResult(ResolverResult<SRV> srvResult, ResolverApi resolver) throws NullResultException {
036        super(srvResult.question, srvResult.answer, srvResult.unverifiedReasons);
037        this.resolver = resolver;
038        this.ipVersion = resolver.getClient().getPreferedIpVersion();
039    }
040
041    public List<ResolvedSrvRecord> getSortedSrvResolvedAddresses() throws IOException {
042        if (sortedSrvResolvedAddresses != null) {
043            return sortedSrvResolvedAddresses;
044        }
045
046        throwIseIfErrorResponse();
047
048        List<SRV> srvRecords = SrvUtil.sortSrvRecords(getAnswers());
049
050        List<ResolvedSrvRecord> res = new ArrayList<>(srvRecords.size());
051        for (SRV srvRecord : srvRecords) {
052            ResolverResult<A> aRecordsResult = null;
053            ResolverResult<AAAA> aaaaRecordsResult = null;
054            Set<A> aRecords = Collections.emptySet();
055            if (ipVersion.v4) {
056                aRecordsResult = resolver.resolve(srvRecord.target, A.class);
057                if (aRecordsResult.wasSuccessful() && !aRecordsResult.hasUnverifiedReasons()) {
058                    aRecords = aRecordsResult.getAnswers();
059                }
060            }
061
062            Set<AAAA> aaaaRecords = Collections.emptySet();
063            if (ipVersion.v6) {
064                aaaaRecordsResult = resolver.resolve(srvRecord.target, AAAA.class);
065                if (aaaaRecordsResult.wasSuccessful() && !aaaaRecordsResult.hasUnverifiedReasons()) {
066                    aaaaRecords = aaaaRecordsResult.getAnswers();
067                }
068            }
069
070            if (aRecords.isEmpty() && aaaaRecords.isEmpty()) {
071                // TODO Possibly check for (C|D)NAME usage and throw a meaningful exception that it is not allowed for
072                // the target of an SRV to be an alias as per RFC 2782.
073                /*
074                ResolverResult<CNAME> cnameRecordResult = resolve(srvRecord.name, CNAME.class);
075                if (cnameRecordResult.wasSuccessful()) {
076                }
077                */
078                continue;
079            }
080
081            List<InternetAddressRR> srvAddresses = new ArrayList<>(aRecords.size() + aaaaRecords.size());
082            switch (ipVersion) {
083            case v4only:
084                srvAddresses.addAll(aRecords);
085                break;
086            case v6only:
087                srvAddresses.addAll(aaaaRecords);
088                break;
089            case v4v6:
090                srvAddresses.addAll(aRecords);
091                srvAddresses.addAll(aaaaRecords);
092                break;
093            case v6v4:
094                srvAddresses.addAll(aaaaRecords);
095                srvAddresses.addAll(aRecords);
096                break;
097            }
098
099            ResolvedSrvRecord resolvedSrvAddresses = new ResolvedSrvRecord(question.name, srvRecord, srvAddresses,
100                    aRecordsResult, aaaaRecordsResult);
101            res.add(resolvedSrvAddresses);
102        }
103
104        sortedSrvResolvedAddresses = res;
105
106        return res;
107    }
108
109    public static class ResolvedSrvRecord {
110        public final DnsName name;
111        public final SRV srv;
112        public final List<InternetAddressRR> addresses;
113        public final ResolverResult<A> aRecordsResult;
114        public final ResolverResult<AAAA> aaaaRecordsResult;
115
116        /**
117         * The port announced by the SRV RR. This is simply a shortcut for <code>srv.port</code>.
118         */
119        public final int port;
120
121        private ResolvedSrvRecord(DnsName name, SRV srv, List<InternetAddressRR> addresses, ResolverResult<A> aRecordsResult, ResolverResult<AAAA> aaaaRecordsResult) {
122            this.name = name;
123            this.srv = srv;
124            this.addresses = Collections.unmodifiableList(addresses);
125            this.port = srv.port;
126            this.aRecordsResult = aRecordsResult;
127            this.aaaaRecordsResult = aaaaRecordsResult;
128        }
129    }
130}