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    }