Use Java to Ping Another Host

Use Java to determine whether another host is present and responsive on a network. Two approaches are provided: low level and layer 4, connection oriented. The lower level approach will result in a ping if used with the right permissions.

advertisement

Pinging another device on a local network can be important to determine whether the host is present and not in an indeterministic state (e.g., the processor in a security camera has become unresponsive and no longer triggers on motion). However, not all hosts reply to low level ICMP ECHO ping requests. Therefore, it may be necessary to utilize another form of echo packet or even initiate a layer 4 handshake (e.g., connect on port 80 for an HTTP server). The example Java class Scan provided below provides two ways to scan / ping a host:

  1. InetAddress.isReachable initiates a traditional ping request when used with the right permissions.
  2. SocketChannel.connect initiates a TCP handshake / connection.

The purpose of the InetAddress class in the java.net package is to represent, test, and interact with an IP address and the host attached to the address. One of the methods in the InetAddress class is IsReachable. From the JAVA API Specification, the method can be used to...

"test whether that address is reachable. Best effort is made by the implementation to try to reach the host.... A typical implementation will use ICMP ECHO REQUESTs if the privilege can be obtained, otherwise it will try to establish a TCP connection on port 7 (Echo) of the destination host."

An alternative to IsReachable is to establish a TCP socket utilizing the InetSocketAddress and SocketChannel classes. This has shown to have similar time delay performances as ping as long as the remote host is listening on the chosen port. For surveillance nodes that typically act as an embedded web server, the use of port 80 (HTTP) can be a good choice and provide good performance (just a few ms of delay).

The section below provides examples and Wireshark test results on MacOS Sierra using Java 8 Update 121 and invoking Scan from the command line. Note that the delays shown in Wireshark and on the command line are not perfectly correlated, so it's up to the implementer to determine which delay is more important when choosing the best approach to scan a host. Note that the results on Ubuntu 16.04 / Linux provide similar results.

Results on Windows are a little different. Invoking Scan in Windows 10 from a power shell will transmit a ping (without running as administrator). In previous releases of Windows and prior versions of Java, multiple TCP echo transmits were witnessed before a success would be returned.

Transmit a TCP SYN packet on port 7 (Echo):

$ java Scan <host>
scan using inetAddress.isReachable: responded in 1 ms
Figure 1. Scan with Java using TCP Echo Request (port 7)
Scan with Java on port 7

Transmit a true ping:

$ sudo java Scan <host>
scan using inetAddress.isReachable: responded in 1 ms
Figure 2. Ping with Java using root permissions
Ping with Java using root permissions

Initiate a TCP SYN, ACK, FIN handshake on port 80 (HTTP):

$ java Scan <host> 80
scan using SocketChannel.connect: responded in 5 ms
Figure 3. Scan using SocketChannel.connect on port 80
Scan with Java on port 80

Transmit a TCP SYN packet on port 7 (Echo), but port is down (still in ARP cache). Note that the full transaction now takes seconds

$ java Scan <host> 80
scan using inetAddress.isReachable:Failed
Figure 4. Scan with host down
Scan with Java on port 80

Note that this method can hang for a substantial time if the port is down. However, the performance is similar to the echo request if the destination port is closed (e.g., no web serever configured for port 80).

Transmit a TCP SYN packet on port 7 (Echo) with echo service enabled:

$ java Scan <host>
scan using inetAddress.isReachable: responded in 2 ms
Figure 5. Scan with an echo service enabled
Scan with an echo service enabled

Note, running an echo daemon is not the usual, but you can do it easily with inetd. The following was used to enable echo service on Ubuntu 16.04:

$ sudo update-inetd --verbose --add "echo stream tcp daemon internal internal"
Processing /etc/inetd.conf
Processing service `echo' ... added
Number of currently enabled services: 1
Using tempfile /tmp/inetd...
About to send SIGHUP to inetd (pid: 4569)
New service(s) added
Java: Scan.java
/*
 *  Copyright (c) 2017 Mind Chasers Inc.
 * 
 * 	file: Scan.java
 * 
 * 	ping / scan another host on your network
 * 
 * 	THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
 *  WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 *  WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
 *  FOR A PARTICULAR PURPOSE.
 *  
 */

package com.mindchasers;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.SocketChannel;
import java.util.Date;

public class Scan {

	public static void main(String[] args) {
		String usage = "java Scan 
[]"; String hostAddress = ""; int port; long timeToRespond = 0; // in milliseconds if (args.length < 1 || args.length > 2) { System.out.println("usage: " + usage); return; } try { hostAddress = args[0]; // copy the string if (args.length == 2) port = Integer.parseInt(args[1]); // convert the integer else port = 80; if (args.length == 1) { System.out.printf("scan using inetAddress.isReachable:"); timeToRespond = test(hostAddress); } else { System.out.printf("scan using SocketChannel.connect:"); timeToRespond = test(hostAddress, port); } } catch (NumberFormatException e) { System.out.println("Problem with arguments, usage: " + usage); e.printStackTrace(); } if (timeToRespond >= 0) System.out.println(" responded in " + timeToRespond + " ms"); else System.out.println("Failed"); } /** * Connect using layer3 * * @param hostAddress * @return delay if the specified host responded, -1 if failed */ static long test(String hostAddress) { InetAddress inetAddress = null; Date start, stop; try { inetAddress = InetAddress.getByName(hostAddress); } catch (UnknownHostException e) { System.out.println("Problem, unknown host:"); e.printStackTrace(); } try { start = new Date(); if (inetAddress.isReachable(5000)) { stop = new Date(); return (stop.getTime() - start.getTime()); } } catch (IOException e1) { System.out.println("Problem, a network error has occurred:"); e1.printStackTrace(); } catch (IllegalArgumentException e1) { System.out.println("Problem, timeout was invalid:"); e1.printStackTrace(); } return -1; // to indicate failure } /** * Connect using layer4 (sockets) * * @param * @return delay if the specified host responded, -1 if failed */ static long test(String hostAddress, int port) { InetAddress inetAddress = null; InetSocketAddress socketAddress = null; SocketChannel sc = null; long timeToRespond = -1; Date start, stop; try { inetAddress = InetAddress.getByName(hostAddress); } catch (UnknownHostException e) { System.out.println("Problem, unknown host:"); e.printStackTrace(); } try { socketAddress = new InetSocketAddress(inetAddress, port); } catch (IllegalArgumentException e) { System.out.println("Problem, port may be invalid:"); e.printStackTrace(); } // Open the channel, set it to non-blocking, initiate connect try { sc = SocketChannel.open(); sc.configureBlocking(true); start = new Date(); if (sc.connect(socketAddress)) { stop = new Date(); timeToRespond = (stop.getTime() - start.getTime()); } } catch (IOException e) { System.out.println("Problem, connection could not be made:"); e.printStackTrace(); } try { sc.close(); } catch (IOException e) { e.printStackTrace(); } return timeToRespond; } }

References

  • Echo Protocol RFC 862
  • INTERNET CONTROL MESSAGE PROTOCOL (ICMP) RFC 792

Please help us improve this article by adding your comment or question:

email addresses are neither displayed nor shared