What is the common way in Java to validate and convert a string of the form host:port into an instance of InetSocketAddress?
It would be nice if following criteria were met:
No address lookups;
Working for IPv4, IPv6, and "string" hostnames;
(For IPv4 it's ip:port, for IPv6 it's [ip]:port, right? Is there some RFC which defines all these schemes?)
Preferable without parsing the string by hand.
(I'm thinking about all those special cases, when someone think he knows all valid forms of socket addresses, but forgets about "that special case" which leads to unexpected results.)
I myself propose one possible workaround solution.
Convert a string into URI (this would validate it automatically) and then query the URI's host and port components.
Sadly, an URI with a host component MUST have a scheme. This is why this solution is "not perfect".
String string = ... // some string which has to be validated
try {
// WORKAROUND: add any scheme to make the resulting URI valid.
URI uri = new URI("my://" + string); // may throw URISyntaxException
String host = uri.getHost();
int port = uri.getPort();
if (uri.getHost() == null || uri.getPort() == -1) {
throw new URISyntaxException(uri.toString(),
"URI must have host and port parts");
}
// here, additional checks can be performed, such as
// presence of path, query, fragment, ...
// validation succeeded
return new InetSocketAddress (host, port);
} catch (URISyntaxException ex) {
// validation failed
}
This solution needs no custom string parsing, works with IPv4 (1.1.1.1:123), IPv6 ([::0]:123) and host names (my.host.com:123).
Accidentally, this solution is well suited for my scenario. I was going to use URI schemes anyway.
A regex will do this quite neatly:
Pattern p = Pattern.compile("^\\s*(.*?):(\\d+)\\s*$");
Matcher m = p.matcher("127.0.0.1:8080");
if (m.matches()) {
String host = m.group(1);
int port = Integer.parseInt(m.group(2));
}
You can this in many ways such as making the port optional or doing some validation on the host.
It doesn't answer the question exactly, but this answer could still be useful others like me who just want to parse a host and port, but not necessarily a full InetAddress. Guava has a HostAndPort class with a parseString method.
Another person has given a regex answer which is what I was doing to do when originally asking the question about hosts. I will still do because it's an example of a regex that is slightly more advanced and can help determine what kind of address you are dealing with.
String ipPattern = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}):(\\d+)";
String ipV6Pattern = "\\[([a-zA-Z0-9:]+)\\]:(\\d+)";
String hostPattern = "([\\w\\.\\-]+):(\\d+)"; // note will allow _ in host name
Pattern p = Pattern.compile( ipPattern + "|" + ipV6Pattern + "|" + hostPattern );
Matcher m = p.matcher( someString );
if( m.matches() ) {
if( m.group(1) != null ) {
// group(1) IP address, group(2) is port
} else if( m.group(3) != null ) {
// group(3) is IPv6 address, group(4) is port
} else if( m.group(5) != null ) {
// group(5) is hostname, group(6) is port
} else {
// Not a valid address
}
}
Modifying so that port is optional is pretty straight forward. Wrap the ":(\d+)" as "(?::(\d+))?" and then check for null for group(2), etc.
Edit: I'll note that there's no "common way" way that I'm aware of but the above is how I'd do it if I had to.
Also note: the IPv4 case can be removed if the host and IPv4 cases will actually be handled the same. I split them out because sometimes you can avoid an ultimate host look-up if you know you have the IP address.
new InetSocketAddress(
addressString.substring(0, addressString.lastIndexOf(":")),
Integer.parseInt(addressString.substring(addressString.lastIndexOf(":")+1, addressString.length));
? I probably made some little silly mistake. and I'm assuming you just wanted a new InetSocketAddress object out of the String in only that format. host:port
All kind of peculiar hackery, and elegant but unsafe solutions provided elsewhere. Sometimes the inelegant brute-force solution is the way.
public static InetSocketAddress parseInetSocketAddress(String addressAndPort) throws IllegalArgumentException {
int portPosition = addressAndPort.length();
int portNumber = 0;
while (portPosition > 1 && Character.isDigit(addressAndPort.charAt(portPosition-1)))
{
--portPosition;
}
String address;
if (portPosition > 1 && addressAndPort.charAt(portPosition-1) == ':')
{
try {
portNumber = Integer.parseInt(addressAndPort.substring(portPosition));
} catch (NumberFormatException ignored)
{
throw new IllegalArgumentException("Invalid port number.");
}
address = addressAndPort.substring(0,portPosition-1);
} else {
portNumber = 0;
address = addressAndPort;
}
return new InetSocketAddress(address,portNumber);
}
The open-source IPAddress Java library has a HostName class which will do the required parsing. Disclaimer: I am the project manager of the IPAddress library.
It will parse IPv4, IPv6 and string host names with or without ports. It will handle all the various formats of hosts and addresses. BTW, there is no single RFC for this, there are a number of RFCs that apply in different ways.
String hostName = "[a:b:c:d:e:f:a:b]:8080";
String hostName2 = "1.2.3.4:8080";
String hostName3 = "a.com:8080";
try {
HostName host = new HostName(hostName);
host.validate();
InetSocketAddress address = host.asInetSocketAddress();
HostName host2 = new HostName(hostName2);
host2.validate();
InetSocketAddress address2 = host2.asInetSocketAddress();
HostName host3 = new HostName(hostName3);
host3.validate();
InetSocketAddress address3 = host3.asInetSocketAddress();
// use socket address
} catch (HostNameException e) {
String msg = e.getMessage();
// handle improperly formatted host name or address string
}
URI can accomplish this:
URI uri = new URI(null, "example.com:80", null, null, null);
Unfortunately, there's a bug in current OpenJDK (or in the documentation) where the authority isn't properly validated. The documentation states:
The resulting URI string is then parsed as if by invoking the URI(String) constructor and then invoking the parseServerAuthority() method upon the result
That call to parseServerAuthority just doesn't happen unfortunately so the real solution here that properly validates is:
URI uri = new URI(null, "example.com:80", null, null, null).parseServerAuthority();
then
InetSocketAddress address = new InetSocketAddress(uri.getHost(), uri.getPort());
Related
IMPORTANT
I have been blocked by hotmail services. There is a control mechanism
called spamhaus which kicked me out. I'm stuck right now.
I am trying to detect an email address is valid and if its valid then check if this email address potentially used (I know that its not certain). For example, lets assume that there is a website with domain myimaginarydomain.com. If I run code below, I guess it won't fail because domain address is valid. But nobody can take an email address with that domain.
Is there any way to find out that email address is valid? (In this case its invalid)
I don't want to send confirmation email
Sending ping may be useful?
public class Application {
private static EmailValidator validator = EmailValidator.getInstance();
public static void main(String[] args) {
while (true) {
Scanner scn = new Scanner(System.in);
String email = scn.nextLine();
boolean isValid = validateEmail(email);
System.out.println("Syntax is : " + isValid);
if (isValid) {
String domain = email.split("#")[1];
try {
int test = doLookup(domain);
System.out.println(domain + " has " + test + " mail servers");
} catch (NamingException e) {
System.out.println(domain + " has 0 mail servers");
}
}
}
}
private static boolean validateEmail(String email) {
return validator.isValid(email);
}
static int doLookup(String hostName) throws NamingException {
Hashtable env = new Hashtable();
env.put("java.naming.factory.initial",
"com.sun.jndi.dns.DnsContextFactory");
DirContext ictx = new InitialDirContext(env);
Attributes attrs =
ictx.getAttributes(hostName, new String[]{"MX"});
Attribute attr = attrs.get("MX");
if (attr == null) return (0);
return (attr.size());
}
}
There is no failsafe way to do this in all cases, but, assuming the server uses SMTP then https://www.labnol.org/software/verify-email-address/18220/ gives quite a good tutorial on one method that may work.
The method used in the tutorial relies on OS tools, so you will need to ensure they exist before using them. a ProcessBuilder may help. Alternatively, you can open a socket directly in code and avoid using OS-dependent tools.
Essentially, you find out what the mail servers are (using nslookup), then telnet to one of the mail servers and start writing an email:
3a: Connect to the mail server:
telnet gmail-smtp-in.l.google.com 25
3b: Say hello to the other server
HELO
3c: Identify yourself with some fictitious email address
mail from:<labnol#labnol.org>
3d: Type the recipient’s email address that you are trying to verify:
rcpt to:<billgates#gmail.com>
The server response for rcpt to command will give you an idea whether an email address is valid or not. You’ll get an “OK” if the address exists else a 550 error
There really is no sensible way except trying to send a notification with a token to the address and ask the other party to confirm it, usually by visiting a web-page:
the recipients MX may be unavailable at the moment but come back online later, so you cannot rely on a lookup in real time;
just because the MX accepts the email doesn't mean that the address is valid, the message could bounce later down the pipe (think UUCP);
if this is some kind of registration service, you need to provide some confirmation step anyway as otherwise it'd become too easy to subscribe random strangers on the internet that do not want your service.
I'm weak in network technologies and maybe you can help me. I have a simple code
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(request.getRequestURL().toString()).build();
UriComponents newUriComponents = UriComponentsBuilder.newInstance().scheme(uriComponents.getScheme())
.host(uriComponents.getHost()).port(uriComponents.getPort()).build();
return newUriComponents.toUriString() + request.getContextPath();
This code should return link to my server with specific path. The problem is - on product server uriComponents.getHost() returns IP instead of domain name. Domain works when I go via browser to server. I can go to
http://exmaple.com/some/one/path and want to get in answer (in JSON, there are no redirections. just get request and json answer) - http://exmaple.com/some/another/path but code which I have showed returns - http://78.54.128.98.com/some/another/path (IP address just example). So I don't know why my code returns IP but not domain name. Only what I can to say more - in my local machine I don't have any problems with it. Code returns localhost, or if i add 127.0.0.1 exmaple.com to hosts file, my code will return correct exmaple.com, no any ip
This is not a problem of the URIComponents, it parses what it gets in input. More specifically looking at the source of UriComponentsBuilder.fromHttpUrl you see:
public static UriComponentsBuilder fromHttpUrl(String httpUrl) {
Assert.notNull(httpUrl, "HTTP URL must not be null");
Matcher matcher = HTTP_URL_PATTERN.matcher(httpUrl);
if (matcher.matches()) {
UriComponentsBuilder builder = new UriComponentsBuilder();
String scheme = matcher.group(1);
builder.scheme(scheme != null ? scheme.toLowerCase() : null);
builder.userInfo(matcher.group(4));
String host = matcher.group(5);
if (StringUtils.hasLength(scheme) && !StringUtils.hasLength(host)) {
throw new IllegalArgumentException("[" + httpUrl + "] is not a valid HTTP URL");
}
builder.host(host);
String port = matcher.group(7);
if (StringUtils.hasLength(port)) {
builder.port(port);
}
builder.path(matcher.group(8));
builder.query(matcher.group(10));
return builder;
}
else {
throw new IllegalArgumentException("[" + httpUrl + "] is not a valid HTTP URL");
}
}
where you can notice that a pattern matcher is defined on an expected structure of the url and parts are parsed according to the matcher. If you see IP it means that the url specified in input (request.getRequestURL().toString()) contained the IP address as a host.
This means that you should be looking for the guilty one above in the chain, starting by whoever calls this piece of code and following the links until you find the cause.
Why is the 1st one returning null, while the 2nd one is returning mail.yahoo.com?
Isn't this weird? If not, what's the logic behind this behavior?
Is the underscore the culprit? Why?
public static void main(String[] args) throws Exception {
java.net.URI uri = new java.net.URI("http://broken_arrow.huntingtonhelps.com");
String host = uri.getHost();
System.out.println("Host = [" + host + "].");
uri = new java.net.URI("http://mail.yahoo.com");
host = uri.getHost();
System.out.println("Host = [" + host + "].");
}
As mentioned in the comments by #hsz it is a known bug.
But, let's debug and look inside the sources of URI class. The problem is inside the method:
private int parseHostname(int start, int n):
parsing first URI fails at lines if ((p < n) && !at(p, n, ':')) fail("Illegal character in hostname", p);
this is because _ symbol isn't foreseen inside the scan block, so it allows only alphas, digits and -symbol (L_ALPHANUM, H_ALPHANUM, L_DASH and H_DASH).
And yes, this is not yet fixed in Java 7.
It's because of underscore in base uri.
Just Remove underscore to check that out.It's working.
Like given below :
public static void main(String[] args) throws Exception {
java.net.URI uri = new java.net.URI("http://brokenarrow.huntingtonhelps.com");
String host = uri.getHost();
System.out.println("Host = [" + host + "].");
uri = new java.net.URI("http://mail.yahoo.com");
host = uri.getHost();
System.out.println("Host = [" + host + "].");
}
I don't think it's a bug in Java, I think Java is parsing hostnames correctly according to the spec, there are good explanations of the spec here: http://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names and here: http://www.netregister.biz/faqit.htm#1
Specifically hostnames MUST NOT contain underscores.
Consider using: new java.net.URL("http://broken_arrow.huntingtonhelps.com").getHost() instead. It has alternative parsing implementation. If you have an URI myUri instance, then call myUri.toURL().getHost().
I faced this URI issue in OpenJDK 1.8 and it worked fine with URL.
As mentioned, it is a known JVM bug.
Although, if you want to do an HTTP request to such a host, you still can try to use a workaround.
The main idea is to construct request basing on the IP, not on the 'wrong' hostname. But in that case you also need to add "Host" header to the request, with the correct (original) hostname.
1: Cut hostname from the URL (it's a rough example, you can use some more smart way):
int n = url.indexOf("://");
if (n > 0) { n += 3; } else { n = 0; }
int m = url.indexOf(":", n);
int k = url.indexOf("/", n);
if (-1 == m) { m = k; }
String hostHeader;
if (k > -1) {
hostHeader = url.substring(n, k);
} else {
hostHeader = url.substring(n);
}
String hostname;
if (m > -1) {
hostname = url.substring(n, m);
} else {
hostname = url.substring(n);
}
2: Get hostname's IP:
String IP = InetAddress.getByName(hostname).getHostAddress();
3: Construct new URL basing on the IP:
String newURL = url.substring(0, n) + IP + url.substring(m);
4: Now use an HTTP library for preparing request on the new URL (pseudocode):
HttpRequest req = ApacheHTTP.get(newUrl);
5: And now you should add "Host" header with the correct (original) hostname:
req.addHeader("Host", hostHeader);
6: Now you can do the request (pseudocode):
String resp = req.getResponse().asString();
With Tomcat setup behind Apache, how can an id (IP address ideally) of the server be easily determined?
The specific situation is that multiple servers are setup behind a load balancer, thus the incoming request host name is non-unique and insufficient to identify a particular server for logging purposes. Using HttpServletRequest.getLocalAddr() is unfortunately returning the same hostname instead of the IP address as would be expected (I am assuming this is related to this very old issue here: https://issues.apache.org/bugzilla/show_bug.cgi?id=46082).
Is there a way to make getLocalAddr() perform as documented, or are other methods required to query the IP address of the server?
On our project, we use JMX to get all the config information.
It takes a few steps, because it is like navigating down the server.xml file
This link has some info: http://oss.wxnet.org/mbeans.html
It is probably overkill if all you want is the IP, but I thought I'd throw it out there.
MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
Set<ObjectName> theConnectors = mbeanServer.queryNames(
new ObjectName("Catalina:type=Connector,*"),
null);
if (theConnectors != null)
{
for (ObjectName nextConnectorName : theConnectors)
{
InetAddress theInetAddress = (InetAddress) mbeanServer.getAttribute(
nextConnectorName,
"address");
if (theInetAddress != null)
{
ipAddress = theInetAddress.getHostAddress();
}
if (!StringUtil.isEmpty(ipAddress))
{
// found the IP address
break;
}
}
}
For my situation, the solution was to get the IP address of the server directly instead of attempting to get the local address via HttpServleRequest.
I cached the IP for use in my filter via:
private static final String serverIp;
static {
String addressString = null;
try
{
InetAddress address = InetAddress.getLocalHost();
addressString = address.getHostAddress();
} catch (Exception e)
{
logger.error("Exception while attempting to determine local ip address",e);
}
if (addressString != null) serverIp = addressString;
else serverIp = "unknown";
}
I had a similar issue recently (a few years after the original question) and found this question and answers. The issue in my case was that the ServletRequest#getLocalAddr() implementation was returning the remote address instead of the local address. The issue was caused by a regression in Tomcat v9.0.22. It was fixed in v9.0.23. See the question and answer here:
https://stackoverflow.com/a/57725039/9602527
I'm having some troubles to detect client's private ip that conect to a web application I built.
Take a look at my tests results(In machines that runs windows):
1-In some machines(from different location ,countries..) the applet give me the correct ip but
2-In others I've obtained ip=127.0.0.1 :
What have I tried to solve this?
A- for example: I've stopped the avast program protection(web shield) and the applet start to give me the correct private ip.
B- In others machines I tried "point A" but It didn't work
C- I also edit host file but I didn't work as well
What I need from you is to help me to understand what is happening? where to look in order to resolve this...
Please don't answer saying "Why do you need the private ip? It could change..." ... I know all the machines that are going to connect to my web application so I can configure them.
Part of the source code that my applet use:
private String PrivateIP(boolean flag)
{
String s1 = "unknown";
String s2 = getDocumentBase().getHost();
int i = 80;
if(getDocumentBase().getPort() != -1)
i = getDocumentBase().getPort();
try
{
String s = (new Socket(s2, i)).getLocalAddress().getHostAddress();
if(!s.equals("255.255.255.255"))
s1 = s;
}
catch(SecurityException _ex)
{
s1 = "FORBIDDEN";
}
catch(Exception _ex)
{
s1 = "ERROR";
}
if(flag)
try
{
s1 = (new Socket(s2, i)).getLocalAddress().getHostName();
}
catch(Exception _ex)
{
Stat = "Cannot Lookup this IP";
}
return s1;
}
I'll let you more information:
I've traid this http://www.auditmypc.com/digital-footprint.asp in order to obtain the ip from probably other method but the same result, I've also run http://www.auditmypc.com/firewall-test.asp and obtained in the machines that I couldn't obtained the correct ip a message like "Congratulations you don't have any port to be open" xD...
Thanks in advance!
First of all, there can be more than one IP address available on the client, if there is more than one network interface. Which one is returned by your method depends on which is used for new Socket() to open.
Now, you do not have to open sockets to get the client's IP. What you can do instead is to enumerate them like this:
String host = InetAddress.getLocalHost().getHostName();
InetAddress[] addressArray = InetAddress.getAllByName(host);
String[] ipArray = new String[addressArray.length];
for (int i = 0; i < addressArray.length; i++) {
InetAddress addr = addressArray[i];
ipArray[i] = addr.getHostAddress();
}
return ipArray;
Now the ipArray will hold a list of available IP adresses on client's workstation.