001/*
002 * Copyright 2015-2018 the original author or authors
003 *
004 * This software is licensed under the Apache License, Version 2.0,
005 * the GNU Lesser General Public License version 2 or later ("LGPL")
006 * and the WTFPL.
007 * You may choose either license to govern your use of this software only
008 * upon the condition that you accept all of the terms of either
009 * the Apache License 2.0, the LGPL 2.1+ or the WTFPL.
010 */
011package org.minidns.dnsserverlookup;
012
013import org.minidns.util.PlatformDetection;
014
015import java.io.BufferedReader;
016import java.io.IOException;
017import java.io.InputStream;
018import java.io.InputStreamReader;
019import java.io.LineNumberReader;
020import java.net.InetAddress;
021import java.net.UnknownHostException;
022import java.util.ArrayList;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Set;
026import java.util.logging.Level;
027
028/**
029 * Try to retrieve the list of DNS server by executing getprop.
030 */
031public class AndroidUsingExec extends AbstractDnsServerLookupMechanism {
032
033    public static final DnsServerLookupMechanism INSTANCE = new AndroidUsingExec();
034    public static final int PRIORITY = AndroidUsingReflection.PRIORITY - 1;
035
036    private AndroidUsingExec() {
037        super(AndroidUsingExec.class.getSimpleName(), PRIORITY);
038    }
039
040    @Override
041    public List<String> getDnsServerAddresses() {
042        try {
043            Process process = Runtime.getRuntime().exec("getprop");
044            InputStream inputStream = process.getInputStream();
045            LineNumberReader lnr = new LineNumberReader(
046                new InputStreamReader(inputStream));
047            Set<String> server = parseProps(lnr, true);
048            if (server.size() > 0) {
049                List<String> res = new ArrayList<>(server.size());
050                res.addAll(server);
051                return res;
052            }
053        } catch (IOException e) {
054            LOGGER.log(Level.WARNING, "Exception in findDNSByExec", e);
055        }
056        return null;
057    }
058
059    @Override
060    public boolean isAvailable() {
061        return PlatformDetection.isAndroid();
062    }
063
064    private static final String PROP_DELIM = "]: [";
065    protected static Set<String> parseProps(BufferedReader lnr, boolean logWarning) throws UnknownHostException, IOException {
066        String line = null;
067        Set<String> server = new HashSet<String>(6);
068
069        while ((line = lnr.readLine()) != null) {
070            int split = line.indexOf(PROP_DELIM);
071            if (split == -1) {
072                continue;
073            }
074            String property = line.substring(1, split);
075
076            int valueStart = split + PROP_DELIM.length();
077            int valueEnd = line.length() - 1;
078            if (valueEnd < valueStart) {
079                // This can happen if a newline sneaks in as the first character of the property value. For example
080                // "[propName]: [\n…]".
081                if (logWarning) {
082                    LOGGER.warning("Malformed property detected: \"" + line + '"');
083                }
084                continue;
085            }
086
087            String value = line.substring(valueStart, valueEnd);
088
089            if (value.isEmpty()) {
090                continue;
091            }
092
093            if (property.endsWith(".dns") || property.endsWith(".dns1") ||
094                property.endsWith(".dns2") || property.endsWith(".dns3") ||
095                property.endsWith(".dns4")) {
096
097                // normalize the address
098
099                InetAddress ip = InetAddress.getByName(value);
100
101                if (ip == null) continue;
102
103                value = ip.getHostAddress();
104
105                if (value == null) continue;
106                if (value.length() == 0) continue;
107
108                server.add(value);
109            }
110        }
111
112        return server;
113    }
114}