diff --git a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java
index 8a75a971e6495b1f470a0cee40aa3fced823f327..3429d06b82922618a7c037c66e4b7f15cf69a5ae 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/LanTcpPlugin.java
@@ -1,14 +1,19 @@
 package org.briarproject.plugins.tcp;
 
 import org.briarproject.api.TransportId;
+import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.plugins.Backoff;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
 import org.briarproject.api.properties.TransportProperties;
+import org.briarproject.api.settings.Settings;
+import org.briarproject.util.StringUtils;
 
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -17,6 +22,10 @@ class LanTcpPlugin extends TcpPlugin {
 
 	static final TransportId ID = new TransportId("lan");
 
+	private static final int MAX_ADDRESSES = 5;
+	private static final String PROP_IP_PORTS = "ipPorts";
+	private static final String SEPARATOR = ",";
+
 	LanTcpPlugin(Executor ioExecutor, Backoff backoff,
 			DuplexPluginCallback callback, int maxLatency, int maxIdleTime) {
 		super(ioExecutor, backoff, callback, maxLatency, maxIdleTime);
@@ -30,18 +39,74 @@ class LanTcpPlugin extends TcpPlugin {
 	protected List<SocketAddress> getLocalSocketAddresses() {
 		// Use the same address and port as last time if available
 		TransportProperties p = callback.getLocalProperties();
-		String oldAddress = p.get("address"), oldPort = p.get("port");
-		InetSocketAddress old = parseSocketAddress(oldAddress, oldPort);
-		List<SocketAddress> addrs = new LinkedList<SocketAddress>();
-		for (InetAddress a : getLocalIpAddresses()) {
-			if (isAcceptableAddress(a)) {
+		String oldIpPorts = p.get(PROP_IP_PORTS);
+		List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts);
+		List<SocketAddress> locals = new LinkedList<SocketAddress>();
+		for (InetAddress local : getLocalIpAddresses()) {
+			if (isAcceptableAddress(local)) {
 				// If this is the old address, try to use the same port
-				if (old != null && old.getAddress().equals(a))
-					addrs.add(0, new InetSocketAddress(a, old.getPort()));
-				addrs.add(new InetSocketAddress(a, 0));
+				for (InetSocketAddress old : olds) {
+					if (old.getAddress().equals(local)) {
+						int port = old.getPort();
+						locals.add(0, new InetSocketAddress(local, port));
+					}
+				}
+				locals.add(new InetSocketAddress(local, 0));
 			}
 		}
-		return addrs;
+		return locals;
+	}
+
+	private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
+		if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
+		String[] split = ipPorts.split(SEPARATOR);
+		List<InetSocketAddress> remotes = new ArrayList<InetSocketAddress>();
+		for (String ipPort : split) {
+			InetSocketAddress a = parseSocketAddress(ipPort);
+			if (a != null) remotes.add(a);
+		}
+		return remotes;
+	}
+
+	@Override
+	protected void setLocalSocketAddress(InetSocketAddress a) {
+		String ipPort = getIpPortString(a);
+		// Get the list of recently used addresses
+		String setting = callback.getSettings().get(PROP_IP_PORTS);
+		List<String> recent = new ArrayList<String>();
+		if (!StringUtils.isNullOrEmpty(setting))
+			Collections.addAll(recent, setting.split(SEPARATOR));
+		// Is the address already in the list?
+		if (recent.remove(ipPort)) {
+			// Move the address to the start of the list
+			recent.add(0, ipPort);
+			setting = StringUtils.join(recent, SEPARATOR);
+		} else {
+			// Add the address to the start of the list
+			recent.add(0, ipPort);
+			// Drop the least recently used address if the list is full
+			if (recent.size() > MAX_ADDRESSES)
+				recent = recent.subList(0, MAX_ADDRESSES);
+			setting = StringUtils.join(recent, SEPARATOR);
+			// Update the list of addresses shared with contacts
+			List<String> shared = new ArrayList<String>(recent);
+			Collections.sort(shared);
+			String property = StringUtils.join(shared, SEPARATOR);
+			TransportProperties properties = new TransportProperties();
+			properties.put(PROP_IP_PORTS, property);
+			callback.mergeLocalProperties(properties);
+		}
+		// Save the setting
+		Settings settings = new Settings();
+		settings.put(PROP_IP_PORTS, setting);
+		callback.mergeSettings(settings);
+	}
+
+	@Override
+	protected List<InetSocketAddress> getRemoteSocketAddresses(ContactId c) {
+		TransportProperties p = callback.getRemoteProperties().get(c);
+		if (p == null) return Collections.emptyList();
+		return parseSocketAddresses(p.get(PROP_IP_PORTS));
 	}
 
 	private boolean isAcceptableAddress(InetAddress a) {
diff --git a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java
index d6effe2a722930d94a5a6dac0022e02005ecb30c..ead2c80b89680017659d240bd2224cab5683c214 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/TcpPlugin.java
@@ -8,7 +8,6 @@ import org.briarproject.api.plugins.Backoff;
 import org.briarproject.api.plugins.duplex.DuplexPlugin;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
 import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
-import org.briarproject.api.properties.TransportProperties;
 import org.briarproject.util.StringUtils;
 
 import java.io.IOException;
@@ -52,7 +51,22 @@ abstract class TcpPlugin implements DuplexPlugin {
 	 */
 	protected abstract List<SocketAddress> getLocalSocketAddresses();
 
-	/** Returns true if connections to the given address can be attempted. */
+	/**
+	 * Adds the address on which the plugin is listening to the transport
+	 * properties.
+	 */
+	protected abstract void setLocalSocketAddress(InetSocketAddress a);
+
+	/**
+	 * Returns zero or more socket addresses for connecting to the given
+	 * contact.
+	 */
+	protected abstract List<InetSocketAddress> getRemoteSocketAddresses(
+			ContactId c);
+
+	/**
+	 * Returns true if connections to the given address can be attempted.
+	 */
 	protected abstract boolean isConnectable(InetSocketAddress remote);
 
 	protected TcpPlugin(Executor ioExecutor, Backoff backoff,
@@ -126,17 +140,11 @@ abstract class TcpPlugin implements DuplexPlugin {
 		}
 	}
 
-	protected String getHostAddress(InetAddress a) {
-		String addr = a.getHostAddress();
+	protected String getIpPortString(InetSocketAddress a) {
+		String addr = a.getAddress().getHostAddress();
 		int percent = addr.indexOf('%');
-		return percent == -1 ? addr : addr.substring(0, percent);
-	}
-
-	protected void setLocalSocketAddress(InetSocketAddress a) {
-		TransportProperties p = new TransportProperties();
-		p.put("address", getHostAddress(a.getAddress()));
-		p.put("port", String.valueOf(a.getPort()));
-		callback.mergeLocalProperties(p);
+		if (percent != -1) addr = addr.substring(0, percent);
+		return addr + ":" + a.getPort();
 	}
 
 	private void acceptContactConnections() {
@@ -197,37 +205,34 @@ abstract class TcpPlugin implements DuplexPlugin {
 
 	public DuplexTransportConnection createConnection(ContactId c) {
 		if (!isRunning()) return null;
-		InetSocketAddress remote = getRemoteSocketAddress(c);
-		if (remote == null) return null;
-		if (!isConnectable(remote)) {
-			if (LOG.isLoggable(INFO)) {
-				SocketAddress local = socket.getLocalSocketAddress();
-				LOG.info(remote + " is not connectable from " + local);
+		for (InetSocketAddress remote : getRemoteSocketAddresses(c)) {
+			if (!isConnectable(remote)) {
+				if (LOG.isLoggable(INFO)) {
+					SocketAddress local = socket.getLocalSocketAddress();
+					LOG.info(remote + " is not connectable from " + local);
+				}
+				continue;
+			}
+			Socket s = new Socket();
+			try {
+				if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote);
+				s.connect(remote);
+				s.setSoTimeout(socketTimeout);
+				if (LOG.isLoggable(INFO)) LOG.info("Connected to " + remote);
+				return new TcpTransportConnection(this, s);
+			} catch (IOException e) {
+				if (LOG.isLoggable(INFO))
+					LOG.info("Could not connect to " + remote);
 			}
-			return null;
-		}
-		Socket s = new Socket();
-		try {
-			if (LOG.isLoggable(INFO)) LOG.info("Connecting to " + remote);
-			s.connect(remote);
-			s.setSoTimeout(socketTimeout);
-			if (LOG.isLoggable(INFO)) LOG.info("Connected to " + remote);
-			return new TcpTransportConnection(this, s);
-		} catch (IOException e) {
-			if (LOG.isLoggable(INFO)) LOG.info("Could not connect to " + remote);
-			return null;
 		}
+		return null;
 	}
 
-	private InetSocketAddress getRemoteSocketAddress(ContactId c) {
-		TransportProperties p = callback.getRemoteProperties().get(c);
-		if (p == null) return null;
-		return parseSocketAddress(p.get("address"), p.get("port"));
-	}
-
-	protected InetSocketAddress parseSocketAddress(String addr, String port) {
-		if (StringUtils.isNullOrEmpty(addr)) return null;
-		if (StringUtils.isNullOrEmpty(port)) return null;
+	protected InetSocketAddress parseSocketAddress(String ipPort) {
+		if (StringUtils.isNullOrEmpty(ipPort)) return null;
+		String[] split = ipPort.split(":");
+		if (split.length != 2) return null;
+		String addr = split[0], port = split[1];
 		// Ensure getByName() won't perform a DNS lookup
 		if (!DOTTED_QUAD.matcher(addr).matches()) return null;
 		try {
@@ -235,10 +240,12 @@ abstract class TcpPlugin implements DuplexPlugin {
 			int p = Integer.parseInt(port);
 			return new InetSocketAddress(a, p);
 		} catch (UnknownHostException e) {
-			if (LOG.isLoggable(WARNING)) LOG.warning("Invalid address: " + addr);
+			if (LOG.isLoggable(WARNING))
+				LOG.warning("Invalid address: " + addr);
 			return null;
 		} catch (NumberFormatException e) {
-			if (LOG.isLoggable(WARNING)) LOG.warning("Invalid port: " + port);
+			if (LOG.isLoggable(WARNING))
+				LOG.warning("Invalid port: " + port);
 			return null;
 		}
 	}
diff --git a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java
index beabc8bec54f71f6814a4077cc30e6bfd20dc67e..4274a8cbe4aa7e486ee99eea00aca5b69d7d1059 100644
--- a/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java
+++ b/briar-core/src/org/briarproject/plugins/tcp/WanTcpPlugin.java
@@ -1,6 +1,7 @@
 package org.briarproject.plugins.tcp;
 
 import org.briarproject.api.TransportId;
+import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.plugins.Backoff;
 import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
 import org.briarproject.api.properties.TransportProperties;
@@ -9,6 +10,7 @@ import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.concurrent.Executor;
@@ -17,6 +19,8 @@ class WanTcpPlugin extends TcpPlugin {
 
 	static final TransportId ID = new TransportId("wan");
 
+	private static final String PROP_IP_PORT = "ipPort";
+
 	private final PortMapper portMapper;
 
 	private volatile MappingResult mappingResult;
@@ -35,8 +39,7 @@ class WanTcpPlugin extends TcpPlugin {
 	protected List<SocketAddress> getLocalSocketAddresses() {
 		// Use the same address and port as last time if available
 		TransportProperties p = callback.getLocalProperties();
-		String oldAddress = p.get("address"), oldPort = p.get("port");
-		InetSocketAddress old = parseSocketAddress(oldAddress, oldPort);
+		InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT));
 		List<SocketAddress> addrs = new LinkedList<SocketAddress>();
 		for (InetAddress a : getLocalIpAddresses()) {
 			if (isAcceptableAddress(a)) {
@@ -69,6 +72,15 @@ class WanTcpPlugin extends TcpPlugin {
 		return 32768 + (int) (Math.random() * 32768);
 	}
 
+	@Override
+	protected List<InetSocketAddress> getRemoteSocketAddresses(ContactId c) {
+		TransportProperties p = callback.getRemoteProperties().get(c);
+		if (p == null) return Collections.emptyList();
+		InetSocketAddress parsed = parseSocketAddress(p.get(PROP_IP_PORT));
+		if (parsed == null) return Collections.emptyList();
+		return Collections.singletonList(parsed);
+	}
+
 	@Override
 	protected boolean isConnectable(InetSocketAddress remote) {
 		if (remote.getPort() == 0) return false;
@@ -83,8 +95,7 @@ class WanTcpPlugin extends TcpPlugin {
 				a = mappingResult.getExternal();
 		}
 		TransportProperties p = new TransportProperties();
-		p.put("address", getHostAddress(a.getAddress()));
-		p.put("port", String.valueOf(a.getPort()));
+		p.put(PROP_IP_PORT, getIpPortString(a));
 		callback.mergeLocalProperties(p);
 	}
 }
diff --git a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java
index 1554a0832e6a66fc75130acc11035c3753ecefc5..de5f3ce81b395467ceea89662151086e87467574 100644
--- a/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java
+++ b/briar-tests/src/org/briarproject/plugins/tcp/LanTcpPluginTest.java
@@ -26,6 +26,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -91,14 +92,17 @@ public class LanTcpPluginTest extends BriarTestCase {
 		plugin.start();
 		// The plugin should have bound a socket and stored the port number
 		assertTrue(callback.propertiesLatch.await(5, SECONDS));
-		String addrString = callback.local.get("address");
-		assertNotNull(addrString);
+		String ipPorts = callback.local.get("ipPorts");
+		assertNotNull(ipPorts);
+		String[] split = ipPorts.split(",");
+		assertEquals(1, split.length);
+		split = split[0].split(":");
+		assertEquals(2, split.length);
+		String addrString = split[0], portString = split[1];
 		InetAddress addr = InetAddress.getByName(addrString);
 		assertTrue(addr instanceof Inet4Address);
 		assertFalse(addr.isLoopbackAddress());
 		assertTrue(addr.isLinkLocalAddress() || addr.isSiteLocalAddress());
-		String portString = callback.local.get("port");
-		assertNotNull(portString);
 		int port = Integer.parseInt(portString);
 		assertTrue(port > 0 && port < 65536);
 		// The plugin should be listening on the port
@@ -124,11 +128,17 @@ public class LanTcpPluginTest extends BriarTestCase {
 		plugin.start();
 		// The plugin should have bound a socket and stored the port number
 		assertTrue(callback.propertiesLatch.await(5, SECONDS));
-		String addr = callback.local.get("address");
-		assertNotNull(addr);
+		assertTrue(callback.propertiesLatch.await(5, SECONDS));
+		String ipPorts = callback.local.get("ipPorts");
+		assertNotNull(ipPorts);
+		String[] split = ipPorts.split(",");
+		assertEquals(1, split.length);
+		split = split[0].split(":");
+		assertEquals(2, split.length);
+		String addrString = split[0];
 		// Listen on the same interface as the plugin
 		final ServerSocket ss = new ServerSocket();
-		ss.bind(new InetSocketAddress(addr, 0), 10);
+		ss.bind(new InetSocketAddress(addrString, 0), 10);
 		int port = ss.getLocalPort();
 		final CountDownLatch latch = new CountDownLatch(1);
 		final AtomicBoolean error = new AtomicBoolean(false);
@@ -145,8 +155,7 @@ public class LanTcpPluginTest extends BriarTestCase {
 		}.start();
 		// Tell the plugin about the port
 		TransportProperties p = new TransportProperties();
-		p.put("address", addr);
-		p.put("port", String.valueOf(port));
+		p.put("ipPorts", addrString + ":" + port);
 		callback.remote.put(contactId, p);
 		// Connect to the port
 		DuplexTransportConnection d = plugin.createConnection(contactId);
@@ -175,7 +184,7 @@ public class LanTcpPluginTest extends BriarTestCase {
 	private static class Callback implements DuplexPluginCallback {
 
 		private final Map<ContactId, TransportProperties> remote =
-				new Hashtable<ContactId, TransportProperties>();
+				new Hashtable<>();
 		private final CountDownLatch propertiesLatch = new CountDownLatch(1);
 		private final CountDownLatch connectionsLatch = new CountDownLatch(1);
 		private final TransportProperties local = new TransportProperties();
@@ -192,7 +201,8 @@ public class LanTcpPluginTest extends BriarTestCase {
 			return remote;
 		}
 
-		public void mergeSettings(Settings s) {}
+		public void mergeSettings(Settings s) {
+		}
 
 		public void mergeLocalProperties(TransportProperties p) {
 			local.putAll(p);
@@ -207,18 +217,22 @@ public class LanTcpPluginTest extends BriarTestCase {
 			return false;
 		}
 
-		public void showMessage(String... message) {}
+		public void showMessage(String... message) {
+		}
 
 		public void incomingConnectionCreated(DuplexTransportConnection d) {
 			connectionsLatch.countDown();
 		}
 
 		public void outgoingConnectionCreated(ContactId c,
-				DuplexTransportConnection d) {}
+				DuplexTransportConnection d) {
+		}
 
-		public void transportEnabled() {}
+		public void transportEnabled() {
+		}
 
-		public void transportDisabled() {}
+		public void transportDisabled() {
+		}
 	}
 
 	private static class TestBackoff implements Backoff {
@@ -227,8 +241,10 @@ public class LanTcpPluginTest extends BriarTestCase {
 			return 60 * 1000;
 		}
 
-		public void increment() {}
+		public void increment() {
+		}
 
-		public void reset() {}
+		public void reset() {
+		}
 	}
 }