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");
Related
I have this code which I want to use to check domain registration:
private final static String WHO ="cnn.com";
private final static String WHOIS_HOST = "whois.verisign-grs.com";
private final static int WHOIS_PORT = 43;
public static void main(final String[] args) throws IOException {
SpringApplication.run(TestApplication.class, args);
int c;
Socket socket = null;
String query = WHO + "\r\n";
byte buf[] = query.getBytes();
String regex = ".*Registry Expiry Date:*";
try {
socket = new Socket(WHOIS_HOST, WHOIS_PORT);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
out.write(buf);
out.flush();
StringBuilder text = new StringBuilder();
while ((c = in.read()) != -1) {
System.out.print((char) c);
text.append(c);
}
boolean matches = Pattern.matches(regex, text.toString());
System.out.print("\nDone\n" + matches);
} catch (IOException ex) {
System.out.print(ex.getMessage());
} finally {
if(socket != null){
try {
socket.close();
} catch (IOException ex) {
}
}
}
}
I get this output:
Domain Name: CNN.COM
Registry Domain ID: 3269879_DOMAIN_COM-VRSN
Registrar WHOIS Server: whois.corporatedomains.com
Registrar URL: http://cscdbs.com
Updated Date: 2018-04-10T16:43:38Z
Creation Date: 1993-09-22T04:00:00Z
Registry Expiry Date: 2026-09-21T04:00:00Z
Registrar: CSC Corporate Domains, Inc.
Registrar IANA ID: 299
Registrar Abuse Contact Email: domainabuse#cscglobal.com
Registrar Abuse Contact Phone: 8887802723
Domain Status: clientTransferProhibited https://icann.org/epp#clientTransferProhibited
Domain Status: serverDeleteProhibited https://icann.org/epp#serverDeleteProhibited
Domain Status: serverTransferProhibited https://icann.org/epp#serverTransferProhibited
Domain Status: serverUpdateProhibited https://icann.org/epp#serverUpdateProhibited
Name Server: NS-1086.AWSDNS-07.ORG
Name Server: NS-1630.AWSDNS-11.CO.UK
Name Server: NS-47.AWSDNS-05.COM
Name Server: NS-576.AWSDNS-08.NET
DNSSEC: unsigned
URL of the ICANN Whois Inaccuracy Complaint Form: https://www.icann.org/wicf/
>>> Last update of whois database: 2022-07-29T20:55:54Z <<<
For more information on Whois status codes, please visit https://icann.org/epp
NOTICE: The expiration date displayed in this record is the date the
registrar's sponsorship of the domain name registration in the registry is
currently set to expire. This date does not necessarily reflect the expiration
date of the domain name registrant's agreement with the sponsoring
registrar. Users may consult the sponsoring registrar's Whois database to
view the registrar's reported date of expiration for this registration.
TERMS OF USE: You are not authorized to access or query our Whois
database through the use of electronic processes that are high-volume and
automated except as reasonably necessary to register domain names or
modify existing registrations; the Data in VeriSign Global Registry
Services' ("VeriSign") Whois database is provided by VeriSign for
information purposes only, and to assist persons in obtaining information
about or related to a domain name registration record. VeriSign does not
guarantee its accuracy. By submitting a Whois query, you agree to abide
by the following terms of use: You agree that you may use this Data only
for lawful purposes and that under no circumstances will you use this Data
to: (1) allow, enable, or otherwise support the transmission of mass
unsolicited, commercial advertising or solicitations via e-mail, telephone,
or facsimile; or (2) enable high volume, automated, electronic processes
that apply to VeriSign (or its computer systems). The compilation,
repackaging, dissemination or other use of this Data is expressly
prohibited without the prior written consent of VeriSign. You agree not to
use electronic processes that are automated and high-volume to access or
query the Whois database except as reasonably necessary to register
domain names or modify existing registrations. VeriSign reserves the right
to restrict your access to the Whois database in its sole discretion to ensure
operational stability. VeriSign may restrict or terminate your access to the
Whois database for failure to abide by these terms of use. VeriSign
reserves the right to modify these terms at any time.
The Registry database contains ONLY .COM, .NET, .EDU domains and
Registrars.
Done
false
Do you know how I can get only the line content Registry Expiry Date: 2026-09-21T04:00:00Z?
there is a problem in the while you are appending the byte value to StringBuilder so the matcher doesn't works if you change like this way you can have the value of the regex you need
StringBuilder text = new StringBuilder();
while ((c = in.read()) != -1) {
// System.out.print((char) c);
text.append((char) c);
}
System.out.println(text);
String regex = ".*Registry Expiry Date.*Z";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(text.toString());
if (matcher.find())
{
System.out.println("\nDone\n" + matcher.group(0));
}
in this case the result will be
Done
Registry Expiry Date: 2026-09-21T04:00:00Z
There are a few things to clean up:
c is defined as an int, so when you call text.append(c), you are appending an integer value – to fix that, you can cast "c" to be a character: text.append((char) c)
your code reads the entire response into a StringBuilder, then processes that text afterward, looking for any potential matches – this isn't a huge inefficiency, but it's not necessary either; you could instead inspect the data along the way to see if you've encountered the interesting part of the data and if so, skip processing the rest
Using Pattern and Matcher is ok, but for the case you've presented, it's extra complexity.
Here's a solution that:
Opens the socket and "out" in a try-with-resources block – that way, both will be closed for you automatically (simpler code)
Opens the input stream in a try-with-resources block – again, less code, automatic management of the opened resources
Wraps the input stream in a BufferedReader – this allows you to read the input line by line
In the while loop, instead of using Pattern and Matcher, it simply checks if each line of text contains "Registry Expiry Date"
If a match is found, it prints the match, then breaks from the loop – it isn't necessary to look at any more input data
String WHO = "cnn.com";
String WHOIS_HOST = "whois.verisign-grs.com";
int WHOIS_PORT = 43;
try (Socket socket = new Socket(WHOIS_HOST, WHOIS_PORT)) {
try (OutputStream out = socket.getOutputStream()) {
out.write((WHO + "\r\n").getBytes());
out.flush();
try (BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
String line;
while ((line = input.readLine()) != null) {
if (line.contains("Registry Expiry Date")) {
System.out.println("---> " + line);
break; // don't need to read any more input
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
Here's the output:
---> Registry Expiry Date: 2026-09-21T04:00:00Z
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
I am trying to work through a socket chat with just one client and the server. I have it successfully running, as in the server is capable of passing messages back and forth, but when I attempt to make the server side implementation a bit more complex, adding commands and such, that the client can use, the communication fails. It appears it might go out of sync even as using the same commands over and over again can produce different results, even though I flush everything after every command.
Example of simplistic output, this works as expected, every time:
Client:
import java.io.*;
import java.net.*;
public class Test1Client
{
public static void main(String args[])
{
InputStreamReader convert = new InputStreamReader(System.in);
BufferedReader stdin = new BufferedReader(convert);
try
{
Socket echoClient = new Socket("localhost", 17);
PrintStream outs = new PrintStream(echoClient.getOutputStream());
BufferedReader ins = new BufferedReader(new InputStreamReader(echoClient.getInputStream()));
while(true){
System.out.print("Type whatever you want: ");
String line = stdin.readLine();
outs.println(line);
System.out.println("Server says: " + ins.readLine());
}
}
catch (IOException e)
{
System.out.println(e);
}
}
}
Server:
import java.net.*;
import java.util.ArrayList;
import java.io.*;
public class Test1Server
{
public static void main(String args[])
{
try
{
ServerSocket socket= new ServerSocket(12167);
//Try not to use port number < 2000.
System.out.println("Waiting for a client to connect...");
Socket s = socket.accept();
System.out.println("Client Connected.");
BufferedReader ins = new BufferedReader(new InputStreamReader(s.getInputStream()));
PrintStream outs = new PrintStream(s.getOutputStream());
while (true)
{
String line = ins.readLine();
outs.println(line);
}
}
catch (IOException e)
{
e.getStackTrace();
}
}
}
I get output like this, it works every time just spitting it back out:
Type whatever you want: login
Server says: login
Type whatever you want: login
Server says: login
Type whatever you want: login
Server says: login
Type whatever you want: login
Server says: login
But when I make the server side a bit more complex by replacing its while(true) block with the following, I get a much more messy result:
String line = ins.readLine();
String response = manager.process(line);
outs.println(response);
outs.flush();
process:
msg= "User logged in successfully \n";
return msg;
You'll also notice some commented lines in the process command code. When I give back a simple message the server seems to keep up, but when I use the login function as well it gives the terrible output like this:
Type whatever you want: login ryanne
Server says: ryanne logged in successfully
Type whatever you want: login ryanne
Server says:
Type whatever you want: login ryanne
Server says: You may already be logged in or did not use correct username or password
Type whatever you want: login ryanne
Server says:
Type whatever you want: newuser jeff
Server says: You may already be logged in or did not use correct username or password
Type whatever you want: newuser jeff 12345
Server says:
Type whatever you want: new user jeff 12345
Server says: You may already be logged in or did not use correct username or password
Type whatever you want:
Again, notice the blanks where nothing comes back from the server, and then even the change in the commands does not prompt different responses. Its as if it went out of sync, just by using one additional function?
You have some "\n" at the end of some strings.
If you both put "\n" and use println, you will have double carriage returns, which will mess up your protocol. Remove the "\n"'s, and it should work better.
Maybe, data you sent was not flushed. Use outs.flush(); after outs.println(line); or change it's constructor call to PrintStream(echoClient.getOutputStream(),true); (enable auto-flush on printing new line)
I've created a java application I'm selling for money, and the verification system involves using an unique HWID to ID the computer to see if they've paid. I was wondering if there was a way for a java application to "kill" itself, maybe deleting some of it's own class files, corrupting itself, or overriding itself.
Is there any way?
Make it web based, keep records in the database, make the user log in to use the system. Any dedicated cracker will defeat your system in a matter of time.
If this is a commercial grade app, then I would recommend using a security solution designed by professionals. Security and Cryptography is best left to experts
Layman solution :
Could you execute a getmac (assuming this app runs out of windows) from within your system and do the check.? MAC ids are assumed to be unique for a PC. There are ways to override it but should address 90% of the cases.
Corrupting your app doesn't seem to be a good solution.
public static String getURLSource(String link) {
try {
URL url = new URL(link);
BufferedReader reader = new BufferedReader(new InputStreamReader(url.openStream()));
StringBuilder str = new StringBuilder();
String inputLine;
while ((inputLine = reader.readLine()) != null) {
str.append(inputLine);
}
reader.close();
return str.toString();
} catch (Exception e) {
throw new RuntimeException("Couldn't properly connect to internet.");
}
}
public void main(String[] args) {
if(!getUrlSource("yourlink").contains("a string you want when it's not killswitched")) { //The link must be readable text by java
//Do stuff here
}
}
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