FileDocCategorySizeDatePackage
DNSServer.javaAPI DocApache James 2.3.120915Fri Jan 12 12:56:24 GMT 2007org.apache.james.dnsserver

DNSServer

public class DNSServer extends org.apache.avalon.framework.logger.AbstractLogEnabled implements org.apache.avalon.framework.activity.Disposable, org.apache.avalon.framework.activity.Initializable, org.apache.james.services.DNSServer, org.apache.avalon.framework.configuration.Configurable, DNSServerMBean
Provides DNS client functionality to services running inside James

Fields Summary
protected Resolver
resolver
A resolver instance used to retrieve DNS records. This is a reference to a third party library object.
private Cache
cache
A TTL cache of results received from the DNS server. This is a reference to a third party library object.
private int
maxCacheSize
Maximum number of RR to cache.
private int
dnsCredibility
Whether the DNS response is required to be authoritative
private List
dnsServers
The DNS servers to be used by this service
private Comparator
mxComparator
The MX Comparator used in the MX sort.
Constructors Summary
Methods Summary
private static java.lang.StringallowIPLiteral(java.lang.String host)

        if ((host.charAt(host.length() - 1) == '.")) {
            String possible_ip_literal = host.substring(0, host.length() - 1);
            if (org.xbill.DNS.Address.isDottedQuad(possible_ip_literal)) {
                host = possible_ip_literal;
            }
        }
        return host;
    
public voidconfigure(org.apache.avalon.framework.configuration.Configuration configuration)

see
org.apache.avalon.framework.configuration.Configurable#configure(Configuration)


           
          
          

        final boolean autodiscover =
            configuration.getChild( "autodiscover" ).getValueAsBoolean( true );

        if (autodiscover) {
            getLogger().info("Autodiscovery is enabled - trying to discover your system's DNS Servers");
            String[] serversArray = ResolverConfig.getCurrentConfig().servers();
            if (serversArray != null) {
                for ( int i = 0; i < serversArray.length; i++ ) {
                    dnsServers.add(serversArray[ i ]);
                    getLogger().info("Adding autodiscovered server " + serversArray[i]);
                }
            }
        }

        // Get the DNS servers that this service will use for lookups
        final Configuration serversConfiguration = configuration.getChild( "servers" );
        final Configuration[] serverConfigurations =
            serversConfiguration.getChildren( "server" );

        for ( int i = 0; i < serverConfigurations.length; i++ ) {
            dnsServers.add( serverConfigurations[ i ].getValue() );
        }

        if (dnsServers.isEmpty()) {
            getLogger().info("No DNS servers have been specified or found by autodiscovery - adding 127.0.0.1");
            dnsServers.add("127.0.0.1");
        }

        final boolean authoritative =
            configuration.getChild( "authoritative" ).getValueAsBoolean( false );
        // TODO: Check to see if the credibility field is being used correctly.  From the
        //       docs I don't think so
        dnsCredibility = authoritative ? Credibility.AUTH_ANSWER : Credibility.NONAUTH_ANSWER;

        maxCacheSize = (int) configuration.getChild( "maxcachesize" ).getValueAsLong( maxCacheSize );
    
public voiddispose()
The dispose operation is called at the end of a components lifecycle. Instances of this class use this method to release and destroy any resources that they own. This implementation no longer shuts down org.xbill.DNS.Cache because dnsjava 2.0.0 removed the need for a cleaner thread!

throws
Exception if an error is encountered during shutdown

    
public java.util.CollectionfindMXRecords(java.lang.String hostname)

Return a prioritized unmodifiable list of host handling mail for the domain.

First lookup MX hosts, then MX hosts of the CNAME adress, and if no server is found return the IP of the hostname

param
hostname domain name to look up
return
a unmodifiable list of handling servers corresponding to this mail domain name

        List servers = new ArrayList();
        try {
            servers = findMXRecordsRaw(hostname);
            return Collections.unmodifiableCollection(servers);
        } finally {
            //If we found no results, we'll add the original domain name if
            //it's a valid DNS entry
            if (servers.size () == 0) {
                StringBuffer logBuffer =
                    new StringBuffer(128)
                            .append("Couldn't resolve MX records for domain ")
                            .append(hostname)
                            .append(".");
                getLogger().info(logBuffer.toString());
                Record cnames[] = lookup(hostname, Type.CNAME);
                Collection cnameMXrecords = null;
                if (cnames!=null && cnames.length > 0) {
                    cnameMXrecords = findMXRecordsRaw(((CNAMERecord) cnames[0]).getTarget().toString());
                } else {
                    logBuffer = new StringBuffer(128)
                            .append("Couldn't find CNAME records for domain ")
                            .append(hostname)
                            .append(".");
                    getLogger().info(logBuffer.toString());
                }
                if (cnameMXrecords==null) {
                    try {
                        getByName(hostname);
                        servers.add(hostname);
                    } catch (UnknownHostException uhe) {
                        // The original domain name is not a valid host,
                        // so we can't add it to the server list.  In this
                        // case we return an empty list of servers
                        logBuffer = new StringBuffer(128)
                                  .append("Couldn't resolve IP address for host ")
                                  .append(hostname)
                                  .append(".");
                        getLogger().error(logBuffer.toString());
                    }
                } else {
                    servers.addAll(cnameMXrecords);
                }
            }
        }
    
public java.util.ListfindMXRecordsRaw(java.lang.String hostname)

Return a prioritized unmodifiable list of MX records obtained from the server.

param
hostname domain name to look up
return
a list of MX records corresponding to this mail domain

        Record answers[] = lookup(hostname, Type.MX);
        List servers = new ArrayList();
        if (answers == null) {
            return servers;
        }

        MXRecord mxAnswers[] = new MXRecord[answers.length];
        for (int i = 0; i < answers.length; i++) {
            mxAnswers[i] = (MXRecord)answers[i];
        }

        Arrays.sort(mxAnswers, mxComparator);

        for (int i = 0; i < mxAnswers.length; i++) {
            servers.add(mxAnswers[i].getTarget ().toString ());
            getLogger().debug(new StringBuffer("Found MX record ").append(mxAnswers[i].getTarget ().toString ()).toString());
        }
        return servers;
    
public static java.net.InetAddress[]getAllByName(java.lang.String host)

see
java.net.InetAddress#getByAllName(String)

        return org.xbill.DNS.Address.getAllByName(allowIPLiteral(host));
    
public static java.net.InetAddressgetByName(java.lang.String host)

see
java.net.InetAddress#getByName(String)

        return org.xbill.DNS.Address.getByName(allowIPLiteral(host));
    
public java.lang.String[]getDNSServers()

Return the list of DNS servers in use by this service

return
an array of DNS server names

        return (String[])dnsServers.toArray(new String[0]);
    
public java.util.IteratorgetSMTPHostAddresses(java.lang.String domainName)

        return new Iterator() {
            private Iterator mxHosts = findMXRecords(domainName).iterator();
            private Iterator addresses = null;

            public boolean hasNext() {
                /* Make sure that when next() is called, that we can
                 * provide a HostAddress.  This means that we need to
                 * have an inner iterator, and verify that it has
                 * addresses.  We could, for example, run into a
                 * situation where the next mxHost didn't have any valid
                 * addresses.
                 */
                if ((addresses == null || !addresses.hasNext()) && mxHosts.hasNext()) do {
                    final String nextHostname = (String)mxHosts.next();
                    InetAddress[] addrs = null;
                    try {
                        addrs = getAllByName(nextHostname);
                    } catch (UnknownHostException uhe) {
                        // this should never happen, since we just got
                        // this host from mxHosts, which should have
                        // already done this check.
                        StringBuffer logBuffer = new StringBuffer(128)
                                                 .append("Couldn't resolve IP address for discovered host ")
                                                 .append(nextHostname)
                                                 .append(".");
                        getLogger().error(logBuffer.toString());
                    }
                    final InetAddress[] ipAddresses = addrs;

                    addresses = new Iterator() {
                        int i = 0;

                        public boolean hasNext() {
                            return ipAddresses != null && i < ipAddresses.length;
                        }

                        public Object next() {
                            return new org.apache.mailet.HostAddress(nextHostname, "smtp://" + ipAddresses[i++].getHostAddress());
                        }

                        public void remove() {
                            throw new UnsupportedOperationException ("remove not supported by this iterator");
                        }
                    };
                } while (!addresses.hasNext() && mxHosts.hasNext());

                return addresses != null && addresses.hasNext();
            }

            public Object next() {
                return addresses != null ? addresses.next() : null;
            }

            public void remove() {
                throw new UnsupportedOperationException ("remove not supported by this iterator");
            }
        };
    
public voidinitialize()

see
org.apache.avalon.framework.activity.Initializable#initialize()


        getLogger().debug("DNSServer init...");

        // If no DNS servers were configured, default to local host
        if (dnsServers.isEmpty()) {
            try {
                dnsServers.add( InetAddress.getLocalHost().getHostName() );
            } catch ( UnknownHostException ue ) {
                dnsServers.add( "127.0.0.1" );
            }
        }

        //Create the extended resolver...
        final String[] serversArray = (String[])dnsServers.toArray(new String[0]);

        if (getLogger().isInfoEnabled()) {
            for(int c = 0; c < serversArray.length; c++) {
                getLogger().info("DNS Server is: " + serversArray[c]);
            }
        }

        try {
            resolver = new ExtendedResolver( serversArray );
            Lookup.setDefaultResolver(resolver);
        } catch (UnknownHostException uhe) {
            getLogger().fatalError("DNS service could not be initialized.  The DNS servers specified are not recognized hosts.", uhe);
            throw uhe;
        }

        cache = new Cache (DClass.IN);
        cache.setMaxEntries(maxCacheSize);
        Lookup.setDefaultCache(cache, DClass.IN);
        
        getLogger().debug("DNSServer ...init end");
    
public org.xbill.DNS.Record[]lookup(java.lang.String name, int type)
Looks up DNS records of the specified type for the specified name. This method is a public wrapper for the private implementation method

param
name the name of the host to be looked up
param
type the type of record desired

        return rawDNSLookup(name,false,type);
    
protected org.xbill.DNS.Record[]processSetResponse(org.xbill.DNS.SetResponse sr)

        Record [] answers;
        int answerCount = 0, n = 0;

        RRset [] rrsets = sr.answers();
        answerCount = 0;
        for (int i = 0; i < rrsets.length; i++) {
            answerCount += rrsets[i].size();
        }

        answers = new Record[answerCount];

        for (int i = 0; i < rrsets.length; i++) {
            Iterator iter = rrsets[i].rrs();
            while (iter.hasNext()) {
                Record r = (Record)iter.next();
                answers[n++] = r;
            }
        }
        return answers;
    
private org.xbill.DNS.Record[]rawDNSLookup(java.lang.String namestr, boolean querysent, int type)
Looks up DNS records of the specified type for the specified name

param
namestr the name of the host to be looked up
param
querysent whether the query has already been sent to the DNS servers
param
type the type of record desired

        Name name = null;
        try {
            name = Name.fromString(namestr, Name.root);
        } catch (TextParseException tpe) {
            // TODO: Figure out how to handle this correctly.
            getLogger().error("Couldn't parse name " + namestr, tpe);
            return null;
        }
        int dclass = DClass.IN;

        SetResponse cached = cache.lookupRecords(name, type, dnsCredibility);
        if (cached.isSuccessful()) {
            getLogger().debug(new StringBuffer(256)
                             .append("Retrieving MX record for ")
                             .append(name).append(" from cache")
                             .toString());

            return processSetResponse(cached);
        }
        else if (cached.isNXDOMAIN() || cached.isNXRRSET()) {
            return null;
        }
        else if (querysent) {
            return null;
        }
        else {
            getLogger().debug(new StringBuffer(256)
                             .append("Looking up MX record for ")
                             .append(name)
                             .toString());
            Record question = Record.newRecord(name, type, dclass);
            Message query = Message.newQuery(question);
            Message response = null;

            try {
                response = resolver.send(query);
            }
            catch (Exception ex) {
                getLogger().warn("Query error!", ex);
                return null;
            }

            int rcode = response.getHeader().getRcode();
            if (rcode == Rcode.NOERROR || rcode == Rcode.NXDOMAIN) {
                cached = cache.addMessage(response);
                if (cached != null && cached.isSuccessful()) {
                    return processSetResponse(cached);
                }
            }

            if (rcode != Rcode.NOERROR) {
                return null;
            }

            return rawDNSLookup(namestr, true, type);
        }