1/*
2 * Copyright 2015-2022 the original author or authors
3 *
4 * This software is licensed under the Apache License, Version 2.0,
5 * the GNU Lesser General Public License version 2 or later ("LGPL")
6 * and the WTFPL.
7 * You may choose either license to govern your use of this software only
8 * upon the condition that you accept all of the terms of either
9 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
10 */
11package de.gultsch.minidns;
12
13import java.util.Collections;
14import java.util.Set;
15
16import org.minidns.MiniDnsException;
17import org.minidns.MiniDnsException.NullResultException;
18import org.minidns.dnsmessage.DnsMessage;
19import org.minidns.dnsmessage.Question;
20import org.minidns.dnsqueryresult.DnsQueryResult;
21import org.minidns.dnsmessage.DnsMessage.RESPONSE_CODE;
22import org.minidns.dnssec.DnssecResultNotAuthenticException;
23import org.minidns.dnssec.DnssecUnverifiedReason;
24import org.minidns.record.Data;
25
26public class ResolverResult<D extends Data> {
27
28 protected final Question question;
29 private final RESPONSE_CODE responseCode;
30 private final Set<D> data;
31 private final boolean isAuthenticData;
32 protected final Set<DnssecUnverifiedReason> unverifiedReasons;
33 protected final DnsMessage answer;
34 protected final DnsQueryResult result;
35
36 public ResolverResult(Question question, DnsQueryResult result, Set<DnssecUnverifiedReason> unverifiedReasons) throws NullResultException {
37 // TODO: Is this null check still needed?
38 if (result == null) {
39 throw new MiniDnsException.NullResultException(question.asMessageBuilder().build());
40 }
41
42 this.result = result;
43
44 DnsMessage answer = result.response;
45 this.question = question;
46 this.responseCode = answer.responseCode;
47 this.answer = answer;
48
49 Set<D> r = answer.getAnswersFor(question);
50 if (r == null) {
51 this.data = Collections.emptySet();
52 } else {
53 this.data = Collections.unmodifiableSet(r);
54 }
55
56 if (unverifiedReasons == null) {
57 this.unverifiedReasons = null;
58 isAuthenticData = false;
59 } else {
60 this.unverifiedReasons = Collections.unmodifiableSet(unverifiedReasons);
61 isAuthenticData = this.unverifiedReasons.isEmpty();
62 }
63 }
64
65 public boolean wasSuccessful() {
66 return responseCode == RESPONSE_CODE.NO_ERROR;
67 }
68
69 public Set<D> getAnswers() {
70 throwIseIfErrorResponse();
71 return data;
72 }
73
74 public Set<D> getAnswersOrEmptySet() {
75 return data;
76 }
77
78 public RESPONSE_CODE getResponseCode() {
79 return responseCode;
80 }
81
82 public boolean isAuthenticData() {
83 throwIseIfErrorResponse();
84 return isAuthenticData;
85 }
86
87 /**
88 * Get the reasons the result could not be verified if any exists.
89 *
90 * @return The reasons the result could not be verified or <code>null</code>.
91 */
92 public Set<DnssecUnverifiedReason> getUnverifiedReasons() {
93 throwIseIfErrorResponse();
94 return unverifiedReasons;
95 }
96
97 public Question getQuestion() {
98 return question;
99 }
100
101 public void throwIfErrorResponse() throws ResolutionUnsuccessfulException {
102 ResolutionUnsuccessfulException resolutionUnsuccessfulException = getResolutionUnsuccessfulException();
103 if (resolutionUnsuccessfulException != null) throw resolutionUnsuccessfulException;
104 }
105
106 private ResolutionUnsuccessfulException resolutionUnsuccessfulException;
107
108 public ResolutionUnsuccessfulException getResolutionUnsuccessfulException() {
109 if (wasSuccessful()) return null;
110
111 if (resolutionUnsuccessfulException == null) {
112 resolutionUnsuccessfulException = new ResolutionUnsuccessfulException(question, responseCode);
113 }
114
115 return resolutionUnsuccessfulException;
116 }
117
118 private DnssecResultNotAuthenticException dnssecResultNotAuthenticException;
119
120 public DnssecResultNotAuthenticException getDnssecResultNotAuthenticException() {
121 if (!wasSuccessful())
122 return null;
123 if (isAuthenticData)
124 return null;
125
126 if (dnssecResultNotAuthenticException == null) {
127 dnssecResultNotAuthenticException = DnssecResultNotAuthenticException.from(getUnverifiedReasons());
128 }
129
130 return dnssecResultNotAuthenticException;
131 }
132
133 /**
134 * Get the raw answer DNS message we received. <b>This is likely not what you want</b>, try {@link #getAnswers()} instead.
135 *
136 * @return the raw answer DNS Message.
137 * @see #getAnswers()
138 */
139 public DnsMessage getRawAnswer() {
140 return answer;
141 }
142
143 public DnsQueryResult getDnsQueryResult() {
144 return result;
145 }
146
147 @Override
148 public final String toString() {
149 StringBuilder sb = new StringBuilder();
150
151 sb.append(getClass().getName()).append('\n')
152 .append("Question: ").append(question).append('\n')
153 .append("Response Code: ").append(responseCode).append('\n');
154
155 if (responseCode == RESPONSE_CODE.NO_ERROR) {
156 if (isAuthenticData) {
157 sb.append("Results verified via DNSSEC\n");
158 }
159 if (hasUnverifiedReasons()) {
160 sb.append(unverifiedReasons).append('\n');
161 }
162 sb.append(answer.answerSection);
163 }
164
165 return sb.toString();
166 }
167
168 boolean hasUnverifiedReasons() {
169 return unverifiedReasons != null && !unverifiedReasons.isEmpty();
170 }
171
172 protected void throwIseIfErrorResponse() {
173 ResolutionUnsuccessfulException resolutionUnsuccessfulException = getResolutionUnsuccessfulException();
174 if (resolutionUnsuccessfulException != null)
175 throw new IllegalStateException("Can not perform operation because the DNS resolution was unsuccessful",
176 resolutionUnsuccessfulException);
177 }
178}