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.util.Collections; 014import java.util.Set; 015 016import org.minidns.MiniDnsException; 017import org.minidns.MiniDnsException.NullResultException; 018import org.minidns.dnsmessage.DnsMessage; 019import org.minidns.dnsmessage.Question; 020import org.minidns.dnsqueryresult.DnsQueryResult; 021import org.minidns.dnsmessage.DnsMessage.RESPONSE_CODE; 022import org.minidns.dnssec.DnssecResultNotAuthenticException; 023import org.minidns.dnssec.DnssecUnverifiedReason; 024import org.minidns.record.Data; 025 026public class ResolverResult<D extends Data> { 027 028 protected final Question question; 029 private final RESPONSE_CODE responseCode; 030 private final Set<D> data; 031 private final boolean isAuthenticData; 032 protected final Set<DnssecUnverifiedReason> unverifiedReasons; 033 protected final DnsMessage answer; 034 protected final DnsQueryResult result; 035 036 ResolverResult(Question question, DnsQueryResult result, Set<DnssecUnverifiedReason> unverifiedReasons) throws NullResultException { 037 // TODO: Is this null check still needed? 038 if (result == null) { 039 throw new MiniDnsException.NullResultException(question.asMessageBuilder().build()); 040 } 041 042 this.result = result; 043 044 DnsMessage answer = result.response; 045 this.question = question; 046 this.responseCode = answer.responseCode; 047 this.answer = answer; 048 049 Set<D> r = answer.getAnswersFor(question); 050 if (r == null) { 051 this.data = Collections.emptySet(); 052 } else { 053 this.data = Collections.unmodifiableSet(r); 054 } 055 056 if (unverifiedReasons == null) { 057 this.unverifiedReasons = null; 058 isAuthenticData = false; 059 } else { 060 this.unverifiedReasons = Collections.unmodifiableSet(unverifiedReasons); 061 isAuthenticData = this.unverifiedReasons.isEmpty(); 062 } 063 } 064 065 public boolean wasSuccessful() { 066 return responseCode == RESPONSE_CODE.NO_ERROR; 067 } 068 069 public Set<D> getAnswers() { 070 throwIseIfErrorResponse(); 071 return data; 072 } 073 074 public Set<D> getAnswersOrEmptySet() { 075 return data; 076 } 077 078 public RESPONSE_CODE getResponseCode() { 079 return responseCode; 080 } 081 082 public boolean isAuthenticData() { 083 throwIseIfErrorResponse(); 084 return isAuthenticData; 085 } 086 087 /** 088 * Get the reasons the result could not be verified if any exists. 089 * 090 * @return The reasons the result could not be verified or <code>null</code>. 091 */ 092 public Set<DnssecUnverifiedReason> getUnverifiedReasons() { 093 throwIseIfErrorResponse(); 094 return unverifiedReasons; 095 } 096 097 public Question getQuestion() { 098 return question; 099 } 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}