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 }