Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use https:// for XMR explorer API endpoints, except if localhost or Tor #4492

Merged
merged 6 commits into from
Sep 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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://, 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;
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/bisq/core/user/Preferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid

private static final ArrayList<String> XMR_TX_PROOF_SERVICES_CLEAR_NET = new ArrayList<>(Arrays.asList(
"xmrblocks.monero.emzy.de", // @emzy
"node77.monero.wiz.biz" // @wiz
"explorer.monero.wiz.biz" // @wiz
));
private static final ArrayList<String> XMR_TX_PROOF_SERVICES = new ArrayList<>(Arrays.asList(
"monero3bec7m26vx6si6qo7q7imlaoz45ot5m2b5z2ppgoooo6jx2rqd.onion", // @emzy
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/resources/i18n/displayStrings.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1098,7 +1098,7 @@ setting.preferences.autoConfirmXMR=XMR auto-confirm
setting.preferences.autoConfirmEnabled=Enabled
setting.preferences.autoConfirmRequiredConfirmations=Required confirmations
setting.preferences.autoConfirmMaxTradeSize=Max. trade amount (BTC)
setting.preferences.autoConfirmServiceAddresses=Service addresses
setting.preferences.autoConfirmServiceAddresses=Monero Explorer URLs (uses Tor, except for localhost, LAN IP addresses, and *.local hostnames)
setting.preferences.deviationToLarge=Values higher than {0}% are not allowed.
setting.preferences.txFee=Withdrawal transaction fee (satoshis/byte)
setting.preferences.useCustomValue=Use custom value
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 @@ -672,12 +673,41 @@ private void initializeAutoConfirmOptions() {

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);
}
};

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