Browse Source

Renamed the "bindAddress" setting to "bindAddresses". It is now an array of strings, allowing for multiple addresses to be specified in order of importance. Each address is tried in turn until one successfully binds, trying IPv6 first by default and then IPv4. This fixes an issue where the core failed to start on IPv4-only networks unless the bindAddress was manually overridden in Settings.json. This was reported in issue #25, and is also quite commonly reported by new users on Discord. It will now start correctly on both IPv4-only and IPv6-only networks, because it will automatically fall back to an IPv4 bindAddress if the IPv6 one fails to bind.

bindaddress-ipv4
CalDescent 4 years ago
parent
commit
45a9654d5e
  1. 5
      src/main/java/org/qortal/api/ApiService.java
  2. 57
      src/main/java/org/qortal/network/Network.java
  3. 10
      src/main/java/org/qortal/settings/Settings.java

5
src/main/java/org/qortal/api/ApiService.java

@ -46,6 +46,7 @@ import org.qortal.api.websocket.ChatMessagesWebSocket;
import org.qortal.api.websocket.PresenceWebSocket;
import org.qortal.api.websocket.TradeBotWebSocket;
import org.qortal.api.websocket.TradeOffersWebSocket;
import org.qortal.network.Network;
import org.qortal.settings.Settings;
public class ApiService {
@ -118,13 +119,13 @@ public class ApiService {
ServerConnector portUnifiedConnector = new ServerConnector(this.server,
new DetectorConnectionFactory(sslConnectionFactory),
httpConnectionFactory);
portUnifiedConnector.setHost(Settings.getInstance().getBindAddress());
portUnifiedConnector.setHost(Network.getInstance().getBindAddress());
portUnifiedConnector.setPort(Settings.getInstance().getApiPort());
this.server.addConnector(portUnifiedConnector);
} else {
// Non-SSL
InetAddress bindAddr = InetAddress.getByName(Settings.getInstance().getBindAddress());
InetAddress bindAddr = InetAddress.getByName(Network.getInstance().getBindAddress());
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, Settings.getInstance().getApiPort());
this.server = new Server(endpoint);
}

57
src/main/java/org/qortal/network/Network.java

@ -108,6 +108,8 @@ public class Network {
private ServerSocketChannel serverChannel;
private Iterator<SelectionKey> channelIterator = null;
private String bindAddress = null;
// volatile because value is updated inside any one of the EPC threads
private volatile long nextConnectTaskTimestamp = 0L; // ms - try first connect once NTP syncs
@ -138,25 +140,42 @@ public class Network {
// Grab P2P port from settings
int listenPort = Settings.getInstance().getListenPort();
// Grab P2P bind address from settings
try {
InetAddress bindAddr = InetAddress.getByName(Settings.getInstance().getBindAddress());
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, listenPort);
// Grab P2P bind addresses from settings
String[] bindAddresses = Settings.getInstance().getBindAddresses();
for (int i=0; i<bindAddresses.length; i++) {
channelSelector = Selector.open();
String bindAddress = bindAddresses[i];
try {
LOGGER.info(String.format("Binding to address %s", bindAddress));
InetAddress bindAddr = InetAddress.getByName(bindAddress);
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, listenPort);
channelSelector = Selector.open();
// Set up listen socket
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
serverChannel.bind(endpoint, LISTEN_BACKLOG);
serverChannel.register(channelSelector, SelectionKey.OP_ACCEPT);
this.bindAddress = bindAddress; // Store the selected address, so that it can be used by other parts of the app
break; // We don't want to bind to more than one address
} catch (UnknownHostException e) {
LOGGER.error(String.format("Can't bind listen socket to address %s", bindAddress));
if (i == bindAddresses.length-1) { // Only throw an exception if all addresses have been tried
throw new IOException("Can't bind listen socket to address", e);
}
} catch (IOException e) {
LOGGER.error(String.format("Can't create listen socket: %s", e.getMessage()));
if (i == bindAddresses.length-1) { // Only throw an exception if all addresses have been tried
throw new IOException("Can't create listen socket", e);
}
}
// Set up listen socket
serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
serverChannel.bind(endpoint, LISTEN_BACKLOG);
serverChannel.register(channelSelector, SelectionKey.OP_ACCEPT);
} catch (UnknownHostException e) {
LOGGER.error(String.format("Can't bind listen socket to address %s", Settings.getInstance().getBindAddress()));
throw new IOException("Can't bind listen socket to address", e);
} catch (IOException e) {
LOGGER.error(String.format("Can't create listen socket: %s", e.getMessage()));
throw new IOException("Can't create listen socket", e);
}
// Load all known peers from repository
@ -179,6 +198,10 @@ public class Network {
return instance;
}
public String getBindAddress() {
return this.bindAddress;
}
public byte[] getMessageMagic() {
return Settings.getInstance().isTestNet() ? TESTNET_MESSAGE_MAGIC : MAINNET_MESSAGE_MAGIC;
}

10
src/main/java/org/qortal/settings/Settings.java

@ -46,8 +46,10 @@ public class Settings {
// General
private String localeLang = Locale.getDefault().getLanguage();
// Common to all networking (API/P2P)
private String bindAddress = "::"; // Use IPv6 wildcard to listen on all local addresses
// Common to all networking (API/P2P). They are tried in order until one successfully binds.
private String[] bindAddresses = new String[] {
"::", "0.0.0.0"
};
// UI servers
private int uiPort = 12388;
@ -376,8 +378,8 @@ public class Settings {
return this.isTestNet ? TESTNET_LISTEN_PORT : MAINNET_LISTEN_PORT;
}
public String getBindAddress() {
return this.bindAddress;
public String[] getBindAddresses() {
return this.bindAddresses;
}
public int getMinBlockchainPeers() {

Loading…
Cancel
Save