diff --git a/node/src/main/kotlin/net/corda/node/internal/Node.kt b/node/src/main/kotlin/net/corda/node/internal/Node.kt index 2cb8452750..28c0ffc245 100644 --- a/node/src/main/kotlin/net/corda/node/internal/Node.kt +++ b/node/src/main/kotlin/net/corda/node/internal/Node.kt @@ -22,9 +22,8 @@ import net.corda.node.services.config.FullNodeConfiguration import net.corda.node.services.messaging.ArtemisMessagingComponent.NetworkMapAddress import net.corda.node.services.messaging.ArtemisMessagingServer import net.corda.node.services.messaging.NodeMessagingClient -import net.corda.node.services.transactions.PersistentUniquenessProvider -import net.corda.node.services.transactions.RaftUniquenessProvider -import net.corda.node.services.transactions.RaftValidatingNotaryService +import net.corda.node.services.transactions.* +import net.corda.node.utilities.AddressUtils import net.corda.node.utilities.AffinityExecutor import java.io.RandomAccessFile import java.lang.management.ManagementFactory @@ -122,16 +121,47 @@ class Node(override val configuration: FullNodeConfiguration, override fun makeMessagingService(): MessagingServiceInternal { userService = RPCUserServiceImpl(configuration) - - val serverAddress = with(configuration) { - messagingServerAddress ?: { - messageBroker = ArtemisMessagingServer(this, artemisAddress, services.networkMapCache, userService) - artemisAddress - }() - } + val serverAddress = configuration.messagingServerAddress ?: makeLocalMessageBroker() val myIdentityOrNullIfNetworkMapService = if (networkMapAddress != null) obtainLegalIdentity().owningKey else null - return NodeMessagingClient(configuration, serverAddress, myIdentityOrNullIfNetworkMapService, serverThread, database, - networkMapRegistrationFuture) + + return NodeMessagingClient( + configuration, + serverAddress, + myIdentityOrNullIfNetworkMapService, + serverThread, + database, + networkMapRegistrationFuture + ) + } + + private fun makeLocalMessageBroker(): HostAndPort { + with(configuration) { + val useHost = tryDetectIfNotPublicHost(artemisAddress.hostText) + val useAddress = useHost?.let { HostAndPort.fromParts(it, artemisAddress.port) } ?: artemisAddress + messageBroker = ArtemisMessagingServer(this, useAddress, services.networkMapCache, userService) + return useAddress + } + } + + /** + * Checks whether the specified [host] is a public IP address or hostname. If not, tries to discover the current + * machine's public IP address to be used instead. Note that it will only work if the machine is internet-facing. + * If none found, outputs a warning message. + */ + private fun tryDetectIfNotPublicHost(host: String): String? { + if (!AddressUtils.isPublic(host)) { + val foundPublicIP = AddressUtils.tryDetectPublicIP() + if (foundPublicIP == null) { + val message = "The specified messaging host \"$host\" is private, " + + "this node will not be reachable by any other nodes outside the private network." + println("WARNING: $message") + log.warn(message) + } else { + log.info("Detected public IP: $foundPublicIP. This will be used instead the provided \"$host\" as the advertised address.") + } + return foundPublicIP?.hostAddress + } + return null } override fun startMessagingService(rpcOps: RPCOps) { diff --git a/node/src/main/kotlin/net/corda/node/utilities/AddressUtils.kt b/node/src/main/kotlin/net/corda/node/utilities/AddressUtils.kt new file mode 100644 index 0000000000..f22156e22f --- /dev/null +++ b/node/src/main/kotlin/net/corda/node/utilities/AddressUtils.kt @@ -0,0 +1,25 @@ +package net.corda.node.utilities + +import java.net.InetAddress +import java.net.NetworkInterface + +object AddressUtils { + /** Returns the first public IP address found on any of the network interfaces, or `null` if none found. */ + fun tryDetectPublicIP(): InetAddress? { + for (int in NetworkInterface.getNetworkInterfaces()) { + for (address in int.inetAddresses) { + if (isPublic(address)) return address + } + } + return null + } + + /** Returns `true` if the provided `address` is a public IP address or hostname. */ + fun isPublic(address: InetAddress): Boolean { + return !(address.isSiteLocalAddress || address.isAnyLocalAddress || + address.isLinkLocalAddress || address.isLoopbackAddress || + address.isMulticastAddress) + } + + fun isPublic(hostText: String) = isPublic(InetAddress.getByName(hostText)) +} \ No newline at end of file diff --git a/node/src/test/kotlin/net/corda/node/utilities/AddressUtilsTests.kt b/node/src/test/kotlin/net/corda/node/utilities/AddressUtilsTests.kt new file mode 100644 index 0000000000..26ce708b4f --- /dev/null +++ b/node/src/test/kotlin/net/corda/node/utilities/AddressUtilsTests.kt @@ -0,0 +1,31 @@ +package net.corda.node.utilities + +import org.junit.Test +import java.net.InetAddress +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class AddressUtilsTests { + @Test + fun `correctly determines if the provided address is public`() { + val hostName = InetAddress.getLocalHost() + assertFalse { AddressUtils.isPublic(hostName) } + assertFalse { AddressUtils.isPublic("localhost") } + assertFalse { AddressUtils.isPublic("127.0.0.1") } + assertFalse { AddressUtils.isPublic("::1") } + assertFalse { AddressUtils.isPublic("0.0.0.0") } + assertFalse { AddressUtils.isPublic("::") } + assertFalse { AddressUtils.isPublic("10.0.0.0") } + assertFalse { AddressUtils.isPublic("10.255.255.255") } + assertFalse { AddressUtils.isPublic("192.168.0.10") } + assertFalse { AddressUtils.isPublic("192.168.255.255") } + assertFalse { AddressUtils.isPublic("172.16.0.0") } + assertFalse { AddressUtils.isPublic("172.31.255.255") } + + assertTrue { AddressUtils.isPublic("172.32.0.0") } + assertTrue { AddressUtils.isPublic("192.169.0.0") } + assertTrue { AddressUtils.isPublic("11.0.0.0") } + assertTrue { AddressUtils.isPublic("corda.net") } + assertTrue { AddressUtils.isPublic("2607:f298:5:110f::eef:8729") } + } +} \ No newline at end of file