From 686f595065d93998871bbb3d861ff86c6c0f8859 Mon Sep 17 00:00:00 2001 From: Matthew Nelson Date: Mon, 3 Jun 2024 03:33:38 -0400 Subject: [PATCH 1/2] Make Signal.Reload a Privileged command --- library/runtime-core/api/runtime-core.api | 2 +- .../kotlin/io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/runtime-core/api/runtime-core.api b/library/runtime-core/api/runtime-core.api index d4227982a..726437b28 100644 --- a/library/runtime-core/api/runtime-core.api +++ b/library/runtime-core/api/runtime-core.api @@ -3699,7 +3699,7 @@ public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Signal$NewN public fun hashCode ()I } -public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Signal$Reload : io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Unprivileged { +public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Signal$Reload : io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Privileged { public static final field INSTANCE Lio/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Signal$Reload; public fun equals (Ljava/lang/Object;)Z public fun hashCode ()I diff --git a/library/runtime-core/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd.kt b/library/runtime-core/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd.kt index b8046f54a..0b692d41b 100644 --- a/library/runtime-core/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd.kt +++ b/library/runtime-core/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd.kt @@ -452,7 +452,7 @@ public sealed class TorCmd private constructor( public data object Active: Unprivileged("SIGNAL") public data object Dormant: Unprivileged("SIGNAL") - public data object Reload: Unprivileged("SIGNAL") + public data object Reload: Privileged("SIGNAL") public data object Shutdown: Privileged("SIGNAL") public data object Halt: Privileged("SIGNAL") } From d79768712e801572ef890a9873c53d0f296f3f08 Mon Sep 17 00:00:00 2001 From: Matthew Nelson Date: Mon, 3 Jun 2024 05:00:48 -0400 Subject: [PATCH 2/2] Update TorListeners to also handle DirPort, MetricsPort, NATDPort, ORPort, ExtORPort --- library/runtime/api/runtime.api | 28 +-- .../kmp/tor/runtime/RuntimeEvent.kt | 40 ++++ .../kmp/tor/runtime/TorListeners.kt | 208 +++++++++++++----- .../observer/ObserverProcessStdout.kt | 34 +-- .../runtime/TorListenersManagerUnitTest.kt | 56 ++++- .../observer/ObserverProcessStdoutUnitTest.kt | 18 +- 6 files changed, 297 insertions(+), 87 deletions(-) diff --git a/library/runtime/api/runtime.api b/library/runtime/api/runtime.api index efa17c4ba..1c4d13f29 100644 --- a/library/runtime/api/runtime.api +++ b/library/runtime/api/runtime.api @@ -315,8 +315,13 @@ public final class io/matthewnelson/kmp/tor/runtime/TorCmdJob : io/matthewnelson } public final class io/matthewnelson/kmp/tor/runtime/TorListeners { + public final field dir Ljava/util/Set; public final field dns Ljava/util/Set; public final field http Ljava/util/Set; + public final field metrics Ljava/util/Set; + public final field natd Ljava/util/Set; + public final field or Ljava/util/Set; + public final field orExt Ljava/util/Set; public final field socks Ljava/util/Set; public final field socksUnix Ljava/util/Set; public final field trans Ljava/util/Set; @@ -326,20 +331,15 @@ public final class io/matthewnelson/kmp/tor/runtime/TorListeners { public fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V public fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V public fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V - public synthetic fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/util/Set; - public final fun component2 ()Ljava/util/Set; - public final fun component3 ()Ljava/util/Set; - public final fun component4 ()Ljava/util/Set; - public final fun component5 ()Ljava/util/Set; - public final fun copy (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; - public static synthetic fun copy$default (Lio/matthewnelson/kmp/tor/runtime/TorListeners;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; - public final fun copyDns (Ljava/util/Set;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; - public final fun copyHttp (Ljava/util/Set;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; - public final fun copySocks (Ljava/util/Set;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; - public final fun copySocksUnix (Ljava/util/Set;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; - public final fun copyTrans (Ljava/util/Set;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; + public fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V + public fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V + public fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V + public fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V + public fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V + public synthetic fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/lang/String;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun copy (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; + public static synthetic fun copy$default (Lio/matthewnelson/kmp/tor/runtime/TorListeners;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ILjava/lang/Object;)Lio/matthewnelson/kmp/tor/runtime/TorListeners; public fun equals (Ljava/lang/Object;)Z public fun hashCode ()I public final fun isEmpty ()Z diff --git a/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/RuntimeEvent.kt b/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/RuntimeEvent.kt index e57136758..2200cc227 100644 --- a/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/RuntimeEvent.kt +++ b/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/RuntimeEvent.kt @@ -232,8 +232,13 @@ public sealed class RuntimeEvent private constructor( * TorState[fid=6E96…6985, daemon=On{95%}, network=Enabled] * TorState[fid=6E96…6985, daemon=On{100%}, network=Enabled] * TorListeners[fid=6E96…6985]: [ + * dir: [] * dns: [] * http: [] + * metrics: [] + * natd: [] + * or: [] + * orExt: [] * socks: [ * 127.0.0.1:35607 * ] @@ -243,8 +248,13 @@ public sealed class RuntimeEvent private constructor( * Tor[fid=6E96…6985] IS READY * TorState[fid=6E96…6985, daemon=On{100%}, network=Disabled] * TorListeners[fid=6E96…6985]: [ + * dir: [] * dns: [] * http: [] + * metrics: [] + * natd: [] + * or: [] + * orExt: [] * socks: [] * socksUnix: [] * trans: [] @@ -319,8 +329,13 @@ public sealed class RuntimeEvent private constructor( * TorState[fid=6E96…6985, daemon=On{95%}, network=Enabled] * TorState[fid=6E96…6985, daemon=On{100%}, network=Enabled] * TorListeners[fid=6E96…6985]: [ + * dir: [] * dns: [] * http: [] + * metrics: [] + * natd: [] + * or: [] + * orExt: [] * socks: [ * 127.0.0.1:35607 * ] @@ -330,16 +345,26 @@ public sealed class RuntimeEvent private constructor( * Tor[fid=6E96…6985] IS READY * TorState[fid=6E96…6985, daemon=On{100%}, network=Disabled] * TorListeners[fid=6E96…6985]: [ + * dir: [] * dns: [] * http: [] + * metrics: [] + * natd: [] + * or: [] + * orExt: [] * socks: [] * socksUnix: [] * trans: [] * ] * TorState[fid=6E96…6985, daemon=On{100%}, network=Enabled] * TorListeners[fid=6E96…6985]: [ + * dir: [] * dns: [] * http: [] + * metrics: [] + * natd: [] + * or: [] + * orExt: [] * socks: [ * 127.0.0.1:38255 * ] @@ -348,8 +373,13 @@ public sealed class RuntimeEvent private constructor( * ] * TorState[fid=6E96…6985, daemon=Stopping, network=Enabled] * TorListeners[fid=6E96…6985]: [ + * dir: [] * dns: [] * http: [] + * metrics: [] + * natd: [] + * or: [] + * orExt: [] * socks: [] * socksUnix: [] * trans: [] @@ -379,8 +409,13 @@ public sealed class RuntimeEvent private constructor( * TorState[fid=6E96…6985, daemon=On{95%}, network=Enabled] * TorState[fid=6E96…6985, daemon=On{100%}, network=Enabled] * TorListeners[fid=6E96…6985]: [ + * dir: [] * dns: [] * http: [] + * metrics: [] + * natd: [] + * or: [] + * orExt: [] * socks: [ * 127.0.0.1:35607 * ] @@ -390,8 +425,13 @@ public sealed class RuntimeEvent private constructor( * Tor[fid=6E96…6985] IS READY * TorState[fid=6E96…6985, daemon=On{100%}, network=Disabled] * TorListeners[fid=6E96…6985]: [ + * dir: [] * dns: [] * http: [] + * metrics: [] + * natd: [] + * or: [] + * orExt: [] * socks: [] * socksUnix: [] * trans: [] diff --git a/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/TorListeners.kt b/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/TorListeners.kt index 19813c3ba..6aaeb54c3 100644 --- a/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/TorListeners.kt +++ b/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/TorListeners.kt @@ -50,8 +50,13 @@ import kotlin.time.Duration.Companion.milliseconds * @see [RuntimeEvent.LISTENERS] * */ public class TorListeners private constructor( + dir: Set, dns: Set, http: Set, + metrics: Set, + natd: Set, + or: Set, + orExt: Set, socks: Set, socksUnix: Set, trans: Set, @@ -60,28 +65,89 @@ public class TorListeners private constructor( @JvmOverloads public constructor( + dir: Set = emptySet(), dns: Set = emptySet(), http: Set = emptySet(), + metrics: Set = emptySet(), + natd: Set = emptySet(), + or: Set = emptySet(), + orExt: Set = emptySet(), socks: Set = emptySet(), socksUnix: Set = emptySet(), trans: Set = emptySet(), ): this( + dir = dir, dns = dns, http = http, + metrics = metrics, + natd = natd, + or = or, + orExt = orExt, socks = socks, socksUnix = socksUnix, trans = trans, fid = null, ) + /** + * Listeners defined by [TorConfig.__DirPort]. + * */ + @JvmField + public val dir: Set = dir.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__DNSPort]. + * */ @JvmField public val dns: Set = dns.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__HTTPTunnelPort]. + * */ @JvmField public val http: Set = http.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__MetricsPort]. + * */ + @JvmField + public val metrics: Set = metrics.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__NATDPort]. + * */ + @JvmField + public val natd: Set = natd.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__ORPort]. + * */ + @JvmField + public val or: Set = or.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__ExtORPort]. + * */ + @JvmField + public val orExt: Set = orExt.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__SocksPort] configured + * to use a TCP port. + * */ @JvmField public val socks: Set = socks.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__SocksPort] configured + * to use a unix socket. + * */ @JvmField public val socksUnix: Set = socksUnix.toImmutableSet() + + /** + * Listeners defined by [TorConfig.__TransPort]. + * */ @JvmField public val trans: Set = trans.toImmutableSet() @@ -93,29 +159,37 @@ public class TorListeners private constructor( * false if there is at least 1 listener available. * */ @get:JvmName("isEmpty") - public val isEmpty: Boolean get() = dns.isEmpty() + public val isEmpty: Boolean get() = dir.isEmpty() + && dns.isEmpty() && http.isEmpty() + && metrics.isEmpty() + && natd.isEmpty() + && or.isEmpty() + && orExt.isEmpty() && socks.isEmpty() && socksUnix.isEmpty() && trans.isEmpty() - public operator fun component1(): Set = dns - public operator fun component2(): Set = http - public operator fun component3(): Set = socks - public operator fun component4(): Set = socksUnix - public operator fun component5(): Set = trans - - // JvmOverloads is worthless here b/c of type erasure public fun copy( + dir: Set = this.dir, dns: Set = this.dns, http: Set = this.http, + metrics: Set = this.metrics, + natd: Set = this.natd, + or: Set = this.or, + orExt: Set = this.orExt, socks: Set = this.socks, socksUnix: Set = this.socksUnix, trans: Set = this.trans, ): TorListeners { if ( - dns == this.dns + dir == this.dir + && dns == this.dns && http == this.http + && metrics == this.metrics + && natd == this.natd + && or == this.or + && orExt == this.orExt && socks == this.socks && socksUnix == this.socksUnix && trans == this.trans @@ -124,8 +198,13 @@ public class TorListeners private constructor( } return TorListeners( + dir = dir, dns = dns, http = http, + metrics = metrics, + natd = natd, + or = or, + orExt = orExt, socks = socks, socksUnix = socksUnix, trans = trans, @@ -133,49 +212,29 @@ public class TorListeners private constructor( ) } - /** - * Copies the current [TorListeners], replacing the - * [TorListeners.dns] value with [dns]. - * */ - public fun copyDns(dns: Set): TorListeners = copy(dns = dns) - - /** - * Copies the current [TorListeners], replacing the - * [TorListeners.http] value with [http]. - * */ - public fun copyHttp(http: Set): TorListeners = copy(http = http) - - /** - * Copies the current [TorListeners], replacing the - * [TorListeners.socks] value with [socks]. - * */ - public fun copySocks(socks: Set): TorListeners = copy(socks = socks) - - /** - * Copies the current [TorListeners], replacing the - * [TorListeners.socksUnix] value with [socksUnix]. - * */ - public fun copySocksUnix(socksUnix: Set): TorListeners = copy(socksUnix = socksUnix) - - /** - * Copies the current [TorListeners], replacing the - * [TorListeners.trans] value with [trans]. - * */ - public fun copyTrans(trans: Set): TorListeners = copy(trans = trans) - internal companion object { @JvmSynthetic internal fun of( + dir: Set = emptySet(), dns: Set = emptySet(), http: Set = emptySet(), + metrics: Set = emptySet(), + natd: Set = emptySet(), + or: Set = emptySet(), + orExt: Set = emptySet(), socks: Set = emptySet(), socksUnix: Set = emptySet(), trans: Set = emptySet(), fid: FileID?, ): TorListeners = TorListeners( + dir = dir, dns = dns, http = http, + metrics = metrics, + natd = natd, + or = or, + orExt = orExt, socks = socks, socksUnix = socksUnix, trans = trans, @@ -185,8 +244,13 @@ public class TorListeners private constructor( public override fun equals(other: Any?): Boolean { return other is TorListeners + && other.dir == dir && other.dns == dns && other.http == http + && other.metrics == metrics + && other.natd == natd + && other.or == or + && other.orExt == orExt && other.socks == socks && other.socksUnix == socksUnix && other.trans == trans @@ -194,8 +258,13 @@ public class TorListeners private constructor( public override fun hashCode(): Int { var result = 15 + result = result * 31 + dir.hashCode() result = result * 31 + dns.hashCode() result = result * 31 + http.hashCode() + result = result * 31 + metrics.hashCode() + result = result * 31 + natd.hashCode() + result = result * 31 + or.hashCode() + result = result * 31 + orExt.hashCode() result = result * 31 + socks.hashCode() result = result * 31 + socksUnix.hashCode() result = result * 31 + trans.hashCode() @@ -212,10 +281,20 @@ public class TorListeners private constructor( } appendLine(": [") + append(" dir: [") + appendListeners(dir) append(" dns: [") appendListeners(dns) append(" http: [") appendListeners(http) + append(" metrics: [") + appendListeners(metrics) + append(" natd: [") + appendListeners(natd) + append(" or: [") + appendListeners(or) + append(" orExt: [") + appendListeners(orExt) append(" socks: [") appendListeners(socks) append(" socksUnix: [") @@ -354,9 +433,14 @@ public class TorListeners private constructor( } private fun Type.onClose(address: String) = when (this) { + is Type.DIR, is Type.DNS, is Type.HTTP, - is Type.TRANSPARENT -> { + is Type.METRICS, + is Type.NATD, + is Type.OR, + is Type.OREXT, + is Type.TRANS -> { address.toIPSocketAddressOrNull() ?.update(type = this, wasClosed = true) } @@ -387,9 +471,14 @@ public class TorListeners private constructor( } private fun Type.onOpen(address: String) = when (this) { + is Type.DIR, is Type.DNS, is Type.HTTP, - is Type.TRANSPARENT -> { + is Type.METRICS, + is Type.NATD, + is Type.OR, + is Type.OREXT, + is Type.TRANS -> { address.toIPSocketAddressOrNull() ?.update(type = this, wasClosed = false) } @@ -416,10 +505,15 @@ public class TorListeners private constructor( address: IPSocketAddress, wasClosed: Boolean, ) = when (type) { + is Type.DIR -> dir to Copy.Address { copy(dir = it) } is Type.DNS -> dns to Copy.Address { copy(dns = it) } is Type.HTTP -> http to Copy.Address { copy(http = it) } + is Type.METRICS -> metrics to Copy.Address { copy(metrics = it) } + is Type.NATD -> natd to Copy.Address { copy(natd = it) } + is Type.OR -> or to Copy.Address { copy(or = it) } + is Type.OREXT -> orExt to Copy.Address { copy(orExt = it) } is Type.SOCKS -> socks to Copy.Address { copy(socks = it) } - is Type.TRANSPARENT -> trans to Copy.Address { copy(trans = it) } + is Type.TRANS -> trans to Copy.Address { copy(trans = it) } }.update(address, wasClosed) private fun Pair, Copy>.update( @@ -445,7 +539,10 @@ public class TorListeners private constructor( protected open fun onConfigChangeJob(cmd: TorCmd.Config.Reset, job: EnqueuedJob) { job.invokeOnErrorRecovery(recovery = { - if (cmd.keywords.contains(TorConfig.__SocksPort)) { + if ( + cmd.keywords.contains(TorConfig.__SocksPort) + || cmd.keywords.contains(TorConfig.SocksPort) + ) { // SocksPort was set to default. tor will still // close all other socks listeners that may be // open, but will not dispatch the CONF_CHANGED @@ -457,10 +554,11 @@ public class TorListeners private constructor( protected open fun onConfigChangeJob(cmd: TorCmd.Config.Set, job: EnqueuedJob) { job.invokeOnErrorRecovery(recovery = { - val changes = cmd.settings - .filterByKeyword() - .takeIf { it.isNotEmpty() } - ?.let { settings -> + val changes = run { + val eSocks = cmd.settings.filterByKeyword() + val socks = cmd.settings.filterByKeyword() + eSocks + socks + }.takeIf { it.isNotEmpty() }?.let { settings -> settings.mapTo(LinkedHashSet(settings.size, 1.0F)) { setting -> setting.toString().substringAfter(' ') } @@ -543,18 +641,28 @@ public class TorListeners private constructor( private sealed class Type { + data object DIR: Type() data object DNS: Type() data object HTTP: Type() + data object METRICS: Type() + data object NATD: Type() + data object OR: Type() + data object OREXT: Type() data object SOCKS: Type() - data object TRANSPARENT: Type() + data object TRANS: Type() companion object { fun valueOfOrNull(name: String): Type? = when (name.uppercase()) { + "DIRECTORY" -> DIR "DNS" -> DNS - "HTTP" -> HTTP + "HTTP TUNNEL" -> HTTP + "METRICS" -> METRICS + "TRANSPARENT NATD" -> NATD + "OR" -> OR + "EXTENDED OR" -> OREXT "SOCKS" -> SOCKS - "TRANSPARENT" -> TRANSPARENT + "TRANSPARENT PF/NETFILTER" -> TRANS else -> null } } diff --git a/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/internal/observer/ObserverProcessStdout.kt b/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/internal/observer/ObserverProcessStdout.kt index 325ab989c..cf73e70ed 100644 --- a/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/internal/observer/ObserverProcessStdout.kt +++ b/library/runtime/src/commonMain/kotlin/io/matthewnelson/kmp/tor/runtime/internal/observer/ObserverProcessStdout.kt @@ -55,16 +55,18 @@ internal open class ObserverProcessStdout internal constructor( // UnixDomainSocket // [notice] Closing no-longer-configured Socks listener on ???:0 private fun String.parseListenerClosing() { - val type = substringAfter(CLOSING, "") + val trimmed = substringAfter(CLOSING, "") .substringAfter(' ', "") - .substringBefore(' ', "") - .trim() + .ifEmpty { return } - val address = if (contains(CONN_READY_ON)) { - CONN_READY_ON - } else { - LISTENER_ON - }.let { substringAfter(it, "").trim() } + val type = trimmed + .substringBefore(" listener ", "") + .ifEmpty { return } + + val address = trimmed + .substringAfter(CONN_READY_ON, "") + .ifEmpty { trimmed.substringAfter(LISTENER_ON, "") } + .ifEmpty { return } manager.update(type, address, wasClosed = true) } @@ -74,12 +76,18 @@ internal open class ObserverProcessStdout internal constructor( // [notice] Opened Socks listener connection (ready) on 127.0.0.1:36237 // [notice] Opened Socks listener connection (ready) on /tmp/kmp_tor_test/sf_restart/work/socks.sock // [notice] Opened Transparent pf/netfilter listener connection (ready) on 127.0.0.1:37527 + // [notice] Opened Transparent natd listener connection (ready) on 127.0.0.1:9060 + // [notice] Opened Metrics listener connection (ready) on 127.0.0.1:9059 + // [notice] Opened OR listener connection (ready) on 0.0.0.0:9061 + // [notice] Opened Extended OR listener connection (ready) on 127.0.0.1:9058 + // [notice] Opened Directory listener connection (ready) on 0.0.0.0:9057 private fun String.parseListenerOpened() { - val type = substringAfter(OPENED, "") - .substringBefore(' ', "") - .trim() - val address = substringAfter(CONN_READY_ON, "") - .trim() + val iOpened = indexOf(OPENED) + val iReady = indexOf(CONN_READY_ON) + if (iOpened == -1 || iReady == -1) return + + val type = substring(iOpened + OPENED.length, iReady) + val address = substring(iReady + CONN_READY_ON.length, length) manager.update(type, address, wasClosed = false) } diff --git a/library/runtime/src/commonTest/kotlin/io/matthewnelson/kmp/tor/runtime/TorListenersManagerUnitTest.kt b/library/runtime/src/commonTest/kotlin/io/matthewnelson/kmp/tor/runtime/TorListenersManagerUnitTest.kt index 0a02a912b..823298c9d 100644 --- a/library/runtime/src/commonTest/kotlin/io/matthewnelson/kmp/tor/runtime/TorListenersManagerUnitTest.kt +++ b/library/runtime/src/commonTest/kotlin/io/matthewnelson/kmp/tor/runtime/TorListenersManagerUnitTest.kt @@ -92,6 +92,12 @@ class TorListenersManagerUnitTest { assertTrue(manager.listeners.isEmpty) } + @Test + fun givenOpen_whenDirectory_thenUpdates() = runListenerTest { manager -> + manager.update("Directory", address.value, wasClosed = false) + assertEquals(1, manager.listeners.dir.size) + } + @Test fun givenOpen_whenDNS_thenUpdates() = runListenerTest { manager -> manager.update("DNS", address.value, wasClosed = false) @@ -100,10 +106,28 @@ class TorListenersManagerUnitTest { @Test fun givenOpen_whenHTTP_thenUpdates() = runListenerTest { manager -> - manager.update("HTTP", address.value, wasClosed = false) + manager.update("HTTP tunnel", address.value, wasClosed = false) assertEquals(1, manager.listeners.http.size) } + @Test + fun givenOpen_whenMetrics_thenUpdates() = runListenerTest { manager -> + manager.update("Metrics", address.value, wasClosed = false) + assertEquals(1, manager.listeners.metrics.size) + } + + @Test + fun givenOpen_whenOR_thenUpdates() = runListenerTest { manager -> + manager.update("OR", address.value, wasClosed = false) + assertEquals(1, manager.listeners.or.size) + } + + @Test + fun givenOpen_whenExtendedOR_thenUpdates() = runListenerTest { manager -> + manager.update("Extended OR", address.value, wasClosed = false) + assertEquals(1, manager.listeners.orExt.size) + } + @Test fun givenOpen_whenSocksIP_thenUpdates() = runListenerTest { manager -> manager.update("Socks", address.value, wasClosed = false) @@ -126,8 +150,8 @@ class TorListenersManagerUnitTest { } @Test - fun givenOpen_whenTransparent_thenUpdates() = runListenerTest { manager -> - manager.update("Transparent", address.value, wasClosed = false) + fun givenOpen_whenTrans_thenUpdates() = runListenerTest { manager -> + manager.update("Transparent pf/netfilter", address.value, wasClosed = false) assertEquals(1, manager.listeners.trans.size) } @@ -140,7 +164,17 @@ class TorListenersManagerUnitTest { @Test fun givenClose_whenAddressPresent_thenUpdates() = runListenerTest { manager -> - val types = listOf("DNS", "HTTP", "Socks", "Transparent") + val types = listOf( + "Directory", + "DNS", + "HTTP tunnel", + "Socks", + "Metrics", + "OR", + "Extended OR", + "Transparent natd", + "Transparent pf/netfilter" + ) types.forEach { type -> manager.update(type, address.value, wasClosed = false) @@ -155,7 +189,17 @@ class TorListenersManagerUnitTest { @Test fun givenClose_whenAddressNotPresent_thenDoesNotRemove() = runListenerTest { manager -> - val types = listOf("DNS", "HTTP", "Socks", "Transparent") + val types = listOf( + "Directory", + "DNS", + "HTTP tunnel", + "Socks", + "Metrics", + "OR", + "Extended OR", + "Transparent natd", + "Transparent pf/netfilter" + ) types.forEach { type -> manager.update(type, address.value, wasClosed = false) @@ -229,7 +273,7 @@ class TorListenersManagerUnitTest { assertEquals(2, manager.notifyListeners.size) manager.update("Socks", address.value, wasClosed = true) - manager.update("Transparent", address.value, wasClosed = false) + manager.update("Transparent pf/netfilter", address.value, wasClosed = false) assertEquals(2, manager.notifyListeners.size) // job for dispatching Socks closure should have been cancelled diff --git a/library/runtime/src/commonTest/kotlin/io/matthewnelson/kmp/tor/runtime/internal/observer/ObserverProcessStdoutUnitTest.kt b/library/runtime/src/commonTest/kotlin/io/matthewnelson/kmp/tor/runtime/internal/observer/ObserverProcessStdoutUnitTest.kt index dce32dcd7..a6da0480e 100644 --- a/library/runtime/src/commonTest/kotlin/io/matthewnelson/kmp/tor/runtime/internal/observer/ObserverProcessStdoutUnitTest.kt +++ b/library/runtime/src/commonTest/kotlin/io/matthewnelson/kmp/tor/runtime/internal/observer/ObserverProcessStdoutUnitTest.kt @@ -58,10 +58,15 @@ class ObserverProcessStdoutUnitTest { @Test fun givenCloseListener_whenParsed_thenUpdatesTorListenersManager() { val values = listOf( + Pair("Directory", "127.0.0.1:53085") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured Directory listener on 127.0.0.1:53085", Pair("DNS", "127.0.0.1:53085") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured DNS listener on 127.0.0.1:53085", - Pair("HTTP", "127.0.0.1:48932") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured HTTP tunnel listener on 127.0.0.1:48932", + Pair("HTTP tunnel", "127.0.0.1:48932") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured HTTP tunnel listener on 127.0.0.1:48932", + Pair("Metrics", "127.0.0.1:9150") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured Metrics listener on 127.0.0.1:9150", + Pair("OR", "127.0.0.1:9150") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured OR listener on 127.0.0.1:9150", + Pair("Extended OR", "127.0.0.1:9150") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured Extended OR listener on 127.0.0.1:9150", Pair("Socks", "127.0.0.1:9150") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured Socks listener on 127.0.0.1:9150", - Pair("Transparent", "127.0.0.1:45963") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured Transparent pf/netfilter listener on 127.0.0.1:45963", + Pair("Transparent natd", "127.0.0.1:45963") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured Transparent natd listener on 127.0.0.1:45963", + Pair("Transparent pf/netfilter", "127.0.0.1:45963") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured Transparent pf/netfilter listener on 127.0.0.1:45963", Pair("Socks", "???:0") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing no-longer-configured Socks listener on ???:0", Pair("Socks", "/tmp/kmp_tor_test/obs_conn_no_net/work/socks5.sock") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Closing partially-constructed Socks listener connection (ready) on /tmp/kmp_tor_test/obs_conn_no_net/work/socks5.sock", ) @@ -82,10 +87,15 @@ class ObserverProcessStdoutUnitTest { @Test fun givenOpenListener_whenParsed_thenUpdatesTorListenersManager() { val values = listOf( + Pair("Directory", "127.0.0.1:53085") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened Directory listener connection (ready) on 127.0.0.1:53085", Pair("DNS", "127.0.0.1:53085") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened DNS listener connection (ready) on 127.0.0.1:53085", - Pair("HTTP", "127.0.0.1:48932") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened HTTP tunnel listener connection (ready) on 127.0.0.1:48932", + Pair("HTTP tunnel", "127.0.0.1:48932") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened HTTP tunnel listener connection (ready) on 127.0.0.1:48932", + Pair("Metrics", "127.0.0.1:9150") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened Metrics listener connection (ready) on 127.0.0.1:9150", + Pair("OR", "127.0.0.1:9150") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened OR listener connection (ready) on 127.0.0.1:9150", + Pair("Extended OR", "127.0.0.1:9150") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened Extended OR listener connection (ready) on 127.0.0.1:9150", Pair("Socks", "127.0.0.1:9150") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened Socks listener connection (ready) on 127.0.0.1:9150", - Pair("Transparent", "127.0.0.1:45963") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened Transparent pf/netfilter listener connection (ready) on 127.0.0.1:45963", + Pair("Transparent natd", "127.0.0.1:45963") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened Transparent natd listener connection (ready) on 127.0.0.1:45963", + Pair("Transparent pf/netfilter", "127.0.0.1:45963") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened Transparent pf/netfilter listener connection (ready) on 127.0.0.1:45963", Pair("Socks", "/tmp/kmp_tor_test/sf_restart/work/socks.sock") to "TorDaemon[fid=A3C2…6595]@1414604497 May 24 18:10:05.000 [notice] Opened Socks listener connection (ready) on /tmp/kmp_tor_test/sf_restart/work/socks.sock", )