Skip to content

Commit

Permalink
Use fancy regex to use http or https for XMR explorer API endpoint
Browse files Browse the repository at this point in the history
* 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
* Otherwise use HTTPS over clearnet without Tor proxy
  • Loading branch information
wiz committed Sep 7, 2020
1 parent e7d24ef commit 95186c9
Show file tree
Hide file tree
Showing 4 changed files with 380 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,19 @@ public String toString() {
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://, don't use Tor
} if (model.getServiceAddress().regionMatches(0, "https:", 0, 6)) {
httpClient.setBaseUrl(model.getServiceAddress());
httpClient.setIgnoreSocks5Proxy(true);
// it's a raw onion so add http:// and use Tor proxy
} else {
httpClient.setBaseUrl("http://" + model.getServiceAddress());
httpClient.setIgnoreSocks5Proxy(false);
}

terminated = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
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;
Expand Down Expand Up @@ -671,13 +672,40 @@ private void initializeAutoConfirmOptions() {
displayCurrenciesGridRowIndex += 4;

autoConfServiceAddressListener = (observable, oldValue, newValue) -> {
if (!newValue.equals(oldValue)) {
List<String> serviceAddresses = Arrays.asList(StringUtils.deleteWhitespace(newValue).split(","));
if (!newValue.equals(oldValue) && autoConfServiceAddressTf.getValidator().validate(newValue).isValid) {

RegexValidator onionRegex = GUIUtil.onionAddressRegexValidator();
RegexValidator localhostRegex = GUIUtil.localhostAddressRegexValidator();
RegexValidator localnetRegex = GUIUtil.localnetAddressRegexValidator();

List<String> serviceAddressesRaw = Arrays.asList(StringUtils.deleteWhitespace(newValue).split(","));
List<String> serviceAddressesParsed = new ArrayList<String>();

// 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
serviceAddressesRaw.forEach((addr) -> {
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);
}
});

// 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);
preferences.setAutoConfServiceAddresses("XMR", serviceAddressesParsed);
}
};

Expand Down
121 changes: 121 additions & 0 deletions desktop/src/main/java/bisq/desktop/util/GUIUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -1161,6 +1161,127 @@ public static RegexValidator addressRegexValidator() {
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 "";
Expand Down
Loading

0 comments on commit 95186c9

Please sign in to comment.