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.impl; 021 022 import org.apache.james.jspf.core.DNSRequest; 023 import org.apache.james.jspf.core.DNSService; 024 import org.apache.james.jspf.core.IPAddr; 025 import org.apache.james.jspf.core.Logger; 026 import org.apache.james.jspf.core.exceptions.TimeoutException; 027 import org.xbill.DNS.AAAARecord; 028 import org.xbill.DNS.ARecord; 029 import org.xbill.DNS.Lookup; 030 import org.xbill.DNS.MXRecord; 031 import org.xbill.DNS.PTRRecord; 032 import org.xbill.DNS.Record; 033 import org.xbill.DNS.Resolver; 034 import org.xbill.DNS.SPFRecord; 035 import org.xbill.DNS.TXTRecord; 036 import org.xbill.DNS.TextParseException; 037 import org.xbill.DNS.Type; 038 039 import java.net.InetAddress; 040 import java.net.UnknownHostException; 041 import java.util.ArrayList; 042 import java.util.Iterator; 043 import java.util.List; 044 045 /** 046 * This class contains helper to get all neccassary DNS infos that are needed 047 * for SPF 048 */ 049 public class DNSServiceXBillImpl implements DNSService { 050 051 // The logger 052 protected Logger log; 053 054 // The record limit for lookups 055 protected int recordLimit; 056 057 // The resolver used for the lookup 058 protected Resolver resolver; 059 060 /** 061 * Default Constructor. 062 * Uses the DNSJava static DefaultResolver 063 */ 064 public DNSServiceXBillImpl(Logger logger) { 065 this(logger, Lookup.getDefaultResolver()); 066 } 067 068 /** 069 * Constructor to specify a custom resolver. 070 */ 071 public DNSServiceXBillImpl(Logger logger, Resolver resolver) { 072 this.log = logger; 073 this.resolver = resolver; 074 // Default record limit is 10 075 this.recordLimit = 10; 076 } 077 078 /** 079 * NOTE if this class is created with the default constructor it 080 * will use the static DefaultResolver from DNSJava and this method 081 * will change it's timeout. 082 * Other tools using DNSJava in the same JVM could be affected by 083 * this timeout change. 084 * 085 * @see org.apache.james.jspf.core.DNSService#setTimeOut(int) 086 */ 087 public synchronized void setTimeOut(int timeOut) { 088 this.resolver.setTimeout(timeOut); 089 } 090 091 /** 092 * @see org.apache.james.jspf.core.DNSService#getLocalDomainNames() 093 */ 094 public List<String> getLocalDomainNames() { 095 List<String> names = new ArrayList<String>(); 096 097 log.debug("Start Local ipaddress lookup"); 098 try { 099 InetAddress ia[] = InetAddress.getAllByName(InetAddress 100 .getLocalHost().getHostName()); 101 102 for (int i = 0; i < ia.length; i++) { 103 String host = ia[i].getHostName(); 104 names.add(host); 105 106 log.debug("Add hostname " + host + " to list"); 107 } 108 } catch (UnknownHostException e) { 109 // just ignore this.. 110 } 111 return names; 112 113 } 114 115 /** 116 * @return the current record limit 117 */ 118 public int getRecordLimit() { 119 return recordLimit; 120 } 121 122 /** 123 * Set a new limit for the number of records for MX and PTR lookups. 124 * @param recordLimit 125 */ 126 public void setRecordLimit(int recordLimit) { 127 this.recordLimit = recordLimit; 128 } 129 130 /** 131 * @see org.apache.james.jspf.core.DNSService#getRecords(org.apache.james.jspf.core.DNSRequest) 132 */ 133 public List<String> getRecords(DNSRequest request) 134 throws TimeoutException { 135 String recordTypeDescription; 136 int dnsJavaType; 137 switch (request.getRecordType()) { 138 case DNSRequest.A: recordTypeDescription = "A"; dnsJavaType = Type.A; break; 139 case DNSRequest.AAAA: recordTypeDescription = "AAAA"; dnsJavaType = Type.AAAA; break; 140 case DNSRequest.MX: recordTypeDescription = "MX"; dnsJavaType = Type.MX; break; 141 case DNSRequest.PTR: recordTypeDescription = "PTR"; dnsJavaType = Type.PTR; break; 142 case DNSRequest.TXT: recordTypeDescription = "TXT"; dnsJavaType = Type.TXT; break; 143 case DNSRequest.SPF: recordTypeDescription= "SPF"; dnsJavaType = Type.SPF; break; 144 default: // TODO fail! 145 return null; 146 } 147 try { 148 149 log.debug("Start "+recordTypeDescription+"-Record lookup for : " + request.getHostname()); 150 151 Lookup query = new Lookup(request.getHostname(), dnsJavaType); 152 query.setResolver(resolver); 153 154 Record[] rr = query.run(); 155 int queryResult = query.getResult(); 156 157 158 if (queryResult == Lookup.TRY_AGAIN) { 159 throw new TimeoutException(query.getErrorString()); 160 } 161 162 List<String> records = convertRecordsToList(rr); 163 164 log.debug("Found " + (rr != null ? rr.length : 0) + " "+recordTypeDescription+"-Records"); 165 return records; 166 } catch (TextParseException e) { 167 // i think this is the best we could do 168 log.debug("No "+recordTypeDescription+" Record found for host: " + request.getHostname()); 169 return null; 170 } 171 } 172 173 /** 174 * Convert the given Record array to a List 175 * 176 * @param rr Record array 177 * @return list 178 */ 179 @SuppressWarnings("unchecked") 180 public static List<String> convertRecordsToList(Record[] rr) { 181 List<String> records; 182 if (rr != null && rr.length > 0) { 183 records = new ArrayList<String>(); 184 for (int i = 0; i < rr.length; i++) { 185 switch (rr[i].getType()) { 186 case Type.A: 187 ARecord a = (ARecord) rr[i]; 188 records.add(a.getAddress().getHostAddress()); 189 break; 190 case Type.AAAA: 191 AAAARecord aaaa = (AAAARecord) rr[i]; 192 records.add(aaaa.getAddress().getHostAddress()); 193 break; 194 case Type.MX: 195 MXRecord mx = (MXRecord) rr[i]; 196 records.add(mx.getTarget().toString()); 197 break; 198 case Type.PTR: 199 PTRRecord ptr = (PTRRecord) rr[i]; 200 records.add(IPAddr.stripDot(ptr.getTarget().toString())); 201 break; 202 case Type.TXT: 203 TXTRecord txt = (TXTRecord) rr[i]; 204 if (txt.getStrings().size() == 1) { 205 records.add((String)txt.getStrings().get(0)); 206 } else { 207 StringBuffer sb = new StringBuffer(); 208 for (Iterator<String> it = txt.getStrings().iterator(); it 209 .hasNext();) { 210 String k = (String) it.next(); 211 sb.append(k); 212 } 213 records.add(sb.toString()); 214 } 215 break; 216 case Type.SPF: 217 SPFRecord spf = (SPFRecord) rr[i]; 218 if (spf.getStrings().size() == 1) { 219 records.add((String)spf.getStrings().get(0)); 220 } else { 221 StringBuffer sb = new StringBuffer(); 222 for (Iterator<String> it = spf.getStrings().iterator(); it 223 .hasNext();) { 224 String k = (String) it.next(); 225 sb.append(k); 226 } 227 records.add(sb.toString()); 228 } 229 break; 230 default: 231 return null; 232 } 233 } 234 } else { 235 records = null; 236 } 237 return records; 238 } 239 }