From e1084ffadd9d70b6d28f8e254523097283c9eee2 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 17 Feb 2020 11:07:14 +0000 Subject: [PATCH] Support IPv6 SLAAC addresses. --- .../plugin/tcp/AndroidLanTcpPlugin.java | 27 ++- .../bramble/api/plugin/LanTcpConstants.java | 1 + .../bramble/util/PrivacyUtils.java | 52 ++--- .../bramble/plugin/tcp/LanTcpPlugin.java | 114 +++++++++-- .../bramble/plugin/tcp/PortMapperImpl.java | 10 +- .../bramble/plugin/tcp/TcpPlugin.java | 183 +++++++++++------- .../bramble/plugin/tcp/WanTcpPlugin.java | 17 +- .../android/reporting/BriarReportPrimer.java | 20 +- 8 files changed, 279 insertions(+), 145 deletions(-) diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java index c3d080918..f82f02ee3 100644 --- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java +++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tcp/AndroidLanTcpPlugin.java @@ -83,7 +83,12 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { } @Override - protected List getUsableLocalInetAddresses() { + protected List getUsableLocalInetAddresses(boolean ipv4) { + if (ipv4) return getUsableLocalIpv4Addresses(); + else return getUsableLocalIpv6Addresses(); + } + + private List getUsableLocalIpv4Addresses() { // If the device doesn't have wifi, don't open any sockets if (wifiManager == null) return emptyList(); // If we're connected to a wifi network, return its address @@ -100,6 +105,17 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { return emptyList(); } + private List getUsableLocalIpv6Addresses() { + // If the device doesn't have wifi, don't open any sockets + if (wifiManager == null) return emptyList(); + // If we have a SLAAC address, return it + for (InetAddress addr : getLocalInetAddresses()) { + if (isSlaacAddress(addr)) return singletonList(addr); + } + // No suitable addresses + return emptyList(); + } + private InetAddress intToInetAddress(int ip) { byte[] ipBytes = new byte[4]; ipBytes[0] = (byte) (ip & 0xFF); @@ -150,12 +166,13 @@ class AndroidLanTcpPlugin extends LanTcpPlugin { } else if (addrs.isEmpty()) { LOG.info("Not connected to wifi"); socketFactory = SocketFactory.getDefault(); - // Server socket may not have been closed automatically when - // interface was taken down. Socket will be cleared and state + // Server sockets may not have been closed automatically when + // interface was taken down. Sockets will be cleared and state // updated in acceptContactConnections() if (s == ACTIVE) { - LOG.info("Closing server socket"); - tryToClose(state.getServerSocket(), LOG, WARNING); + LOG.info("Closing server sockets"); + tryToClose(state.getServerSocket(true), LOG, WARNING); + tryToClose(state.getServerSocket(false), LOG, WARNING); } } else { LOG.info("Connected to wifi"); diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java index d546a60cd..835d67b5a 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/LanTcpConstants.java @@ -7,6 +7,7 @@ public interface LanTcpConstants { // Transport properties (shared with contacts) String PROP_IP_PORTS = "ipPorts"; String PROP_PORT = "port"; + String PROP_SLAAC = "slaac"; // A local setting String PREF_LAN_IP_PORTS = "ipPorts"; diff --git a/bramble-api/src/main/java/org/briarproject/bramble/util/PrivacyUtils.java b/bramble-api/src/main/java/org/briarproject/bramble/util/PrivacyUtils.java index 9156b9caa..10df6686c 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/util/PrivacyUtils.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/util/PrivacyUtils.java @@ -2,13 +2,17 @@ package org.briarproject.bramble.util; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import java.net.Inet6Address; +import java.net.Inet4Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import javax.annotation.Nullable; +import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; +import static org.briarproject.bramble.util.StringUtils.isValidMac; +import static org.briarproject.bramble.util.StringUtils.toHexString; + @NotNullByDefault public class PrivacyUtils { @@ -19,7 +23,7 @@ public class PrivacyUtils { @Nullable public static String scrubMacAddress(@Nullable String address) { - if (address == null || address.length() == 0) return null; + if (isNullOrEmpty(address) || !isValidMac(address)) return address; // this is a fake address we need to know about if (address.equals("02:00:00:00:00:00")) return address; // keep first and last octet of MAC address @@ -27,39 +31,37 @@ public class PrivacyUtils { + address.substring(14, 17); } - @Nullable public static String scrubInetAddress(InetAddress address) { - // don't scrub link and site local addresses - if (address.isLinkLocalAddress() || address.isSiteLocalAddress()) - return address.toString(); - // completely scrub IPv6 addresses - if (address instanceof Inet6Address) return "[scrubbed]"; - // keep first and last octet of IPv4 addresses - return scrubInetAddress(address.toString()); + if (address instanceof Inet4Address) { + // Don't scrub local IPv4 addresses + if (address.isLoopbackAddress() || address.isLinkLocalAddress() || + address.isSiteLocalAddress()) { + return address.getHostAddress(); + } + // Keep first and last octet of non-local IPv4 addresses + return scrubIpv4Address(address.getAddress()); + } else { + // Keep first and last octet of IPv6 addresses + return scrubIpv6Address(address.getAddress()); + } } - @Nullable - public static String scrubInetAddress(@Nullable String address) { - if (address == null) return null; - - int firstDot = address.indexOf("."); - if (firstDot == -1) return "[scrubbed]"; - String prefix = address.substring(0, firstDot + 1); - int lastDot = address.lastIndexOf("."); - String suffix = address.substring(lastDot, address.length()); - return prefix + "[scrubbed]" + suffix; + private static String scrubIpv4Address(byte[] ipv4) { + return (ipv4[0] & 0xFF) + ".[scrubbed]." + (ipv4[3] & 0xFF); + } + + private static String scrubIpv6Address(byte[] ipv6) { + String hex = toHexString(ipv6).toLowerCase(); + return hex.substring(0, 2) + "[scrubbed]" + hex.substring(30); } - @Nullable public static String scrubSocketAddress(InetSocketAddress address) { - InetAddress inetAddress = address.getAddress(); - return scrubInetAddress(inetAddress); + return scrubInetAddress(address.getAddress()); } - @Nullable public static String scrubSocketAddress(SocketAddress address) { if (address instanceof InetSocketAddress) return scrubSocketAddress((InetSocketAddress) address); - return scrubInetAddress(address.toString()); + return "[scrubbed]"; } } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java index 1200b10b1..b3c583728 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/LanTcpPlugin.java @@ -14,6 +14,7 @@ import org.briarproject.bramble.api.settings.Settings; import java.io.IOException; import java.net.Inet4Address; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.InterfaceAddress; @@ -22,7 +23,6 @@ import java.net.Socket; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.List; -import java.util.Random; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -30,6 +30,8 @@ import javax.annotation.Nullable; import static java.lang.Integer.parseInt; import static java.util.Collections.addAll; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; import static java.util.Collections.sort; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; @@ -39,11 +41,14 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS; import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_PORT; +import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_SLAAC; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress; +import static org.briarproject.bramble.util.StringUtils.fromHexString; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.join; +import static org.briarproject.bramble.util.StringUtils.toHexString; @NotNullByDefault class LanTcpPlugin extends TcpPlugin { @@ -53,6 +58,13 @@ class LanTcpPlugin extends TcpPlugin { private static final int MAX_ADDRESSES = 4; private static final String SEPARATOR = ","; + /** + * The network prefix of a SLAAC IPv6 address. See + * https://tools.ietf.org/html/rfc4862#section-5.3 + */ + private static final byte[] SLAAC_PREFIX = + new byte[] {(byte) 0xFE, (byte) 0x80, 0, 0, 0, 0, 0, 0}; + /** * The IP address of an Android device providing a wifi access point. */ @@ -99,22 +111,22 @@ class LanTcpPlugin extends TcpPlugin { protected void initialisePortProperty() { TransportProperties p = callback.getLocalProperties(); if (isNullOrEmpty(p.get(PROP_PORT))) { - int port = new Random().nextInt(32768) + 32768; + int port = chooseEphemeralPort(); p.put(PROP_PORT, String.valueOf(port)); callback.mergeLocalProperties(p); } } @Override - protected List getLocalSocketAddresses() { + protected List getLocalSocketAddresses(boolean ipv4) { TransportProperties p = callback.getLocalProperties(); int preferredPort = parsePortProperty(p.get(PROP_PORT)); String oldIpPorts = p.get(PROP_IP_PORTS); - List olds = parseSocketAddresses(oldIpPorts); + List olds = parseIpv4SocketAddresses(oldIpPorts); List locals = new ArrayList<>(); List fallbacks = new ArrayList<>(); - for (InetAddress local : getUsableLocalInetAddresses()) { + for (InetAddress local : getUsableLocalInetAddresses(ipv4)) { // If we've used this address before, try to use the same port int port = preferredPort; for (InetSocketAddress old : olds) { @@ -140,17 +152,17 @@ class LanTcpPlugin extends TcpPlugin { } } - private List parseSocketAddresses(String ipPorts) { + private List parseIpv4SocketAddresses(String ipPorts) { List addresses = new ArrayList<>(); if (isNullOrEmpty(ipPorts)) return addresses; for (String ipPort : ipPorts.split(SEPARATOR)) { - InetSocketAddress a = parseSocketAddress(ipPort); + InetSocketAddress a = parseIpv4SocketAddress(ipPort); if (a != null) addresses.add(a); } return addresses; } - protected List getUsableLocalInetAddresses() { + protected List getUsableLocalInetAddresses(boolean ipv4) { List ifAddrs = new ArrayList<>(getLocalInterfaceAddresses()); // Prefer longer network prefixes @@ -159,13 +171,18 @@ class LanTcpPlugin extends TcpPlugin { List addrs = new ArrayList<>(); for (InterfaceAddress ifAddr : ifAddrs) { InetAddress addr = ifAddr.getAddress(); - if (isAcceptableAddress(addr)) addrs.add(addr); + if (isAcceptableAddress(addr, ipv4)) addrs.add(addr); } return addrs; } @Override - protected void setLocalSocketAddress(InetSocketAddress a) { + protected void setLocalSocketAddress(InetSocketAddress a, boolean ipv4) { + if (ipv4) setLocalIpv4SocketAddress(a); + else setLocalIpv6SocketAddress(a); + } + + private void setLocalIpv4SocketAddress(InetSocketAddress a) { String ipPort = getIpPortString(a); // Get the list of recently used addresses String setting = callback.getSettings().get(PREF_LAN_IP_PORTS); @@ -198,11 +215,38 @@ class LanTcpPlugin extends TcpPlugin { callback.mergeSettings(settings); } + private void setLocalIpv6SocketAddress(InetSocketAddress a) { + if (isSlaacAddress(a.getAddress())) { + String property = toHexString(a.getAddress().getAddress()); + TransportProperties properties = new TransportProperties(); + properties.put(PROP_SLAAC, property); + callback.mergeLocalProperties(properties); + } + } + + // See https://tools.ietf.org/html/rfc4862#section-5.3 + protected boolean isSlaacAddress(InetAddress a) { + if (!(a instanceof Inet6Address)) return false; + byte[] ip = a.getAddress(); + for (int i = 0; i < 8; i++) { + if (ip[i] != SLAAC_PREFIX[i]) return false; + } + return (ip[8] & 0x02) == 0x02 + && ip[11] == (byte) 0xFF + && ip[12] == (byte) 0xFE; + } + @Override protected List getRemoteSocketAddresses( + TransportProperties p, boolean ipv4) { + if (ipv4) return getRemoteIpv4SocketAddresses(p); + else return getRemoteIpv6SocketAddresses(p); + } + + private List getRemoteIpv4SocketAddresses( TransportProperties p) { String ipPorts = p.get(PROP_IP_PORTS); - List remotes = parseSocketAddresses(ipPorts); + List remotes = parseIpv4SocketAddresses(ipPorts); int port = parsePortProperty(p.get(PROP_PORT)); // If the contact has a preferred port, we can guess their IP:port when // they're providing a wifi access point @@ -217,20 +261,48 @@ class LanTcpPlugin extends TcpPlugin { return remotes; } - private boolean isAcceptableAddress(InetAddress a) { - // Accept link-local and site-local IPv4 addresses - boolean ipv4 = a instanceof Inet4Address; - boolean loop = a.isLoopbackAddress(); - boolean link = a.isLinkLocalAddress(); - boolean site = a.isSiteLocalAddress(); - return ipv4 && !loop && (link || site); + private List getRemoteIpv6SocketAddresses( + TransportProperties p) { + InetAddress slaac = parseSlaacProperty(p.get(PROP_SLAAC)); + int port = parsePortProperty(p.get(PROP_PORT)); + if (slaac == null || port == 0) return emptyList(); + return singletonList(new InetSocketAddress(slaac, port)); + } + + @Nullable + private InetAddress parseSlaacProperty(String slaacProperty) { + if (isNullOrEmpty(slaacProperty)) return null; + try { + byte[] ip = fromHexString(slaacProperty); + if (ip.length != 16) return null; + InetAddress a = InetAddress.getByAddress(ip); + return isSlaacAddress(a) ? a : null; + } catch (IllegalArgumentException | UnknownHostException e) { + return null; + } + } + + private boolean isAcceptableAddress(InetAddress a, boolean ipv4) { + if (ipv4) { + // Accept link-local and site-local IPv4 addresses + boolean isIpv4 = a instanceof Inet4Address; + boolean loop = a.isLoopbackAddress(); + boolean link = a.isLinkLocalAddress(); + boolean site = a.isSiteLocalAddress(); + return isIpv4 && !loop && (link || site); + } else { + // Accept IPv6 SLAAC addresses + return isSlaacAddress(a); + } } @Override protected boolean isConnectable(InterfaceAddress local, InetSocketAddress remote) { if (remote.getPort() == 0) return false; - if (!isAcceptableAddress(remote.getAddress())) return false; + InetAddress remoteAddress = remote.getAddress(); + boolean ipv4 = local.getAddress() instanceof Inet4Address; + if (!isAcceptableAddress(remoteAddress, ipv4)) return false; // Try to determine whether the address is on the same LAN as us byte[] localIp = local.getAddress().getAddress(); byte[] remoteIp = remote.getAddress().getAddress(); @@ -262,7 +334,7 @@ class LanTcpPlugin extends TcpPlugin { @Override public KeyAgreementListener createKeyAgreementListener(byte[] commitment) { ServerSocket ss = null; - for (InetSocketAddress addr : getLocalSocketAddresses()) { + for (InetSocketAddress addr : getLocalSocketAddresses(true)) { // Don't try to reuse the same port we use for contact connections addr = new InetSocketAddress(addr.getAddress(), 0); try { @@ -291,7 +363,7 @@ class LanTcpPlugin extends TcpPlugin { @Override public DuplexTransportConnection createKeyAgreementConnection( byte[] commitment, BdfList descriptor) { - ServerSocket ss = state.getServerSocket(); + ServerSocket ss = state.getServerSocket(true); if (ss == null) return null; InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress()); if (local == null) { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java index 584510705..8b5adc876 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/PortMapperImpl.java @@ -54,11 +54,13 @@ class PortMapperImpl implements PortMapper { shutdownManager.addShutdownHook(() -> deleteMapping(port)); } String externalString = gateway.getExternalIPAddress(); - if (LOG.isLoggable(INFO)) - LOG.info( - "External address " + scrubInetAddress(externalString)); - if (externalString != null) + if (externalString == null) { + LOG.info("External address not available"); + } else { external = InetAddress.getByName(externalString); + if (LOG.isLoggable(INFO)) + LOG.info("External address " + scrubInetAddress(external)); + } } catch (IOException | SAXException e) { logException(LOG, WARNING, e); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index 4a8ea8753..954868367 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -43,6 +43,7 @@ import javax.annotation.Nullable; import javax.annotation.concurrent.GuardedBy; import javax.annotation.concurrent.ThreadSafe; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.list; import static java.util.logging.Level.INFO; @@ -78,20 +79,22 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { * Returns zero or more socket addresses on which the plugin should listen, * in order of preference. At most one of the addresses will be bound. */ - protected abstract List getLocalSocketAddresses(); + protected abstract List getLocalSocketAddresses( + boolean ipv4); /** * Adds the address on which the plugin is listening to the transport * properties. */ - protected abstract void setLocalSocketAddress(InetSocketAddress a); + protected abstract void setLocalSocketAddress(InetSocketAddress a, + boolean ipv4); /** * Returns zero or more socket addresses for connecting to a contact with * the given transport properties. */ protected abstract List getRemoteSocketAddresses( - TransportProperties p); + TransportProperties p, boolean ipv4); /** * Returns true if connections to the given address can be attempted. @@ -136,37 +139,43 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { protected void bind() { bindExecutor.execute(() -> { if (getState() != INACTIVE) return; - ServerSocket ss = null; - for (InetSocketAddress addr : getLocalSocketAddresses()) { - try { - ss = new ServerSocket(); - ss.bind(addr); - break; - } catch (IOException e) { - if (LOG.isLoggable(INFO)) - LOG.info("Failed to bind " + scrubSocketAddress(addr)); - tryToClose(ss, LOG, WARNING); - } - } - if (ss == null) { - LOG.info("Could not bind server socket"); - return; - } - if (!state.setServerSocket(ss)) { - LOG.info("Closing redundant server socket"); - tryToClose(ss, LOG, WARNING); - return; - } - backoff.reset(); - InetSocketAddress local = - (InetSocketAddress) ss.getLocalSocketAddress(); - setLocalSocketAddress(local); - if (LOG.isLoggable(INFO)) - LOG.info("Listening on " + scrubSocketAddress(local)); - acceptContactConnections(ss); + bind(true); + bind(false); }); } + private void bind(boolean ipv4) { + ServerSocket ss = null; + for (InetSocketAddress addr : getLocalSocketAddresses(ipv4)) { + try { + ss = new ServerSocket(); + ss.bind(addr); + break; + } catch (IOException e) { + if (LOG.isLoggable(INFO)) + LOG.info("Failed to bind " + scrubSocketAddress(addr)); + tryToClose(ss, LOG, WARNING); + } + } + if (ss == null) { + LOG.info("Could not bind server socket"); + return; + } + if (!state.setServerSocket(ss, ipv4)) { + LOG.info("Closing redundant server socket"); + tryToClose(ss, LOG, WARNING); + return; + } + backoff.reset(); + InetSocketAddress local = + (InetSocketAddress) ss.getLocalSocketAddress(); + setLocalSocketAddress(local, ipv4); + if (LOG.isLoggable(INFO)) + LOG.info("Listening on " + scrubSocketAddress(local)); + ServerSocket finalSocket = ss; + ioExecutor.execute(() -> acceptContactConnections(finalSocket, ipv4)); + } + String getIpPortString(InetSocketAddress a) { String addr = a.getAddress().getHostAddress(); int percent = addr.indexOf('%'); @@ -174,7 +183,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { return addr + ":" + a.getPort(); } - private void acceptContactConnections(ServerSocket ss) { + private void acceptContactConnections(ServerSocket ss, boolean ipv4) { while (true) { Socket s; try { @@ -183,12 +192,13 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { } catch (IOException e) { // This is expected when the server socket is closed LOG.info("Server socket closed"); - state.clearServerSocket(ss); + state.clearServerSocket(ss, ipv4); return; } - if (LOG.isLoggable(INFO)) + if (LOG.isLoggable(INFO)) { LOG.info("Connection from " + scrubSocketAddress(s.getRemoteSocketAddress())); + } backoff.reset(); callback.handleConnection(new TcpTransportConnection(this, s)); } @@ -196,8 +206,9 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @Override public void stop() { - ServerSocket ss = state.setStopped(); - tryToClose(ss, LOG, WARNING); + for (@Nullable ServerSocket ss : state.setStopped()) { + tryToClose(ss, LOG, WARNING); + } } @Override @@ -242,14 +253,22 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @Override public DuplexTransportConnection createConnection(TransportProperties p) { - ServerSocket ss = state.getServerSocket(); + DuplexTransportConnection c = createConnection(p, true); + if (c != null) return c; + return createConnection(p, false); + } + + @Nullable + private DuplexTransportConnection createConnection(TransportProperties p, + boolean ipv4) { + ServerSocket ss = state.getServerSocket(ipv4); if (ss == null) return null; InterfaceAddress local = getLocalInterfaceAddress(ss.getInetAddress()); if (local == null) { LOG.warning("No interface for server socket"); return null; } - for (InetSocketAddress remote : getRemoteSocketAddresses(p)) { + for (InetSocketAddress remote : getRemoteSocketAddresses(p, ipv4)) { // Don't try to connect to our own address if (!canConnectToOwnAddress() && remote.getAddress().equals(ss.getInetAddress())) { @@ -274,9 +293,10 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { LOG.info("Connected to " + scrubSocketAddress(remote)); return new TcpTransportConnection(this, s); } catch (IOException e) { - if (LOG.isLoggable(INFO)) + if (LOG.isLoggable(INFO)) { LOG.info("Could not connect to " + scrubSocketAddress(remote)); + } } } return null; @@ -299,8 +319,12 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { return new Socket(); } + int chooseEphemeralPort() { + return 32768 + (int) (Math.random() * 32768); + } + @Nullable - InetSocketAddress parseSocketAddress(String ipPort) { + InetSocketAddress parseIpv4SocketAddress(String ipPort) { if (isNullOrEmpty(ipPort)) return null; String[] split = ipPort.split(":"); if (split.length != 2) return null; @@ -311,14 +335,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { InetAddress a = InetAddress.getByName(addr); int p = Integer.parseInt(port); return new InetSocketAddress(a, p); - } catch (UnknownHostException e) { - if (LOG.isLoggable(WARNING)) - // not scrubbing to enable us to find the problem - LOG.warning("Invalid address: " + addr); - return null; - } catch (NumberFormatException e) { - if (LOG.isLoggable(WARNING)) - LOG.warning("Invalid port: " + port); + } catch (UnknownHostException | NumberFormatException e) { return null; } } @@ -389,13 +406,15 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @IoExecutor private void onSettingsUpdated(Settings settings) { boolean enabledByUser = settings.getBoolean(PREF_PLUGIN_ENABLE, false); - ServerSocket ss = state.setEnabledByUser(enabledByUser); + List toClose = state.setEnabledByUser(enabledByUser); State s = getState(); - if (ss != null) { - LOG.info("Disabled by user, closing server socket"); - tryToClose(ss, LOG, WARNING); + if (!toClose.isEmpty()) { + LOG.info("Disabled by user, closing server sockets"); + for (@Nullable ServerSocket ss : toClose) { + tryToClose(ss, LOG, WARNING); + } } else if (s == INACTIVE) { - LOG.info("Enabled by user, opening server socket"); + LOG.info("Enabled by user, opening server sockets"); bind(); } } @@ -409,7 +428,7 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { @GuardedBy("this") @Nullable - private ServerSocket serverSocket = null; + private ServerSocket serverSocketV4 = null, serverSocketV6 = null; synchronized void setStarted(boolean enabledByUser) { started = true; @@ -417,48 +436,62 @@ abstract class TcpPlugin implements DuplexPlugin, EventListener { callback.pluginStateChanged(getState()); } - @Nullable - synchronized ServerSocket setStopped() { + synchronized List setStopped() { stopped = true; - ServerSocket ss = serverSocket; - serverSocket = null; + List toClose = clearServerSockets(); callback.pluginStateChanged(getState()); - return ss; + return toClose; } - @Nullable - synchronized ServerSocket setEnabledByUser(boolean enabledByUser) { + @GuardedBy("this") + private List clearServerSockets() { + List toClose = asList(serverSocketV4, serverSocketV6); + serverSocketV4 = null; + serverSocketV6 = null; + return toClose; + } + + synchronized List setEnabledByUser( + boolean enabledByUser) { this.enabledByUser = enabledByUser; - ServerSocket ss = null; - if (!enabledByUser) { - ss = serverSocket; - serverSocket = null; - } + List toClose = enabledByUser + ? emptyList() : clearServerSockets(); callback.pluginStateChanged(getState()); - return ss; + return toClose; } @Nullable - synchronized ServerSocket getServerSocket() { - return serverSocket; + synchronized ServerSocket getServerSocket(boolean ipv4) { + return ipv4 ? serverSocketV4 : serverSocketV6; } - synchronized boolean setServerSocket(ServerSocket ss) { - if (stopped || serverSocket != null) return false; - serverSocket = ss; + synchronized boolean setServerSocket(ServerSocket ss, boolean ipv4) { + if (stopped) return false; + if (ipv4) { + if (serverSocketV4 != null) return false; + serverSocketV4 = ss; + } else { + if (serverSocketV6 != null) return false; + serverSocketV6 = ss; + } callback.pluginStateChanged(getState()); return true; } - synchronized void clearServerSocket(ServerSocket ss) { - if (serverSocket == ss) serverSocket = null; + synchronized void clearServerSocket(ServerSocket ss, boolean ipv4) { + if (ipv4) { + if (serverSocketV4 == ss) serverSocketV4 = null; + } else { + if (serverSocketV6 == ss) serverSocketV6 = null; + } callback.pluginStateChanged(getState()); } synchronized State getState() { if (!started || stopped) return STARTING_STOPPING; if (!enabledByUser) return DISABLED; - return serverSocket == null ? INACTIVE : ACTIVE; + if (serverSocketV4 != null || serverSocketV6 != null) return ACTIVE; + return INACTIVE; } synchronized int getReasonsDisabled() { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPlugin.java index 0f4643cf1..42f686ebc 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/WanTcpPlugin.java @@ -43,10 +43,11 @@ class WanTcpPlugin extends TcpPlugin { } @Override - protected List getLocalSocketAddresses() { + protected List getLocalSocketAddresses(boolean ipv4) { + if (!ipv4) return emptyList(); // Use the same address and port as last time if available TransportProperties p = callback.getLocalProperties(); - InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT)); + InetSocketAddress old = parseIpv4SocketAddress(p.get(PROP_IP_PORT)); List addrs = new LinkedList<>(); for (InetAddress a : getLocalInetAddresses()) { if (isAcceptableAddress(a)) { @@ -76,14 +77,11 @@ class WanTcpPlugin extends TcpPlugin { return ipv4 && !loop && !link && !site; } - private int chooseEphemeralPort() { - return 32768 + (int) (Math.random() * 32768); - } - @Override protected List getRemoteSocketAddresses( - TransportProperties p) { - InetSocketAddress parsed = parseSocketAddress(p.get(PROP_IP_PORT)); + TransportProperties p, boolean ipv4) { + if (!ipv4) return emptyList(); + InetSocketAddress parsed = parseIpv4SocketAddress(p.get(PROP_IP_PORT)); if (parsed == null) return emptyList(); return singletonList(parsed); } @@ -96,7 +94,8 @@ class WanTcpPlugin extends TcpPlugin { } @Override - protected void setLocalSocketAddress(InetSocketAddress a) { + protected void setLocalSocketAddress(InetSocketAddress a, boolean ipv4) { + if (!ipv4) throw new AssertionError(); if (mappingResult != null && mappingResult.isUsable()) { // Advertise the external address to contacts if (a.equals(mappingResult.getInternal())) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java index 253c3a94f..8c5e3e6e3 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java @@ -21,6 +21,8 @@ import org.briarproject.briar.android.logging.BriefLogFormatter; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.Callable; @@ -185,12 +187,18 @@ public class BriarReportPrimer implements ReportPrimer { WifiInfo wifiInfo = wm.getConnectionInfo(); if (wifiInfo != null) { int ip = wifiInfo.getIpAddress(); // Nice API, Google - int ip1 = ip & 0xFF; - int ip2 = (ip >> 8) & 0xFF; - int ip3 = (ip >> 16) & 0xFF; - int ip4 = (ip >> 24) & 0xFF; - String address = ip1 + "." + ip2 + "." + ip3 + "." + ip4; - customData.put("Wi-Fi address", scrubInetAddress(address)); + byte[] ipBytes = new byte[4]; + ipBytes[0] = (byte) (ip & 0xFF); + ipBytes[1] = (byte) ((ip >> 8) & 0xFF); + ipBytes[2] = (byte) ((ip >> 16) & 0xFF); + ipBytes[3] = (byte) ((ip >> 24) & 0xFF); + try { + InetAddress address = InetAddress.getByAddress(ipBytes); + customData.put("Wi-Fi address", + scrubInetAddress(address)); + } catch (UnknownHostException ignored) { + // Should only be thrown if address has illegal length + } } }