View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  package org.apache.james.jspf.policies;
21  
22  import org.apache.james.jspf.core.DNSLookupContinuation;
23  import org.apache.james.jspf.core.DNSRequest;
24  import org.apache.james.jspf.core.DNSResponse;
25  import org.apache.james.jspf.core.SPF1Constants;
26  import org.apache.james.jspf.core.SPF1Record;
27  import org.apache.james.jspf.core.SPF1Utils;
28  import org.apache.james.jspf.core.SPFChecker;
29  import org.apache.james.jspf.core.SPFCheckerDNSResponseListener;
30  import org.apache.james.jspf.core.SPFSession;
31  import org.apache.james.jspf.core.exceptions.NeutralException;
32  import org.apache.james.jspf.core.exceptions.NoneException;
33  import org.apache.james.jspf.core.exceptions.PermErrorException;
34  import org.apache.james.jspf.core.exceptions.TempErrorException;
35  import org.apache.james.jspf.core.exceptions.TimeoutException;
36  
37  import java.util.Iterator;
38  import java.util.List;
39  
40  /**
41   * Get the raw dns txt or spf entry which contains a spf entry
42   */
43  public class SPFRetriever implements SPFChecker {
44      
45      private static final class SPFRecordHandlerDNSResponseListener implements SPFCheckerDNSResponseListener {
46  
47          /**
48           * @see org.apache.james.jspf.core.SPFCheckerDNSResponseListener#onDNSResponse(org.apache.james.jspf.core.DNSResponse, org.apache.james.jspf.core.SPFSession)
49           */
50          public DNSLookupContinuation onDNSResponse(
51                  DNSResponse response, SPFSession session)
52                  throws PermErrorException,
53                  NoneException, TempErrorException,
54                  NeutralException {
55              
56              List spfR;
57              try {
58                  spfR = response.getResponse();
59                  String record = extractSPFRecord(spfR);
60                  if (record != null) {
61                      session.setAttribute(SPF1Utils.ATTRIBUTE_SPF1_RECORD, new SPF1Record(record));
62                  }
63              } catch (TimeoutException e) {
64                  throw new TempErrorException("Timeout querying dns");
65              }
66              return null;
67              
68          }
69          
70      }
71  
72      private static final class SPFRetrieverDNSResponseListener implements SPFCheckerDNSResponseListener {
73  
74          /**
75           * @see org.apache.james.jspf.core.SPFCheckerDNSResponseListener#onDNSResponse(org.apache.james.jspf.core.DNSResponse, org.apache.james.jspf.core.SPFSession)
76           */
77          public DNSLookupContinuation onDNSResponse(
78                  DNSResponse response, SPFSession session)
79                  throws PermErrorException, NoneException,
80                  TempErrorException, NeutralException {
81              try {
82                  List spfR = response.getResponse();
83                  
84                  if (spfR == null || spfR.isEmpty()) {
85                      
86                      String currentDomain = session.getCurrentDomain();
87                      return new DNSLookupContinuation(new DNSRequest(currentDomain, DNSRequest.TXT), new SPFRecordHandlerDNSResponseListener());
88  
89                  } else {
90                      
91                      String record = extractSPFRecord(spfR);
92                      if (record != null) {
93                          session.setAttribute(SPF1Utils.ATTRIBUTE_SPF1_RECORD, new SPF1Record(record));
94                      }
95                      
96                  }
97                  
98                  return null;
99                  
100             } catch (TimeoutException e) {
101                 throw new TempErrorException("Timeout querying dns");
102             }
103         }
104         
105     }
106 
107   /**
108     * This is used for testing purpose. Setting this to true will skip the initial
109     * lookups for SPF records and instead will simply check the TXT records.
110     */
111   private static final boolean CHECK_ONLY_TXT_RECORDS = false;
112     
113     /**
114      * Return the extracted SPF-Record 
115      *  
116      * @param spfR the List which holds TXT/SPF - Records
117      * @return returnValue the extracted SPF-Record
118      * @throws PermErrorException if more then one SPF - Record was found in the 
119      *                            given List.
120      */
121     protected static String extractSPFRecord(List spfR) throws PermErrorException {
122         if (spfR == null || spfR.isEmpty()) return null;
123         
124         String returnValue = null;
125         Iterator all = spfR.iterator();
126            
127         while (all.hasNext()) {
128             // DO NOT trim the result!
129             String compare = all.next().toString();
130 
131             // We trim the compare value only for the comparison
132             if (compare.toLowerCase().trim().startsWith(SPF1Constants.SPF_VERSION1 + " ") || compare.trim().equalsIgnoreCase(SPF1Constants.SPF_VERSION1)) {
133                 if (returnValue == null) {
134                     returnValue = compare;
135                 } else {
136                     throw new PermErrorException(
137                             "More than 1 SPF record found");
138                 }
139             }
140         }
141         
142         return returnValue;
143     }
144     
145 
146     /**
147      * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession)
148      */
149     public DNSLookupContinuation checkSPF(SPFSession spfData)
150             throws PermErrorException, TempErrorException, NeutralException,
151             NoneException {
152         SPF1Record res = (SPF1Record) spfData.getAttribute(SPF1Utils.ATTRIBUTE_SPF1_RECORD);
153         if (res == null) {
154             String currentDomain = spfData.getCurrentDomain();
155             if (CHECK_ONLY_TXT_RECORDS) {
156                 return new DNSLookupContinuation(new DNSRequest(currentDomain, DNSRequest.TXT), new SPFRecordHandlerDNSResponseListener());
157             } else {
158                 return new DNSLookupContinuation(new DNSRequest(currentDomain, DNSRequest.SPF), new SPFRetrieverDNSResponseListener());
159             }
160             
161         }
162         return null;
163     }
164 
165 }