Query ARP cache to get MAC ID - java

I need to get the MAC ID of a host in my network. For that, if I ping to that IP and query the ARP cache arp -a, I am able to get the MAC ID. I just wonder if I can get any API to query the ARP and get the MAC id.
Also, if there is a better method to get the MAC ID from IP address, please suggest.
P.S: I am working in JAVA.
Thanks.

Java provides no direct way to query the MAC address of a host in your network, as this is abstracted away by Java's socket libraries.
In a way, this makes sense, because the MAC address of a host actually says very little. There is no such thing as "the" MAC address of a host.
Many hosts will have several NICs, all with a separate MAC address, with which they can connect to the network. The computer I'm on at the moment has a wired ethernet adapter, a WiFi adapter, and a Firewire adapter, and they all have their own MAC address. This means that there is no definitive MAC address for a host.
If the host is on a different subnet, ARP will actually give you the MAC address for the last router your packet passed through, instead of the MAC address of the host you're scanning.
Put both of these issues together, and that means that one host may have many different MAC addresses (if it has more than one NIC), and one MAC address may represent many different hosts (if traffic passes through a router).
Assuming you know all this and you still need to get the MAC address of a host, the only way to do that in Java is by "going native":
Native to the client that runs your program:
You could launch an ARP command-line tool and parse its output.
You could use some sort of JNI call. I'm not too familiar with JNI, though, so I can't help you with that.
Write a separate, small native app that you can access from Java via Telnet or some such protocol, and which will run the ARP command for you.
Native to the host that you want to scan:
You could use SNMP, as some of the other answers to this thread suggest. I defer to these answers for making that work for you. SNMP is a great protocol, but be aware that SNMP's OIDs can be both platform-dependent and vendor-dependent. OIDs that work for Windows don't always work for Linux and vice versa.
If you know that your host runs Windows, you could use WMI. The Win32_NetworkAdapter class holds the information you want, but be aware that this returns all of the hosts NICs, even the ones Windows makes up. Also, it requires administrator credentials to the host you are scanning. Google will tell you how to connect to WMI from Java.
If you know your host runs OS X, you might be able to SSH into the machine and parse the output of the system_profile command.
For Linux, a tool similar to OS X's system_profile probably exists.

There is a much simpler way:
private static final String ARP_GET_IP_HW = "arp -a";
public String getARPTable(String cmd) throws IOException {
Scanner s = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
System.out.println(getARPTable(ARP_GET_IP_HW ));
And you get the eintire ARP Table with IP and HW sorted on each row.
Then you can split the table into separate String rows and use regular expressions on each row to match both HW and IP Adress. And you're done.

ARP is the way to map IP addresses to MAC addresses. That's how the IP stack does it.
I'm not sure there is a portable way to get that info, since it is typically only important for kernel developers and system administrators.
From a lot of web searching, it looks like it is possible to get a router's ARP table using SNMP, but I didn't find a lot of specific info on how to do it. I did find a free Java library for SNMP here though. Some spelunking through there might prove productive.

You can get your own MAC address via:
Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces();
while ( it.hasMoreElements() ) {
byte[] macAddress = it.nextElement().getHardwareAddress();
}
There is definitely no way you can get the MAC address of another host via vanilla java. You'd have to use Process execution or a native library to do it.
If you control the other machines, you can let them query their own MAC and send it back across a TCP/IP channel, but I'm guessing that's not what you want. For more details, see jqno's answer.

The arp cache is provided as standard in the set of SNMP data available. You can use SNMP4J to write a trivial agent to query this data.
e.g. from a command line SNMP toolset
snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2
(that huge period-delimited string is the OID, or identifier, of the ARP cache in SNMP terms. That will work for all SNMP implementations)

This may not be solvable in the context of Java (because it is platform independent), but you should also consider whether or not you can get the MAC addresses via a system service. There are probably situations where you cannot reliably find the MAC address via ARP, it depends on why you would need the MAC address.

As others have said, ARP is the way to go. Following is an implementation of jqnos second suggestion based on this example on GitSpot.
Two libraries are required:
system library for network traffic capture:
Linux-based: libpcap available from www.tcpdump.org
Windows: winpcap available from www.winpcap.org
the jpcap java library available from the jpcap sourceforge site, which provides a high-level interface to the first library through JNI
public class GetMACAddress {
/**
*
* #param ip address containing an IP
* #return MAC-Address as formatted String
* #throws IOException
* #throws IllegalArgumentException
*/
public static String getMACAdressByIp(Inet4Address ip) throws IOException, IllegalArgumentException {
byte[] mac = GetMACAddress.getMACAddressByARP(ip);
StringBuilder formattedMac = new StringBuilder();
boolean first = true;
for (byte b : mac) {
if (first) {
first = false;
} else {
formattedMac.append(":");
}
String hexStr = Integer.toHexString(b & 0xff);
if (hexStr.length() == 1) {
formattedMac.append("0");
}
formattedMac.append(hexStr);
}
return formattedMac.toString();
}
private static byte[] getMACAddressByARP(Inet4Address ip) throws IOException, IllegalArgumentException {
NetworkInterface networkDevice = getNetworkDeviceByTargetIP(ip);
JpcapCaptor captor = JpcapCaptor.openDevice(networkDevice, 2000, false, 3000);
captor.setFilter("arp", true);
JpcapSender sender = captor.getJpcapSenderInstance();
InetAddress srcip = null;
for (NetworkInterfaceAddress addr : networkDevice.addresses)
if (addr.address instanceof Inet4Address) {
srcip = addr.address;
break;
}
byte[] broadcast = new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255 };
ARPPacket arp = new ARPPacket();
arp.hardtype = ARPPacket.HARDTYPE_ETHER;
arp.prototype = ARPPacket.PROTOTYPE_IP;
arp.operation = ARPPacket.ARP_REQUEST;
arp.hlen = 6;
arp.plen = 4;
arp.sender_hardaddr = networkDevice.mac_address;
arp.sender_protoaddr = srcip.getAddress();
arp.target_hardaddr = broadcast;
arp.target_protoaddr = ip.getAddress();
EthernetPacket ether = new EthernetPacket();
ether.frametype = EthernetPacket.ETHERTYPE_ARP;
ether.src_mac = networkDevice.mac_address;
ether.dst_mac = broadcast;
arp.datalink = ether;
sender.sendPacket(arp);
while (true) {
ARPPacket p = (ARPPacket) captor.getPacket();
if (p == null) {
throw new IllegalArgumentException(ip + " is not a local address");
}
if (Arrays.equals(p.target_protoaddr, srcip.getAddress())) {
return p.sender_hardaddr;
}
}
}
private static NetworkInterface getNetworkDeviceByTargetIP(Inet4Address ip) throws IllegalArgumentException {
NetworkInterface networkDevice = null;
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
loop: for (NetworkInterface device : devices) {
for (NetworkInterfaceAddress addr : device.addresses) {
if (!(addr.address instanceof Inet4Address)) {
continue;
}
byte[] bip = ip.getAddress();
byte[] subnet = addr.subnet.getAddress();
byte[] bif = addr.address.getAddress();
for (int i = 0; i < 4; i++) {
bip[i] = (byte) (bip[i] & subnet[i]);
bif[i] = (byte) (bif[i] & subnet[i]);
}
if (Arrays.equals(bip, bif)) {
networkDevice = device;
break loop;
}
}
}
if (networkDevice == null) {
throw new IllegalArgumentException(ip + " is not a local address");
}
return networkDevice;
}
}

Inspired by greenspand answer i came up with this code that will query for the MAC address using IP and CMD command using specified IP.
Note that this code work on Windows and i believe it can work on Linux too with little modifications.
public static String getARPTable(String ip) throws IOException {
String systemInput = "";
//to renew the system table before querying
Runtime.getRuntime().exec("arp -a");
Scanner s = new Scanner(Runtime.getRuntime().exec("arp -a " + ip).getInputStream()).useDelimiter("\\A");
systemInput = s.next();
String mac = "";
Pattern pattern = Pattern.compile("\\s{0,}([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})");
Matcher matcher = pattern.matcher(systemInput);
if (matcher.find()) {
mac = mac + matcher.group().replaceAll("\\s", "");
} else {
System.out.println("No string found");
}
return mac;
}
public static void main(String[] args) throws IOException {
System.out.println(getARPTable("192.168.1.23"));
// prints 74-d4-35-76-11-ef
}

I provided a fully production ready method by using pcap4j+libpcap to detect IPV4 and ipv6 mac address here: https://github.com/gaoxingliang/mac-address-detector-java

Related

Is there a possible method to implement traceroute on Android utilizing Ping function?

I am now developing an Android network diagnosis application (in Java) with my friends.
We've implemented the Ping function and now want to implement traceroute function using Ping, this is what happened in Windows cmd:
Tracert in Windows
Then I tried to simulate the traceroute process by continuous Ping:Simulate traceroute by continuous Ping
To explain this, I am trying to simulate traceroute process by continuous Ping the same goal IP. Every time I increment ttl by 1 until reach the goal IP: Code in Android studio,record intermediate routers' IP address and then print out.
However on Android, I found the ICMP returned from intermediate did not contain their IP address info:
Output in android studio.
Is there something wrong with the Ping command on Android or maybe it's the problem of the ICMP format? How can I get the IP address of the intermediate gateway? Is it possible to implement traceroute using Ping on Android? Appreciate if any suggestion.
This is 'Ping' part of our very primitive version:
public String ping(String url) {
String str = "";
String tmp="";
try {
Process process = Runtime.getRuntime().exec(
"ping -c 1 -t 1 " + url);
BufferedReader reader = new BufferedReader(new InputStreamReader(
process.getInputStream()));
int i;
char[] buffer = new char[4096];
StringBuffer output = new StringBuffer();
while ((i = reader.read(buffer)) > 0) {
output.append(buffer, 0, i);
tmp=reader.readLine();
System.out.println(tmp);
}
reader.close();
// body.append(output.toString()+"\n");
str = output.toString();
// Log.d(TAG, str);
} catch (IOException e) {
// body.append("Error\n");
e.printStackTrace();
}
return str;
}

How can I get client's mac address using java? [duplicate]

I need to get the MAC ID of a host in my network. For that, if I ping to that IP and query the ARP cache arp -a, I am able to get the MAC ID. I just wonder if I can get any API to query the ARP and get the MAC id.
Also, if there is a better method to get the MAC ID from IP address, please suggest.
P.S: I am working in JAVA.
Thanks.
Java provides no direct way to query the MAC address of a host in your network, as this is abstracted away by Java's socket libraries.
In a way, this makes sense, because the MAC address of a host actually says very little. There is no such thing as "the" MAC address of a host.
Many hosts will have several NICs, all with a separate MAC address, with which they can connect to the network. The computer I'm on at the moment has a wired ethernet adapter, a WiFi adapter, and a Firewire adapter, and they all have their own MAC address. This means that there is no definitive MAC address for a host.
If the host is on a different subnet, ARP will actually give you the MAC address for the last router your packet passed through, instead of the MAC address of the host you're scanning.
Put both of these issues together, and that means that one host may have many different MAC addresses (if it has more than one NIC), and one MAC address may represent many different hosts (if traffic passes through a router).
Assuming you know all this and you still need to get the MAC address of a host, the only way to do that in Java is by "going native":
Native to the client that runs your program:
You could launch an ARP command-line tool and parse its output.
You could use some sort of JNI call. I'm not too familiar with JNI, though, so I can't help you with that.
Write a separate, small native app that you can access from Java via Telnet or some such protocol, and which will run the ARP command for you.
Native to the host that you want to scan:
You could use SNMP, as some of the other answers to this thread suggest. I defer to these answers for making that work for you. SNMP is a great protocol, but be aware that SNMP's OIDs can be both platform-dependent and vendor-dependent. OIDs that work for Windows don't always work for Linux and vice versa.
If you know that your host runs Windows, you could use WMI. The Win32_NetworkAdapter class holds the information you want, but be aware that this returns all of the hosts NICs, even the ones Windows makes up. Also, it requires administrator credentials to the host you are scanning. Google will tell you how to connect to WMI from Java.
If you know your host runs OS X, you might be able to SSH into the machine and parse the output of the system_profile command.
For Linux, a tool similar to OS X's system_profile probably exists.
There is a much simpler way:
private static final String ARP_GET_IP_HW = "arp -a";
public String getARPTable(String cmd) throws IOException {
Scanner s = new Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
System.out.println(getARPTable(ARP_GET_IP_HW ));
And you get the eintire ARP Table with IP and HW sorted on each row.
Then you can split the table into separate String rows and use regular expressions on each row to match both HW and IP Adress. And you're done.
ARP is the way to map IP addresses to MAC addresses. That's how the IP stack does it.
I'm not sure there is a portable way to get that info, since it is typically only important for kernel developers and system administrators.
From a lot of web searching, it looks like it is possible to get a router's ARP table using SNMP, but I didn't find a lot of specific info on how to do it. I did find a free Java library for SNMP here though. Some spelunking through there might prove productive.
You can get your own MAC address via:
Enumeration<NetworkInterface> it = NetworkInterface.getNetworkInterfaces();
while ( it.hasMoreElements() ) {
byte[] macAddress = it.nextElement().getHardwareAddress();
}
There is definitely no way you can get the MAC address of another host via vanilla java. You'd have to use Process execution or a native library to do it.
If you control the other machines, you can let them query their own MAC and send it back across a TCP/IP channel, but I'm guessing that's not what you want. For more details, see jqno's answer.
The arp cache is provided as standard in the set of SNMP data available. You can use SNMP4J to write a trivial agent to query this data.
e.g. from a command line SNMP toolset
snmpwalk ${hostname} 1.3.6.1.2.1.4.22.1.2
(that huge period-delimited string is the OID, or identifier, of the ARP cache in SNMP terms. That will work for all SNMP implementations)
This may not be solvable in the context of Java (because it is platform independent), but you should also consider whether or not you can get the MAC addresses via a system service. There are probably situations where you cannot reliably find the MAC address via ARP, it depends on why you would need the MAC address.
As others have said, ARP is the way to go. Following is an implementation of jqnos second suggestion based on this example on GitSpot.
Two libraries are required:
system library for network traffic capture:
Linux-based: libpcap available from www.tcpdump.org
Windows: winpcap available from www.winpcap.org
the jpcap java library available from the jpcap sourceforge site, which provides a high-level interface to the first library through JNI
public class GetMACAddress {
/**
*
* #param ip address containing an IP
* #return MAC-Address as formatted String
* #throws IOException
* #throws IllegalArgumentException
*/
public static String getMACAdressByIp(Inet4Address ip) throws IOException, IllegalArgumentException {
byte[] mac = GetMACAddress.getMACAddressByARP(ip);
StringBuilder formattedMac = new StringBuilder();
boolean first = true;
for (byte b : mac) {
if (first) {
first = false;
} else {
formattedMac.append(":");
}
String hexStr = Integer.toHexString(b & 0xff);
if (hexStr.length() == 1) {
formattedMac.append("0");
}
formattedMac.append(hexStr);
}
return formattedMac.toString();
}
private static byte[] getMACAddressByARP(Inet4Address ip) throws IOException, IllegalArgumentException {
NetworkInterface networkDevice = getNetworkDeviceByTargetIP(ip);
JpcapCaptor captor = JpcapCaptor.openDevice(networkDevice, 2000, false, 3000);
captor.setFilter("arp", true);
JpcapSender sender = captor.getJpcapSenderInstance();
InetAddress srcip = null;
for (NetworkInterfaceAddress addr : networkDevice.addresses)
if (addr.address instanceof Inet4Address) {
srcip = addr.address;
break;
}
byte[] broadcast = new byte[] { (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255, (byte) 255 };
ARPPacket arp = new ARPPacket();
arp.hardtype = ARPPacket.HARDTYPE_ETHER;
arp.prototype = ARPPacket.PROTOTYPE_IP;
arp.operation = ARPPacket.ARP_REQUEST;
arp.hlen = 6;
arp.plen = 4;
arp.sender_hardaddr = networkDevice.mac_address;
arp.sender_protoaddr = srcip.getAddress();
arp.target_hardaddr = broadcast;
arp.target_protoaddr = ip.getAddress();
EthernetPacket ether = new EthernetPacket();
ether.frametype = EthernetPacket.ETHERTYPE_ARP;
ether.src_mac = networkDevice.mac_address;
ether.dst_mac = broadcast;
arp.datalink = ether;
sender.sendPacket(arp);
while (true) {
ARPPacket p = (ARPPacket) captor.getPacket();
if (p == null) {
throw new IllegalArgumentException(ip + " is not a local address");
}
if (Arrays.equals(p.target_protoaddr, srcip.getAddress())) {
return p.sender_hardaddr;
}
}
}
private static NetworkInterface getNetworkDeviceByTargetIP(Inet4Address ip) throws IllegalArgumentException {
NetworkInterface networkDevice = null;
NetworkInterface[] devices = JpcapCaptor.getDeviceList();
loop: for (NetworkInterface device : devices) {
for (NetworkInterfaceAddress addr : device.addresses) {
if (!(addr.address instanceof Inet4Address)) {
continue;
}
byte[] bip = ip.getAddress();
byte[] subnet = addr.subnet.getAddress();
byte[] bif = addr.address.getAddress();
for (int i = 0; i < 4; i++) {
bip[i] = (byte) (bip[i] & subnet[i]);
bif[i] = (byte) (bif[i] & subnet[i]);
}
if (Arrays.equals(bip, bif)) {
networkDevice = device;
break loop;
}
}
}
if (networkDevice == null) {
throw new IllegalArgumentException(ip + " is not a local address");
}
return networkDevice;
}
}
Inspired by greenspand answer i came up with this code that will query for the MAC address using IP and CMD command using specified IP.
Note that this code work on Windows and i believe it can work on Linux too with little modifications.
public static String getARPTable(String ip) throws IOException {
String systemInput = "";
//to renew the system table before querying
Runtime.getRuntime().exec("arp -a");
Scanner s = new Scanner(Runtime.getRuntime().exec("arp -a " + ip).getInputStream()).useDelimiter("\\A");
systemInput = s.next();
String mac = "";
Pattern pattern = Pattern.compile("\\s{0,}([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})");
Matcher matcher = pattern.matcher(systemInput);
if (matcher.find()) {
mac = mac + matcher.group().replaceAll("\\s", "");
} else {
System.out.println("No string found");
}
return mac;
}
public static void main(String[] args) throws IOException {
System.out.println(getARPTable("192.168.1.23"));
// prints 74-d4-35-76-11-ef
}
I provided a fully production ready method by using pcap4j+libpcap to detect IPV4 and ipv6 mac address here: https://github.com/gaoxingliang/mac-address-detector-java

How to get the remote user's username in java

I am working on application which doesn't have any login mechanism, any user in my organization can use that. But I want to pick the username of the remote users who will use my tool. I have a button clicking on that I want to get their usernames.
I tried request.getRemoteUser got null. tried System.getenv("USERNAME") getting the logged in user of the localhost where the server resides. Tried getHostName, System.getProperty got the localhost name. Tried this also - new com.sun.security.auth.module.NTSystem().getName() but same result.
I am using java6, windows server and glassfish3 server.
Please suggest something as I don't want to use any external link and tool.
You want to do something called SSO (Single Sign On): A user is logged in somewhere (in your case his Windows computer) and you want to authenticate the user with this (already done) login. This is a very common use case and there are different ways to do that. However, the big question is always how you can trust those third party system. And this is where the trouble begins.
Since your question is not very clear, I assume you have a Java Glassfish server running on Windows Server and a Java client (because you asked for Java code). So the Java server must authenticate who the user of the Java client is. And the server must trust this information.
Using System.getProperty("user.name"); isn't a good idea since anybody can change it. You can start your Java program with java -Duser.name=Joe <your_program> and that's it.
But since you are on Windows, you could use Windows to help you. If both, your client and server, are in the same domain, they are authenticated against the same system. You can ask this system for the user identity. Typically machines of a company are in the same domain.
To do this there is a tool called Waffle. It does a secure Windows authentication between machines in the same domain. If your client and server are in the same domain, it is an easy way to perform an SSO (a single sign on). You can find it on GitHub: http://dblock.github.io/waffle/
Here is a simple example from one of my own questions a couple of months ago (see here):
// client credentials handle
IWindowsCredentialsHandle credentials= WindowsCredentialsHandleImpl.getCurrent("Negotiate");
credentials.initialize();
// initial client security context
WindowsSecurityContextImpl clientContext = new WindowsSecurityContextImpl();
clientContext.setPrincipalName(Advapi32Util.getUserName());
clientContext.setCredentialsHandle(credentials.getHandle());
clientContext.setSecurityPackage(securityPackage);
clientContext.initialize();
// accept on the server
WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
IWindowsSecurityContext serverContext = null;
do {
if (serverContext != null) {
// initialize on the client
SecBufferDesc continueToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, serverContext.getToken());
clientContext.initialize(clientContext.getHandle(), continueToken);
}
// accept the token on the server
serverContext = provider.acceptSecurityToken(clientContext.getToken(), "Negotiate");
} while (clientContext.getContinue() || serverContext.getContinue());
System.out.println(serverContext.getIdentity().getFqn());
for (IWindowsAccount group : serverContext.getIdentity().getGroups())
System.out.println(" " + group.getFqn());
You can use Waffle also for websites. However, I didn't do that and cannot explain you what to do in this case.
And one important remark: I think you are a little bit confused. If you do request.getRemoteHost() on your server, you try to get the identity of the client who send the request (by the way, it is not secure, a client could send anything). However, if you do System.getProperty("user.name") on your server, you try to get the name of the server itself. Be aware where you are (on client or server) and what you want. And make sure whether you can trust this information or not. Security is difficult.
java class code to find who loggedin into a remote computer in a domain
package com.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import com.test.Pubfun;
public class UserName {
public static HashMap <String,String> hmun=new HashMap<String, String>();
public String setUserFromIP(String arg1) {
String m = arg1;
StringBuilder user = new StringBuilder();
String u = "";
String user2 = null;
try {
Process p = Runtime.getRuntime().exec("query user /server:" + m);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
String line = reader.readLine();
while (line != null) {
line = reader.readLine();
user.append(line);
line=null;
}
} catch (IOException e1) {
} catch (InterruptedException e2) {
}
u = user.toString().replace("null", "");
try {
user2 = this.getUserFromString(u);
} catch (ArrayIndexOutOfBoundsException ae) {
}
u.replace("null", " ");
System.out.println(user2);
hmun.put("username",user2);
return user2;
}
public static String gethmun()
{
String t=hmun.get("username");
return t;
}
public String getUserFromString(String u) {
HashMap <String,String> hmun=new HashMap<String, String>();
String input = u;
int length, size;
length = input.length();
size = length ;
String strarray[] = new String[size];
strarray = input.split("\\s+");
for (int i = 0; i < strarray.length; i++) {
if(strarray[i].equals("Active")){
hmun.put("username", strarray[i-3]);
}
}
String user1=hmun.get("username");
return user1;
}
}
HttpServletRequest.getRemoteUser() might optionally return the login of user making the request (if authenticated), but it is not the username of the user logged in on the remote machine.
There is no way to query the username of the remote machine. Browsers or applications making the requests might send this info voluntarily, but if they don't, you won't find a way to get it. And by default they don't send it so don't count on this.
This gives you the current logged in Username from your local Windows System System.getProperty("user.name");

Which configuration on Windows 7 could have an impact on NetworkInterface.getHardwareAddress() in Java?

I'm displaying network information using a very simple code that uses Java API: NetworkInterface#getHardwareAddress() .
The code is working on Windows XP, XP 64, Debian.
I find two different behaviors on Win 7: computer of my company vs mine.
Information displayed are not the same as ipconfig /all, I get only the physical address of the last virtual network card.
I reproduce the issue using java 1.6 u32, 1.7 u21 and 1.7 u40 (both versions x86/64): looking at the output, eth3 and eth4 return the wrong mac address.
I think the code is correct: this is the same as suggested on Stack Overflow and the result is correct on my personal computer.
Does anyone know what parameters might influence the result?
What settings should I check on Windows to determine differences between
different machines?
Any suggestions ?
ToDo
I will try to disable virtual interfaces then relaunch the tool. (Needs the IT intervention...).
I have the same problem. Here is the code which works on my Windows 7 machine with VMVare virtual cards:
private String getMacJava5() throws Exception {
String mac = "";
InetAddress ip = InetAddress.getLocalHost();
String[] command = {"ipconfig", "/all"};
Pattern physAddr = Pattern.compile("\\s*Physi.*: (.*)");
Pattern ipAddr = Pattern.compile("\\s*IPv.*: ([^(]*).*");
Process p = Runtime.getRuntime().exec(command);
BufferedReader inn = new BufferedReader(new InputStreamReader(p.getInputStream()));
while (true) {
String line = inn.readLine();
if (line == null) {
break;
}
Matcher mm = physAddr.matcher(line);
if (mm.matches()) {
mac = mm.group(1);
}
mm = ipAddr.matcher(line);
if (mm.matches()) {
if (mm.group(1).equals(ip.getHostAddress())) {
break;
}
mac = "";
}
}
return mac + " IP: " + ip.getHostAddress();
}

Why does InetAddress.isReachable return false, when I can ping the IP address?

InetAddress byName = InetAddress.getByName("173.39.161.140");
System.out.println(byName);
System.out.println(byName.isReachable(1000));
Why does isReachable return false? I can ping the IP.
The "isReachable" method has not been worthy of using for me in many cases. You can scroll to the bottom to see my alternative for simply testing if you're online and capable of resolving external hosts (i.e. google.com) ... Which generally seems to work on *NIX machines.
The issue
There is alot of chatter about this :
Here are other, similar questions :
Detect internet Connection using Java
How do I test the availability of the internet in Java?
And even a reported bug on this same matter :
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4921816
Part 1 : A reproducible example of the problem
Note that in this case, it fails.
//also, this fails for an invalid address, like "www.sjdosgoogle.com1234sd"
InetAddress[] addresses = InetAddress.getAllByName("www.google.com");
for (InetAddress address : addresses) {
if (address.isReachable(10000))
{
System.out.println("Connected "+ address);
}
else
{
System.out.println("Failed "+address);
}
}
//output:*Failed www.google.com/74.125.227.114*
Part 2 : A Hackish Workaround
As an alternative, you can do this :
// in case of Linux change the 'n' to 'c'
Process p1 = java.lang.Runtime.getRuntime().exec("ping -n 1 www.google.com");
int returnVal = p1.waitFor();
boolean reachable = (returnVal==0);
The -c option of ping will allow ping to simply try to reach the server once(as opposed to the infinite ping which we're used to using at the terminal).
This will return 0 if the host is reachable. Otherwise, you will get "2" as a return value.
Much simpler - but of course it is platform specific.
And there may be certain privilege caveats to using this command - but I find it works on my machines.
PLEASE Note that :
1) This solution is not production quality. Its a bit of a hack. If google is down, or your internet is temporarily slow, or maybe even if there is some funniness in your privileges/system settings, if could return false negatives (i.e. it could fail even though the input address is reachable).
2) The isReachable failure is an outstanding issue. Again - there are several online resources indicating that there is no "perfect" way of doing this at the time of this writing, due to the way the JVM tries to reach hosts - I guess it is an intrinsically platform specific task which, although simple, hasn't yet been abstracted sufficiently by the JVM.
I came here to get an answer for this same question, but I was unsatisfied by any of the answers because I was looking for a platform independent solution. Here is the code which I wrote and is platform independent, but requires information about any open port on the other machine (which we have most of the time).
private static boolean isReachable(String addr, int openPort, int timeOutMillis) {
// Any Open port on other machine
// openPort = 22 - ssh, 80 or 443 - webserver, 25 - mailserver etc.
try {
try (Socket soc = new Socket()) {
soc.connect(new InetSocketAddress(addr, openPort), timeOutMillis);
}
return true;
} catch (IOException ex) {
return false;
}
}
Update: Based on a recent comment to this answer, here is a succinct version of the above code:
private static boolean isReachable(String addr, int openPort, int timeOutMillis) {
// Any Open port on other machine
// openPort = 22 - ssh, 80 or 443 - webserver, 25 - mailserver etc.
try (Socket soc = new Socket()) {
soc.connect(new InetSocketAddress(addr, openPort), timeOutMillis);
return true;
} catch (IOException ex) {
return false;
}
}
If you only want to check if it is connected to internet use this method , It returns true if internet is connected, Its preferable if you use the address of the site you are trying to connect through the program.
public static boolean isInternetReachable()
{
try {
//make a URL to a known source
URL url = new URL("http://www.google.com");
//open a connection to that source
HttpURLConnection urlConnect = (HttpURLConnection)url.openConnection();
//trying to retrieve data from the source. If there
//is no connection, this line will fail
Object objData = urlConnect.getContent();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
Just mentioning it explicitly since the other answers don't. The ping part of isReachable() requires root access on Unix. And as pointed out by bestsss in 4779367:
And if you ask why ping from bash doesn't, actually it does need as well. Do that ls -l /bin/ping.
Since using root was not an option in my case the solution was to allow access to port 7 in the firewall to the specific server I was interested in.
I am not sure what was the state when the original question was asked back in 2012.
As it stands now, ping will be executed as a root. Through the ping executable's authorization you will see the +s flag, and the process belonging to root, meaning it will run as root. run ls -liat on where the ping is located and you should see it.
So, if you run InetAddress.getByName("www.google.com").isReacheable(5000) as root, it should return true.
you need proper authorizations for the raw socket, which is used by ICMP (the protocol used by ping)
InetAddress.getByName is as reliable as ping, but you need proper permissions on the process to have it running properly.
Since you can ping the computer, your Java process should run with sufficient privileges to perform the check. Probably due to use of ports in the lower range. If you run your java program with sudo/superuser, I'll bet it works.
I would suggest that the ONLY reliable way to test an internet connection is to actually connect AND download a file, OR to parse the output of an OS ping call via exec(). You cannot rely on the exit code for ping and isReachable() is crap.
You cannot rely on a ping exit code as it returns 0 if the ping command executes correctly. Unfortunately, ping executes correctly if it can't reach the target host but gets a "Destination host unreachable" from your home ADSL router. This is kind of a reply that gets treated as a successfull hit, thus exit code = 0. Have to add though that this is on a Windows system. Not checked *nixes.
private boolean isReachable(int nping, int wping, String ipping) throws Exception {
int nReceived = 0;
int nLost = 0;
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("ping -n " + nping + " -w " + wping + " " + ipping);
Scanner scanner = new Scanner(process.getInputStream());
process.waitFor();
ArrayList<String> strings = new ArrayList<>();
String data = "";
//
while (scanner.hasNextLine()) {
String string = scanner.nextLine();
data = data + string + "\n";
strings.add(string);
}
if (data.contains("IP address must be specified.")
|| (data.contains("Ping request could not find host " + ipping + ".")
|| data.contains("Please check the name and try again."))) {
throw new Exception(data);
} else if (nping > strings.size()) {
throw new Exception(data);
}
int index = 2;
for (int i = index; i < nping + index; i++) {
String string = strings.get(i);
if (string.contains("Destination host unreachable.")) {
nLost++;
} else if (string.contains("Request timed out.")) {
nLost++;
} else if (string.contains("bytes") && string.contains("time") && string.contains("TTL")) {
nReceived++;
} else {
}
}
return nReceived > 0;
}
nping is number of try to ping ip(packets), if you have busy network or systems choose biger nping numbers.
wping is time waiting for pong from ip, you can set it 2000ms
for using this method u can write this:
isReachable(5, 2000, "192.168.7.93");
Or using this way:
public static boolean exists(final String host)
{
try
{
InetAddress.getByName(host);
return true;
}
catch (final UnknownHostException exception)
{
exception.printStackTrace();
// Handler
}
return false;
}
InetAddress.isReachable is flappy, and sometimes returns unreachable for addresses which we can ping.
I tried the following:
ping -c 1 <fqdn> and check the exit status.
Works for all the cases i had tried where InetAddress.isReachable doesn't work.
To Check Internet
public boolean isInternetAvailable() {
try {
InetAddress ipAddr = InetAddress.getByName("google.com");
//You can replace it with your name
return !ipAddr.equals("");
} catch (Exception e1) {
try {
Process p1 = java.lang.Runtime.getRuntime().exec("/system/bin/ping -W 1 -c 1 www.google.com");
int returnVal = 0;
returnVal = p1.waitFor();
boolean reachable = (returnVal==0);
return reachable;
} catch (Exception e2) {
e2.printStackTrace();
return false;
}
}
}
To check network connectivity
private boolean isNetworkConnected() {
ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
return cm.getActiveNetworkInfo() != null && cm.getActiveNetworkInfo().isConnected();
}
Because isReachable is using the TCP protocol(by WireShark) The Ping command is using ICMP protocol,if you want to return true you need to open the 7 port

Categories