Fetch unique identifier for a WiFi router - java

Is there any way I can get any unique identifier for a particular wifi router?
I'm trying to write an Android app that needs to know which router it is connected to. I know that android provides a way to get the BSSID of the currently connected network, but to my surprise, this is not unique.
I found out that on dual band routers, two different devices could be getting two different BSSID, even if they are on the same SSID.
Is there any other parameter I can fetch that can uniquely identify the wifi I am connected to? I would love to try some third party library, if that would allow me, since I am quite certain Android does not come packaged with a better method than giving me the BSSID.
Edit: I'm trying to find out who all have set their home wifi (via the app) as the same Wi-Fi as me. Each user sets their 'home Wi-Fi' which gets saved on the server ( the mac address is what I'm saving). Then each user can query who all are on their Wi-Fi, and if they are currently connected on that Wi-Fi or not. The query of 'who is on my wifi' is done by searching for the same MAC address as the one I'm connected to. This fails if my home has a dual band, since they could be connected to the second frequency (and thus second MAC).

You are correct in assuming that getBSSID() will return two different addresses for the two different bands, as they are essentially two different access points, one 2.4GHz and one 5 GHz, simply wrapped up in the same package. I wrote a quick app that gets and displays all of the available fields that may be obtained using the WifiInfo class. When I connected to the 2.4 GHz band of my access point the details were as follows:
Note that you can tell you are connected to the 2.4 GHz band ("Frequency: 2452"). Repeating the process with the 5 GHz band shows the following:
As you can see, the MAC addresses for the two different bands differ by only one number; I do not know if this is the case for all routers. If this is indeed the case, then you can conclusively determine which access point you are connected to by analyzing a combination of the MAC address (BSSID) and the frequency.

Sadly, there is no guaranteed method of uniquely identifying a specific access point. You can come close by using the SSID, MAC, etc. but it wouldn't necessarily work every time and it would still be possible for someone to spoof this if they desired.
If you wish to consider different WiFi interfaces(1) (as opposed to the physical device supporting it) then the MAC address is, by definition, what you need (barring broken devices that wrongly re-use a MAC).
(1) I use the word "interfaces" because it's possible to support multiple networks (i.e. SSIDs) on the same interface. An interface will effectively be a single channel at a single frequency.

Just use MAC address of the AP. (MAC Address is unique)
Here is how:
Can I find the MAC address of my Access point in Android?

For unique identifier, you can use
BSSI+Frequency
Example:-
AA:AA:AA:AA:AA:AA+2432 and AA:AA:AA:AA:AA:AA+5230 are unique in dual band case

You can find the Mac address of your router and use that as your basis for uniquely identifying it.
You can do so by checking your Android devices ARP table and compare the router IP that you are connected to. An example can be shown here:
http://www.flattermann.net/2011/02/android-howto-find-the-hardware-mac-address-of-a-remote-host/
Relevant code extracted:
/**
* Try to extract a hardware MAC address from a given IP address using the
* ARP cache (/proc/net/arp).<br>
* <br>
* We assume that the file has this structure:<br>
* <br>
* IP address HW type Flags HW address Mask Device
* 192.168.18.11 0x1 0x2 00:04:20:06:55:1a * eth0
* 192.168.18.36 0x1 0x2 00:22:43:ab:2a:5b * eth0
*
* #param ip
* #return the MAC from the ARP cache
*/
public static String getMacFromArpCache(String ip) {
if (ip == null)
return null;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("/proc/net/arp"));
String line;
while ((line = br.readLine()) != null) {
String[] splitted = line.split(" +");
if (splitted != null && splitted.length >= 4 && ip.equals(splitted[0])) {
// Basic sanity check
String mac = splitted[3];
if (mac.matches("..:..:..:..:..:..")) {
return mac;
} else {
return null;
}
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}

Related

How to find the IP address of a specific device from a multicast LAN address?

I am trying to program an android app dashboard(latest Java android 5.1 lollipop) that would be able to control my govee lamp which has a LAN control api which connects to a multicast address on my LAN(239.255.255.250) but I need a way to find the IP of the specific lamp so that I can send it commands after user interaction on my app.
Pinged the multicast IP and got no response(operation timed out). Looked around google for a way but couldn't find anything so wondering if someone here could help me out. TY for your time :)
Assuming you know the mac address you can send a reverse ARP packet (Reverse Address Resolution Protocol) to obtain the IP address. ARP itself is normally used to map an IP address to the mac address so the local router can properly forward the packet. RARP does the opposite.
Another way is to interrogate the local ARP cache from within the program on any attached device and see if that has it. So on a windows OS within the program you can initiate an arp -a in the console window to see the current arp cache. The only caveat is that the particular value may have time out and been removed.
Here is an example of the latter.
String mac = "1c-7e-81-9e-48-e8";
try {
Runtime rt = Runtime.getRuntime();
// send broadcast address
rt.exec(new String[]{
"ping","-n 1 -w 200 192.255.255.255"
});
// interrogate arp cache
Process p = rt.exec(new String[]{
"cmd.exe","/c","arp","-a"
});
BufferedReader in = new BufferedReader(
new java.io.InputStreamReader(p.getInputStream()));
String line1;
while ((line1 = in.readLine()) != null) {
if (line1.contains(mac)) {
System.out.println(line1);
}
}
} catch (Exception e) {
e.printStackTrace();
}
prints something like
192.168.1.107 1c-7e-81-9e-48-e8 dynamic
The above may need to be adjusted to fulfill your exact requirements.
Of course the easiest way might be to force the issue and assign a static address of your choosing.

PcapNetworkInterface returns null on given IP

I am trying to capture packages. In my "exploration" example I use the IP of a website that I visit in the browser. I am using PCAP4J to capture package information.
Based on step 3 on https://www.pcap4j.org/ I have the impression that I can simply have an internet address and start listening to it:
InetAddress addr = InetAddress.getByName("192.168.10.100");
PcapNetworkInterface nif = Pcaps.getDevByAddress(addr);
However, when I change this ip to my personal example (185.57.10.32) the nif returns null.
I have printed out a list of PcapNetworkInterfaces as follows:
System.out.println("#### LIST OF DEVS ####");
List<PcapNetworkInterface> devices = Pcaps.findAllDevs();
for (PcapNetworkInterface device : devices) {
System.out.println(device.getName());
}
System.out.println("###############");
Which returns the following:
wlp2s0
any
lo
docker0
enp3s0f1
br-df16c72d2764
bluetooth0
nflog
nfqueue
usbmon1
usbmon2
So in that sense I understand that nif returns null as it is not in the list. However, it makes me not understand why the example given by the author is not workign as I expect.
So I think the first question would be: Can one listen to a specific ip via Pcap4J? In this case an ip of a website. Or are websites not possible and should I make another test case?
Thank you!
An IP addreess you should pass to Pcaps.getDevByAddress() is one a NIF has.
You can capture packets from any IP addresses with the NIF.

Creating a DHCP client list java

I'm trying to write a program that will show a DHCP client list in Java. I want to get the IP addresses, MAC addresses and the host names of all the devices connected to my wifi network.
I have a Belkin router. Its homepage has a 'DHCP client list' option which when clicked shows me this table :
That's exactly what I'm looking for. But I want to show all this data in the form of a list in a Java Swing program. I also want to be able to update this list by pressing a refresh button. Is there any way to achieve this?
It should look something like this :
I've written a basic java program that shows all the IP addresses that are online. Here's the code :
public static void main(String[] args) throws IOException {
InetAddress localhost = InetAddress.getLocalHost();
// this code assumes IPv4 is used
byte[] ip = localhost.getAddress();
for (int i = 1; i <= 254; i++)
{
ip[3] = (byte)i;
InetAddress address = InetAddress.getByAddress(ip);
if (address.isReachable(1000))
{
// machine is turned on and can be pinged
System.out.println(address + "is online");
}
else if (!address.getHostAddress().equals(address.getHostName()))
{
// machine is known in a DNS lookup
System.out.println(address + "is in a DNS lookup");
}
else
{
// the host address and host name are equal, meaning the host name could not be resolved
System.out.println(address + " is not online");
}
}
}
But this doesn't serve the purpose and it's really slow. I want to write a Swing program that'll show me the DHCP client list as seen in the image above.
Any help is appreciated.
I would think about three possible alternatives:
1-The one you implemented that is slow but it can work. You need to find a JAVA API to get the MAC addresses of received messages (I don't know if it exists or not). You can also send ARP messages asking "who has this IP address) and obtain the MAC address from the response. Use some Java interface for pcap library: jNetPcap vs Jpcap , http://jnetpcap.com/
2-Create an app that accesses your router web interface using HTTP and sending the appropriate messages with data as if you were using the UI. In this way you can programatically follow the steps a human would go and get the list that you browser shows, parse it and obtain the data.
3-If the router/access point provides a web API, which I doubt, you can use it.

How can I detect if a network card is not connected with Java without delay?

It there an API (NetworkInterface, InetAddress, etc) in Java with which I can detect that a network card is not connected.
The problem is that addressing some of the network API has a large timeout. But if the network card is not connected to any cable then we can skip it. The problem seems only to occur with Windows 7.
I had this same problem. I ended up using this:
Enumeration<NetworkInterface> eni = NetworkInterface.getNetworkInterfaces();
while(eni.hasMoreElements()) {
Enumeration<InetAddress> eia = eni.nextElement().getInetAddresses();
while(eia.hasMoreElements()) {
InetAddress ia = eia.nextElement();
if (!ia.isAnyLocalAddress() && !ia.isLoopbackAddress() && !ia.isSiteLocalAddress()) {
if (!ia.getHostName().equals(ia.getHostAddress()))
return true;
}
}
}
Works with Java 5+. Also indirectly checks DNS (by comparing names/addresses).
You could use some java system properties to set up the default time out for socket connection using
sun.net.client.defaultConnectTimeout
(all properties here)
But it seems to me that checking wether a specific Medium Access Controller is present or not is the job of the underlying OS. I can't tell you much about windows 7, I got a good OS since 12 years now (linux).
Regards,
Stéphane

Cannot get hostname from getHostName

I am trying to get hostname/computer name using this method. Unfortunately i only can get localhost but not other computer.
private String getHostName(String _strIP) {
try {
InetAddress inetAddress = InetAddress.getByName(_strIP);
System.out.println("getHostAddress : " + inetAddress.getHostAddress());
System.out.println("getHostName : " + inetAddress.getHostName());
System.out.println("getCanonicalHostName : " + inetAddress.getCanonicalHostName());
return inetAddress.getHostName();
} catch (UnknownHostException e) {
e.printStackTrace();
}
return strDefaultHostName;
}
the result (not localhost)
getHostAddress : 192.168.2.139
getHostName : 192.168.2.139
getCanonicalHostName : 192.168.2.139
the result (localhost)
getHostAddress : 127.0.0.1
getHostName : localhost
getCanonicalHostName : localhost
Thank you
We've established roughly what the problem is in tangens' answer.
I think you can fix the problem pretty simply by putting host names into your hosts file.
%SystemRoot%\system32\drivers\etc\hosts
is the file you're looking for; localhost is defined here. You want to put a name and address line in it for every host you want to resolve.
I've never tried this. If it doesn't work, you get your money back.
Update
The above is the "quick hack" solution. This essentially entails that whenever someone manually changes the IP address of a host you're interested in, someone must at the same time change the hosts files on any machines that want to access those hosts.
The other alternative is to operate your own DNS server. You still need to update IP addresses when a host's address changes, but you only need to do so in one place, and you get both forward and reverse name resolution throughout your network. This takes more setting up but is easier to maintain in the long run.
Here is a very useful reference: http://www.dns.net/dnsrd/servers/windows.html
They mention that the "built in" Microsoft DNS server is a terrible solution (up until the one in Windows 2003 Server) but mention at least two alternatives, one commercial and one free. BIND is what is currently holding much of the Internet together, DNS-wise, and it's great that they have a Windows port too.
Looking at the source for InetAddress.getHostName() (Sun JDK8)...
The method performs the following logic:
Loops through the available sun.net.spi.nameservice.NameService's
Performs a reverse DNS lookup - e.g. 192.168.0.23 -> frodo.baggins.com.au
*Checks with the java.lang.SecurityManager, to see if "we have permission to connect" to hostname
*Performs a forward DNS lookup on the hostname, to prevent spoofing - e.g. frodo.baggins.com.au -> 192.168.0.99
If forward lookup result matches the original address (e.g. 192.168.0.23 == 192.168.0.99?), return hostname, otherwise return getHostAddress()
*If step 3 or 4 throws a SecurityException/UnknownHostException, return getHostAddress()
For me, step #2 successfully resolved the hostname, but failed at step #4 with an UnknownHostException.
TLDR; you must fulfill ALL of the following requirements:
the SecurityManager must provide permission to access the host
you must be able to forward AND reverse DNS lookup your InetAddress
the forward lookup details MUST match the reverse lookup details
Only then will Java give you the hostname.
OR, you could bypass these steps with the following method, and just get the hostname.
#SuppressWarnings("unchecked")
public static String getHostName(InetAddress addr) {
String host = null;
List<NameService> nameServicesImpl = new ArrayList<>();
try {
// do naughty things...
Field nameServices = InetAddress.class.getDeclaredField("nameServices");
nameServices.setAccessible(true);
nameServicesImpl = (List<NameService>) nameServices.get(null);
} catch (Throwable t) {
throw new RuntimeException("Got caught doing naughty things.", t);
}
for (NameService nameService : nameServicesImpl) {
try {
// lookup the hostname...
host = nameService.getHostByAddr(addr.getAddress());
} catch (Throwable t) {
// NOOP: problem getting hostname from this name service, continue looping...
}
}
return host != null ? host : addr.getHostAddress();
}
Your DNS is broken. Then IP-numbers are returned instead.
The javadoc of InetAddress.getCanonicalHostName() says:
Gets the fully qualified domain name for this IP address. Best effort method, meaning we may not be able to return the FQDN depending on the underlying system configuration.
If there is a security manager, this method first calls its checkConnect method with the hostname and -1 as its arguments to see if the calling code is allowed to know the hostname for this IP address, i.e., to connect to the host. If the operation is not allowed, it will return the textual representation of the IP address.
I looks like your system configuration isn't correct. Are you running from within an applet?
Reply Feedback for Carl Smotricz
Great answer, but we still don't know if the host name has been updated or not...
This is something like we hardcode.
Anyway thank you so much
# Copyright (c) 1993-1999 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
127.0.0.1 localhost
192.168.2.139 dev-testing
The problem can be caused by multiple reasons.
Reason 1: the IP address doesn't have a hostname
This is probably the most common reason, and has nothing to do with security managers.
If an IP address doesn't resolve to a hostname, because there is no hostname, then you would expect getHostName() to return null or throw a UnknownHostException, but this doesn't happen. Instead getHostName() simply returns the IP address as a string back again. For reasons unknown to me, this common situation is undocumented.
So if the IP address is the same as the result returned by getHostName(), then the hostname doesn't exist.
Detailed explanation
The following JDK code is the cause of this undocumented problem:
https://github.com/openjdk/jdk/blob/jdk-17+35/src/java.base/share/classes/java/net/InetAddress.java#L697
public class InetAddress implements java.io.Serializable {
private static String getHostFromNameService(InetAddress addr, boolean check) {
String host = null;
try {
// first lookup the hostname
host = nameService.getHostByAddr(addr.getAddress());
/* check to see if calling code is allowed to know
* the hostname for this IP address, ie, connect to the host
*/
if (check) {
#SuppressWarnings("removal")
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkConnect(host, -1);
}
}
/* now get all the IP addresses for this hostname,
* and make sure one of them matches the original IP
* address. We do this to try and prevent spoofing.
*/
InetAddress[] arr = InetAddress.getAllByName0(host, check);
boolean ok = false;
if(arr != null) {
for(int i = 0; !ok && i < arr.length; i++) {
ok = addr.equals(arr[i]);
}
}
//XXX: if it looks a spoof just return the address?
if (!ok) {
host = addr.getHostAddress();
return host;
}
} catch (SecurityException e) {
host = addr.getHostAddress();
} catch (UnknownHostException e) {
host = addr.getHostAddress();
// let next provider resolve the hostname
}
return host;
}
}
So what happens is that the IP-address is passed to NameService.getHostByAddr() (NameService is a private interface), which has this (private) documentation in the source code:
Lookup the host corresponding to the IP address provided
#param addr byte array representing an IP address
#return {#code String} representing the host name mapping
#throws UnknownHostException if no host found for the specified IP address
So NameService.getHostByAddr() throws an UnknownHostException if the IP doesn't have a hostname, but InetAddress.getHostFromNameService() swallows this exception and instead, it returns the provided IP-address itself!!! IMO it should have let the exception be thrown instead of swallowing it, because swallowing it makes it more difficult for the client to determine whether a hostname exists.
You can check if the IP address has a hostname by using the nslookup commandline tool: nslookup 192.168.2.139. If it returns something like:
** server can't find 139.2.168.192.in-addr.arpa: NXDOMAIN (Linux) or *** can't find 192.168.2.139: Non-existent domain (Windows) then there is no hostname.
Reason 2: a security manager is applied
By default, Java doesn't have a security manager enabled. In that case, this reason doesn't apply.
A security manager is an object that defines a security policy for an application. If you have a security manager and want to find out if it is the cause of your problem, then you should check whether it is allowing you to open a socket to the resolved hostname (if any). To do so, first use nslookup 192.168.2.139 and verify if a hostname is resolved. If no hostname is resolved, then your problem is caused by "Reason 1". If it does resolve to a hostname, for example myhostname, then try this:
SecurityManager sec = System.getSecurityManager();
if (sec != null) {
sec.checkConnect("myhostname", -1);
}
If checkConnect() throws a SecurityException, then a SecurityManager is active and is causing the problem. So then you could look into how you can configure your securityManager to solve the problem.

Categories