001 /****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one *
003 * or more contributor license agreements. See the NOTICE file *
004 * distributed with this work for additional information *
005 * regarding copyright ownership. The ASF licenses this file *
006 * to you under the Apache License, Version 2.0 (the *
007 * "License"); you may not use this file except in compliance *
008 * with the License. You may obtain a copy of the License at *
009 * *
010 * http://www.apache.org/licenses/LICENSE-2.0 *
011 * *
012 * Unless required by applicable law or agreed to in writing, *
013 * software distributed under the License is distributed on an *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
015 * KIND, either express or implied. See the License for the *
016 * specific language governing permissions and limitations *
017 * under the License. *
018 ****************************************************************/
019
020
021 package org.apache.james.jspf.terms;
022
023 import org.apache.james.jspf.core.DNSLookupContinuation;
024 import org.apache.james.jspf.core.DNSRequest;
025 import org.apache.james.jspf.core.DNSResponse;
026 import org.apache.james.jspf.core.DNSService;
027 import org.apache.james.jspf.core.DNSServiceEnabled;
028 import org.apache.james.jspf.core.IPAddr;
029 import org.apache.james.jspf.core.MacroExpand;
030 import org.apache.james.jspf.core.SPFChecker;
031 import org.apache.james.jspf.core.SPFCheckerDNSResponseListener;
032 import org.apache.james.jspf.core.SPFSession;
033 import org.apache.james.jspf.core.SPFTermsRegexps;
034 import org.apache.james.jspf.core.exceptions.NeutralException;
035 import org.apache.james.jspf.core.exceptions.NoneException;
036 import org.apache.james.jspf.core.exceptions.PermErrorException;
037 import org.apache.james.jspf.core.exceptions.TempErrorException;
038 import org.apache.james.jspf.core.exceptions.TimeoutException;
039
040 import java.util.List;
041
042 /**
043 * This class represent the ptr mechanism
044 *
045 */
046 public class PTRMechanism extends GenericMechanism implements DNSServiceEnabled, SPFCheckerDNSResponseListener {
047
048 private final class ExpandedChecker implements SPFChecker {
049 private CleanupChecker cleanupChecker = new CleanupChecker();
050
051 private final class CleanupChecker implements SPFChecker {
052
053 /**
054 * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
055 */
056 public DNSLookupContinuation checkSPF(SPFSession spfData)
057 throws PermErrorException, TempErrorException,
058 NeutralException, NoneException {
059 spfData.removeAttribute(ATTRIBUTE_DOMAIN_LIST);
060 spfData.removeAttribute(ATTRIBUTE_CURRENT_DOMAIN);
061 return null;
062 }
063 }
064
065 /**
066 * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
067 */
068 public DNSLookupContinuation checkSPF(SPFSession spfData) throws PermErrorException,
069 TempErrorException, NeutralException, NoneException {
070
071 // Get PTR Records for the ipAddress which is provided by SPF1Data
072 IPAddr ip = IPAddr.getAddress(spfData.getIpAddress());
073
074 // Get the right host.
075 String host = expandHost(spfData);
076
077 spfData.setAttribute(ATTRIBUTE_EXPANDED_HOST, host);
078
079 spfData.pushChecker(cleanupChecker);
080
081 return new DNSLookupContinuation(new DNSRequest(ip.getReverseIP(), DNSRequest.PTR), PTRMechanism.this);
082 }
083 }
084
085 private static final String ATTRIBUTE_CURRENT_DOMAIN = "PTRMechanism.currentDomain";
086
087 private static final String ATTRIBUTE_EXPANDED_HOST = "PTRMechanism.expandedHost";
088
089 private static final String ATTRIBUTE_DOMAIN_LIST = "PTRMechanism.domainListCheck";
090
091 /**
092 * ABNF: PTR = "ptr" [ ":" domain-spec ]
093 */
094 public static final String REGEX = "[pP][tT][rR]" + "(?:\\:"
095 + SPFTermsRegexps.DOMAIN_SPEC_REGEX + ")?";
096
097 private DNSService dnsService;
098
099 private SPFChecker expandedChecker = new ExpandedChecker();
100
101 /**
102 * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
103 */
104 public DNSLookupContinuation checkSPF(SPFSession spfData) throws PermErrorException,
105 TempErrorException, NeutralException, NoneException {
106 // update currentDepth
107 spfData.increaseCurrentDepth();
108
109 spfData.pushChecker(expandedChecker);
110 return macroExpand.checkExpand(getDomain(), spfData, MacroExpand.DOMAIN);
111 }
112
113 /**
114 * @see org.apache.james.jspf.core.DNSServiceEnabled#enableDNSService(org.apache.james.jspf.core.DNSService)
115 */
116 public void enableDNSService(DNSService service) {
117 this.dnsService = service;
118 }
119
120 /**
121 * @see org.apache.james.jspf.core.SPFCheckerDNSResponseListener#onDNSResponse(org.apache.james.jspf.core.DNSResponse, org.apache.james.jspf.core.SPFSession)
122 */
123 @SuppressWarnings("unchecked")
124 public DNSLookupContinuation onDNSResponse(DNSResponse response, SPFSession spfSession)
125 throws PermErrorException, TempErrorException, NoneException, NeutralException {
126
127 List<String> domainList = (List<String>) spfSession.getAttribute(ATTRIBUTE_DOMAIN_LIST);
128 try {
129 if (domainList == null) {
130
131 domainList = response.getResponse();
132
133 // No PTR records found
134 if (domainList == null) {
135 spfSession.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.FALSE);
136 return null;
137 }
138
139 // check if the maximum lookup count is reached
140 if (dnsService.getRecordLimit() > 0 && domainList.size() > dnsService.getRecordLimit()) {
141 // Truncate the PTR list to getRecordLimit.
142 // See #ptr-limit rfc4408 test
143 domainList = domainList.subList(0, dnsService.getRecordLimit()-1);
144 // throw new PermErrorException("Maximum PTR lookup count reached");
145 }
146
147 spfSession.setAttribute(ATTRIBUTE_DOMAIN_LIST, domainList);
148
149 } else {
150
151 String compareDomain = (String) spfSession.getAttribute(ATTRIBUTE_CURRENT_DOMAIN);
152 String host = (String) spfSession.getAttribute(ATTRIBUTE_EXPANDED_HOST);
153
154 List<String> aList = response.getResponse();
155
156
157 if (aList != null) {
158 for (int j = 0; j < aList.size(); j++) {
159 // Added the IPAddr parsing/toString to have matching in IPV6 multiple ways to
160 if (IPAddr.getAddress((String) aList.get(j)).getIPAddress().equals(IPAddr.getAddress(spfSession.getIpAddress()).getIPAddress())) {
161
162 if (compareDomain.equals(host)
163 || compareDomain.endsWith("." + host)) {
164 spfSession.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.TRUE);
165 return null;
166 }
167 }
168 }
169 }
170
171 }
172 } catch (TimeoutException e) {
173 throw new TempErrorException("Timeout querying the dns server");
174 }
175
176
177 if (domainList.size() > 0) {
178 String currentDomain = (String) domainList.remove(0);
179
180 DNSRequest dnsRequest;
181 // check if the connecting ip is ip6. If so lookup AAAA record
182 if (IPAddr.isIPV6(spfSession.getIpAddress())) {
183 // Get aaaa record for this
184 dnsRequest = new DNSRequest(currentDomain, DNSRequest.AAAA);
185 } else {
186 // Get a record for this
187 dnsRequest = new DNSRequest(currentDomain, DNSRequest.A);
188 }
189
190 spfSession.setAttribute(ATTRIBUTE_CURRENT_DOMAIN, currentDomain);
191
192 return new DNSLookupContinuation(dnsRequest, PTRMechanism.this);
193 } else {
194 spfSession.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.FALSE);
195 return null;
196 }
197
198 }
199
200
201 }