Package net.azib.ipscan.core.net

Source Code of net.azib.ipscan.core.net.ICMPSharedPinger$PacketReceiverThread

/**
* This file is a part of Angry IP Scanner source code,
* see http://www.angryip.org/ for more information.
* Licensed under GPLv2.
*/
package net.azib.ipscan.core.net;

import net.azib.ipscan.core.ScanningSubject;
import org.savarese.rocksaw.net.RawSocket;
import org.savarese.vserv.tcpip.ICMPEchoPacket;
import org.savarese.vserv.tcpip.ICMPPacket;
import org.savarese.vserv.tcpip.IPPacket;
import org.savarese.vserv.tcpip.OctetConverter;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import static java.util.logging.Level.*;
import static net.azib.ipscan.util.IOUtils.*;

/**
* Shared multi-threaded pinger.
*
* @author Anton Keks
*/
public class ICMPSharedPinger implements Pinger {
  static final Logger LOG = Logger.getLogger(ICMPSharedPinger.class.getName());

  /** a single raw socket for sending of all ICMP packets */
  private final RawSocket sendingSocket;
  /** a single raw socket for receiving of all ICMP packets */
  private final RawSocket receivingSocket;
  /** the map with PingResults, keys are InetAddress */
  private Map<InetAddress, PingResult> results = new ConcurrentHashMap<InetAddress, PingResult>();
 
  private Thread receiverThread;
 
  private int timeout;
  private int timeOffsetInPacket;

  public ICMPSharedPinger(int timeout) throws IOException {
    // we use two shared sockets, because it works more efficiently
    // OSs tend to copy all received ICMP packets to all open raw sockets,
    // so it is very bad to have a separate raw socket for each scanning thread
    sendingSocket = new RawSocket();
    sendingSocket.open(RawSocket.PF_INET, IPPacket.PROTOCOL_ICMP);
    receivingSocket = new RawSocket();
    receivingSocket.open(RawSocket.PF_INET, IPPacket.PROTOCOL_ICMP);
    this.timeout = timeout;

    try {
      sendingSocket.setSendTimeout(timeout);
      receivingSocket.setReceiveTimeout(timeout);
      //receivingSocket.setReceiveBufferSize()
    }
    catch (java.net.SocketException se) {
      sendingSocket.setUseSelectTimeout(true);
      receivingSocket.setUseSelectTimeout(true);
      sendingSocket.setSendTimeout(timeout);
      receivingSocket.setReceiveTimeout(timeout);
    }
   
    receiverThread = new PacketReceiverThread();
    receiverThread.start();
  }

  public void close() throws IOException {
    synchronized (sendingSocket) {
      sendingSocket.close();     
    }
    receiverThread.interrupt();
  }

  public PingResult ping(ScanningSubject subject, int count) throws IOException {
    InetAddress address = subject.getAddress();
    PingResult result = new PingResult(address);
    results.put(address, result);
   
    // TODO: make ICMPEchoPacket accept byte array in the constructor
    ICMPEchoPacket packet = new ICMPEchoPacket(1);
    byte[] data = new byte[84];
    packet.setData(data);
    packet.setIPHeaderLength(5);
    packet.setICMPDataByteLength(56);
    packet.setType(ICMPPacket.TYPE_ECHO_REQUEST);
    packet.setCode(0);
    packet.setIdentifier(hashCode() & 0xFFFF); // some identification stuff
   
    try {
      // send a bunch of packets
      // note: we send sequence numbers starting from 1 (this is used by the ReceiverThread)
      for (int i = 1; i <= count  && !Thread.currentThread().isInterrupted(); i++) {
        packet.setSequenceNumber(i);
       
        int offset = packet.getIPHeaderByteLength();
        timeOffsetInPacket = offset + packet.getICMPHeaderByteLength();
        int length = packet.getICMPPacketByteLength();
       
        OctetConverter.longToOctets(System.currentTimeMillis(), data, timeOffsetInPacket);
        packet.computeICMPChecksum();
       
        if (LOG.isLoggable(FINEST)) {
          LOG.finest("Pinging " + i + result.address);
        }
        synchronized (sendingSocket) {
          sendingSocket.write(result.address, data, offset, length)
        }
       
        try {
          // a small pause between sending the packets
          Thread.sleep(15);
        }
        catch (InterruptedException e) {
          // leave the interrupted flag
          Thread.currentThread().interrupt();
        }
      }
       
      int totalTimeout = timeout * count;
      while (totalTimeout > 0 && result.getReplyCount() < count) {
        if (LOG.isLoggable(FINEST)) {
          LOG.finest("Waiting for response " + address + ": " + totalTimeout);
        }
        synchronized (result) {
          // wait until we have an answer
          try {
            result.wait(timeout);
          }
          catch (InterruptedException ignore) {}
        }
        totalTimeout -= timeout;
      }
     
      return result;
    }
    finally {
      // remove garbage
      results.remove(address);
    }
  }

  /**
   * An internal thread for receiving of packets
   */
  private class PacketReceiverThread extends Thread {
   
    public PacketReceiverThread() {
      super("Ping packet receiver");
      setDaemon(true);
      setPriority(Thread.MAX_PRIORITY);
    }

    public void run() {
      ICMPEchoPacket packet = new ICMPEchoPacket(1);
      byte[] data = new byte[84];
      packet.setData(data);
      packet.setIPHeaderLength(5);
      packet.setICMPDataByteLength(56);
     
      // we use this address for receiving
      // due to some reason, raw sockets return packets coming from any addresses anyway
      InetAddress tmpAddress = null;
      try {
        tmpAddress = InetAddress.getLocalHost();
      }
      catch (UnknownHostException e) {
        LOG.log(SEVERE, null, e);
      }
     
      try {
        // Windows OS cannot read from a raw socket before anything has been sent through it
        receivingSocket.write(tmpAddress, data);
      }
      catch (IOException e) {
        LOG.log(WARNING, "Sending of test packet failed", e);
      }
     
      do {
        try {
          receivingSocket.read(tmpAddress, data);
         
          if (packet.getType() == ICMPPacket.TYPE_ECHO_REPLY &&
            packet.getIdentifier() == (ICMPSharedPinger.this.hashCode() & 0xFFFF) &&
            packet.getSequenceNumber() > 0) {
           
            long endTime = System.currentTimeMillis();
           
            PingResult result = results.get(packet.getSourceAsInetAddress());
            if (result == null) {
              LOG.warning("ICMP packet received from an unknown address: " + packet.getSourceAsInetAddress());
              continue;
            }
           
            long startTime = OctetConverter.octetsToLong(data, timeOffsetInPacket);
            long time = endTime - startTime;
           
            if (LOG.isLoggable(FINEST)) {
              LOG.finest("Received " + packet.getSequenceNumber() + packet.getSourceAsInetAddress() + ": " + time);
            }

            result.addReply(time);
            // TTL should be the same among all packets
            result.setTTL(packet.getTTL() & 0xFF);
           
            synchronized (result) {
              // notify the sender that we have an answer :-)
              result.notifyAll();
            }
          }
          else
          if (packet.getType() == ICMPPacket.TYPE_HOST_UNREACHABLE) {
            // TODO: received non-ECHO_REPLY packets may also be useful, saying "destination is unreachable"
            // packet body in this case is the sent ICMP_REQUEST packet
          }
        }
        catch (InterruptedIOException e) {
          // socket read timeout
          LOG.finer("Receive timeout");
          // TODO: make RawSocket to throw Exceptions without the stack trace (for speed)
        }
        catch (UnknownHostException e) {
          LOG.log(WARNING, "Cannot retrieve the source address of an ICMP packet", e);
        }
        catch (IOException e) {
          LOG.log(WARNING, "Unable to read from the socket", e);
        }
     
      }
      while(!interrupted());

      closeQuietly(receivingSocket);
      LOG.fine("Terminated");
    }
  }
}
TOP

Related Classes of net.azib.ipscan.core.net.ICMPSharedPinger$PacketReceiverThread

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.