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 }