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.IPAddr; 027 import org.apache.james.jspf.core.MacroExpand; 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.SPFTermsRegexps; 032 import org.apache.james.jspf.core.exceptions.NeutralException; 033 import org.apache.james.jspf.core.exceptions.NoneException; 034 import org.apache.james.jspf.core.exceptions.PermErrorException; 035 import org.apache.james.jspf.core.exceptions.TempErrorException; 036 import org.apache.james.jspf.core.exceptions.TimeoutException; 037 038 import java.util.ArrayList; 039 import java.util.List; 040 041 /** 042 * This class represent the mx mechanism 043 * 044 */ 045 public class MXMechanism extends AMechanism implements SPFCheckerDNSResponseListener { 046 047 private final class ExpandedChecker implements SPFChecker { 048 049 /** 050 * @see org.apache.james.jspf.core.SPFChecker#checkSPF(org.apache.james.jspf.core.SPFSession) 051 */ 052 public DNSLookupContinuation checkSPF(SPFSession spfData) throws PermErrorException, 053 TempErrorException, NeutralException, NoneException { 054 055 // Get the right host. 056 String host = expandHost(spfData); 057 058 return new DNSLookupContinuation(new DNSRequest(host, DNSRequest.MX), MXMechanism.this); 059 } 060 } 061 062 private static final String ATTRIBUTE_MX_RECORDS = "MXMechanism.mxRecords"; 063 private static final String ATTRIBUTE_CHECK_RECORDS = "MXMechanism.checkRecords"; 064 /** 065 * ABNF: MX = "mx" [ ":" domain-spec ] [ dual-cidr-length ] 066 */ 067 public static final String REGEX = "[mM][xX]" + "(?:\\:" 068 + SPFTermsRegexps.DOMAIN_SPEC_REGEX + ")?" + "(?:" 069 + DUAL_CIDR_LENGTH_REGEX + ")?"; 070 071 private SPFChecker expandedChecker = new ExpandedChecker(); 072 073 /** 074 * @see org.apache.james.jspf.terms.AMechanism#checkSPF(org.apache.james.jspf.core.SPFSession) 075 */ 076 public DNSLookupContinuation checkSPF(SPFSession spfData) throws PermErrorException, 077 TempErrorException, NeutralException, NoneException{ 078 079 // update currentDepth 080 spfData.increaseCurrentDepth(); 081 082 spfData.pushChecker(expandedChecker); 083 return macroExpand.checkExpand(getDomain(), spfData, MacroExpand.DOMAIN); 084 } 085 086 /** 087 * @see org.apache.james.jspf.terms.AMechanism#onDNSResponse(org.apache.james.jspf.core.DNSResponse, org.apache.james.jspf.core.SPFSession) 088 */ 089 @SuppressWarnings("unchecked") 090 public DNSLookupContinuation onDNSResponse(DNSResponse response, SPFSession spfSession) 091 throws PermErrorException, TempErrorException, NoneException, NeutralException { 092 try { 093 094 List<String> records = (List<String>) spfSession.getAttribute(ATTRIBUTE_CHECK_RECORDS); 095 List<String> mxR = (List<String>) spfSession.getAttribute(ATTRIBUTE_MX_RECORDS); 096 097 if (records == null) { 098 099 records = response.getResponse(); 100 101 if (records == null) { 102 // no mx record found 103 spfSession.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.FALSE); 104 return null; 105 } 106 107 spfSession.setAttribute(ATTRIBUTE_CHECK_RECORDS, records); 108 109 } else { 110 111 List<String> res = response.getResponse(); 112 113 if (res != null) { 114 if (mxR == null) { 115 mxR = new ArrayList<String>(); 116 spfSession.setAttribute(ATTRIBUTE_MX_RECORDS, mxR); 117 } 118 mxR.addAll(res); 119 } 120 121 } 122 123 // if the remote IP is an ipv6 we check ipv6 addresses, otherwise ip4 124 boolean isIPv6 = IPAddr.isIPV6(spfSession.getIpAddress()); 125 126 String mx; 127 while (records.size() > 0 && (mx = records.remove(0)) != null && mx.length() > 0) { 128 log.debug("Add MX-Record " + mx + " to list"); 129 130 return new DNSLookupContinuation(new DNSRequest(mx, isIPv6 ? DNSRequest.AAAA : DNSRequest.A), MXMechanism.this); 131 132 } 133 134 // no mx record found 135 if (mxR == null || mxR.size() == 0) { 136 spfSession.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.FALSE); 137 return null; 138 } 139 140 // get the ipAddress 141 IPAddr checkAddress; 142 checkAddress = IPAddr.getAddress(spfSession.getIpAddress(), isIPv6 ? getIp6cidr() : getIp4cidr()); 143 144 // clean up attributes 145 spfSession.removeAttribute(ATTRIBUTE_CHECK_RECORDS); 146 spfSession.removeAttribute(ATTRIBUTE_MX_RECORDS); 147 spfSession.setAttribute(Directive.ATTRIBUTE_MECHANISM_RESULT, Boolean.valueOf(checkAddressList(checkAddress, mxR, getIp4cidr()))); 148 return null; 149 150 } catch (TimeoutException e) { 151 spfSession.setAttribute(ATTRIBUTE_CHECK_RECORDS, null); 152 spfSession.setAttribute(ATTRIBUTE_MX_RECORDS, null); 153 throw new TempErrorException("Timeout querying the dns server"); 154 } 155 } 156 157 /** 158 * @see org.apache.james.jspf.terms.AMechanism#toString() 159 */ 160 public String toString() { 161 return super.toString("mx"); 162 } 163 164 }