001/*
002 * Copyright 2015-2024 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.nio.charset.StandardCharsets;
023import java.util.ArrayList;
024import java.util.HashSet;
025import java.util.List;
026import java.util.Set;
027import java.util.logging.Level;
028
029/**
030 * Try to retrieve the list of DNS server by executing getprop.
031 */
032public final class AndroidUsingExec extends AbstractDnsServerLookupMechanism {
033
034    public static final DnsServerLookupMechanism INSTANCE = new AndroidUsingExec();
035    public static final int PRIORITY = AndroidUsingReflection.PRIORITY - 1;
036
037    private AndroidUsingExec() {
038        super(AndroidUsingExec.class.getSimpleName(), PRIORITY);
039    }
040
041    @Override
042    public List<String> getDnsServerAddresses() {
043        try {
044            Process process = Runtime.getRuntime().exec("getprop");
045            InputStream inputStream = process.getInputStream();
046            LineNumberReader lnr = new LineNumberReader(
047                new InputStreamReader(inputStream, StandardCharsets.UTF_8));
048            Set<String> server = parseProps(lnr, true);
049            if (server.size() > 0) {
050                List<String> res = new ArrayList<>(server.size());
051                res.addAll(server);
052                return res;
053            }
054        } catch (IOException e) {
055            LOGGER.log(Level.WARNING, "Exception in findDNSByExec", e);
056        }
057        return null;
058    }
059
060    @Override
061    public boolean isAvailable() {
062        return PlatformDetection.isAndroid();
063    }
064
065    private static final String PROP_DELIM = "]: [";
066    static Set<String> parseProps(BufferedReader lnr, boolean logWarning) throws UnknownHostException, IOException {
067        String line = null;
068        Set<String> server = new HashSet<String>(6);
069
070        while ((line = lnr.readLine()) != null) {
071            int split = line.indexOf(PROP_DELIM);
072            if (split == -1) {
073                continue;
074            }
075            String property = line.substring(1, split);
076
077            int valueStart = split + PROP_DELIM.length();
078            int valueEnd = line.length() - 1;
079            if (valueEnd < valueStart) {
080                // This can happen if a newline sneaks in as the first character of the property value. For example
081                // "[propName]: [\n…]".
082                if (logWarning) {
083                    LOGGER.warning("Malformed property detected: \"" + line + '"');
084                }
085                continue;
086            }
087
088            String value = line.substring(valueStart, valueEnd);
089
090            if (value.isEmpty()) {
091                continue;
092            }
093
094            if (property.endsWith(".dns") || property.endsWith(".dns1") ||
095                property.endsWith(".dns2") || property.endsWith(".dns3") ||
096                property.endsWith(".dns4")) {
097
098                // normalize the address
099
100                InetAddress ip = InetAddress.getByName(value);
101
102                if (ip == null) continue;
103
104                value = ip.getHostAddress();
105
106                if (value == null) continue;
107                if (value.length() == 0) continue;
108
109                server.add(value);
110            }
111        }
112
113        return server;
114    }
115}