/* * [QuickDNS.java] * * Summary: hosts file updater. * * Copyright: (c) 2014-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2014-09-06 original. * 1.1 2016-05-31 Add error messages to detect running without administrator privilege, now required in Windows 10. */ package com.mindprod.quickdns; import com.mindprod.common18.EIO; import com.mindprod.common18.ST; import com.mindprod.hunkio.HunkIO; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.util.regex.Pattern; import static java.lang.System.*; /** * hosts file updater. * * @author Roedy Green, Canadian Mind Products * @version 1.1 2016-05-31 Add error messages to detect running without administrator privilege, now required in Windows 10. * @since 2014-09-06 */ public final class QuickDNS { private static final int FIRST_COPYRIGHT_YEAR = 2014; /** * when this version was released */ @SuppressWarnings( { "UnusedDeclaration" } ) private static final String RELEASE_DATE = "2016-05-31"; /** * embedded version string */ @SuppressWarnings( { "UnusedDeclaration" } ) private static final String VERSION_STRING = "1.1"; /** * not displayed copyright */ @SuppressWarnings( { "UnusedDeclaration" } ) private static final String EMBEDDED_COPYRIGHT = "Copyright: (c) 2014-2017 Roedy Green, Canadian Mind Products, http://mindprod.com"; /** * true if want extra debugging output */ private static final boolean DEBUGGING = false; /** * Splits field on spaces/tabs */ private static final Pattern SPLITTER = Pattern.compile( "[ \t]+" ); // methods /** * display how many updates we made to the hosts file * * @param hosts hosts file * @param updates count of updates */ public static void displayUpdates( final File hosts, final int updates ) { // leave hosts.bak in place switch ( updates ) { case 0: out.print( "no updates" ); break; case 1: out.print( "one update" ); break; default: out.print( updates + " updates" ); } out.println( " to your " + EIO.getCanOrAbsPath( hosts ) + " file." ); } /** * update the hosts file */ public static void main( String[] args ) throws IOException { final File hosts = new File( "C:/Windows//system32/drivers/etc/hosts." ); final File backup = new File( "C:/Windows//system32/drivers/etc/hosts.bak" ); final File updated = new File( "C:/Windows//system32/drivers/etc/hosts.updated" ); safetyChecks( hosts, backup ); // we have to rename/hide hosts so its entries will not interfere with lookup try { HunkIO.deleteAndRename( hosts, backup ); } catch ( IOException e ) { err.println( "Rename access to " + EIO.getCanOrAbsPath( hosts ) + " blocked.\nPerhaps missing run-as-administrator." ); System.exit( 2 ); } final BufferedReader br = EIO.getBufferedReader( backup, 16 * 1024, EIO.UTF8 ); final PrintWriter pw = EIO.getPrintWriter( updated, 16 * 1024, EIO.UTF8 ); out.println( "QuickDNS " + VERSION_STRING ); String originalLine; int updates = 0; while ( ( originalLine = br.readLine() ) != null ) { int commentPlace = originalLine.indexOf( '#' ); final String comment; final String pair; final boolean leave; if ( commentPlace < 0 ) { // no comment comment = ""; pair = originalLine.trim(); leave = false; } else { // is comment comment = originalLine.substring( commentPlace + 1 ).trim(); leave = comment.toLowerCase().contains( "leave" ); pair = originalLine.substring( 0, commentPlace ).trim(); } // handle blank line or pure comment line if ( pair.length() == 0 ) { pw.println( "# " + comment ); } else { updates = standardLine( pw, originalLine, updates, comment, pair, leave ); } } // end loop br.close(); pw.close(); try { HunkIO.deleteAndRename( updated, hosts ); } catch ( IOException e ) { err.println( "Delete access to " + EIO.getCanOrAbsPath( hosts ) + " blocked.\nPerhaps missing run-as-administrator." ); System.exit( 2 ); } if ( !hosts.exists() ) { err.println( "Lost " + EIO.getCanOrAbsPath( hosts ) + ".\nRestore from backup or hosts.bak." ); System.exit( 2 ); } displayUpdates( hosts, updates ); } // /method public static void safetyChecks( final File hosts, final File backup ) { if ( !hosts.exists() ) { err.println( "Missing " + EIO.getCanOrAbsPath( hosts ) + ".\nRestore from backup or hosts.bak." ); System.exit( 2 ); } if ( !hosts.canWrite() ) { err.println( "Cannot modify " + EIO.getCanOrAbsPath( hosts ) + ".\nPerhaps missing run-as-administrator." ); System.exit( 2 ); } if ( backup.exists() && !backup.canWrite() ) { err.println( "Cannot create " + EIO.getCanOrAbsPath( backup ) + ".\nPerhaps missing run-as-administrator." ); System.exit( 2 ); } } /** * Process one stardand line in hosts. * * @param pw printwriter where we compose new hosts. * @param originalLine originals line from old hosts. * @param updates count of how many changes we made * @param comment comment on end of line * @param pair dns / ip * @param leave true if should leave this line as is * * @return new updates count */ public static int standardLine( final PrintWriter pw, final String originalLine, int updates, final String comment, final String pair, final boolean leave ) { String pieces[] = SPLITTER.split( pair ); switch ( pieces.length ) { case 2: String ip = pieces[ 0 ]; final String domain = pieces[ 1 ]; if ( !leave ) { try { final InetAddress newIP = InetAddress.getByName( domain ); if ( newIP == null ) { err.println( "Lookup of IP failed on " + ip + " for " + domain + ".\n" + "Usually this means the site does not exist or is temporarily down." ); pw.println( originalLine ); break; } final String newIp = newIP.getHostAddress(); if ( !newIp.equals( ip ) ) { out.println( "updated: " + ip + " to " + newIp + " for " + domain ); ip = newIp; updates++; } } catch ( IOException e ) { err.println( "Lookup of IP failed on " + ip + " for " + domain + ".\n" + "Usually this means the site does not exist or is temporarily down.\n" + "Technically " + e.getMessage() ); pw.println( originalLine ); break; } } pw.println( ST.leftPad( ip, 37, false ) + " " + ST.rightPad( domain, 35, false ) + ( comment.length() > 0 ? ( "# " + comment ) : "" ) ); break; default: err.println( "Line needs both ip and dns separated by space.\n" + originalLine ); pw.println( originalLine ); if ( DEBUGGING ) { err.println( "{" + pair + "}" ); for ( String piece : pieces ) { err.println( "[" + piece + "]" ); } } } return updates; } // /methods }