Support IPv6 SLAAC addresses.

This commit is contained in:
akwizgran
2020-02-17 11:07:14 +00:00
parent 2bd2f67693
commit e1084ffadd
8 changed files with 279 additions and 145 deletions

View File

@@ -83,7 +83,12 @@ class AndroidLanTcpPlugin extends LanTcpPlugin {
}
@Override
protected List<InetAddress> getUsableLocalInetAddresses() {
protected List<InetAddress> getUsableLocalInetAddresses(boolean ipv4) {
if (ipv4) return getUsableLocalIpv4Addresses();
else return getUsableLocalIpv6Addresses();
}
private List<InetAddress> 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<InetAddress> 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");

View File

@@ -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";

View File

@@ -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]";
}
}

View File

@@ -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<InetSocketAddress> getLocalSocketAddresses() {
protected List<InetSocketAddress> getLocalSocketAddresses(boolean ipv4) {
TransportProperties p = callback.getLocalProperties();
int preferredPort = parsePortProperty(p.get(PROP_PORT));
String oldIpPorts = p.get(PROP_IP_PORTS);
List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts);
List<InetSocketAddress> olds = parseIpv4SocketAddresses(oldIpPorts);
List<InetSocketAddress> locals = new ArrayList<>();
List<InetSocketAddress> 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<InetSocketAddress> parseSocketAddresses(String ipPorts) {
private List<InetSocketAddress> parseIpv4SocketAddresses(String ipPorts) {
List<InetSocketAddress> 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<InetAddress> getUsableLocalInetAddresses() {
protected List<InetAddress> getUsableLocalInetAddresses(boolean ipv4) {
List<InterfaceAddress> ifAddrs =
new ArrayList<>(getLocalInterfaceAddresses());
// Prefer longer network prefixes
@@ -159,13 +171,18 @@ class LanTcpPlugin extends TcpPlugin {
List<InetAddress> 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<InetSocketAddress> getRemoteSocketAddresses(
TransportProperties p, boolean ipv4) {
if (ipv4) return getRemoteIpv4SocketAddresses(p);
else return getRemoteIpv6SocketAddresses(p);
}
private List<InetSocketAddress> getRemoteIpv4SocketAddresses(
TransportProperties p) {
String ipPorts = p.get(PROP_IP_PORTS);
List<InetSocketAddress> remotes = parseSocketAddresses(ipPorts);
List<InetSocketAddress> 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<InetSocketAddress> 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) {

View File

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

View File

@@ -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<InetSocketAddress> getLocalSocketAddresses();
protected abstract List<InetSocketAddress> 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<InetSocketAddress> 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<ServerSocket> 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<ServerSocket> setStopped() {
stopped = true;
ServerSocket ss = serverSocket;
serverSocket = null;
List<ServerSocket> toClose = clearServerSockets();
callback.pluginStateChanged(getState());
return ss;
return toClose;
}
@Nullable
synchronized ServerSocket setEnabledByUser(boolean enabledByUser) {
@GuardedBy("this")
private List<ServerSocket> clearServerSockets() {
List<ServerSocket> toClose = asList(serverSocketV4, serverSocketV6);
serverSocketV4 = null;
serverSocketV6 = null;
return toClose;
}
synchronized List<ServerSocket> setEnabledByUser(
boolean enabledByUser) {
this.enabledByUser = enabledByUser;
ServerSocket ss = null;
if (!enabledByUser) {
ss = serverSocket;
serverSocket = null;
}
List<ServerSocket> 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() {

View File

@@ -43,10 +43,11 @@ class WanTcpPlugin extends TcpPlugin {
}
@Override
protected List<InetSocketAddress> getLocalSocketAddresses() {
protected List<InetSocketAddress> 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<InetSocketAddress> 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<InetSocketAddress> 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())) {

View File

@@ -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
}
}
}