noel        2004/03/29 16:37:46

  Modified:    src/java/org/apache/james/dnsserver DNSServer.java
  Log:
  Fix JAMES-236.  This was a newly introduced problem.  The iterator's hasNext() needs 
to do the work previously deferred to next(), so that we know next() will have a valid 
return value.
  
  Revision  Changes    Path
  1.25      +182 -12   james-server/src/java/org/apache/james/dnsserver/DNSServer.java
  
  Index: DNSServer.java
  ===================================================================
  RCS file: /home/cvs/james-server/src/java/org/apache/james/dnsserver/DNSServer.java,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- DNSServer.java    19 Feb 2004 10:12:35 -0000      1.24
  +++ DNSServer.java    30 Mar 2004 00:37:46 -0000      1.25
  @@ -29,6 +29,7 @@
   import org.xbill.DNS.FindServer;
   import org.xbill.DNS.Message;
   import org.xbill.DNS.MXRecord;
  +import org.xbill.DNS.ARecord;
   import org.xbill.DNS.Name;
   import org.xbill.DNS.Rcode;
   import org.xbill.DNS.Record;
  @@ -42,7 +43,7 @@
   import java.net.UnknownHostException;
   import java.util.*;
   
  -/**
  +/*
    * Provides DNS client functionality to services running
    * inside James
    */
  @@ -51,34 +52,34 @@
       implements Configurable, Initializable,
       org.apache.james.services.DNSServer, DNSServerMBean {
   
  -    /**
  +    /*
        * A resolver instance used to retrieve DNS records.  This
        * is a reference to a third party library object.
        */
       private Resolver resolver;
   
  -    /**
  +    /*
        * A TTL cache of results received from the DNS server.  This
        * is a reference to a third party library object.
        */
       private Cache cache;
   
  -    /**
  +    /*
        * Whether the DNS response is required to be authoritative
        */
       private byte dnsCredibility;
   
  -    /**
  +    /*
        * The DNS servers to be used by this service
        */
       private List dnsServers = new ArrayList();
   
  -    /**
  +    /*
        * The MX Comparator used in the MX sort.
        */
       private Comparator mxComparator = new MXRecordComparator();
   
  -    /**
  +    /*
        * @see 
org.apache.avalon.framework.configuration.Configurable#configure(Configuration)
        */
       public void configure( final Configuration configuration )
  @@ -119,7 +120,7 @@
           dnsCredibility = authoritative ? Credibility.AUTH_ANSWER : 
Credibility.NONAUTH_ANSWER;
       }
   
  -    /**
  +    /*
        * @see org.apache.avalon.framework.activity.Initializable#initialize()
        */
       public void initialize()
  @@ -157,7 +158,7 @@
           getLogger().debug("DNSServer ...init end");
       }
   
  -    /**
  +    /*
        * <p>Return the list of DNS servers in use by this service</p>
        *
        * @return an array of DNS server names
  @@ -166,7 +167,7 @@
           return (String[])dnsServers.toArray(new String[0]);
       }
       
  -    /**
  +    /*
        * <p>Return a prioritized unmodifiable list of MX records
        * obtained from the server.</p>
        *
  @@ -222,7 +223,7 @@
           }
       }
   
  -    /**
  +    /*
        * Looks up DNS records of the specified type for the specified name.
        *
        * This method is a public wrapper for the private implementation
  @@ -235,7 +236,7 @@
           return rawDNSLookup(name,false,type);
       }
   
  -    /**
  +    /*
        * Looks up DNS records of the specified type for the specified name
        *
        * @param namestr the name of the host to be looked up
  @@ -346,6 +347,175 @@
               int pa = ((MXRecord)a).getPriority();
               int pb = ((MXRecord)b).getPriority();
               return (pa == pb) ? (512 - random.nextInt(1024)) : pa - pb;
  +        }
  +    }
  +
  +    /*
  +     * Returns an Iterator over org.apache.mailet.HostAddress, a
  +     * specialized subclass of javax.mail.URLName, which provides
  +     * location information for servers that are specified as mail
  +     * handlers for the given hostname.  This is done using MX records,
  +     * and the HostAddress instances are returned sorted by MX priority.
  +     * If no host is found for domainName, the Iterator returned will be
  +     * empty and the first call to hasNext() will return false.  The
  +     * Iterator is a nested iterator: the outer iteration is over the
  +     * results of the MX record lookup, and the inner iteration is over
  +     * potentially multiple A records for each MX record.  DNS lookups
  +     * are deferred until actually needed.
  +     *
  +     * @since v2.2.0a16-unstable
  +     * @param domainName - the domain for which to find mail servers
  +     * @return an Iterator over HostAddress instances, sorted by priority
  +     */
  +    public Iterator getSMTPHostAddresses(final String domainName) {
  +        return new Iterator() {
  +            private Iterator mxHosts = new MxSorter(domainName);
  +            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();
  +                    addresses = new Iterator() {
  +                        private Record[] aRecords = lookup(nextHostname, Type.A);
  +                        int i = 0;
  +
  +                        public boolean hasNext() {
  +                            return aRecords != null && i < aRecords.length;
  +                        }
  +
  +                        public Object next() {
  +                            return new org.apache.mailet.HostAddress(nextHostname, 
"smtp://" + ((ARecord)aRecords[i++]).getAddress().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");
  +            }
  +        };
  +    }
  +
  +    /* A way to get mail hosts to try.  If any MX hosts are found for the
  +     * domain name with which this is constructed, then these MX hostnames
  +     * are returned in priority sorted order, lowest priority numbers coming
  +     * first.  And, whenever multiple hosts have the same priority then these
  +     * are returned in a randomized order within that priority group, as
  +     * specified in RFC 2821, Section 5.
  +     *
  +     * If no MX hosts are found for the domain name, then a DNS search is
  +     * performed for an A record.  If an A record is found then domainName itself
  +     * will be returned by the Iterator, and it will be the only object in
  +     * the Iterator.  If however no A record is found (in addition to no MX
  +     * record) then the Iterator constructed will be empty; the first call to
  +     * its hasNext() will return false.
  +     *
  +     * This behavior attempts to satisfy the requirements of RFC 2821, Section 5.
  +     * @since v2.2.0a16-unstable
  +     */
  +    private class MxSorter implements Iterator {
  +        private int priorListPriority = Integer.MIN_VALUE;
  +        private ArrayList equiPriorityList = new ArrayList();
  +        private Record[] mxRecords;
  +        private Random rnd = new Random ();
  +
  +        /* The implementation of this class attempts to achieve efficiency by
  +         * performing no more sorting of the rawMxRecords than necessary. In the
  +         * large majority of cases the first attempt, made by a client of this class
  +         * to connect to an SMTP server for a given domain, will succeed. As such,
  +         * in most cases only one call will be made to this Iterator's
  +         * next(), and in that majority of cases there will have been no need
  +         * to sort the array of MX Records.  This implementation would, however, be
  +         * relatively inefficient in the case where all hosts fail, when every
  +         * Object is called out of a long Iterator.
  +         */
  +
  +        private MxSorter(String domainName) {
  +            mxRecords =  lookup(domainName, Type.MX);
  +            if (mxRecords == null || mxRecords.length == 0) {
  +                //no MX records were found, so try to use the domainName
  +                Record[] aRecords = lookup(domainName, Type.A);
  +                if(aRecords != null && aRecords.length > 0) {
  +                    equiPriorityList.add(domainName);
  +                }
  +            }
  +        }
  +
  +        /*
  +         * Sets presentPriorityList to contain all hosts
  +         * which have the least priority greater than pastPriority.
  +         * When this is called, both (rawMxRecords.length > 0) and
  +         * (presentPriorityList.size() == 0), by contract.
  +         * In the case where this is called repeatedly, so that priorListPriority
  +         * has already become the highest of the priorities in the rawMxRecords,
  +         * then this returns without having added any elements to
  +         * presentPriorityList; presentPriorityList.size remains zero.
  +         */
  +        private void createPriorityList(){
  +            int leastPriorityFound = Integer.MAX_VALUE;
  +            /* We loop once through the rawMxRecords, finding the lowest priority
  +             * greater than priorListPriority, and collecting all the hostnames
  +             * with that priority into equiPriorityList.
  +             */
  +            for (int i = 0; i < mxRecords.length; i++) {
  +                MXRecord thisRecord = (MXRecord)mxRecords[i];
  +                int thisRecordPriority = thisRecord.getPriority();
  +                if (thisRecordPriority > priorListPriority) {
  +                    if (thisRecordPriority < leastPriorityFound) {
  +                        equiPriorityList.clear();
  +                        leastPriorityFound = thisRecordPriority;
  +                        equiPriorityList.add(thisRecord.getTarget().toString());
  +                    } else if (thisRecordPriority == leastPriorityFound) {
  +                        equiPriorityList.add(thisRecord.getTarget().toString());
  +                    }
  +                }
  +            }
  +            priorListPriority = leastPriorityFound;
  +        }
  +
  +        public boolean hasNext(){
  +            if (equiPriorityList.size() > 0){
  +                return true;
  +            }else if (mxRecords != null && mxRecords.length > 0){
  +                createPriorityList();
  +                return equiPriorityList.size() > 0;
  +            } else{
  +                return false;
  +            }
  +        }
  +
  +        public Object next(){
  +            if (hasNext()){
  +                /* this randomization is done to comply with RFC-2821 */
  +                /* Note: java.util.Random.nextInt(limit) is about twice as fast as 
(int)(Math.random()*limit) */
  +                int getIndex = rnd.nextInt(equiPriorityList.size());
  +                Object returnElement = equiPriorityList.get(getIndex);
  +                equiPriorityList.remove(getIndex);
  +                return returnElement;
  +            }else{
  +                throw new NoSuchElementException();
  +            }
  +        }
  +
  +        public void remove () {
  +            throw new UnsupportedOperationException ("remove not supported by this 
iterator");
           }
       }
   }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to