Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TorCmd.MapAddress implementation #432

Merged
merged 3 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions library/runtime-core/api/runtime-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -3422,11 +3422,51 @@ public final class io/matthewnelson/kmp/tor/runtime/core/builder/UnixSocketBuild
}

public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping {
public fun <init> ()V
public static final field Companion Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping$Companion;
public final field from Ljava/lang/String;
public final field to Ljava/lang/String;
public fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;)V
public fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;Lio/matthewnelson/kmp/tor/runtime/core/address/OnionAddress;)V
public fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;)V
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public static final fun anyHostIPv4To (Lio/matthewnelson/kmp/tor/runtime/core/address/OnionAddress;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public static final fun anyHostIPv4To (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public static final fun anyHostIPv6To (Lio/matthewnelson/kmp/tor/runtime/core/address/OnionAddress;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public static final fun anyHostIPv6To (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public static final fun anyHostTo (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/lang/String;
public final fun copy ()Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun copy (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun copy (Ljava/lang/String;Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public static synthetic fun copy$default (Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
public static final fun unmapFrom (Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public static final fun unmapFrom (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
}

public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping$Companion {
public final fun anyHostIPv4To (Lio/matthewnelson/kmp/tor/runtime/core/address/OnionAddress;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun anyHostIPv4To (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun anyHostIPv6To (Lio/matthewnelson/kmp/tor/runtime/core/address/OnionAddress;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun anyHostIPv6To (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun anyHostTo (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun unmapFrom (Lio/matthewnelson/kmp/tor/runtime/core/address/IPAddress;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
public final fun unmapFrom (Ljava/lang/String;)Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
}

public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping$Result {
public fun <init> ()V
public final field from Ljava/lang/String;
public final field isUnmapping Z
public final field to Ljava/lang/String;
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public fun equals (Ljava/lang/Object;)Z
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
public final fun toUnmapping ()Lio/matthewnelson/kmp/tor/runtime/core/ctrl/AddressMapping;
}

public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/ClientAuthEntry {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,13 +109,15 @@ public sealed class IPAddress private constructor(

/**
* Holder for an IPv4 address
*
* @see [AnyHost]
* */
public open class V4 private constructor(bytes: ByteArray, value: String): IPAddress(bytes, value) {

/**
* `0.0.0.0`
* Static instance for `0.0.0.0`
* */
public object AnyHost: V4(ByteArray(4) { 0 }, "0.0.0.0")
public object AnyHost: V4(ByteArray(4), "0.0.0.0")

public companion object {

Expand Down Expand Up @@ -238,6 +240,7 @@ public sealed class IPAddress private constructor(
*
* @param [scope] The network interface name or index
* number, or null if no scope was expressed.
* @see [AnyHost]
* */
public open class V6 private constructor(
@JvmField
Expand All @@ -247,7 +250,7 @@ public sealed class IPAddress private constructor(
): IPAddress(bytes, value + if (scope == null) "" else "%$scope") {

/**
* `::0`
* Holder for `::0`
*
* @see [of]
* @see [NoScope]
Expand All @@ -263,7 +266,7 @@ public sealed class IPAddress private constructor(
* */
public companion object NoScope: AnyHost(
scope = null,
bytes = ByteArray(16) { 0 },
bytes = ByteArray(16),
value = "0:0:0:0:0:0:0:0"
) {

Expand Down Expand Up @@ -358,20 +361,21 @@ public sealed class IPAddress private constructor(

// Square brackets
run {
val iClosing = stripped.indexOfLast { it == ']' }
val startBracket = stripped.startsWith('[')

// No start bracket, yes closing bracket. Invalid.
if (!startBracket && iClosing != -1) return null
val hasOpenBracket = stripped.startsWith('[')
val iClosingBracket = stripped.indexOfLast { it == ']' }

if (iClosing == -1) {
if (iClosingBracket == -1) {
// Yes start bracket, no closing bracket. Invalid.
if (startBracket) return null
if (hasOpenBracket) return null

// No start bracket, no closing bracket. Valid.
// stripped = stripped
} else {
// No start bracket, yes closing bracket. Invalid
if (!hasOpenBracket) return null

// Yes start bracket, yes closing bracket. Strip.
stripped = stripped.substring(1, iClosing)
stripped = stripped.substring(1, iClosingBracket)
}
}

Expand All @@ -382,8 +386,10 @@ public sealed class IPAddress private constructor(
@Suppress("LocalVariableName")
val _scope = stripped.substring(iPct + 1)

val msg = _scope.isValidScopeOrErrorMessage()

// Interface name or index number bad. Invalid.
if (_scope.isValidScopeOrErrorMessage() != null) return null
if (msg != null) return null

stripped = stripped.substring(0, iPct)
_scope
Expand All @@ -397,33 +403,36 @@ public sealed class IPAddress private constructor(
else -> null
}?.let { return it }

val blocks8: List<String> = stripped.split(':', limit = 10).let { split ->
// min (3) to max (9)
// *:: or ::* to ::*:*:*:*:*:*:* or *:*:*:*:*:*:*::
if (split.size !in 3..9) return@let emptyList()
val blocks8: List<String> = stripped.split(':', limit = 10).let { splits ->
// min (3) to max (9)
// *:: or ::* to ::*:*:*:*:*:*:* or *:*:*:*:*:*:*::
if (splits.size !in 3..9) return@let emptyList()

var iExpand = -1

val blocks: MutableList<String> = run {
val emptyFirst = split.first().isEmpty()
val emptyLast = split.last().isEmpty()
// Will be empty if started with `:`
val emptyFirst = splits.first().isEmpty()
// Will be empty if ended with `:`
val emptyLast = splits.last().isEmpty()

// Started and ended with `:`, but `::` was eliminated. Invalid.
if (emptyFirst && emptyLast) return@let emptyList()

@Suppress("LocalVariableName")
val _blocks = (split as? MutableList<String>) ?: split.toMutableList()
val _blocks = (splits as? MutableList<String>) ?: splits.toMutableList()

// Replace first/last empty block with `0`
// Replace first or last empty block with `0` so that
// parsing for `::` results in the proper iExpand check.
if (emptyFirst) {
// Must start with ::, otherwise invalid.
// Must start with `::`, otherwise invalid.
iExpand = 1
_blocks.removeFirst()
_blocks.add(0, "0")
}
if (emptyLast) {
// Must end with ::, otherwise invalid.
iExpand = split.lastIndex - 1
// Must end with `::`, otherwise invalid.
iExpand = splits.lastIndex - 1
_blocks.removeLast()
_blocks.add("0")
}
Expand All @@ -449,31 +458,29 @@ public sealed class IPAddress private constructor(
// No expression of `::`
if (iExpand == -1) return@let blocks

// Have single `::` expression. Deal with it.

// Indicates that first or last block was
// empty at start and replaced with `0` + had
// iExpanded set to the expected index, but
// parsing all blocks did not observe any empty
// blocks at all.
//
// So, started or ended with single `:` instead
// of expected `::`. Invalid.
// If the first or last block was empty at (started or
// ended with `:`) those blocks were replaced with `0`
// and iExpanded was set to the expected index. If when
// all blocks were checked for emptiness resulted in none
// being found, then the expected iExpand value was not
// confirmed. This indicates that it started with single
// `:` instead of expected `::`. Invalid.
if (!hasEmptyBlock) return@let emptyList()

// Have single `::` expression. Deal with it.
blocks.removeAt(iExpand)
while (blocks.size < 8) { blocks.add(iExpand, "0") }
blocks
}

if (blocks8.size != 8) return null

// 8 non-empty blocks. Decode.
var iB = 0
val bytes = ByteArray(16)

// 8 non-empty blocks. Decode.
try {
BASE_16.newDecoderFeed { byte -> bytes[iB++] = byte }.use { feed ->
BASE_16.newDecoderFeed(out = { byte -> bytes[iB++] = byte }).use { feed ->
for (block in blocks8) {
val iNonZero = block.indexOfFirst { it != '0' }

Expand Down Expand Up @@ -573,6 +580,7 @@ public sealed class IPAddress private constructor(
private val BASE_16 = Base16 { strict(); encodeToLowercase = true }
}

// Typical IPv4 loopback address of `::1`
private open class Loopback private constructor(
scope: String?,
bytes: ByteArray,
Expand All @@ -581,7 +589,7 @@ public sealed class IPAddress private constructor(

companion object NoScope: Loopback(
scope = null,
bytes = ByteArray(16) { i -> if (i == 15) 1 else 0 },
bytes = ByteArray(16).apply { this[15] = 1 },
value = "0:0:0:0:0:0:0:1",
) {

Expand Down
Loading
Loading