diff options
author | Alex Clayton <alex.clayton@isode.com> | 2016-07-13 15:42:34 (GMT) |
---|---|---|
committer | Alex Clayton <alex.clayton@isode.com> | 2016-07-15 15:19:04 (GMT) |
commit | 4e70a84a8a792cfeae96bf7a0cb4c9b549e44d29 (patch) | |
tree | af606f030af75a86dcb0187c20d6466b1501b851 | |
parent | 8708b7b01cf4f36524bff3dc9aa5a7a6534ce221 (diff) | |
download | stroke-4e70a84a8a792cfeae96bf7a0cb4c9b549e44d29.zip stroke-4e70a84a8a792cfeae96bf7a0cb4c9b549e44d29.tar.bz2 |
Stop HostAddress constructor running DNS lookup.
When running the latest stroke against harrier android a NetworkOnMainThread exception
was encountered. This was traced to the new constructor HostAddress(String) that was
been called by Connector.start() method.
The issue was dues to difference between the java code in stroke and
the c++ code in swiften.
In swiften the equivalent conde calls boost::asio::ip::address::from_string, which
parses a string that may be an ipv4 or ipv6 address into an IP address object. If
the string is not one of these then the object is left invalid.
In the java code InetAddress.getByName(String name) is used instead. If this
is an ipv4 or ipv6 address then it parses it into an InetAddress object. However if
is not one of these it does a DNS lookup on the address. This was what was causing
the error.
To match the C++ the java code should only create an InetAddress if the input is
an IP address, if not it should be left null.
This was done by copying the IPv4 and IPv6 regexes in the Regex class in isode lib.
The input to HostAddress(String) is checked against these if it matches InetAddress.getByName
is called, otherwise address is set to null.
Test-information:
Ran unit tests in stroke they all pass.
Ran code against android harrier, no longer fails.
Ran against MLC (with updates for other changes in stroke api) it still passes.
Change-Id: I1945c7c3cdfece8feb45b9196483131c0d9c4e7c
-rw-r--r-- | src/com/isode/stroke/network/HostAddress.java | 70 |
1 files changed, 64 insertions, 6 deletions
diff --git a/src/com/isode/stroke/network/HostAddress.java b/src/com/isode/stroke/network/HostAddress.java index a8eea1b..dae2558 100644 --- a/src/com/isode/stroke/network/HostAddress.java +++ b/src/com/isode/stroke/network/HostAddress.java @@ -4,13 +4,14 @@ * See Documentation/Licenses/GPLv3.txt for more information. */ /* - * Copyright (c) 2010, Isode Limited, London, England. + * Copyright (c) 2010-2016, Isode Limited, London, England. * All rights reserved. */ package com.isode.stroke.network; import java.net.InetAddress; import java.net.UnknownHostException; +import java.util.regex.Pattern; public class HostAddress { @@ -19,11 +20,15 @@ public class HostAddress { } public HostAddress(String address) { - try { - address_ = InetAddress.getByName(address); - } - catch (UnknownHostException e) { - address_ = null; + // To match C++ code this should try and create a InetAddress + // if and only if the input is a IPv4 or IPv6 address. + if (isIPv4OrIPv6Address(address)) { + try { + address_ = InetAddress.getByName(address); + } + catch (UnknownHostException e) { + address_ = null; + } } } @@ -86,4 +91,57 @@ public class HostAddress { } private InetAddress address_; + + /** + * Indicates if the value is an IPv4 address in dot notation or + * an IPv6 address in hexadecimal notation + * @param value A string to test. If {@code null} result will + * be false. + * @return {@code true} if the string is an IPv4 address in dot + * notation, or a IPv6 address in hexadecimal notation. + */ + private static boolean isIPv4OrIPv6Address(String value) { + if (value == null) { + return false; + } + if (ipv4Pattern.matcher(value).matches()) { + return true; + } + if (ipv6Pattern.matcher(value).matches()) { + return true; + } + return false; + } + + /** + * Regular expression for IPv4 address. + * + * @see <a href="http://www.mkyong.com/regular-expressions/how-to-validate-ip-address-with-regular-expression/"> web link</a> + */ + private static final String ipv4Regex = + "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\." + + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])$"; + /** + * A pattern that can be used to match an IPv4 address (e.g. "1.2.3.4"). + * + */ + private static final Pattern ipv4Pattern = Pattern.compile(ipv4Regex); + + /** + * Regular expression for IPv6 address. Note that this needs to be + * compiled with the CASE_INSENSITIVE option. + */ + private static final String ipv6Regex = + "^(((?=(?>.*?::)(?!.*::)))(::)?([0-9A-F]{1,4}::?){0,5}" + + "|([0-9A-F]{1,4}:){6})(\\2([0-9A-F]{1,4}(::?|$)){0,2}" + + "|((25[0-5]|(2[0-4]|1\\d|[1-9])?\\d)(\\.|$)){4}" + + "|[0-9A-F]{1,4}:[0-9A-F]{1,4})(?<![^:]:|\\.)\\z"; + + /** + * A pattern that can be used to match an IPv6 address (e.g. "3ffe:1900:4545:3:200:f8ff:fe21:67cf"). + */ + private static final Pattern ipv6Pattern = Pattern.compile(ipv6Regex, Pattern.CASE_INSENSITIVE); + } |