Use lots of regex to add http or https for XMR explorer API endpoint

* If Tor *.onion hostname, use HTTP with Tor proxy
* If 127.0.0.1 or localhost, use HTTP without Tor proxy
* If LAN address or *.local FQDN, use HTTP without Tor proxy
* If any other FQDN hostname, use HTTPS with Tor proxy
This commit is contained in:
wiz 2020-09-07 18:52:33 +09:00 committed by Christoph Atteneder
parent 5a53cc6a73
commit cc7e6559ff
No known key found for this signature in database
GPG key ID: CD5DC1C529CDFD3B
4 changed files with 381 additions and 7 deletions

View file

@ -163,10 +163,19 @@ class XmrTxProofRequest implements AssetTxProofRequest<XmrTxProofRequest.Result>
this.model = model;
httpClient = new XmrTxProofHttpClient(socks5ProxyProvider);
httpClient.setBaseUrl("http://" + model.getServiceAddress());
if (model.getServiceAddress().matches("^192.*|^localhost.*")) {
log.info("Ignoring Socks5 proxy for local net address: {}", model.getServiceAddress());
// localhost, LAN address, or *.local FQDN starts with http://, don't use Tor
if (model.getServiceAddress().regionMatches(0, "http:", 0, 5)) {
httpClient.setBaseUrl(model.getServiceAddress());
httpClient.setIgnoreSocks5Proxy(true);
// any non-onion FQDN starts with https://, use Tor
} else if (model.getServiceAddress().regionMatches(0, "https:", 0, 6)) {
httpClient.setBaseUrl(model.getServiceAddress());
httpClient.setIgnoreSocks5Proxy(false);
// it's a raw onion so add http:// and use Tor proxy
} else {
httpClient.setBaseUrl("http://" + model.getServiceAddress());
httpClient.setIgnoreSocks5Proxy(false);
}
terminated = false;

View file

@ -95,6 +95,7 @@ import javafx.util.StringConverter;
import java.io.File;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
@ -672,12 +673,41 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
autoConfServiceAddressListener = (observable, oldValue, newValue) -> {
if (!newValue.equals(oldValue)) {
List<String> serviceAddresses = Arrays.asList(StringUtils.deleteWhitespace(newValue).split(","));
RegexValidator onionRegex = GUIUtil.onionAddressRegexValidator();
RegexValidator localhostRegex = GUIUtil.localhostAddressRegexValidator();
RegexValidator localnetRegex = GUIUtil.localnetAddressRegexValidator();
List<String> serviceAddressesRaw = Arrays.asList(StringUtils.deleteWhitespace(newValue).split(","));
// revert to default service providers when user empties the list
if (serviceAddresses.size() == 1 && serviceAddresses.get(0).isEmpty()) {
serviceAddresses = preferences.getDefaultXmrTxProofServices();
if (serviceAddressesRaw.size() == 1 && serviceAddressesRaw.get(0).isEmpty()) {
serviceAddressesRaw = preferences.getDefaultXmrTxProofServices();
}
preferences.setAutoConfServiceAddresses("XMR", serviceAddresses);
// we must always communicate with XMR explorer API securely
// if *.onion hostname, we use Tor normally
// if localhost, LAN address, or *.local FQDN we use HTTP without Tor
// otherwise we enforce https:// for any clearnet FQDN hostname
List<String> serviceAddressesParsed = new ArrayList<String>();
serviceAddressesRaw.forEach((addr) -> {
addr = addr.replaceAll("http://", "").replaceAll("https://", "");
if (onionRegex.validate(addr).isValid) {
log.info("Using Tor for onion hostname: {}", addr);
serviceAddressesParsed.add(addr);
} else if (localhostRegex.validate(addr).isValid) {
log.info("Using HTTP without Tor for Loopback address: {}", addr);
serviceAddressesParsed.add("http://" + addr);
} else if (localnetRegex.validate(addr).isValid) {
log.info("Using HTTP without Tor for LAN address: {}", addr);
serviceAddressesParsed.add("http://" + addr);
} else {
log.info("Using HTTPS with Tor for Clearnet address: {}", addr);
serviceAddressesParsed.add("https://" + addr);
}
});
preferences.setAutoConfServiceAddresses("XMR", serviceAddressesParsed);
}
};

View file

@ -1161,6 +1161,127 @@ public class GUIUtil {
return regexValidator;
}
// checks if valid tor onion hostname with optional port at the end
public static RegexValidator onionAddressRegexValidator() {
RegexValidator regexValidator = new RegexValidator();
String portRegexPattern = "(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])";
String onionV2RegexPattern = String.format("[a-zA-Z2-7]{16}\\.onion(?:\\:%1$s)?", portRegexPattern);
String onionV3RegexPattern = String.format("[a-zA-Z2-7]{56}\\.onion(?:\\:%1$s)?", portRegexPattern);
regexValidator.setPattern(String.format("^(?:(?:(?:%1$s)|(?:%2$s)),\\s*)*(?:(?:%1$s)|(?:%2$s))*$",
onionV2RegexPattern, onionV3RegexPattern));
return regexValidator;
}
// checks if localhost address, with optional port at the end
public static RegexValidator localhostAddressRegexValidator() {
RegexValidator regexValidator = new RegexValidator();
// match 0 ~ 65535
String portRegexPattern = "(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])";
// match 127/8 (127.0.0.0 ~ 127.255.255.255)
String localhostIpv4RegexPattern = String.format(
"(?:127\\.)" +
"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){2}" +
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" +
"(?:\\:%1$s)?",
portRegexPattern);
// match ::/64 with optional port at the end, i.e. ::1 or [::1]:8081
String localhostIpv6RegexPattern = "(:((:[0-9a-fA-F]{1,4}){1,4}|:)|)";
localhostIpv6RegexPattern = String.format("(?:%1$s)|(?:\\[%1$s\\]\\:%2$s)", localhostIpv6RegexPattern, portRegexPattern);
// match *.local
String localhostFqdnRegexPattern = String.format("(localhost(?:\\:%1$s)?)", portRegexPattern);
regexValidator.setPattern(String.format("^(?:(?:(?:%1$s)|(?:%2$s)|(?:%3$s)),\\s*)*(?:(?:%1$s)|(?:%2$s)|(?:%3$s))*$",
localhostIpv4RegexPattern, localhostIpv6RegexPattern, localhostFqdnRegexPattern));
return regexValidator;
}
// checks if local area network address, with optional port at the end
public static RegexValidator localnetAddressRegexValidator() {
RegexValidator regexValidator = new RegexValidator();
// match 0 ~ 65535
String portRegexPattern = "(0|[1-9][0-9]{0,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])";
// match 10/8 (10.0.0.0 ~ 10.255.255.255)
String localnetIpv4RegexPatternA = String.format(
"(?:10\\.)" +
"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){2}" +
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" +
"(?:\\:%1$s)?",
portRegexPattern);
// match 172.16/12 (172.16.0.0 ~ 172.31.255.255)
String localnetIpv4RegexPatternB = String.format(
"(?:172\\.)" +
"(?:(?:1[6-9]|2[0-9]|[3][0-1])\\.)" +
"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.)" +
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" +
"(?:\\:%1$s)?",
portRegexPattern);
// match 192.168/16 (192.168.0.0 ~ 192.168.255.255)
String localnetIpv4RegexPatternC = String.format(
"(?:192\\.)" +
"(?:168\\.)" +
"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.)" +
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" +
"(?:\\:%1$s)?",
portRegexPattern);
// match 169.254/15 (169.254.0.0 ~ 169.255.255.255)
String autolocalIpv4RegexPattern = String.format(
"(?:169\\.)" +
"(?:(?:254|255)\\.)" +
"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.)" +
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" +
"(?:\\:%1$s)?",
portRegexPattern);
// match fc00::/7 (fc00:: ~ fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff)
String localnetIpv6RegexPattern = "(" +
"([fF][cCdD][0-9a-fA-F]{2}:)([0-9a-fA-F]{1,4}:){6}[0-9a-fA-F]{1,4}|" + // fd00:2:3:4:5:6:7:8
"([fF][cCdD][0-9a-fA-F]{2}:)([0-9a-fA-F]{1,4}:){0,7}:|" + // fd00:: fd00:2:3:4:5:6:7::
"([fF][cCdD][0-9a-fA-F]{2}:)([0-9a-fA-F]{1,4}:){0,6}:[0-9a-fA-F]{1,4}|" + // fd00::8 fd00:2:3:4:5:6::8 fd00:2:3:4:5:6::8
"([fF][cCdD][0-9a-fA-F]{2}:)([0-9a-fA-F]{1,4}:){0,5}(:[0-9a-fA-F]{1,4}){1,1}|" + // fd00::7:8 fd00:2:3:4:5::7:8 fd00:2:3:4:5::8
"([fF][cCdD][0-9a-fA-F]{2}:)([0-9a-fA-F]{1,4}:){0,4}(:[0-9a-fA-F]{1,4}){1,2}|" + // fd00::7:8 fd00:2:3:4:5::7:8 fd00:2:3:4:5::8
"([fF][cCdD][0-9a-fA-F]{2}:)([0-9a-fA-F]{1,4}:){0,3}(:[0-9a-fA-F]{1,4}){1,3}|" + // fd00::6:7:8 fd00:2:3:4::6:7:8 fd00:2:3:4::8
"([fF][cCdD][0-9a-fA-F]{2}:)([0-9a-fA-F]{1,4}:){0,2}(:[0-9a-fA-F]{1,4}){1,4}|" + // fd00::5:6:7:8 fd00:2:3::5:6:7:8 fd00:2:3::8
"([fF][cCdD][0-9a-fA-F]{2}:)([0-9a-fA-F]{1,4}:){0,1}(:[0-9a-fA-F]{1,4}){1,5}|" + // fd00::4:5:6:7:8 fd00:2::4:5:6:7:8 fd00:2::8
"([fF][cCdD][0-9a-fA-F]{2}:)(:[0-9a-fA-F]{1,4}){1,6}" + // fd00::3:4:5:6:7:8 fd00::3:4:5:6:7:8 fd00::8
")";
// match fe80::/10 (fe80:: ~ febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff)
String autolocalIpv6RegexPattern = "("+
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)([0-9a-fA-F]{1,4}:){6}[0-9a-fA-F]{1,4}|" + // fe80:2:3:4:5:6:7:8
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)([0-9a-fA-F]{1,4}:){0,7}:|" + // fe80:: fe80:2:3:4:5:6:7::
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)([0-9a-fA-F]{1,4}:){0,6}:[0-9a-fA-F]{1,4}|" + // fe80::8 fe80:2:3:4:5:6::8 fe80:2:3:4:5:6::8
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)([0-9a-fA-F]{1,4}:){0,5}(:[0-9a-fA-F]{1,4}){1,1}|" + // fe80::7:8 fe80:2:3:4:5::7:8 fe80:2:3:4:5::8
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)([0-9a-fA-F]{1,4}:){0,4}(:[0-9a-fA-F]{1,4}){1,2}|" + // fe80::7:8 fe80:2:3:4:5::7:8 fe80:2:3:4:5::8
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)([0-9a-fA-F]{1,4}:){0,3}(:[0-9a-fA-F]{1,4}){1,3}|" + // fe80::6:7:8 fe80:2:3:4::6:7:8 fe80:2:3:4::8
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)([0-9a-fA-F]{1,4}:){0,2}(:[0-9a-fA-F]{1,4}){1,4}|" + // fe80::5:6:7:8 fe80:2:3::5:6:7:8 fe80:2:3::8
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)([0-9a-fA-F]{1,4}:){0,1}(:[0-9a-fA-F]{1,4}){1,5}|" + // fe80::4:5:6:7:8 fe80:2::4:5:6:7:8 fe80:2::8
"([fF][eE][8-9a-bA-B][0-9a-fA-F]:)(:[0-9a-fA-F]{1,4}){1,6}" + // fe80::3:4:5:6:7:8 fe80::3:4:5:6:7:8 fe80::8
")";
// allow for brackets with optional port at the end
localnetIpv6RegexPattern = String.format("(?:%1$s)|(?:\\[%1$s\\]\\:%2$s)", localnetIpv6RegexPattern, portRegexPattern);
// allow for brackets with optional port at the end
autolocalIpv6RegexPattern = String.format("(?:%1$s)|(?:\\[%1$s\\]\\:%2$s)", autolocalIpv6RegexPattern, portRegexPattern);
// match *.local
String localFqdnRegexPattern = String.format("(((?!-)[a-zA-Z0-9-]{1,63}(?<!-)\\.)+local(?:\\:%1$s)?)", portRegexPattern);
regexValidator.setPattern(String.format("^(?:(?:(?:%1$s)|(?:%2$s)|(?:%3$s)|(?:%4$s)|(?:%5$s)|(?:%6$s)|(?:%7$s)),\\s*)*(?:(?:%1$s)|(?:%2$s)|(?:%3$s)|(?:%4$s)|(?:%5$s)|(?:%6$s)|(?:%7$s))*$",
localnetIpv4RegexPatternA, localnetIpv4RegexPatternB, localnetIpv4RegexPatternC, autolocalIpv4RegexPattern, localnetIpv6RegexPattern, autolocalIpv6RegexPattern, localFqdnRegexPattern));
return regexValidator;
}
public static String getProofResultAsString(@Nullable AssetTxProofResult result) {
if (result == null) {
return "";

View file

@ -175,6 +175,220 @@ public class GUIUtilTest {
assertFalse(regexValidator.validate("example-.com").isValid);
}
@Test
public void testOnionAddressRegexValidator() {
RegexValidator regexValidator = GUIUtil.onionAddressRegexValidator();
assertTrue(regexValidator.validate("").isValid);
assertFalse(regexValidator.validate(" ").isValid);
// onion V2 addresses
assertTrue(regexValidator.validate("abcdefghij234567.onion").isValid);
assertTrue(regexValidator.validate("abcdefghijklmnop.onion,abcdefghijklmnop.onion").isValid);
assertTrue(regexValidator.validate("abcdefghijklmnop.onion, abcdefghijklmnop.onion").isValid);
assertTrue(regexValidator.validate("qrstuvwxyzABCDEF.onion,qrstuvwxyzABCDEF.onion,aaaaaaaaaaaaaaaa.onion").isValid);
assertTrue(regexValidator.validate("GHIJKLMNOPQRSTUV.onion:9999").isValid);
assertTrue(regexValidator.validate("WXYZ234567abcdef.onion,GHIJKLMNOPQRSTUV.onion:9999").isValid);
assertTrue(regexValidator.validate("aaaaaaaaaaaaaaaa.onion:9999,WXYZ234567abcdef.onion:9999,2222222222222222.onion:9999").isValid);
assertFalse(regexValidator.validate("abcd.onion").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop,abcdefghijklmnop.onion").isValid);
assertFalse(regexValidator.validate("abcdefghi2345689.onion:9999").isValid);
assertFalse(regexValidator.validate("onion:9999,abcdefghijklmnop.onion:9999").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop.onion:").isValid);
// onion v3 addresses
assertFalse(regexValidator.validate("32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfei.onion:8333").isValid); // 1 missing char
assertTrue(regexValidator.validate("wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000").isValid);
}
@Test
public void testLocalnetAddressRegexValidator() {
RegexValidator regexValidator = GUIUtil.localnetAddressRegexValidator();
assertTrue(regexValidator.validate("").isValid);
assertFalse(regexValidator.validate(" ").isValid);
// onion V2 addresses
assertFalse(regexValidator.validate("abcdefghij234567.onion").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop.onion,abcdefghijklmnop.onion").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop.onion, abcdefghijklmnop.onion").isValid);
assertFalse(regexValidator.validate("qrstuvwxyzABCDEF.onion,qrstuvwxyzABCDEF.onion,aaaaaaaaaaaaaaaa.onion").isValid);
assertFalse(regexValidator.validate("GHIJKLMNOPQRSTUV.onion:9999").isValid);
assertFalse(regexValidator.validate("WXYZ234567abcdef.onion,GHIJKLMNOPQRSTUV.onion:9999").isValid);
assertFalse(regexValidator.validate("aaaaaaaaaaaaaaaa.onion:9999,WXYZ234567abcdef.onion:9999,2222222222222222.onion:9999").isValid);
assertFalse(regexValidator.validate("abcd.onion").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop,abcdefghijklmnop.onion").isValid);
assertFalse(regexValidator.validate("abcdefghi2345689.onion:9999").isValid);
assertFalse(regexValidator.validate("onion:9999,abcdefghijklmnop.onion:9999").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop.onion:").isValid);
// onion v3 addresses
assertFalse(regexValidator.validate("32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfei.onion:8333").isValid); // 1 missing char
assertFalse(regexValidator.validate("wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000").isValid);
// ipv4 addresses
assertFalse(regexValidator.validate("12.34.56.78").isValid);
assertFalse(regexValidator.validate("12.34.56.78,87.65.43.21").isValid);
assertFalse(regexValidator.validate("12.34.56.78:8888").isValid);
assertFalse(regexValidator.validate("12.34.56.788").isValid);
assertFalse(regexValidator.validate("12.34.56.78:").isValid);
// ipv4 local addresses
assertTrue(regexValidator.validate("10.10.10.10").isValid);
assertTrue(regexValidator.validate("172.19.1.1").isValid);
assertTrue(regexValidator.validate("172.19.1.1").isValid);
assertTrue(regexValidator.validate("192.168.1.1").isValid);
assertTrue(regexValidator.validate("192.168.1.1,172.16.1.1").isValid);
assertTrue(regexValidator.validate("192.168.1.1:8888,192.168.1.2:8888").isValid);
assertFalse(regexValidator.validate("192.168.1.888").isValid);
assertFalse(regexValidator.validate("192.168.1.1:").isValid);
// ipv4 autolocal addresses
assertTrue(regexValidator.validate("169.254.123.232").isValid);
// ipv6 local addresses
assertTrue(regexValidator.validate("fe80:2:3:4:5:6:7:8").isValid);
assertTrue(regexValidator.validate("fe80::").isValid);
assertTrue(regexValidator.validate("fc00::").isValid);
assertTrue(regexValidator.validate("fd00::,fe80::1").isValid);
assertTrue(regexValidator.validate("fd00::8").isValid);
assertTrue(regexValidator.validate("fd00::7:8").isValid);
assertTrue(regexValidator.validate("fd00::6:7:8").isValid);
assertTrue(regexValidator.validate("fd00::5:6:7:8").isValid);
assertTrue(regexValidator.validate("fd00::4:5:6:7:8").isValid);
assertTrue(regexValidator.validate("fd00::3:4:5:6:7:8").isValid);
assertTrue(regexValidator.validate("fd00:2:3:4:5:6:7:8").isValid);
assertTrue(regexValidator.validate("fd00::0202:B3FF:FE1E:8329").isValid);
assertTrue(regexValidator.validate("fd00::0202:B3FF:FE1E:8329,FE80::0202:B3FF:FE1E:8329").isValid);
// ipv6 local with optional port at the end
assertTrue(regexValidator.validate("[fd00::1]:8081").isValid);
assertTrue(regexValidator.validate("[fd00::1]:8081,[fc00::1]:8081").isValid);
assertTrue(regexValidator.validate("[FE80::0202:B3FF:FE1E:8329]:8333").isValid);
// ipv6 loopback
assertFalse(regexValidator.validate("::1").isValid);
// ipv6 unicast
assertFalse(regexValidator.validate("2001::").isValid);
assertFalse(regexValidator.validate("[::1]:8333").isValid);
assertFalse(regexValidator.validate("[2001:db8::1]:80").isValid);
assertFalse(regexValidator.validate("[aaaa::bbbb]:8333").isValid);
assertFalse(regexValidator.validate("1200:0000:AB00:1234:O000:2552:7777:1313").isValid);
// *.local fqdn hostnames
assertTrue(regexValidator.validate("mynode.local").isValid);
assertTrue(regexValidator.validate("mynode.local:8081").isValid);
// non-local fqdn hostnames
assertFalse(regexValidator.validate("example.com").isValid);
assertFalse(regexValidator.validate("foo.example.com,bar.example.com").isValid);
assertFalse(regexValidator.validate("foo.example.com:8333,bar.example.com:8333").isValid);
// invalid fqdn hostnames
assertFalse(regexValidator.validate("mynode.local:65536").isValid);
assertFalse(regexValidator.validate("-example.com").isValid);
assertFalse(regexValidator.validate("example-.com").isValid);
}
@Test
public void testLocalhostAddressRegexValidator() {
RegexValidator regexValidator = GUIUtil.localhostAddressRegexValidator();
assertTrue(regexValidator.validate("").isValid);
assertFalse(regexValidator.validate(" ").isValid);
// onion V2 addresses
assertFalse(regexValidator.validate("abcdefghij234567.onion").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop.onion,abcdefghijklmnop.onion").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop.onion, abcdefghijklmnop.onion").isValid);
assertFalse(regexValidator.validate("qrstuvwxyzABCDEF.onion,qrstuvwxyzABCDEF.onion,aaaaaaaaaaaaaaaa.onion").isValid);
assertFalse(regexValidator.validate("GHIJKLMNOPQRSTUV.onion:9999").isValid);
assertFalse(regexValidator.validate("WXYZ234567abcdef.onion,GHIJKLMNOPQRSTUV.onion:9999").isValid);
assertFalse(regexValidator.validate("aaaaaaaaaaaaaaaa.onion:9999,WXYZ234567abcdef.onion:9999,2222222222222222.onion:9999").isValid);
assertFalse(regexValidator.validate("abcd.onion").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop,abcdefghijklmnop.onion").isValid);
assertFalse(regexValidator.validate("abcdefghi2345689.onion:9999").isValid);
assertFalse(regexValidator.validate("onion:9999,abcdefghijklmnop.onion:9999").isValid);
assertFalse(regexValidator.validate("abcdefghijklmnop.onion:").isValid);
// onion v3 addresses
assertFalse(regexValidator.validate("32zzibxmqi2ybxpqyggwwuwz7a3lbvtzoloti7cxoevyvijexvgsfei.onion:8333").isValid); // 1 missing char
assertFalse(regexValidator.validate("wizseedscybbttk4bmb2lzvbuk2jtect37lcpva4l3twktmkzemwbead.onion:8000").isValid);
// ipv4 addresses
assertFalse(regexValidator.validate("12.34.56.78").isValid);
assertFalse(regexValidator.validate("12.34.56.78,87.65.43.21").isValid);
assertFalse(regexValidator.validate("12.34.56.78:8888").isValid);
assertFalse(regexValidator.validate("12.34.56.788").isValid);
assertFalse(regexValidator.validate("12.34.56.78:").isValid);
// ipv4 loopback addresses
assertTrue(regexValidator.validate("127.0.0.1").isValid);
assertTrue(regexValidator.validate("127.0.1.1").isValid);
// ipv4 local addresses
assertFalse(regexValidator.validate("10.10.10.10").isValid);
assertFalse(regexValidator.validate("172.19.1.1").isValid);
assertFalse(regexValidator.validate("172.19.1.1").isValid);
assertFalse(regexValidator.validate("192.168.1.1").isValid);
assertFalse(regexValidator.validate("192.168.1.1,172.16.1.1").isValid);
assertFalse(regexValidator.validate("192.168.1.1:8888,192.168.1.2:8888").isValid);
assertFalse(regexValidator.validate("192.168.1.888").isValid);
assertFalse(regexValidator.validate("192.168.1.1:").isValid);
// ipv4 autolocal addresses
assertFalse(regexValidator.validate("169.254.123.232").isValid);
// ipv6 local addresses
assertFalse(regexValidator.validate("fe80::").isValid);
assertFalse(regexValidator.validate("fc00::").isValid);
assertFalse(regexValidator.validate("fd00::8").isValid);
assertFalse(regexValidator.validate("fd00::7:8").isValid);
assertFalse(regexValidator.validate("fd00::6:7:8").isValid);
assertFalse(regexValidator.validate("fd00::5:6:7:8").isValid);
assertFalse(regexValidator.validate("fd00::3:4:5:6:7:8").isValid);
assertFalse(regexValidator.validate("fd00::4:5:6:7:8").isValid);
assertFalse(regexValidator.validate("fd00:2:3:4:5:6:7:8").isValid);
assertFalse(regexValidator.validate("fd00::0202:B3FF:FE1E:8329").isValid);
assertFalse(regexValidator.validate("FE80:0000:0000:0000:0202:B3FF:FE1E:8329").isValid);
assertFalse(regexValidator.validate("FE80::0202:B3FF:FE1E:8329").isValid);
assertFalse(regexValidator.validate("FE80::0202:B3FF:FE1E:8329,FE80:0000:0000:0000:0202:B3FF:FE1E:8329").isValid);
// ipv6 local with optional port at the end
assertFalse(regexValidator.validate("[fd00::1]:8081").isValid);
assertFalse(regexValidator.validate("[fd00::1]:8081,[fc00::1]:8081").isValid);
// ipv6 loopback
assertTrue(regexValidator.validate("::1").isValid);
assertTrue(regexValidator.validate("::2").isValid);
assertTrue(regexValidator.validate("[::1]:8333").isValid);
// ipv6 unicast
assertFalse(regexValidator.validate("2001::").isValid);
assertFalse(regexValidator.validate("[FE80::0202:B3FF:FE1E:8329]:8333").isValid);
assertFalse(regexValidator.validate("[2001:db8::1]:80").isValid);
assertFalse(regexValidator.validate("[aaaa::bbbb]:8333").isValid);
assertFalse(regexValidator.validate("1200:0000:AB00:1234:O000:2552:7777:1313").isValid);
// localhost fqdn hostnames
assertTrue(regexValidator.validate("localhost").isValid);
assertTrue(regexValidator.validate("localhost:8081").isValid);
// local fqdn hostnames
assertFalse(regexValidator.validate("mynode.local:8081").isValid);
// non-local fqdn hostnames
assertFalse(regexValidator.validate("example.com").isValid);
assertFalse(regexValidator.validate("foo.example.com,bar.example.com").isValid);
assertFalse(regexValidator.validate("foo.example.com:8333,bar.example.com:8333").isValid);
// invalid fqdn hostnames
assertFalse(regexValidator.validate("mynode.local:65536").isValid);
assertFalse(regexValidator.validate("-example.com").isValid);
assertFalse(regexValidator.validate("example-.com").isValid);
}
@Test
public void testGetBsqInUsd() {
PriceFeedService priceFeedService = mock(PriceFeedService.class);