mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 10:49:06 +01:00
Support IPv6 SLAAC addresses.
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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())) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user