All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jivesoftware.smack.util.DNSUtil Maven / Gradle / Ivy

The newest version!
/**
 *
 * Copyright 2003-2005 Jive Software.
 *
 * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jivesoftware.smack.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.jivesoftware.smack.util.dns.DNSResolver;
import org.jivesoftware.smack.util.dns.HostAddress;
import org.jivesoftware.smack.util.dns.SRVRecord;

/**
 * Utility class to perform DNS lookups for XMPP services.
 * 
 * @author Matt Tucker
 */
public class DNSUtil {

    /**
     * Create a cache to hold the 100 most recently accessed DNS lookups for a
     * period of 10 minutes.
     */
    private static Map> cache = new Cache>(
            100, 1000 * 60 * 10);

    private static DNSResolver dnsResolver = null;

    /**
     * Set the DNS resolver that should be used to perform DNS lookups.
     * 
     * @param resolver
     */
    public static void setDNSResolver(DNSResolver resolver) {
        dnsResolver = resolver;
    }

    /**
     * Returns the current DNS resolved used to perform DNS lookups.
     * 
     * @return
     */
    public static DNSResolver getDNSResolver() {
        return dnsResolver;
    }

    /**
     * Returns a list of HostAddresses under which the specified XMPP server can
     * be reached at for client-to-server communication. A DNS lookup for a SRV
     * record in the form "_xmpp-client._tcp.example.com" is attempted,
     * according to section 14.4 of RFC 3920. If that lookup fails, a lookup in
     * the older form of "_jabber._tcp.example.com" is attempted since servers
     * that implement an older version of the protocol may be listed using that
     * notation. If that lookup fails as well, it's assumed that the XMPP server
     * lives at the host resolved by a DNS lookup at the specified domain on the
     * default port of 5222.
     * 

* * As an example, a lookup for "example.com" may return * "im.example.com:5269". * * @param domain * the domain. * @return List of HostAddress, which encompasses the hostname and port that * the XMPP server can be reached at for the specified domain. */ public static List resolveXMPPDomain(final String domain) { if (dnsResolver == null) { List addresses = new ArrayList(1); addresses.add(new HostAddress(domain, 5222)); return addresses; } return resolveDomain(domain, 'c'); } /** * Returns a list of HostAddresses under which the specified XMPP server can * be reached at for server-to-server communication. A DNS lookup for a SRV * record in the form "_xmpp-server._tcp.example.com" is attempted, * according to section 14.4 of RFC 3920. If that lookup fails, a lookup in * the older form of "_jabber._tcp.example.com" is attempted since servers * that implement an older version of the protocol may be listed using that * notation. If that lookup fails as well, it's assumed that the XMPP server * lives at the host resolved by a DNS lookup at the specified domain on the * default port of 5269. *

* * As an example, a lookup for "example.com" may return * "im.example.com:5269". * * @param domain * the domain. * @return List of HostAddress, which encompasses the hostname and port that * the XMPP server can be reached at for the specified domain. */ public static List resolveXMPPServerDomain(final String domain) { if (dnsResolver == null) { List addresses = new ArrayList(1); addresses.add(new HostAddress(domain, 5269)); return addresses; } return resolveDomain(domain, 's'); } private static List resolveDomain(String domain, char keyPrefix) { // Prefix the key with 's' to distinguish him from the client domain // lookups String key = keyPrefix + domain; // Return item from cache if it exists. if (cache.containsKey(key)) { List addresses = cache.get(key); if (addresses != null) { return addresses; } } List addresses = new ArrayList(); // Step one: Do SRV lookups String srvDomain; if (keyPrefix == 's') { srvDomain = "_xmpp-server._tcp." + domain; } else if (keyPrefix == 'c') { srvDomain = "_xmpp-client._tcp." + domain; } else { srvDomain = domain; } List srvRecords = dnsResolver.lookupSRVRecords(srvDomain); List sortedRecords = sortSRVRecords(srvRecords); if (sortedRecords != null) addresses.addAll(sortedRecords); // Step two: Add the hostname to the end of the list addresses.add(new HostAddress(domain)); // Add item to cache. cache.put(key, addresses); return addresses; } /** * Sort a given list of SRVRecords as described in RFC 2782 Note that we * follow the RFC with one exception. In a group of the same priority, only * the first entry is calculated by random. The others are ore simply * ordered by their priority. * * @param records * @return */ protected static List sortSRVRecords(List records) { // RFC 2782, Usage rules: // "If there is precisely one SRV RR, and its Target is "." // (the root domain), abort." if (records.size() == 1 && records.get(0).getFQDN().equals(".")) return null; // sorting the records improves the performance of the bisection later Collections.sort(records); // create the priority buckets SortedMap> buckets = new TreeMap>(); for (SRVRecord r : records) { Integer priority = r.getPriority(); List bucket = buckets.get(priority); // create the list of SRVRecords if it doesn't exist if (bucket == null) { bucket = new LinkedList(); buckets.put(priority, bucket); } bucket.add(r); } List res = new ArrayList(records.size()); for (Integer priority : buckets.keySet()) { List bucket = buckets.get(priority); int bucketSize; while ((bucketSize = bucket.size()) > 0) { int[] totals = new int[bucket.size()]; int running_total = 0; int count = 0; int zeroWeight = 1; for (SRVRecord r : bucket) { if (r.getWeight() > 0) zeroWeight = 0; } for (SRVRecord r : bucket) { running_total += (r.getWeight() + zeroWeight); totals[count] = running_total; count++; } int selectedPos; if (running_total == 0) { // If running total is 0, then all weights in this priority // group are 0. So we simply select one of the weights // randomly // as the other 'normal' algorithm is unable to handle this // case selectedPos = (int) (Math.random() * bucketSize); } else { double rnd = Math.random() * running_total; selectedPos = bisect(totals, rnd); } // add the SRVRecord that was randomly chosen on it's weight // to the start of the result list SRVRecord chosenSRVRecord = bucket.remove(selectedPos); res.add(chosenSRVRecord); } } return res; } // TODO this is not yet really bisection just a stupid linear search private static int bisect(int[] array, double value) { int pos = 0; for (int element : array) { if (value < element) break; pos++; } return pos; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy