7
votes

Problem

My Windows system has multiple ethernet adapters. Given the name of an ethernet adapter, I need to find its IP addresses.

For example, the output of ipconfig command of my system is:

Ethernet adapter GB1:

   Connection-specific DNS Suffix  . : 
   IP Address. . . . . . . . . . . . : 0.0.0.0
   Subnet Mask . . . . . . . . . . . : 0.0.0.0
   Default Gateway . . . . . . . . . : 

Ethernet adapter SWITCH:

   Connection-specific DNS Suffix  . : 
   IP Address. . . . . . . . . . . . : 10.200.1.11
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   IP Address. . . . . . . . . . . . : 10.200.1.51
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 

Ethernet adapter LAN:

   Connection-specific DNS Suffix  . : 
   IP Address. . . . . . . . . . . . : 10.1.2.62
   Subnet Mask . . . . . . . . . . . : 255.255.254.0
   IP Address. . . . . . . . . . . . : 10.1.2.151
   Subnet Mask . . . . . . . . . . . : 255.255.254.0
   Default Gateway . . . . . . . . . : 10.1.2.1

Note: I need not bother about wireless adapters or any other kind of adapters. I need to do this for ethernet adapters only.

For this system, I need to write a Java class that behaves as shown below:

C:>java NameToIp GB1
0.0.0.0

C:>java NameToIp SWITCH
10.200.1.11
10.200.1.51

C:>java NameToIp LAN
10.1.2.62
10.1.2.151

What doesn't work

Using java.net.NetworkInterface didn't help. It's getName() and getDisplayName() methods don't print the adapter connection names as it appears in the output of ipconfig or in Windows Network Connections. They print the actual device names instead. For example, consider the following code:

import java.util.Enumeration;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;

public class ListInterfaces 
{
    public static void main(String[] args) throws SocketException, UnknownHostException {

        Enumeration<NetworkInterface> nwInterfaces = NetworkInterface.getNetworkInterfaces();

        while (nwInterfaces.hasMoreElements()) {

            NetworkInterface nwInterface = nwInterfaces.nextElement();
            System.out.print(nwInterface.getName() + ": " +
                             nwInterface.getDisplayName());

            Enumeration<InetAddress> addresses = nwInterface.getInetAddresses();
            while (addresses.hasMoreElements()) {
                InetAddress address = addresses.nextElement();
                System.out.print(" - " + address.getHostAddress());
            }
            System.out.println();
        }
    }
}

This prints the following output:

C:>java ListInterfaces
lo: MS TCP Loopback interface - 127.0.0.1
eth0: Broadcom BCM5709C NetXtreme II GigE (NDIS VBD Client) # 
eth1: Broadcom BCM5709C NetXtreme II GigE (NDIS VBD Client) #2 - 10.200.1.11 - 10.200.1.51
eth2: Broadcom BCM5709C NetXtreme II GigE (NDIS VBD Client) #3 - 10.1.2.62 - 10.1.2.151

An ugly hack that works

I have written an ugly hack that extracts the IP addresses of the specified adapter name from the output of ipconfig. Here is the code.

import java.util.ArrayList;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.IOException;

public class NameToIp
{
    public static ArrayList<String> getIP(String adapterName)
    throws IOException, InterruptedException
    {
        // Run the Windows 'ipconfig' command and get its stdout
        ProcessBuilder cmdBuilder = new ProcessBuilder("ipconfig");
        Process process = cmdBuilder.start();
        BufferedReader stdout = new BufferedReader(
                new InputStreamReader(process.getInputStream()));

        // Find the section for the specified adapter
        String line;
        boolean foundAdapter = false;
        while ((line = stdout.readLine()) != null) {
            line = line.trim();
            if (line.equals("Ethernet adapter " + adapterName + ':')) {
                foundAdapter = true;
                break;
            }
        }
        if (!foundAdapter) {
            process.waitFor();
            throw new IOException("Adapter not found");
        }

        // Find IP addresses in the found section
        ArrayList<String> ips = new ArrayList<String>();
        while ((line = stdout.readLine()) != null) {
            // Stop parsing if we reach the beginning of the next
            // adapter section in the output of ifconfig
            if (line.length() > 0 && line.charAt(0) != ' ') {
                break;
            }

            line = line.trim();

            // Extract IP addresses
            if (line.startsWith("IP Address.") ||
                line.startsWith("IPv4 Address.")) {

                int colonIndex;
                if ((colonIndex = line.indexOf(':')) != 1) {
                    ips.add(line.substring(colonIndex + 2));
                }
            }
        }
        process.waitFor();

        return ips;
    }

    public static void main(String[] args)
    throws IOException, InterruptedException
    {
        // Print help message if adapter name has not been specified
        if (args.length != 1) {
            StackTraceElement[] stack = Thread.currentThread().getStackTrace();
            String prog = stack[stack.length - 1].getClassName();

            System.err.println("Usage: java " + prog + " ADAPTERNAME");
            System.err.println("Examples:");
            System.err.println("  java " + prog +" \"Local Area Connection\"");
            System.err.println("  java " + prog +" LAN");
            System.err.println("  java " + prog +" SWITCH");
            System.exit(1);
        }

        ArrayList<String> ips = getIP(args[0]);
        for (String ip: ips) {
            System.out.println(ip);
        }
    } 
}

Question

Is there a better way to solve this problem?

2
It looks like Java is designed at being Unix compatible, Windows NIC names have not been well defined and vary per platform.Steve-o

2 Answers

3
votes

Create a dll that uses the Windows API to query the local ethernet address and use JNI to invoke the dll.

0
votes

I'll answer my own question. Following SpaceTrucker's suggestion, I created a Java class using JNI as follows.

// NwInterface.java
import java.util.ArrayList;

public class NwInterface {    

    public native ArrayList<String> getAddresses(String adapterName);    

    static
    {
        System.loadLibrary("nwinterface");
    }        
}

Then I created the 'nwinterface' library in C++ as follows.

// nwinterface.cc
#include <iostream>
#include <winsock2.h>
#include <iphlpapi.h>
#include "NwInterface.h"

#pragma comment(lib, "iphlpapi.lib")
#pragma comment(lib, "advapi32.lib")

bool GetFriendlyName(const char* adapterName, unsigned char* buffer,
                     unsigned long size)
{
    HKEY hKey;

    char key[1024];
    _snprintf_s(key, sizeof key, _TRUNCATE,
                "SYSTEM\\CurrentControlSet\\Control\\Network\\"
                "{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s\\Connection",
                adapterName);

    long ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_READ, &hKey);
    if (ret != ERROR_SUCCESS) {
        return false;
    }

    ret = RegQueryValueEx(hKey, "Name", 0, 0, buffer, &size);
    if (ret != ERROR_SUCCESS) {
        return false;
    }
    buffer[size - 1] = '\0';

    return true;
}

JNIEXPORT jobject JNICALL Java_NwInterface_getAddresses(JNIEnv *env, jobject obj,
                                                        jstring jAdapterName)
{
    // Create a Java ArrayList object
    jclass arrayClass = env->FindClass("java/util/ArrayList");
    jmethodID initMethod = env->GetMethodID(arrayClass, "<init>", "()V");
    jmethodID addMethod = env->GetMethodID(arrayClass, "add", "(Ljava/lang/Object;)Z");
    jobject ips = env->NewObject(arrayClass, initMethod);

    // Get information about all adapters
    IP_ADAPTER_INFO adapterInfo[128];
    unsigned long bufferSize = sizeof adapterInfo;
    unsigned long ret = GetAdaptersInfo(adapterInfo, &bufferSize);

    // If there is an error, return empty ArrayList object
    if (ret != NO_ERROR) {
        return ips;
    }

    // Iterate through the information of each adapter and select the
    // specified adapter
    for (PIP_ADAPTER_INFO adapter = adapterInfo; adapter != NULL;
         adapter = adapter->Next) {

        char friendlyName[1024];
        ret = GetFriendlyName(adapter->AdapterName,
                              (unsigned char *) friendlyName,
                              sizeof friendlyName);
        if (ret == false) {
            continue;
        }

        const char *adapterName = env->GetStringUTFChars(jAdapterName, 0);
        if (strncmp(friendlyName, adapterName, sizeof friendlyName) == 0) {

            for (PIP_ADDR_STRING addr = &(adapter->IpAddressList); addr != NULL;
                 addr = addr->Next) {

                const char *ip = addr->IpAddress.String;
                env->CallBooleanMethod(ips, addMethod, env->NewStringUTF(ip));
            }
            break;
        }

    }

    return ips;
}

Finally, I tested the Java class by writing this Java program.

// NameToIp2.java
import java.util.ArrayList;

public class NameToIp2 
{
    public static void main(String[] args)
    {
        // Print help message if adapter name has not been specified
        if (args.length != 1) {
            StackTraceElement[] stack = Thread.currentThread().getStackTrace();
            String prog = stack[stack.length - 1].getClassName();

            System.err.println("Usage: java " + prog + " ADAPTERNAME");
            System.err.println("Examples:");
            System.err.println("  java " + prog +" \"Local Area Connection\"");
            System.err.println("  java " + prog +" LAN");
            System.err.println("  java " + prog +" SWITCH");
            System.exit(1);
        }

        // Use NwInterface class to translate 
        NwInterface nwInterface = new NwInterface();
        ArrayList<String> ips = nwInterface.getAddresses(args[0]);
        for (String ip: ips) {
            System.out.println(ip);
        }
    }
}

The steps to compile and run the program are as follows:

javac NameToIp2.java
javah -jni NwInterface
cl /LD /EHsc /I C:\jdk1.5.0_13\include /I C:\jdk1.5.0_13\include\win32 nwinterface.cc

Here is the output:

C:>java NameToIp2 GB1
0.0.0.0

C:>java NameToIp2 SWITCH
10.200.1.11
10.200.1.51

C:>java NameToIp2 LAN
10.1.2.62
10.1.2.151