/******************************************************************************* * Copyright 2016 Specure GmbH * * 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 at.alladin.rmbt.android.impl; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.json.JSONException; import org.json.JSONObject; import at.alladin.rmbt.util.tools.TracerouteService; /** * * @author lb * */ public class TracerouteAndroidImpl implements TracerouteService { public static final class PingException extends IOException { /** * */ private static final long serialVersionUID = 1L; public PingException(String msg) { super(msg); } } public final static class PingDetailImpl implements HopDetail { private final int transmitted; private final int received; private final int errors; private final int packetLoss; private long time; private final String fromIp; public final static Pattern PATTERN_PING_PACKET = Pattern.compile("([\\d]*) packets transmitted, ([\\d]*) received, ([+-]?([\\d]*) errors, )?([\\d]*)% packet loss, time ([\\d]*)ms"); //public final static Pattern PATTERN_FROM_IP = Pattern.compile("[fF]rom ([\\.:-_\\d\\w\\s\\(\\)]*):(.*time=([\\d\\.]*))?"); public final static Pattern PATTERN_FROM_IP = Pattern.compile("[fF]rom ([\\.\\-_\\d\\w\\s\\(\\)]*)(:|icmp)+(.*time=([\\d\\.]*))?"); public PingDetailImpl(String pingResult, final long durationNs) { System.out.println(pingResult); time = durationNs;; final Matcher pingPacket = PATTERN_PING_PACKET.matcher(pingResult); if (pingPacket.find()) { transmitted = Integer.parseInt(pingPacket.group(1)); received = Integer.parseInt(pingPacket.group(2)); String errors = pingPacket.group(4); if (errors != null) { this.errors = Integer.parseInt(errors); } else { this.errors = 0; } packetLoss = Integer.parseInt(pingPacket.group(5)); } else { transmitted = 0; received = 0; packetLoss = 0; errors = 0; } final Matcher fromIpMatcher = PATTERN_FROM_IP.matcher(pingResult); if (fromIpMatcher.find()) { fromIp = fromIpMatcher.group(1); String time = fromIpMatcher.group(4); if (time != null) { this.time = TimeUnit.NANOSECONDS.convert((int)(Float.parseFloat(time) * 1000f), TimeUnit.MICROSECONDS); } } else { fromIp = "*"; } } public long getTime() { return time; } public void setTime(long time) { this.time = time; } public int getTransmitted() { return transmitted; } public int getReceived() { return received; } public int getErrors() { return errors; } public int getPacketLoss() { return packetLoss; } public String getFromIp() { return fromIp; } @Override public String toString() { return "PingDetail [transmitted=" + transmitted + ", received=" + received + ", errors=" + errors + ", packetLoss=" + packetLoss + ", time=" + (time / 1000000) + "ms, fromIp=" + fromIp + "]"; } public JSONObject toJson() { JSONObject json = new JSONObject(); try { json.put("host", fromIp); json.put("time", time); return json; } catch (JSONException e) { e.printStackTrace(); return null; } } } private String host; private int maxHops; private AtomicBoolean isRunning = new AtomicBoolean(false); private boolean hasMaxHopsExceeded = true; private List<HopDetail> resultList; public TracerouteAndroidImpl() { } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getMaxHops() { return maxHops; } public void setMaxHops(int maxHops) { this.maxHops = maxHops; } public List<HopDetail> call() throws Exception { isRunning.set(true); if (resultList == null) { resultList = new ArrayList<TracerouteService.HopDetail>(); } final Runtime runtime = Runtime.getRuntime(); for (int i = 1; i <= maxHops; i++) { if (Thread.interrupted() || !isRunning.get()) { throw new InterruptedException(); } final long ts = System.nanoTime(); final Process mIpAddrProcess = runtime.exec("/system/bin/ping -c 1 -t " + i + " -W2 " + host); final String proc = readFromProcess(mIpAddrProcess); final PingDetailImpl pingDetail = new PingDetailImpl(proc, System.nanoTime() - ts); resultList.add(pingDetail); if (pingDetail.getReceived() > 0) { hasMaxHopsExceeded = false; break; } } return resultList; } /** * stop the ping tool task * @return true if task has been stopped or false if it was not running */ public boolean stop() { return isRunning.getAndSet(false); } public static String readFromProcess(Process proc) throws PingException { BufferedReader brErr = null; BufferedReader br = null; StringBuilder sbErr = new StringBuilder(); StringBuilder sb = new StringBuilder(); try { brErr = new BufferedReader(new InputStreamReader(proc.getErrorStream())); String currInputLine = null; while((currInputLine = brErr.readLine()) != null) { sbErr.append(currInputLine); sbErr.append("\n"); } if (sbErr.length() > 0) { throw new PingException(sbErr.toString()); } br = new BufferedReader(new InputStreamReader(proc.getInputStream())); currInputLine = null; while((currInputLine = br.readLine()) != null) { sb.append(currInputLine); sb.append("\n"); } } catch (PingException e) { throw e; } catch (Exception e) { e.printStackTrace(); } finally { try { if (br != null) { br.close(); } } catch (IOException e) { } try { if (brErr != null) { brErr.close(); } } catch (IOException e) { } } return sb.toString(); } @Override public boolean hasMaxHopsExceeded() { return hasMaxHopsExceeded; } @Override public void setResultListObject(List<HopDetail> resultList) { this.resultList = resultList; } }