How can I retrieve IP address from HTTP header in Java [duplicate] - java

This question already has answers here:
How do I get the remote address of a client in servlet?
(11 answers)
Closed 7 years ago.
I am curious if there is any library that handles already this kind of stuff, or I have to do it by myself once again. So, the thing is I want to get IP address field from the visitors HTTP header request on my server, and do the whole thing in Java? Any help would be nice.
Thanks in advance.

Use the getHeader(String Name) method of the javax.servlet.http.HttpServletRequest object to retrieve the value of Remote_Addr variable. Here is the sample code:
String ipAddress = request.getHeader("Remote_Addr");
If this code returns empty string, then use this way:
String ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
if (ipAddress == null) {
ipAddress = request.getRemoteAddr();
}

Even though there's an accepted answer that has been highly upvoted I'd like to suggest an alternative and point out shortcomings of the accepted answer.
request.getHeader("Remote_Addr") is specified to return exactly the same as request.getRemoteAddr(). Hence, it makes no sense to check both. Also note that getRemoteAddr is a method of javax.servlet.ServletRequest (i.e. HTTP-agnostic) while getHeader is in javax.servlet.http.HttpServletRequest.
Furthermore, some proxies use Client-IP rather than X-Forwarded-For. For a discussion see https://stackoverflow.com/a/7446010/131929.
I don't know how reliable the use of HTTP_X_FORWARDED_FOR over X-Forwarded-For is. In Java I'd rather use the direct, short form. For a discussion see https://stackoverflow.com/a/3834169/131929. Upper/lower case makes no difference because getHeader is specified to be case insensitive.
Java alternative
public final class ClientIpAddress {
// CHECKSTYLE:OFF
// https://stackoverflow.com/a/11327345/131929
private static Pattern PRIVATE_ADDRESS_PATTERN = Pattern.compile(
"(^127\\.)|(^192\\.168\\.)|(^10\\.)|(^172\\.1[6-9]\\.)|(^172\\.2[0-9]\\.)|(^172\\.3[0-1]\\.)|(^::1$)|(^[fF][cCdD])",
Pattern.CANON_EQ);
// CHECKSTYLE:ON
private ClientIpAddress() {
}
/**
* Extracts the "real" client IP address from the request. It analyzes request headers
* {#code REMOTE_ADDR}, {#code X-Forwarded-For} as well as {#code Client-IP}. Optionally
* private/local addresses can be filtered in which case an empty string is returned.
*
* #param request HTTP request
* #param filterPrivateAddresses true if private/local addresses (see
* https://en.wikipedia.org/wiki/Private_network#Private_IPv4_address_spaces and
* https://en.wikipedia.org/wiki/Unique_local_address) should be filtered i.e. omitted
* #return IP address or empty string
*/
public static String getFrom(HttpServletRequest request, boolean filterPrivateAddresses) {
String ip = request.getRemoteAddr();
String headerClientIp = request.getHeader("Client-IP");
String headerXForwardedFor = request.getHeader("X-Forwarded-For");
if (StringUtils.isEmpty(ip) && StringUtils.isNotEmpty(headerClientIp)) {
ip = headerClientIp;
} else if (StringUtils.isNotEmpty(headerXForwardedFor)) {
ip = headerXForwardedFor;
}
if (filterPrivateAddresses && isPrivateOrLocalAddress(ip)) {
return StringUtils.EMPTY;
} else {
return ip;
}
}
private static boolean isPrivateOrLocalAddress(String address) {
Matcher regexMatcher = PRIVATE_ADDRESS_PATTERN.matcher(address);
return regexMatcher.matches();
}
}
PHP alternative
function getIp()
{
$ip = $_SERVER['REMOTE_ADDR'];
if (empty($ip) && !empty($_SERVER['HTTP_CLIENT_IP'])) {
$ip = $_SERVER['HTTP_CLIENT_IP'];
} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// omit private IP addresses which a proxy forwarded
$tmpIp = $_SERVER['HTTP_X_FORWARDED_FOR'];
$tmpIp = filter_var(
$tmpIp,
FILTER_VALIDATE_IP,
FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
);
if ($tmpIp != false) {
$ip = $tmpIp;
}
}
return $ip;
}

Related

java.lang.IllegalArgumentException: hostname cannot be null when trying to obtain AWS ECR Authentication Token

I am trying to obtain ECR authorisation token from AWS using aws-java-sdk version 1.11.339. I have created a class to do so as below:
public class ECRTokenGetter {
private static final Logger LOGGER = LoggerFactory.getLogger(ECRTokenGetter.class);
private static final int SINGLE_ELEMENT_INDEX = 0;
public static String getEcrAuthorisationToken(Credentials creds, String awsRegion, String registryId) {
LOGGER.debug("Obtaining temporary authorisation credentials from AWS ECR");
AmazonECR ecrClient = AmazonECRClientBuilder.standard()
.withRegion(awsRegion)
.withCredentials(new AWSStaticCredentialsProvider(new BasicSessionCredentials(
creds.getAccessKeyId(),
creds.getSecretAccessKey(),
creds.getSessionToken())))
.build();
return ecrClient.getAuthorizationToken(new GetAuthorizationTokenRequest()
.withRegistryIds(registryId))
.getAuthorizationData().get(SINGLE_ELEMENT_INDEX)
.getAuthorizationToken();
}
}
Unfortunately, when executing the code, I am getting following exception:
Exception in thread "main" java.lang.IllegalArgumentException: hostname cannot be null
at com.amazonaws.util.AwsHostNameUtils.parseRegion(AwsHostNameUtils.java:79)
at com.amazonaws.util.AwsHostNameUtils.parseRegionName(AwsHostNameUtils.java:59)
at com.amazonaws.auth.internal.AWS4SignerRequestParams.resolveRegion(AWS4SignerRequestParams.java:121)
at com.amazonaws.auth.internal.AWS4SignerRequestParams.<init>(AWS4SignerRequestParams.java:103)
at com.amazonaws.auth.AWS4Signer.sign(AWS4Signer.java:225)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1212)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1058)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:743)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:717)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:699)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:667)
at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:649)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:513)
at com.amazonaws.services.ecr.AmazonECRClient.doInvoke(AmazonECRClient.java:1742)
at com.amazonaws.services.ecr.AmazonECRClient.invoke(AmazonECRClient.java:1718)
at com.amazonaws.services.ecr.AmazonECRClient.executeGetAuthorizationToken(AmazonECRClient.java:992)
at com.amazonaws.services.ecr.AmazonECRClient.getAuthorizationToken(AmazonECRClient.java:966)
at com.mycode.utils.ecr.aws.ECRTokenGetter.getEcrAuthorisationToken(ECRTokenGetter.java:27)
at com.mycode.utils.ecr.AWSAuthTester.main(AWSAuthTester.java:32)
I can also observe the request object created by AWS SDK has a NULL host in the entrypoint, as can be seen in the picture below.
DefaultRequest object
How can I overcome this issue?
It appears that the issue lied down in the String passed to the awsRegion parameter. In the following code
AmazonECR ecrClient = AmazonECRClientBuilder.standard()
.withRegion(awsRegion)
...
if awsRegion is a String, it should use dashes as separators, e.g. "us-west-2",
otherwise it should use Regions enum, e.g. Regions.US_WEST_2.
Alternative solution involves using withEndpointConfiguration instead of withRegion, e.g.
String ecrEndpoint = "ecr.%s.amazonaws.com";
AmazonECR ecrClient = AmazonECRClientBuilder.standard()
.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(String.format(ecrEndpoint, awsRegion), awsRegion))
...
Values of a enum Regions should be converted to Strings with its method getName().
Otherwise enum's method name() or toString() returns a value as a text (with capital letters and underscores):
//Wrong conversion to a String
System.out.println(Regions.US_EAST_1); // result: US_EAST_1
System.out.println(String.format("%s",Regions.US_EAST_1)); // result: US_EAST_1
System.out.println(Regions.US_EAST_1.toString()); // result: US_EAST_1
System.out.println(Regions.US_EAST_1.name()); // result: US_EAST_1
//Correct conversion to a String
System.out.println(Regions.US_EAST_1.getName()); // result: us-east-1
As a result - the URL, defined for the request, is invalid:
I've just encountered the same issue.
The problem is that the URL has underscores (_).
In the class AmazonWebServiceClient there is a method called computeSignerByURI, that in the version I am using, this is the code:
private Signer computeSignerByURI(URI uri, String signerRegionOverride, boolean isRegionIdAsSignerParam) {
if (uri == null) {
throw new IllegalArgumentException("Endpoint is not set. Use setEndpoint to set an endpoint before performing any request.");
}
String service = getServiceNameIntern();
String region = AwsHostNameUtils.parseRegionName(uri.getHost(), getEndpointPrefix());
return computeSignerByServiceRegion(service, region, signerRegionOverride, isRegionIdAsSignerParam);
}
As you can see, there is a call to the .getHost() method in URI, that, when it's computed, it does not accept underscores, so, it returns null. Here you can check that.

Getting interface name/address from (or mapping NetworkInterface to) jpcap device paths

I am trying to do the following:
Display a list of human-readable network interface names and their IP addresses to the user.
Start a jpcap packet capture on the interface the user selects.
However, the following points are giving me trouble:
jpcap only provides PacketCapture.lookupDevices(), which returns a list of Windows' NPF driver device paths to the interfaces (e.g. \Device\NPF_{39966C4C-3728-4368-AE92-1D36ACAF6634}) and a rather bland display string (e.g. Microsoft), and no other info. So I cannot use it to construct the UI interface list.
NetworkInterface.getNetworkInterfaces() provides a list of interfaces on the system with all the info I need for the UI, but NetworkInterface does not provide the NDF driver device path, only display names, and device names such as "net5", "lo", etc.
jpcap's PacketCapture#open() only accepts device paths.
The list of NetworkInterfaces that are both up and not loopback do correspond to the list of devices returned by jpcap, although they are not in the same order.
So, I can't find anything in NetworkInterface that can be passed to PacketCapture#open(), and I don't know how to get UI-appropriate info from the device paths returned by PacketCapture#lookupDevices(). PacketCapture does not accept NetworkInterface#getName(). Therefore, I'm stuck.
I have not tried this on Linux. I suspect the problem is unique to Windows, where NetworkInterface#getName() does not correspond to the device paths recognized by PacketCapture#open().
How can I get the information that jpcap needs to open the device from a NetworkInterface (or the other way around - get a NetworkInterface given a device path), or is there another approach that will allow me to just get a nice display name and IP address for each device directly from jpcap?
Windows' Registry: I've been doing some digging and have at least found information about NPF devices in the registry. Given a jpcap device path, and using either one of the techniques here or a native library, a nice adapter name (equivalent to the ones NetworkInterface returns) and the current IP address can be obtained from the registry as follows:
Extract GUID from path (e.g. {39966C4C-3728-4368-AE92-1D36ACAF6634} from above example). Leave the curly braces and call this .
HKLM\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\<guid> contains current IP address for device as well as some other configuration info.
HKLM\SYSTEM\CurrentControlSet\services\<guid>\Parameters\Tcpip contains similar information.
Search all subkeys of subkeys in HKLM\SYSTEM\CurrentControlSet\Control\Class\. If a subkey is found that contains a key NetCfgInstanceId whose value is <guid>, then the rest of the keys there will contain driver info - the nice display name, vendor info, etc.
I do not know how IPv6 factors into the above (there are a few registry areas with a separate Tcpip6 block of info). I also do not know if these keys are the same outside of Windows 7, but I suspect they are. I will convert the above to an answer, with example code, if no better answers are presented. I am still looking for a more direct (ideally platform-independent and registry-free) way.
Indirect Solution w/ Windows Registry
I have at least found information about NPF devices in the registry, and am expanding the last bit of my question to an answer.
Method
Given a jpcap device path, a nice adapter name (equivalent to the ones NetworkInterface returns) and the current IP address can be obtained from the registry as follows:
Extract GUID from path (e.g. 39966C4C-3728-4368-AE92-1D36ACAF6634 from above example).
HKLM\SYSTEM\CurrentControlSet\services\Tcpip\Parameters\Interfaces\{<guid>} contains current IP address for device as well as some other configuration info.
Search all subkeys of subkeys in HKLM\SYSTEM\CurrentControlSet\Control\Class\. If a subkey is found that contains a key NetCfgInstanceId whose value is {<guid>}, then the rest of the keys there will contain driver info - the nice display name, vendor info, etc.
Implementation
Prerequisites:
The code below requires WinRegistry from https://stackoverflow.com/a/6163701/616460. You may copy and paste it from there.
No native libraries are required.
Issues:
java.util.prefs.WindowsPreferences (and therefore WinRegistry) can only read string keys, not integers. Therefore code below cannot reliably determine if DHCP is enabled. As a hack, logic used is to check static IP/mask and, if blank, fall back on DHCP IP/mask (values are stored separately in registry).
IP address are REG_MULTI_SZ, presumably to account for IPv6 addressing as well (verify?). Code below is simple and does not account for that. I have not tested IPv6 + IPv4.
I have not tested on any other version of Windows besides Windows 7 (Windows 8, somebody verify?).
Tested against the device strings returned by jpcap 0.01.16.
Linux / OSX implementations are left as an exercise to the reader.
Code
Code is below. Full code, including WinRegistry (not present below), is also available on github. Usage is free under SO's CC attribution-sharealike license.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Gets information about network interface given a jpcap device string, on Windows. Makes
* use of WinRegistry class from https://stackoverflow.com/a/6163701/616460. This is tested
* against jpcap 0.01.16, which is available for download at http://sourceforge.net/projects/jpcap/.
*
* All getters return empty strings rather than null if the information is unavailable.
*
* #author https://stackoverflow.com/users/616460/jason-c
*/
public class NetworkDeviceInfo {
private static final int DRIVER_CLASS_ROOT = WinRegistry.HKEY_LOCAL_MACHINE;
private static final String DRIVER_CLASS_PATH = "SYSTEM\\CurrentControlSet\\Control\\Class";
private static final String NETCFG_INSTANCE_KEY = "NetCfgInstanceId";
private static final int IFACE_ROOT = WinRegistry.HKEY_LOCAL_MACHINE;
private static final String IFACE_PATH = "SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces";
private final String jpcapDeviceName;
private final String jpcapDisplayName;
private final String guid;
private final String driverName;
private final String driverVendor;
private final String interfaceAddress;
private final String interfaceSubnetMask;
/**
* Construct from a jpcap device string.
* #param jpcapDeviceString Device string from jpcap.
* #throws IllegalArgumentException If the device string could not be parsed.
* #throws UnsupportedOperationException If the Windows registry could not be read.
*/
public NetworkDeviceInfo (String jpcapDeviceString) throws IllegalArgumentException, UnsupportedOperationException {
// extract jpcap device and display name, and guid, from jpcap device string
String[] jpcapParts = jpcapDeviceString.split("\n", 2);
jpcapDeviceName = (jpcapParts.length > 0) ? jpcapParts[0].trim() : "";
jpcapDisplayName = (jpcapParts.length > 1) ? jpcapParts[1].replaceAll("\n", " ").trim() : "";
Matcher matcher = Pattern.compile("\\{(\\S*)\\}").matcher(jpcapDeviceName);
guid = matcher.find() ? matcher.group(1) : null;
if (guid == null)
throw new IllegalArgumentException("Could not parse GUID from jpcap device name '" + jpcapDeviceName + "'");
try {
// search registry for driver details:
// Search all subkeys of subkeys in HKLM\SYSTEM\CurrentControlSet\Control\Class\. If a subkey
// is found that contains a key NetCfgInstanceId whose value is {guid}, then the rest of the keys
// there will contain driver info - the nice display name, vendor info, etc.
String theDriverName = "";
String theDriverVendor = "";
for (String driverClassSubkey : WinRegistry.readStringSubKeys(DRIVER_CLASS_ROOT, DRIVER_CLASS_PATH)) {
for (String driverSubkey : WinRegistry.readStringSubKeys(DRIVER_CLASS_ROOT, DRIVER_CLASS_PATH + "\\" + driverClassSubkey)) {
String path = DRIVER_CLASS_PATH + "\\" + driverClassSubkey + "\\" + driverSubkey;
String netCfgInstanceId = WinRegistry.readString(DRIVER_CLASS_ROOT, path, NETCFG_INSTANCE_KEY);
if (netCfgInstanceId != null && netCfgInstanceId.equalsIgnoreCase("{" + guid + "}")) {
theDriverName = trimOrDefault(WinRegistry.readString(DRIVER_CLASS_ROOT, path, "DriverDesc"), "");
theDriverVendor = trimOrDefault(WinRegistry.readString(DRIVER_CLASS_ROOT, path, "ProviderName"), "");
// other interesting keys: DriverVersion, DriverDate
break;
}
}
if (!theDriverName.isEmpty())
break;
}
driverName = trimOrDefault(theDriverName, jpcapDisplayName);
driverVendor = trimOrDefault(theDriverVendor, "Unknown");
// read tcp/ip configuration details (HKLM\SYSTEM\CCS\services\Tcpip\Parameters\Interfaces\{guid})
// there is an integer key EnableDHCP, but java.util.prefs.WindowsPreferences (and therefore
// WinRegistry) supports reading string keys only, therefore we'll have to hack it to decide on
// DHCP vs. static IP address and hope it's correct.
// also note the ip addresses are REG_MULTI_SZ, presumably to also hold ipv6 addresses. the results
// here may not be quite correct, then. that's why I'm leaving addresses as strings instead of
// converting them to InetAddresses.
String ifPath = IFACE_PATH + "\\{" + guid + "}";
String dhcpIp = trimOrDefault(WinRegistry.readString(IFACE_ROOT, ifPath, "DhcpIPAddress"), "");
String dhcpMask = trimOrDefault(WinRegistry.readString(IFACE_ROOT, ifPath, "DhcpSubnetMask"), "");
// if static set, use it, otherwise use dhcp
interfaceAddress = trimOrDefault(WinRegistry.readString(IFACE_ROOT, ifPath, "IPAddress"), dhcpIp);
interfaceSubnetMask = trimOrDefault(WinRegistry.readString(IFACE_ROOT, ifPath, "SubnetMask"), dhcpMask);
} catch (Exception x) {
throw new UnsupportedOperationException("Information could not be read from the Windows registry.", x);
}
}
/**
* #param str A string.
* #param def A default string.
* #return Returns def if str is null or empty (after trim), otherwise returns str, trimmed.
*/
private final static String trimOrDefault (String str, String def) {
str = (str == null) ? "" : str.trim();
return str.isEmpty() ? def : str;
}
/**
* Gets the jpcap device name, which can be passed to PacketCapture.
* #return Device name from jpcap. Pass this string to PacketCapture to specify this device.
*/
public final String getJpcapDeviceName () {
return jpcapDeviceName;
}
/**
* Gets the jpcap display name. Usually this is pretty bland.
* #return Display name from jpcap.
*/
public final String getJpcapDisplayName () {
return jpcapDisplayName;
}
/**
* Gets the interface GUID.
* #return Interface GUID.
*/
public final String getGuid () {
return guid;
}
/**
* Get a nice display name for the interface driver. Display this in GUIs.
* #return Interface driver name.
*/
public final String getDriverName () {
return driverName;
}
/**
* Get the interface driver vendor name. Could be displayed in GUIs.
* #return Interface driver vendor name.
*/
public final String getDriverVendor () {
return driverVendor;
}
/**
* Get the interface's IP address.
* #return Interface's IP address.
* #bug This may not be correct for interfaces with multiple IP addresses. For this reason, it is
* left as a raw string rather than being converted to an InetAddress.
*/
public final String getInterfaceAddress () {
return interfaceAddress;
}
/**
* Get the interface's subnet mask.
* #return Interface's subnet mask.
* #bug Same issue as getInterfaceAddress().
*/
public final String getInterfaceSubnetMask () {
return interfaceSubnetMask;
}
/**
* Get a display string, for debugging.
* #return Display string, for debugging.
*/
#Override public String toString () {
return String.format("%s (%s) {%s} # %s/%s", driverName, driverVendor, guid, interfaceAddress, interfaceSubnetMask);
}
}
Example
Here is an example:
import java.util.ArrayList;
import java.util.List;
import net.sourceforge.jpcap.capture.PacketCapture;
public class NetworkDeviceInfoTest {
public static void main (String[] args) throws Exception {
List<NetworkDeviceInfo> infos = new ArrayList<NetworkDeviceInfo>();
// Info can be queried from jpcap device string.
for (String jpcapDevice : PacketCapture.lookupDevices())
infos.add(new NetworkDeviceInfo(jpcapDevice));
// Info can be displayed.
for (NetworkDeviceInfo info : infos) {
System.out.println(info.getJpcapDeviceName() + ":");
System.out.println(" Description: " + info.getDriverName());
System.out.println(" Vendor: " + info.getDriverVendor());
System.out.println(" Address: " + info.getInterfaceAddress());
System.out.println(" Subnet Mask: " + info.getInterfaceSubnetMask());
System.out.println(" jpcap Display: " + info.getJpcapDisplayName());
System.out.println(" GUID: " + info.getGuid());
}
// Device names from NetworkDeviceInfo can be passed directly to jpcap:
NetworkDeviceInfo selected = infos.get(0);
PacketCapture capture = new PacketCapture();
capture.open(selected.getJpcapDeviceName(), true);
}
}
On my machine that outputs:
PacketCapture: loading native library jpcap.. ok
\Device\NPF_{691D289D-7EE5-4BD8-B5C1-3C4729A852D5}:
Description: Microsoft Virtual WiFi Miniport Adapter
Vendor: Microsoft
Address: 0.0.0.0
Subnet Mask: 255.0.0.0
jpcap Display: Microsoft
GUID: 691D289D-7EE5-4BD8-B5C1-3C4729A852D5
\Device\NPF_{39966C4C-3728-4368-AE92-1D36ACAF6634}:
Description: 1x1 11b/g/n Wireless LAN PCI Express Half Mini Card Adapter
Vendor: Realtek Semiconductor Corp.
Address: 192.168.1.23
Subnet Mask: 255.255.255.0
jpcap Display: Microsoft
GUID: 39966C4C-3728-4368-AE92-1D36ACAF6634
Hopefully this is helpful. Improvements are welcome. A better suggestion of a more direct way without using the registry is also welcome.
Platform-Independent, NetworkInterface
Here is an alternate solution that should be platform independent although only provides info for interfaces that are up. The registry solution was my first attempt, it works well, but I believe this is a better solution as long as information about down interfaces is not required.
Method
PacketCapture can provide a network address and subnet mask given a device string (it's an instance method, not a static method, though). For each device string in PacketCapture.lookupDevices():
Get it's network address and mask from a PacketCapture instance (capture does not need to be open).
Search through all network interfaces returned by NetworkInterface.getNetworkInterfaces() and find one that has an address that is on the same network given by the network address and mask that jpcap returned for the device.
That NetworkInterface (probably) corresponds to the device string.
Implementation
Prerequisites:
No dependencies other than jpcap. Tested with version 0.01.16.
Issues:
While platform-independent, unlike the registry-based solution this can only find interfaces that are up.
Byte ordering is weird. I can't make much sense of the jpcap discussion forum on SourceForge but somebody did seem to point it out. Therefore I suppose it's always subject to change in the future.
There are probably a lot of edge cases that will cause this to return incorrect results that I have not tested for.
Code
Code is below. Usage is free under SO's CC attribution-sharealike license. It's self-contained so I did not put it on github.
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import net.sourceforge.jpcap.capture.CaptureDeviceLookupException;
import net.sourceforge.jpcap.capture.PacketCapture;
public class JpcapInterfaceInfo {
/**
* Get a list of interface information for all devices returned by jpcap.
* #param capture An instance of PacketCapture to use for getting network address and mask info. If null,
* a new instance will be created.
* #return List of information.
* #throws CaptureDeviceLookupException
*/
public static List<InterfaceInfo> listInterfaces (PacketCapture capture) throws CaptureDeviceLookupException {
if (capture == null)
capture = new PacketCapture();
List<InterfaceInfo> infos = new ArrayList<InterfaceInfo>();
for (String device : PacketCapture.lookupDevices())
infos.add(getInterfaceInfo(capture, device));
return infos;
}
/**
* Get a list of interface information for all devices returned by jpcap.
* #return List of information.
* #throws CaptureDeviceLookupException
*/
public static List<InterfaceInfo> listInterfaces () throws CaptureDeviceLookupException {
return listInterfaces(null);
}
/**
* Utility to check if an interface address matches a jpcap network address and mask.
* #param address An InetAddress to check.
* #param jpcapAddr Network address.
* #param jpcapMask Network mask.
* #return True if address is an IPv4 address on the network given by jpcapAddr/jpcapMask,
* false otherwise.
*/
private static boolean networkMatches (InetAddress address, int jpcapAddr, int jpcapMask) {
if (!(address instanceof Inet4Address))
return false;
byte[] address4 = address.getAddress();
if (address4.length != 4)
return false;
int addr = ByteBuffer.wrap(address4).order(ByteOrder.LITTLE_ENDIAN).getInt();
return ((addr & jpcapMask) == jpcapAddr);
}
/**
* Get an InterfaceInfo that corresponds to the given jpcap device string. The interface must be
* up in order to query info about it; if it is not then the NetworkInterface in the returned
* InterfaceInfo will be null.
* #param capture A PacketCapture instance used to get network address and mask info.
* #param jpcapDeviceString String from PacketCapture.lookupDevices().
* #return InterfaceInfo.
*/
public static InterfaceInfo getInterfaceInfo (PacketCapture capture, String jpcapDeviceString) {
InterfaceInfo info = null;
String deviceName = jpcapDeviceString.replaceAll("\n.*", "").trim();
try {
int netAddress = capture.getNetwork(deviceName);
int netMask = capture.getNetmask(deviceName);
// go through all addresses of all interfaces and try to find a match.
Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
while (e.hasMoreElements() && info == null) {
NetworkInterface iface = e.nextElement();
Enumeration<InetAddress> ae = iface.getInetAddresses();
while (ae.hasMoreElements() && info == null) {
if (networkMatches(ae.nextElement(), netAddress, netMask))
info = new InterfaceInfo(iface, deviceName);
}
}
} catch (Exception x) {
System.err.println("While querying info for " + deviceName + ":");
x.printStackTrace(System.err);
}
if (info == null)
info = new InterfaceInfo(null, deviceName);
return info;
}
/**
* Information about a network interface for jpcap, which is basically just a NetworkInterface
* with details, and the jpcap device name for use with PacketCapture.
*/
public static class InterfaceInfo {
private final NetworkInterface iface;
private final String deviceName;
InterfaceInfo (NetworkInterface iface, String deviceName) {
this.iface = iface;
this.deviceName = deviceName;
}
/**
* Get NetworkInterface for this interface.
* #return May return null if no matching NetworkInterface was found.
*/
public final NetworkInterface getIface () {
return iface;
}
/**
* Get jpcap device name for this interface. This can be passed to PacketCapture.open().
* #return Device name for interface.
*/
public final String getDeviceName () {
return deviceName;
}
#Override public final String toString () {
return deviceName + " : " + iface;
}
}
}
Example
Here is an example:
import java.util.List;
import net.sourceforge.jpcap.capture.PacketCapture;
public class JpcapInterfaceInfoTest {
public static void main (String[] args) throws Exception {
// Info can be queried from jpcap device list.
List<JpcapInterfaceInfo.InterfaceInfo> infos = JpcapInterfaceInfo.listInterfaces();
// Info can be displayed.
for (JpcapInterfaceInfo.InterfaceInfo info : infos)
System.out.println(info);
// Device names from InterfaceInfo can be passed directly to jpcap:
JpcapInterfaceInfo.InterfaceInfo selected = infos.get(0);
PacketCapture capture = new PacketCapture();
capture.open(selected.getDeviceName(), true);
}
}
On my machine (same setup as registry solution), this outputs:
\Device\NPF_{691D289D-7EE5-4BD8-B5C1-3C4729A852D5} : null
\Device\NPF_{39966C4C-3728-4368-AE92-1D36ACAF6634} : name:net5 (1x1 11b/g/n Wireless LAN PCI Express Half Mini Card Adapter)
I did not make the output as pretty as the other solution. Note that the "virtual wifi miniport adapter" (the first one) has a null NetworkInterface because, since it is not up, a match could not be found (an IP address and network address was not present).

Way to validate a Diameter URI in Java?

I'm wondering if there are any nice simple ways to validate a Diameter URI (description below) using Java?
Note, a Diameter URI must have one of the forms:
aaa://FQDN[:PORT][;transport=TRANS][;protocol=PROT]
aaas://FQDN[:PORT][;transport=TRANS][;protocol=PROT]
The FQDN (mandatory) has to be replaced with the fully qualified host name (or IP), the PORT (optional, default is 3868) with the port number, TRANS (optional) with the transport protocol (can be TCP or SCTP) and PROT (optional) with diameter.
Some examples of the acceptable forms are:
aaa://server.com
aaa://127.0.0.1
aaa://server.com:1234
aaas://server.com:1234;transport=tcp
aaas://[::1]
aaas://[::1]:1234
aaas://[::1]:1234;transport=tcp;protocol=diameter
Note, as shown above, if using an IPv6 address, the address must be placed in box brackets, whereas the port number (if specified), with its colon separator, should be outside of the brackets.
I think doing this using regular expressions would be quite messy and difficult to understand, and other examples I have seen which don't use regex are just as awkward looking (such as https://code.google.com/p/cipango/source/browse/trunk/cipango-diameter/src/main/java/org/cipango/diameter/util/AAAUri.java?r=763).
So was wondering if there were maybe a nicer way to do this, e.g. something like a URI validator library, which takes some rules (such as those for the Diameter URI above) and then applies them to some input to validate it?
I've had a look at the Google Guava libraries as well to see if there was anything that could help but I couldn't see anything offhand.
Many thanks!
Since the URI class is not sufficient, and in fact will create exceptions for valid Diameter URI's, this is not such a trivial task.
I think reg.ex. is the way to go here, but due to the complexities, you might be better off if you place it in a helper class. I agree that the code you linked to did not look very good -- you can do better! :)
Take a look at the following code example, where I've broken down a regEx into its individual parts as a way to "document" what's happening.
It is not in any ways complete, it was created to conform with your examples. Especially the IP6 type addresses needs work. In addition, you might want to give more information in the validation; like why it failed.
But at least it's a beginning, and I think it is quite a bit better than the code you linked to. It might seem like an awful lot of code, but most of it is actually print statements and tests... :) In addition, since each part is broken down and kept as field variables, you can create simple getters to access each part (if that is of importance to you).
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DiameterUri {
private String diameterUri;
private String protocol;
private String host;
private String port;
private String[] params;
public DiameterUri(String diameterUri) throws URISyntaxException {
this.diameterUri = diameterUri;
validate();
System.out.println(diameterUri);
System.out.println(" protocol=" + protocol);
System.out.println(" host=" + host);
System.out.println(" port=" + port);
System.out.println(" params=" + Arrays.toString(params));
}
private void validate() throws URISyntaxException {
String protocol = "(aaa|aaas)://"; // protocol- required
String ip4 = "[A-Za-z0-9.]+"; // ip4 address - part of "host"
String ip6 = "\\[::1\\]"; // ip6 address - part of "host"
String host = "(" + ip4 + "|" + ip6 + ")"; // host - required
String port = "(:\\d+)?"; // port - optional (one occurrence)
String params = "((;[a-zA-Z0-9]+=[a-zA-Z0-9]+)*)"; // params - optional (multiple occurrences)
String regEx = protocol + host + port + params;
Pattern pattern = Pattern.compile(regEx);
Matcher matcher = pattern.matcher(diameterUri);
if (matcher.matches()) {
this.protocol = matcher.group(1);
this.host = matcher.group(2);
this.port = matcher.group(3) == null ? null : matcher.group(3).substring(1);
String paramsFromUri = matcher.group(4);
if (paramsFromUri != null && paramsFromUri.length() > 0) {
this.params = paramsFromUri.substring(1).split(";");
} else {
this.params = new String[0];
}
} else {
throw new URISyntaxException(diameterUri, "invalid");
}
}
public static void main(String[] args) throws URISyntaxException {
new DiameterUri("aaa://server.com");
new DiameterUri("aaa://127.0.0.1");
new DiameterUri("aaa://server.com:1234");
new DiameterUri("aaas://server.com:1234;transport=tcp");
new DiameterUri("aaas://[::1]");
new DiameterUri("aaas://[::1]:1234");
new DiameterUri("aaas://[::1]:1234;transport=tcp;protocol=diameter");
try {
new DiameterUri("127.0.0.1");
throw new RuntimeException("Expected URISyntaxException");
} catch (URISyntaxException ignore) {}
}
}

Get Android Device Name

How to get Android device name? I am using HTC desire. When I connected it via HTC Sync the software is displaying the Name 'HTC Smith' . I would like to fetch this name via code.
How is this possible in Android?
In order to get Android device name you have to add only a single line of code:
android.os.Build.MODEL;
Found here: getting-android-device-name
You can see answers at here Get Android Phone Model Programmatically
public String getDeviceName() {
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
if (model.startsWith(manufacturer)) {
return capitalize(model);
} else {
return capitalize(manufacturer) + " " + model;
}
}
private String capitalize(String s) {
if (s == null || s.length() == 0) {
return "";
}
char first = s.charAt(0);
if (Character.isUpperCase(first)) {
return s;
} else {
return Character.toUpperCase(first) + s.substring(1);
}
}
I solved this by getting the Bluetooth name, but not from the BluetoothAdapter (that needs Bluetooth permission).
Here's the code:
Settings.Secure.getString(getContentResolver(), "bluetooth_name");
No extra permissions needed.
On many popular devices the market name of the device is not available. For example, on the Samsung Galaxy S6 the value of Build.MODEL could be "SM-G920F", "SM-G920I", or "SM-G920W8".
I created a small library that gets the market (consumer friendly) name of a device. It gets the correct name for over 10,000 devices and is constantly updated. If you wish to use my library click the link below:
AndroidDeviceNames Library on Github
If you do not want to use the library above, then this is the best solution for getting a consumer friendly device name:
/** Returns the consumer friendly device name */
public static String getDeviceName() {
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
if (model.startsWith(manufacturer)) {
return capitalize(model);
}
return capitalize(manufacturer) + " " + model;
}
private static String capitalize(String str) {
if (TextUtils.isEmpty(str)) {
return str;
}
char[] arr = str.toCharArray();
boolean capitalizeNext = true;
String phrase = "";
for (char c : arr) {
if (capitalizeNext && Character.isLetter(c)) {
phrase += Character.toUpperCase(c);
capitalizeNext = false;
continue;
} else if (Character.isWhitespace(c)) {
capitalizeNext = true;
}
phrase += c;
}
return phrase;
}
Example from my Verizon HTC One M8:
// using method from above
System.out.println(getDeviceName());
// Using https://github.com/jaredrummler/AndroidDeviceNames
System.out.println(DeviceName.getDeviceName());
Result:
HTC6525LVW
HTC One (M8)
Try it. You can get Device Name through Bluetooth.
Hope it will help you
public String getPhoneName() {
BluetoothAdapter myDevice = BluetoothAdapter.getDefaultAdapter();
String deviceName = myDevice.getName();
return deviceName;
}
You can use:
From android doc:
MANUFACTURER:
String MANUFACTURER
The manufacturer of the product/hardware.
MODEL:
String MODEL
The end-user-visible name for the end product.
DEVICE:
String DEVICE
The name of the industrial design.
As a example:
String deviceName = android.os.Build.MANUFACTURER + " " + android.os.Build.MODEL;
//to add to textview
TextView textView = (TextView) findViewById(R.id.text_view);
textView.setText(deviceName);
Furthermore, their is lot of attribute in Build class that you can use, like:
os.android.Build.BOARD
os.android.Build.BRAND
os.android.Build.BOOTLOADER
os.android.Build.DISPLAY
os.android.Build.CPU_ABI
os.android.Build.PRODUCT
os.android.Build.HARDWARE
os.android.Build.ID
Also their is other ways you can get device name without using Build class(through the bluetooth).
Following works for me.
String deviceName = Settings.Global.getString(.getContentResolver(), Settings.Global.DEVICE_NAME);
I don't think so its duplicate answer. The above ppl are talking about Setting Secure, for me setting secure is giving null, if i use setting global it works. Thanks anyways.
universal way to get user defined DeviceName working for almost all devices and not requiring any permissions
String userDeviceName = Settings.Global.getString(getContentResolver(), Settings.Global.DEVICE_NAME);
if(userDeviceName == null)
userDeviceName = Settings.Secure.getString(getContentResolver(), "bluetooth_name");
Try this code. You get android device name.
public static String getDeviceName() {
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
if (model.startsWith(manufacturer)) {
return model;
}
return manufacturer + " " + model;
}
#hbhakhra's answer will do.
If you're interested in detailed explanation, it is useful to look into Android Compatibility Definition Document. (3.2.2 Build Parameters)
You will find:
DEVICE - A value chosen by the device implementer containing the
development name or code name identifying the configuration of the
hardware features and industrial design of the device. The value of
this field MUST be encodable as 7-bit ASCII and match the regular
expression “^[a-zA-Z0-9_-]+$”.
MODEL - A value chosen by the device implementer containing the name
of the device as known to the end user. This SHOULD be the same name
under which the device is marketed and sold to end users. There are no
requirements on the specific format of this field, except that it MUST
NOT be null or the empty string ("").
MANUFACTURER - The trade name of the Original Equipment Manufacturer
(OEM) of the product. There are no requirements on the specific format
of this field, except that it MUST NOT be null or the empty string
("").
UPDATE
You could retrieve the device from buildprop easitly.
static String GetDeviceName() {
Process p;
String propvalue = "";
try {
p = new ProcessBuilder("/system/bin/getprop", "ro.semc.product.name").redirectErrorStream(true).start();
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = br.readLine()) != null) {
propvalue = line;
}
p.destroy();
} catch (IOException e) {
e.printStackTrace();
}
return propvalue;
}
But keep in mind, this doesn't work on some devices.
Simply use
BluetoothAdapter.getDefaultAdapter().getName()
static String getDeviceName() {
try {
Class systemPropertiesClass = Class.forName("android.os.SystemProperties");
Method getMethod = systemPropertiesClass.getMethod("get", String.class);
Object object = new Object();
Object obj = getMethod.invoke(object, "ro.product.device");
return (obj == null ? "" : (String) obj);
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
you can get 'idol3' by this way.
Tried These libraries but nothing worked according to my expectation and was giving absolutely wrong names.
So i created this library myself using the same data.
Here is the link
AndroidPhoneNamesFinder
To use this library just add this for implementation
implementation 'com.github.aishik212:AndroidPhoneNamesFinder:v1.0.2'
Then use the following kotlin code
DeviceNameFinder.getPhoneValues(this, object : DeviceDetailsListener
{
override fun details(doQuery: DeviceDetailsModel?)
{
super.details(doQuery)
Log.d(TAG, "details: "+doQuery?.calculatedName)
}
})
These are the values you will get from DeviceDetailsModel
val brand: String? #This is the brandName of the Device
val commonName: String?, #This is the most common Name of the Device
val codeName: String?, #This is the codeName of the Device
val modelName: String?, #This is the another uncommon Name of the Device
val calculatedName: String?, #This is the special name that this library tries to create from the above data.
Example of Android Emulator -
brand=Google
commonName=Google Android Emulator
codeName=generic_x86_arm
modelName=sdk_gphone_x86
calculatedName=Google Android Emulator
Within the GNU/Linux environment of Android, e.g., via Termux UNIX shell on a non-root device, it's available through the /system/bin/getprop command, whereas the meaning of each value is explained in Build.java within Android (also at googlesource):
% /system/bin/getprop | fgrep ro.product | tail -3
[ro.product.manufacturer]: [Google]
[ro.product.model]: [Pixel 2 XL]
[ro.product.name]: [taimen]
% /system/bin/getprop ro.product.model
Pixel 2 XL
% /system/bin/getprop ro.product.model | tr ' ' _
Pixel_2_XL
For example, it can be set as the pane_title for the status-right within tmux like so:
tmux select-pane -T "$(getprop ro.product.model)"
Gets an Android system property, or lists them all
adb shell getprop >prop_list.txt
Find your device name in prop_list.txt to get the prop name
e.g. my device name is ro.oppo.market.name
Get oppo.market Operator
adb shell getprop ro.oppo.market.name
My case on windows as follows
D:\winusr\adbl
λ *adb shell getprop ro.oppo.market.name*
OPPO R17

Getting device/driver information related to a COM port?

I have a Serial-to-USB device with a similarly named device driver in the Windows device manager. The devices do not always grab the same COM port on system boot, so my program needs to identify it on start up.
I've tried using RXTX to enumerate the COM ports on the system, but this didn't work because CommPortIdentifier.getName() simply returns the COM name (eg. COM1, COM2, etc.) I need to acquire either the driver manufacturer name, or the driver name as it appears in the device manager, and associate it with the COM name.
Can this easily be done in Java? (I'd be interested in any 3rd party Java libraries that support this.) Otherwise, how I could begin to accomplish this via the win32 API?
I achieved what I wanted by using the WinRegistry class provided by David in this SO question to obtain the FriendlyName from registry key associated with my USB device. I then parse out the COM number from the friendly name.
Some things to consider:
USB devices are located at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\ in the registry (tested on WinXP, Win7.)
I required the device VID + PID to identify the correct device key (eg. VID_xxxx&PID_xxxx.) Since VID and PID are device specific, this key should be reliable across multiple systems.
The VID_xxxx&PID_xxxx key contains another sub-key with device values. I had some trouble enumerating the sub-keys with WinRegistry, so I hard-coded the sub-key name as a quick hack during development. A much safer solution would search sub-keys to find the correct name.
The device keys exist in the registry regardless of whether the device is currently connected. This code makes the assumption that Windows will update FriendlyName if the device is reconnected to a different COM port. I haven't verified this, but things looked good during use-testing.
Example
String keyPath = "SYSTEM\\CurrentControlSet\\Enum\\USB\\Vid_067b&Pid_2303\\";
String device1 = "5&75451e6&0&1";
System.out.println("First COM device: " + getComNumber(keyPath + device1));
Code
import java.util.regex.Pattern;
import java.util.regex.Matcher;
// Given a registry key, attempts to get the 'FriendlyName' value
// Returns null on failure.
//
public static String getFriendlyName(String registryKey) {
if (registryKey == null || registryKey.isEmpty()) {
throw new IllegalArgumentException("'registryKey' null or empty");
}
try {
int hkey = WinRegistry.HKEY_LOCAL_MACHINE;
return WinRegistry.readString(hkey, registryKey, "FriendlyName");
} catch (Exception ex) { // catch-all:
// readString() throws IllegalArg, IllegalAccess, InvocationTarget
System.err.println(ex.getMessage());
return null;
}
}
// Given a registry key, attempts to parse out the integer after
// substring "COM" in the 'FriendlyName' value; returns -1 on failure.
//
public static int getComNumber(String registryKey) {
String friendlyName = getFriendlyName(registryKey);
if (friendlyName != null && friendlyName.indexOf("COM") >= 0) {
String substr = friendlyName.substring(friendlyName.indexOf("COM"));
Matcher matchInt = Pattern.compile("\\d+").matcher(substr);
if (matchInt.find()) {
return Integer.parseInt(matchInt.group());
}
}
return -1;
}
#robjb Your code does not allow for more than one device to be connected. How will the user know the device name? I added to your code thus to return a list of com ports:
ArrayList<String> subKeys = WinRegistry.readStringSubKeys(WinRegistry.HKEY_LOCAL_MACHINE, keyPath);
ArrayList<Integer> comPorts = new ArrayList<Integer>();
for (String subKey : subKeys) {
String friendlyName = getFriendlyName(keyPath + subKey);
if (friendlyName != null && friendlyName.contains("MyDriverName") && friendlyName.contains("COM")) {
int beginIndex = friendlyName.indexOf("COM") + 3 /*length of 'COM'*/;
int endIndex = friendlyName.indexOf(")");
comPorts.add(Integer.parseInt(friendlyName.substring(beginIndex, endIndex)));
}
}
Update: I don't think these are solutions. Why? This information is statically stored in the registry - even when the device is not connected.
Great example, using JNA, here.
The author (Geir Arne Ruud) has released it under Public Domain License.
My example code
public static String getFriendlyName(GoGPSModel model, String name)
{
if(model.getSystem().getOSType() != OSType.Windows32
&& model.getSystem().getOSType() != OSType.Windows64) {
return name;
}
for (DeviceInformation devInfo : infoObjects) {
System.out.println(devInfo.toString());
String friendlyName = devInfo.getFriendlyName();
if(friendlyName != null && !friendlyName.equals("") && friendlyName.contains(name)) {
return devInfo.getManufacturer() + ": " + friendlyName;
}
}
return name;
}

Categories

Resources