Skip to content

Commit

Permalink
Add utils for IPAddress conversions to/from Java InetAddress (#437)
Browse files Browse the repository at this point in the history
  • Loading branch information
05nelsonm authored Jun 9, 2024
1 parent ee49ebb commit 6209d52
Show file tree
Hide file tree
Showing 20 changed files with 266 additions and 81 deletions.
26 changes: 18 additions & 8 deletions library/runtime-core/api/runtime-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -3367,6 +3367,7 @@ public final class io/matthewnelson/kmp/tor/runtime/core/builder/IsolationFlagBu

public final class io/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder {
public field destroyKeyOnJobCompletion Z
public synthetic fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/key/KeyType$Address;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun clientAuth (Lio/matthewnelson/kmp/tor/runtime/core/key/X25519$PublicKey;)Lio/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder;
public final fun flags (Lio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)Lio/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder;
public final fun maxStreams (Lio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)Lio/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder;
Expand Down Expand Up @@ -4029,7 +4030,23 @@ public final class io/matthewnelson/kmp/tor/runtime/core/key/X25519$PublicKey$Co
public final fun getOrNull ([B)Lio/matthewnelson/kmp/tor/runtime/core/key/X25519$PublicKey;
}

public final class io/matthewnelson/kmp/tor/runtime/core/util/CmdUtil {
public final class io/matthewnelson/kmp/tor/runtime/core/util/IPAddressUtil {
public static final fun inet4AddressOf (Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress$V4;)Ljava/net/Inet4Address;
public static final fun inet6AddressOf (Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress$V6;)Ljava/net/Inet6Address;
public static final fun inetAddressOf (Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;)Ljava/net/InetAddress;
public static final fun ipAddressOf (Ljava/net/InetAddress;)Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;
public static final fun ipAddressV4Of (Ljava/net/Inet4Address;)Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress$V4;
public static final fun ipAddressV6Of (Ljava/net/Inet6Address;)Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress$V6;
}

public final class io/matthewnelson/kmp/tor/runtime/core/util/PortUtil {
public static final fun findAvailableAsync (Lio/matthewnelson/kmp/tor/runtime/core/address/Port$Ephemeral;ILio/matthewnelson/kmp/tor/runtime/core/address/LocalHost;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun findAvailableSync (Lio/matthewnelson/kmp/tor/runtime/core/address/Port$Ephemeral;ILio/matthewnelson/kmp/tor/runtime/core/address/LocalHost;)Lio/matthewnelson/kmp/tor/runtime/core/address/Port$Ephemeral;
public static final fun isAvailableAsync (Lio/matthewnelson/kmp/tor/runtime/core/address/Port;Lio/matthewnelson/kmp/tor/runtime/core/address/LocalHost;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun isAvailableSync (Lio/matthewnelson/kmp/tor/runtime/core/address/Port;Lio/matthewnelson/kmp/tor/runtime/core/address/LocalHost;)Z
}

public final class io/matthewnelson/kmp/tor/runtime/core/util/TorCmdUtil {
public static final fun executeAsync (Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Privileged$Processor;Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Privileged;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun executeAsync (Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Unprivileged$Processor;Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Unprivileged;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun executeSync (Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Privileged$Processor;Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Privileged;)Ljava/lang/Object;
Expand All @@ -4040,10 +4057,3 @@ public final class io/matthewnelson/kmp/tor/runtime/core/util/CmdUtil {
public static synthetic fun executeSync$default (Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Unprivileged$Processor;Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Unprivileged;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)Ljava/lang/Object;
}

public final class io/matthewnelson/kmp/tor/runtime/core/util/PortUtil {
public static final fun findAvailableAsync (Lio/matthewnelson/kmp/tor/runtime/core/address/Port$Ephemeral;ILio/matthewnelson/kmp/tor/runtime/core/address/LocalHost;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun findAvailableSync (Lio/matthewnelson/kmp/tor/runtime/core/address/Port$Ephemeral;ILio/matthewnelson/kmp/tor/runtime/core/address/LocalHost;)Lio/matthewnelson/kmp/tor/runtime/core/address/Port$Ephemeral;
public static final fun isAvailableAsync (Lio/matthewnelson/kmp/tor/runtime/core/address/Port;Lio/matthewnelson/kmp/tor/runtime/core/address/LocalHost;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun isAvailableSync (Lio/matthewnelson/kmp/tor/runtime/core/address/Port;Lio/matthewnelson/kmp/tor/runtime/core/address/LocalHost;)Z
}

Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import kotlin.jvm.*
*
* @see [V4]
* @see [V6]
* @see [io.matthewnelson.kmp.tor.runtime.core.util.toInetAddress]
* @see [io.matthewnelson.kmp.tor.runtime.core.util.toIPAddress]
* */
public sealed class IPAddress private constructor(
@JvmField
Expand Down Expand Up @@ -111,6 +113,8 @@ public sealed class IPAddress private constructor(
* Holder for an IPv4 address
*
* @see [AnyHost]
* @see [io.matthewnelson.kmp.tor.runtime.core.util.toInet4Address]
* @see [io.matthewnelson.kmp.tor.runtime.core.util.toIPAddressV4]
* */
public open class V4 private constructor(bytes: ByteArray, value: String): IPAddress(bytes, value) {

Expand Down Expand Up @@ -147,10 +151,7 @@ public sealed class IPAddress private constructor(
@JvmStatic
@JvmName("get")
@Throws(IllegalArgumentException::class)
public fun ByteArray.toIPAddressV4(): V4 {
return toIPAddressV4OrNull()
?: throw IllegalArgumentException("Array must be 4 bytes in length")
}
public fun ByteArray.toIPAddressV4(): V4 = toIPAddressV4(copy = true)

/**
* Parses a String for its IPv4 address.
Expand All @@ -173,13 +174,22 @@ public sealed class IPAddress private constructor(
* */
@JvmStatic
@JvmName("getOrNull")
public fun ByteArray.toIPAddressV4OrNull(): V4? {
if (size != 4) return null
public fun ByteArray.toIPAddressV4OrNull(): V4? = try {
toIPAddressV4(copy = true)
} catch (_: IllegalArgumentException) {
null
}

@JvmSynthetic
@Throws(IllegalArgumentException::class)
internal fun ByteArray.toIPAddressV4(copy: Boolean): V4 {
require(size == 4) { "Array must be 4 bytes in length" }
val bytes = if (copy) copyOf() else this

var anyHost = true
var loopback = true
for (i in indices) {
val b = this[i]
val b = bytes[i]
if (anyHost && b != AnyHost.bytes[i]) {
anyHost = false
}
Expand All @@ -193,10 +203,10 @@ public sealed class IPAddress private constructor(
if (loopback) return Loopback

val hostAddress = buildString(capacity = 3 + (3 * 4)) {
joinTo(this, ".") { it.toUByte().toString() }
bytes.joinTo(this, ".") { it.toUByte().toString() }
}

return V4(copyOf(), hostAddress)
return V4(bytes, hostAddress)
}

@JvmSynthetic
Expand Down Expand Up @@ -241,6 +251,8 @@ public sealed class IPAddress private constructor(
* @param [scope] The network interface name or index
* number, or null if no scope was expressed.
* @see [AnyHost]
* @see [io.matthewnelson.kmp.tor.runtime.core.util.toInet6Address]
* @see [io.matthewnelson.kmp.tor.runtime.core.util.toIPAddressV6]
* */
public open class V6 private constructor(
@JvmField
Expand Down Expand Up @@ -511,8 +523,9 @@ public sealed class IPAddress private constructor(
return bytes.toIPAddressV6(scope, copy = false)
}

@JvmSynthetic
@Throws(IllegalArgumentException::class)
private fun ByteArray.toIPAddressV6(scope: String?, copy: Boolean): V6 {
internal fun ByteArray.toIPAddressV6(scope: String?, copy: Boolean): V6 {
require(size == 16) { "Array must be 16 bytes in length" }
val bytes = if (copy) copyOf() else this

Expand Down Expand Up @@ -603,4 +616,7 @@ public sealed class IPAddress private constructor(
}
}
}

@JvmSynthetic
internal fun addressInternal(): ByteArray = bytes
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,7 @@ import io.matthewnelson.kmp.tor.runtime.core.TorConfig.HiddenServicePort
import io.matthewnelson.kmp.tor.runtime.core.apply
import io.matthewnelson.kmp.tor.runtime.core.ctrl.HiddenServiceEntry
import io.matthewnelson.kmp.tor.runtime.core.ctrl.TorCmd
import io.matthewnelson.kmp.tor.runtime.core.key.AddressKey
import io.matthewnelson.kmp.tor.runtime.core.key.AuthKey
import io.matthewnelson.kmp.tor.runtime.core.key.KeyType
import io.matthewnelson.kmp.tor.runtime.core.key.X25519
import io.matthewnelson.kmp.tor.runtime.core.key.*
import kotlin.jvm.JvmField
import kotlin.jvm.JvmSynthetic

Expand Down Expand Up @@ -93,7 +90,7 @@ import kotlin.jvm.JvmSynthetic
* @see [TorConfig.HiddenServicePort]
* */
@KmpTorDsl
public class OnionAddBuilder private constructor() {
public class OnionAddBuilder private constructor(private val keyType: KeyType.Address<*, *>) {

private val clientAuth = LinkedHashSet<AuthKey.Public>(1, 1.0f)
private val flags = LinkedHashSet<String>(1, 1.0f)
Expand Down Expand Up @@ -128,6 +125,7 @@ public class OnionAddBuilder private constructor() {
public fun clientAuth(
key: X25519.PublicKey,
): OnionAddBuilder {
if (keyType !is ED25519_V3) return this
flags.add("V3Auth")
clientAuth.add(key)
return this
Expand Down Expand Up @@ -203,7 +201,7 @@ public class OnionAddBuilder private constructor() {
internal fun KeyType.Address<*, *>.configure(
block: ThisBlock<OnionAddBuilder>,
): Arguments {
val b = OnionAddBuilder().apply(block)
val b = OnionAddBuilder(this).apply(block)

return Arguments(
clientAuth = b.clientAuth,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ public class OnionClientAuthAddBuilder private constructor() {

internal companion object {

internal val EMPTY = Arguments(null, true, emptySet())
internal val DEFAULT = Arguments(null, true, emptySet())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -208,34 +208,39 @@ public sealed class TorCmd<Success: Any> private constructor(
@JvmField
public val servers: Set<String>

public constructor(addressKey: AddressKey.Public): this(addressKey.address())
public constructor(address: OnionAddress): this(address, emptySet())

public constructor(
addressKey: AddressKey.Public,
server: String,
): this(addressKey.address(), server)
): this(addressKey.address())

public constructor(
address: OnionAddress,
addressKey: AddressKey.Public,
server: String,
): this(address, immutableSetOf(server))
): this(addressKey.address(), server)

public constructor(
addressKey: AddressKey.Public,
vararg servers: String,
): this(addressKey, immutableSetOf(*servers))

public constructor(
address: OnionAddress,
vararg servers: String,
): this(address, immutableSetOf(*servers))

public constructor(
addressKey: AddressKey.Public,
servers: Collection<String>,
): this(addressKey.address(), servers)

public constructor(
address: OnionAddress,
): this(address, emptySet())

public constructor(
address: OnionAddress,
server: String,
): this(address, immutableSetOf(server))

public constructor(
address: OnionAddress,
vararg servers: String,
): this(address, immutableSetOf(*servers))

public constructor(
address: OnionAddress,
servers: Collection<String>,
Expand Down Expand Up @@ -369,7 +374,11 @@ public sealed class TorCmd<Success: Any> private constructor(
this.flags = arguments.flags
this.maxStreams = arguments.maxStreams
this.ports = arguments.ports
this.destroyKeyOnJobCompletion = arguments.destroyKeyOnJobCompletion
this.destroyKeyOnJobCompletion = if (addressKey == null) {
false
} else {
arguments.destroyKeyOnJobCompletion
}
}
}

Expand Down Expand Up @@ -413,7 +422,7 @@ public sealed class TorCmd<Success: Any> private constructor(
public constructor(
address: OnionAddress.V3,
authKey: X25519.PrivateKey,
): this(address, authKey, OnionClientAuthAddBuilder.Arguments.EMPTY)
): this(address, authKey, OnionClientAuthAddBuilder.Arguments.DEFAULT)

public constructor(
address: OnionAddress.V3,
Expand Down Expand Up @@ -444,7 +453,7 @@ public sealed class TorCmd<Success: Any> private constructor(
@JvmField
public val address: OnionAddress

public constructor(key: ED25519_V3.PublicKey): this(key.address())
public constructor(addressKey: ED25519_V3.PublicKey): this(addressKey.address())
public constructor(address: OnionAddress.V3): this(address as OnionAddress)
private constructor(address: OnionAddress): super("ONION_CLIENT_AUTH_REMOVE") {
this.address = address
Expand All @@ -461,7 +470,7 @@ public sealed class TorCmd<Success: Any> private constructor(
@JvmField
public val address: OnionAddress?

public constructor(key: ED25519_V3.PublicKey): this(key.address())
public constructor(addressKey: ED25519_V3.PublicKey): this(addressKey.address())
public constructor(address: OnionAddress.V3): this(address as OnionAddress)
private constructor(address: OnionAddress?): super("ONION_CLIENT_AUTH_VIEW") {
this.address = address
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ import kotlin.coroutines.cancellation.CancellationException
*
* @param [host] either [LocalHost.IPv4] or [LocalHost.IPv6]
* @see [io.matthewnelson.kmp.tor.runtime.core.util.isAvailableSync]
* @throws [IOException] if [LocalHost.resolve] fails
* @throws [CancellationException] if underlying coroutine was cancelled
* */
@Throws(IOException::class, CancellationException::class)
public expect suspend fun Port.isAvailableAsync(
host: LocalHost,
): Boolean
Expand All @@ -44,7 +41,7 @@ public expect suspend fun Port.isAvailableAsync(
* @param [limit] the number of ports to scan. min: 1, max: 1_000
* @see [io.matthewnelson.kmp.tor.runtime.core.util.findAvailableSync]
* @throws [IllegalArgumentException] if [limit] is not between 1 and 1_000 (inclusive)
* @throws [IOException] if [LocalHost.resolve] fails, or no ports are available
* @throws [IOException] if no ports are available
* @throws [CancellationException] if underlying coroutine was cancelled
* */
@Throws(IOException::class, CancellationException::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,7 @@ import kotlin.time.TimeSource
* Checks if the TCP port is available on [LocalHost] or not.
*
* @param [host] either [LocalHost.IPv4] or [LocalHost.IPv6]
* @throws [IOException] if [LocalHost.resolve] fails
* @throws [CancellationException] if underlying coroutine was cancelled
* */
// @Throws(IOException::class, CancellationException::class)
public actual suspend fun Port.isAvailableAsync(
host: LocalHost,
): Boolean = host.resolve()
Expand All @@ -55,7 +52,7 @@ public actual suspend fun Port.isAvailableAsync(
* @param [host] either [LocalHost.IPv4] or [LocalHost.IPv6]
* @param [limit] the number of ports to scan. min: 1, max: 1_000
* @throws [IllegalArgumentException] if [limit] is not between 1 and 1_000 (inclusive)
* @throws [IOException] if [LocalHost.resolve] fails, or no ports are available
* @throws [IOException] if no ports are available
* @throws [CancellationException] if underlying coroutine was cancelled
* */
// @Throws(IOException::class, CancellationException::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@
package io.matthewnelson.kmp.tor.runtime.core.internal

import io.matthewnelson.kmp.file.IOException
import io.matthewnelson.kmp.file.wrapIOException
import io.matthewnelson.kmp.tor.runtime.core.address.IPAddress
import io.matthewnelson.kmp.tor.runtime.core.util.toInetAddress
import java.net.InetAddress
import java.net.ServerSocket

@JvmInline
Expand All @@ -30,23 +31,16 @@ internal actual value class ServerSocketProducer private actual constructor(
@Throws(Exception::class)
@OptIn(ExperimentalStdlibApi::class)
internal actual fun open(port: Int): AutoCloseable {
return ServerSocket(port, 1, value as java.net.InetAddress)
return ServerSocket(port, /* backlog */ 1, value as InetAddress)
}

internal actual companion object {

@JvmSynthetic
@Throws(IOException::class)
internal actual fun IPAddress.toServerSocketProducer(): ServerSocketProducer {
val jInetAddress = try {
// TODO: Issue #336
// Use get by address
java.net.InetAddress.getByName(canonicalHostName())
} catch (t: Throwable) {
throw t.wrapIOException()
}

return ServerSocketProducer(jInetAddress)
val inet = toInetAddress()
return ServerSocketProducer(inet)
}
}
}
Loading

0 comments on commit 6209d52

Please sign in to comment.