Java tutorial
/** * Copyright 2012 Tobias Gierke <tobias.gierke@code-sourcery.de> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.codesourcery.geoip.trace; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import org.apache.commons.exec.CommandLine; import org.apache.commons.exec.DefaultExecuteResultHandler; import org.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.ExecuteException; import org.apache.commons.exec.ExecuteWatchdog; import org.apache.commons.exec.Executor; import org.apache.commons.exec.PumpStreamHandler; import org.apache.commons.lang.StringUtils; /** * * @author tobias.gierke@code-sourcery.de */ public class TracePath { /** * Filesystem path to the Linux 'tracepath' utility on <b>Ubuntu</b> systems. */ public static final String TRACEPATH = "/usr/bin/tracepath"; /** * Filesystem path to the Linux 'traceroute' utility on <b>Ubuntu</b> systems. */ public static final String TRACEROUTE = "/usr/bin/traceroute.db"; public static void main(String[] args) throws Exception { int hop = 1; for (String ip : trace("www.slashdot.org")) { System.out.println(">>>> " + hop + ": " + ip); hop++; } } /** * Check whether path-tracing is available on this system. * * @return */ public static boolean isPathTracingAvailable() { return isTraceRouteAvailable() || isTracePathAvailable(); } private static boolean isTracePathAvailable() { final File executable = new File(TRACEPATH); return executable.exists() && executable.canExecute(); } private static boolean isTraceRouteAvailable() { final File executable = new File(TRACEROUTE); return executable.exists() && executable.canExecute(); } protected interface IPathTracer { public List<String> trace(String address) throws ExecuteException, IOException, InterruptedException; } // IPathTracer protected static IPathTracer getPathTracer() { // prefer traceroute since it's faster if (isTraceRouteAvailable()) { return new TraceRouteTracer(); } if (isTracePathAvailable()) { return new TracePathTracer(); } throw new UnsupportedOperationException("No path tracing available."); } /** * Tries to trace all intermediate hops from the local machine to a specific IP address/host name. * * <p>Intermediate hops whose IP address cannot be determined will be removed from the result (so the result * contains only valid IP addresses).</p> * * @param address * @return List of IP addresses * @throws ExecuteException * @throws IOException * @throws InterruptedException */ public static List<String> trace(String address) throws ExecuteException, IOException, InterruptedException { IPathTracer tracer = getPathTracer(); System.out.print("Tracing path to " + address + " (using " + tracer + ") ..."); return tracer.trace(address); } protected static abstract class AbstractPathTracer implements IPathTracer { protected abstract CommandLine getCommandLine(String address); public List<String> trace(String address) throws ExecuteException, IOException, InterruptedException { final CommandLine cmdLine = getCommandLine(address); final HashMap<String, Object> map = new HashMap<>(); map.put("address", address); cmdLine.setSubstitutionMap(map); final DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler(); final ExecuteWatchdog watchdog = new ExecuteWatchdog(60 * 1000); Executor executor = new DefaultExecutor(); final ByteArrayOutputStream stdOut = new ByteArrayOutputStream(); final ByteArrayOutputStream stdErr = new ByteArrayOutputStream(); final OutputStream wrapper = new OutputStream() { @Override public void write(int b) throws IOException { stdOut.write(b); // System.out.write( b ); // System.out.flush(); } @Override public void close() throws IOException { try { super.close(); } finally { stdOut.close(); } } }; final PumpStreamHandler streamHandler = new PumpStreamHandler(wrapper, stdErr); executor.setStreamHandler(streamHandler); executor.setExitValue(1); executor.setWatchdog(watchdog); executor.execute(cmdLine, resultHandler); // some time later the result handler callback was invoked so we // can safely request the exit value resultHandler.waitFor(); System.out.println("Finished"); if (resultHandler.getExitValue() != 0) { System.err.println(new String(stdErr.toByteArray())); throw new ExecuteException("Execution failed", resultHandler.getExitValue()); } /* 1: 192.168.2.23 0.070ms pmtu 1500 1: 192.168.2.1 0.433ms 1: 192.168.2.1 0.529ms 2: 192.168.2.1 0.380ms pmtu 1492 2: 213.191.64.208 82.772ms 3: 62.53.10.232 47.866ms asymm 4 4: 62.53.8.36 50.041ms 5: 62.53.8.41 47.878ms asymm 6 6: 84.16.7.233 55.952ms asymm 7 7: 94.142.120.238 65.963ms */ final List<String> lines = extractLines(stdOut); final List<String> result = new ArrayList<>(); String previousHop = null; for (String line : lines) { line = line.trim(); String squashWhitespace = squashWhitespace(line); // System.out.println("LINE : "+line); // System.out.println("SQUASH: "+squashWhitespace); // System.out.println("-----"); final String[] parts = squashWhitespace.split(" "); if (parts.length < 2 || StringUtils.isBlank(parts[1])) { continue; } String ip = parts[1].trim(); if (isValidAddress(ip) && !ip.equals(previousHop)) { result.add(ip); previousHop = ip; } } final String endpoint = getIPAddress(address); if (endpoint != null) { System.out.println("Destination " + address + " has IP address " + endpoint); if (result.isEmpty()) { result.add(endpoint); } else if (!endpoint.equals(result.get(result.size() - 1))) { result.add(endpoint); } } return result; } protected List<String> extractLines(final ByteArrayOutputStream stdOut) { final List<String> result = new ArrayList<>( Arrays.asList(new String(stdOut.toByteArray()).split("\n"))); return filterLines(result); } protected List<String> filterLines(List<String> lines) { for (Iterator<String> it = lines.iterator(); it.hasNext();) { String line = it.next().trim(); if (StringUtils.isBlank(line) || !Character.isDigit(line.charAt(0))) { System.out.println("DISCARDED: " + line); it.remove(); } } return lines; } } protected static String getIPAddress(String name) { try { InetAddress inet = Inet4Address.getByName(name); if (inet != null) { return inet.getHostAddress(); } } catch (Exception e) { } try { InetAddress inet = Inet6Address.getByName(name); if (inet != null) { return inet.getHostAddress(); } } catch (Exception e) { } return null; } protected static class TracePathTracer extends AbstractPathTracer { @Override protected CommandLine getCommandLine(String address) { final CommandLine cmdLine = new CommandLine(TRACEPATH); cmdLine.addArgument("-n"); cmdLine.addArgument("${address}"); return cmdLine; } @Override public String toString() { return TRACEPATH; } } protected static class TraceRouteTracer extends AbstractPathTracer { @Override protected CommandLine getCommandLine(String address) { final CommandLine cmdLine = new CommandLine(TRACEROUTE); cmdLine.addArgument("-n"); cmdLine.addArgument("-q"); cmdLine.addArgument("1"); cmdLine.addArgument("-w"); cmdLine.addArgument("1"); cmdLine.addArgument("${address}"); return cmdLine; } @Override public String toString() { return TRACEROUTE; } } /** * Returns whether a string contains a valid IP address or host name. * * @param s * @return */ public static boolean isValidAddress(String s) { if (StringUtils.isBlank(s)) { return false; } try { InetAddress ip = Inet4Address.getByName(s); if (ip != null) { return true; } } catch (Exception e) { } try { InetAddress ip = Inet6Address.getByName(s); if (ip != null) { return true; } } catch (Exception e) { } return false; } private static String squashWhitespace(String input) { StringBuilder out = new StringBuilder(); boolean previousCharWasWhitespace = false; for (int i = 0; i < input.length(); i++) { char c = input.charAt(i); if (!Character.isWhitespace(c)) { out.append(c); previousCharWasWhitespace = false; } else { if (!previousCharWasWhitespace) { out.append(c); previousCharWasWhitespace = true; } } } return out.toString(); } }