001/**
002 *
003 * Copyright 2013-2017 Florian Schmaus
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *     http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.jivesoftware.smack.util.dns.javax;
018
019import java.net.InetAddress;
020import java.util.ArrayList;
021import java.util.Hashtable;
022import java.util.List;
023import java.util.logging.Level;
024
025import javax.naming.NameNotFoundException;
026import javax.naming.NamingEnumeration;
027import javax.naming.NamingException;
028import javax.naming.directory.Attribute;
029import javax.naming.directory.Attributes;
030import javax.naming.directory.DirContext;
031import javax.naming.directory.InitialDirContext;
032
033import org.jivesoftware.smack.ConnectionConfiguration.DnssecMode;
034import org.jivesoftware.smack.initializer.SmackInitializer;
035import org.jivesoftware.smack.util.DNSUtil;
036import org.jivesoftware.smack.util.dns.DNSResolver;
037import org.jivesoftware.smack.util.dns.HostAddress;
038import org.jivesoftware.smack.util.dns.SRVRecord;
039
040/**
041 * A DNS resolver (mostly for SRV records), which makes use of the API provided in the javax.* namespace.
042 * 
043 * @author Florian Schmaus
044 *
045 */
046public class JavaxResolver extends DNSResolver implements SmackInitializer {
047
048    private static JavaxResolver instance;
049    private static DirContext dirContext;
050
051    static {
052        try {
053            Hashtable<String, String> env = new Hashtable<>();
054            env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory");
055            dirContext = new InitialDirContext(env);
056        } catch (Exception e) {
057            // Ignore.
058        }
059
060        // Try to set this DNS resolver as primary one
061        setup();
062    }
063
064    public static synchronized DNSResolver getInstance() {
065        if (instance == null && isSupported()) {
066            instance = new JavaxResolver();
067        }
068        return instance;
069    }
070
071    public static boolean isSupported() {
072        return dirContext != null;
073    }
074
075    public static void setup() {
076        DNSUtil.setDNSResolver(getInstance());
077    }
078
079    public JavaxResolver() {
080         super(false);
081    }
082
083    @Override
084    protected List<SRVRecord> lookupSRVRecords0(String name, List<HostAddress> failedAddresses, DnssecMode dnssecMode) {
085        List<SRVRecord> res = null;
086
087        Attribute srvAttribute;
088        try {
089            Attributes dnsLookup = dirContext.getAttributes(name, new String[] { "SRV" });
090            srvAttribute = dnsLookup.get("SRV");
091            if (srvAttribute == null)
092               return null;
093        } catch (NameNotFoundException e) {
094            LOGGER.log(Level.FINEST, "No DNS SRV RR found for " + name, e);
095            return null;
096        } catch (NamingException e) {
097            LOGGER.log(Level.WARNING, "Exception while resolving DNS SRV RR for " + name, e);
098            return null;
099        }
100
101        try {
102            @SuppressWarnings("unchecked")
103            NamingEnumeration<String> srvRecords = (NamingEnumeration<String>) srvAttribute.getAll();
104            res = new ArrayList<>();
105            while (srvRecords.hasMore()) {
106                String srvRecordString = srvRecords.next();
107                String[] srvRecordEntries = srvRecordString.split(" ");
108                int priority = Integer.parseInt(srvRecordEntries[srvRecordEntries.length - 4]);
109                int port = Integer.parseInt(srvRecordEntries[srvRecordEntries.length - 2]);
110                int weight = Integer.parseInt(srvRecordEntries[srvRecordEntries.length - 3]);
111                String host = srvRecordEntries[srvRecordEntries.length - 1];
112
113                List<InetAddress> hostAddresses = lookupHostAddress0(host, failedAddresses, dnssecMode);
114                if (shouldContinue(name, host, hostAddresses)) {
115                    continue;
116                }
117
118                SRVRecord srvRecord = new SRVRecord(host, port, priority, weight, hostAddresses);
119                res.add(srvRecord);
120            }
121        }
122        catch (NamingException e) {
123            LOGGER.log(Level.SEVERE, "Exception while resolving DNS SRV RR for" + name, e);
124        }
125
126        return res;
127    }
128
129    @Override
130    public List<Exception> initialize() {
131        setup();
132        return null;
133    }
134
135}