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    }