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.File; 017import java.io.FileInputStream; 018import java.io.IOException; 019import java.io.InputStreamReader; 020import java.nio.charset.StandardCharsets; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.logging.Level; 024import java.util.logging.Logger; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028public final class UnixUsingEtcResolvConf extends AbstractDnsServerLookupMechanism { 029 030 public static final DnsServerLookupMechanism INSTANCE = new UnixUsingEtcResolvConf(); 031 public static final int PRIORITY = 2000; 032 033 private static final Logger LOGGER = Logger.getLogger(UnixUsingEtcResolvConf.class.getName()); 034 035 private static final String RESOLV_CONF_FILE = "/etc/resolv.conf"; 036 private static final Pattern NAMESERVER_PATTERN = Pattern.compile("^nameserver\\s+(.*)$"); 037 038 private static List<String> cached; 039 private static long lastModified; 040 041 private UnixUsingEtcResolvConf() { 042 super(UnixUsingEtcResolvConf.class.getSimpleName(), PRIORITY); 043 } 044 045 @Override 046 public List<String> getDnsServerAddresses() { 047 File file = new File(RESOLV_CONF_FILE); 048 if (!file.exists()) { 049 // Not very unixoid systems 050 return null; 051 } 052 053 long currentLastModified = file.lastModified(); 054 if (currentLastModified == lastModified && cached != null) { 055 return cached; 056 } 057 058 List<String> servers = new ArrayList<>(); 059 BufferedReader reader = null; 060 try { 061 reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)); 062 String line; 063 while ((line = reader.readLine()) != null) { 064 Matcher matcher = NAMESERVER_PATTERN.matcher(line); 065 if (matcher.matches()) { 066 servers.add(matcher.group(1).trim()); 067 } 068 } 069 } catch (IOException e) { 070 LOGGER.log(Level.WARNING, "Could not read from " + RESOLV_CONF_FILE, e); 071 return null; 072 } finally { 073 if (reader != null) try { 074 reader.close(); 075 } catch (IOException e) { 076 LOGGER.log(Level.WARNING, "Could not close reader", e); 077 } 078 } 079 080 if (servers.isEmpty()) { 081 LOGGER.fine("Could not find any nameservers in " + RESOLV_CONF_FILE); 082 return null; 083 } 084 085 cached = servers; 086 lastModified = currentLastModified; 087 088 return cached; 089 } 090 091 @Override 092 public boolean isAvailable() { 093 if (PlatformDetection.isAndroid()) { 094 // Don't rely on resolv.conf when on Android 095 return false; 096 } 097 098 File file = new File(RESOLV_CONF_FILE); 099 100 boolean resolvConfFileExists; 101 try { 102 resolvConfFileExists = file.exists(); 103 } catch (SecurityException securityException) { 104 LOGGER.log(Level.FINE, "Access to /etc/resolv.conf not possible", securityException); 105 return false; 106 } 107 return resolvConfFileExists; 108 } 109 110}