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 package org.apache.james.jspf.policies; 021 022 import org.apache.james.jspf.core.DNSLookupContinuation; 023 import org.apache.james.jspf.core.DNSRequest; 024 import org.apache.james.jspf.core.DNSResponse; 025 import org.apache.james.jspf.core.SPF1Constants; 026 import org.apache.james.jspf.core.SPF1Record; 027 import org.apache.james.jspf.core.SPF1Utils; 028 import org.apache.james.jspf.core.SPFChecker; 029 import org.apache.james.jspf.core.SPFCheckerDNSResponseListener; 030 import org.apache.james.jspf.core.SPFSession; 031 import org.apache.james.jspf.core.exceptions.NeutralException; 032 import org.apache.james.jspf.core.exceptions.NoneException; 033 import org.apache.james.jspf.core.exceptions.PermErrorException; 034 import org.apache.james.jspf.core.exceptions.TempErrorException; 035 import org.apache.james.jspf.core.exceptions.TimeoutException; 036 037 import java.util.Iterator; 038 import java.util.List; 039 040 /** 041 * Get the raw dns txt or spf entry which contains a spf entry 042 */ 043 public class SPFRetriever implements SPFChecker { 044 045 private static final class SPFRecordHandlerDNSResponseListener implements SPFCheckerDNSResponseListener { 046 047 /** 048 * @see org.apache.james.jspf.core.SPFCheckerDNSResponseListener#onDNSResponse(org.apache.james.jspf.core.DNSResponse, org.apache.james.jspf.core.SPFSession) 049 */ 050 public DNSLookupContinuation onDNSResponse( 051 DNSResponse response, SPFSession session) 052 throws PermErrorException, 053 NoneException, TempErrorException, 054 NeutralException { 055 056 List<String> spfR; 057 try { 058 spfR = response.getResponse(); 059 String record = extractSPFRecord(spfR); 060 if (record != null) { 061 session.setAttribute(SPF1Utils.ATTRIBUTE_SPF1_RECORD, new SPF1Record(record)); 062 } 063 } catch (TimeoutException e) { 064 throw new TempErrorException("Timeout querying dns"); 065 } 066 return null; 067 068 } 069 070 } 071 072 private static final class SPFRetrieverDNSResponseListener implements SPFCheckerDNSResponseListener { 073 074 /** 075 * @see org.apache.james.jspf.core.SPFCheckerDNSResponseListener#onDNSResponse(org.apache.james.jspf.core.DNSResponse, org.apache.james.jspf.core.SPFSession) 076 */ 077 public DNSLookupContinuation onDNSResponse( 078 DNSResponse response, SPFSession session) 079 throws PermErrorException, NoneException, 080 TempErrorException, NeutralException { 081 try { 082 List<String> spfR = response.getResponse(); 083 084 if (spfR == null || spfR.isEmpty()) { 085 086 String currentDomain = session.getCurrentDomain(); 087 return new DNSLookupContinuation(new DNSRequest(currentDomain, DNSRequest.TXT), new SPFRecordHandlerDNSResponseListener()); 088 089 } else { 090 091 String record = extractSPFRecord(spfR); 092 if (record != null) { 093 session.setAttribute(SPF1Utils.ATTRIBUTE_SPF1_RECORD, new SPF1Record(record)); 094 } 095 096 } 097 098 return null; 099 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<String> spfR) throws PermErrorException { 122 if (spfR == null || spfR.isEmpty()) return null; 123 124 String returnValue = null; 125 Iterator<String> all = spfR.iterator(); 126 127 while (all.hasNext()) { 128 // DO NOT trim the result! 129 String compare = all.next(); 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 }