From cdc3da06f846edb0512b5694df2530ed5dc03561 Mon Sep 17 00:00:00 2001 From: Afshin Arani Date: Sat, 28 Oct 2023 17:54:23 +0330 Subject: [PATCH] fixup: cachePath and use client everywhere --- NOnion.Tests/HiddenServicesTests.cs | 62 +++++++----- NOnion.Tests/TorClientTests.cs | 31 ++++-- NOnion.Tests/TorDirectoryTests.cs | 2 +- NOnion/Client/TorClient.fs | 54 ++++++---- NOnion/Exceptions.fs | 3 +- NOnion/Services/TorServiceClient.fs | 57 ++++------- NOnion/Services/TorServiceHost.fs | 152 +++++++++++----------------- 7 files changed, 177 insertions(+), 184 deletions(-) diff --git a/NOnion.Tests/HiddenServicesTests.cs b/NOnion.Tests/HiddenServicesTests.cs index f75c1473..8bf18c5c 100644 --- a/NOnion.Tests/HiddenServicesTests.cs +++ b/NOnion.Tests/HiddenServicesTests.cs @@ -16,6 +16,7 @@ using NOnion.Network; using NOnion.Http; using NOnion.Cells.Relay; +using NOnion.Client; using NOnion.Directory; using NOnion.Tests.Utility; using NOnion.Services; @@ -24,6 +25,23 @@ namespace NOnion.Tests { public class HiddenServicesTests { + [SetUp] + public void Init() + { + cachePath = + new DirectoryInfo( + Path.Combine( + Path.GetTempPath(), + Path.GetFileNameWithoutExtension( + Path.GetRandomFileName() + ) + ) + ); + cachePath.Create(); + } + + private DirectoryInfo cachePath = null; + /* It's possible that the router returned by GetRandomFallbackDirectory or * GetRandomRoutersForDirectoryBrowsing be inaccessable so we need to continue * retrying if an exceptions happened to make sure the issues are not related @@ -33,11 +51,8 @@ public class HiddenServicesTests private async Task CreateIntroductionCircuit() { - var node = (CircuitNodeDetail.Create)(await CircuitHelper.GetRandomRoutersForDirectoryBrowsingWithRetry()).First(); - using TorGuard guard = await TorGuard.NewClientAsync(node.EndPoint); - var circuit = new TorCircuit(guard); - - await circuit.CreateAsync(CircuitNodeDetail.FastCreate); + using TorClient torClient = await TorClient.BootstrapWithGithubAsync(cachePath); + var circuit = await torClient.CreateCircuitAsync(1, CircuitPurpose.Unknown, FSharpOption.None); await circuit.RegisterAsIntroductionPointAsync(FSharpOption.None, StubCallback, DisconnectionCallback); } @@ -61,12 +76,8 @@ private async Task CreateRendezvousCircuit() var array = new byte[Constants.RendezvousCookieLength]; RandomNumberGenerator.Create().GetNonZeroBytes(array); - var nodes = await CircuitHelper.GetRandomRoutersForDirectoryBrowsingWithRetry(2); - using TorGuard guard = await TorGuard.NewClientAsync(((CircuitNodeDetail.Create)nodes[0]).EndPoint); - var circuit = new TorCircuit(guard); - - await circuit.CreateAsync(nodes[0]); - await circuit.ExtendAsync(nodes[1]); + using TorClient torClient = await TorClient.BootstrapWithGithubAsync(cachePath); + var circuit = await torClient.CreateCircuitAsync(2, CircuitPurpose.Unknown, FSharpOption.None); await circuit.RegisterAsRendezvousPointAsync(array); } @@ -92,10 +103,10 @@ private async Task ReadExact(TorStream stream, byte[] buffer, int off, int public async Task BrowseFacebookOverHS() { - TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath())); + using TorClient torClient = await TorClient.BootstrapWithGithubAsync(cachePath); - var client = await TorServiceClient.ConnectAsync(directory, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion"); - var httpClient = new TorHttpClient(client.GetStream(), "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion"); + var serviceClient = await TorServiceClient.ConnectAsync(torClient, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion"); + var httpClient = new TorHttpClient(serviceClient.GetStream(), "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion"); try { @@ -109,7 +120,6 @@ public async Task BrowseFacebookOverHS() } [Test] - [Retry(TestsRetryCount)] public void CanBrowseFacebookOverHS() { Assert.DoesNotThrowAsync(BrowseFacebookOverHS); @@ -117,11 +127,11 @@ public void CanBrowseFacebookOverHS() public async Task BrowseFacebookOverHSWithTLS() { - TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath())); + using TorClient torClient = await TorClient.BootstrapWithGithubAsync(cachePath); + + var serviceClient = await TorServiceClient.ConnectAsync(torClient, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:443"); - var client = await TorServiceClient.ConnectAsync(directory, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:443"); - - var sslStream = new SslStream(client.GetStream(), true, (sender, cert, chain, sslPolicyErrors) => true); + var sslStream = new SslStream(serviceClient.GetStream(), true, (sender, cert, chain, sslPolicyErrors) => true); await sslStream.AuthenticateAsClientAsync(string.Empty, null, SslProtocols.Tls12, false); var httpClientOverSslStream = new TorHttpClient(sslStream, "www.facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion"); @@ -139,7 +149,6 @@ public async Task BrowseFacebookOverHSWithTLS() } [Test] - [Retry(TestsRetryCount)] public void CanBrowseFacebookOverHSWithTLS() { Assert.DoesNotThrowAsync(BrowseFacebookOverHSWithTLS); @@ -147,10 +156,8 @@ public void CanBrowseFacebookOverHSWithTLS() public async Task EstablishAndCommunicateOverHSConnectionOnionStyle() { - int descriptorUploadRetryLimit = 2; - - TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath())); - + using TorClient torClient = await TorClient.BootstrapWithGithubAsync(cachePath); + TorLogger.Log("Finished bootstraping"); SecureRandom random = new SecureRandom(); @@ -158,7 +165,7 @@ public async Task EstablishAndCommunicateOverHSConnectionOnionStyle() kpGen.Init(new Ed25519KeyGenerationParameters(random)); Ed25519PrivateKeyParameters masterPrivateKey = (Ed25519PrivateKeyParameters)kpGen.GenerateKeyPair().Private; - TorServiceHost host = new TorServiceHost(directory, descriptorUploadRetryLimit, TestsRetryCount, FSharpOption.Some(masterPrivateKey)); + TorServiceHost host = new TorServiceHost(torClient, FSharpOption.Some(masterPrivateKey)); await host.StartAsync(); TorLogger.Log("Finished starting HS host"); @@ -175,8 +182,8 @@ public async Task EstablishAndCommunicateOverHSConnectionOnionStyle() var clientSide = Task.Run(async () => { - var client = await TorServiceClient.ConnectAsync(directory, host.ExportUrl()); - var stream = client.GetStream(); + var serviceClient = await TorServiceClient.ConnectAsync(torClient, host.ExportUrl()); + var stream = serviceClient.GetStream(); var lengthBytes = new byte[sizeof(int)]; await ReadExact(stream, lengthBytes, 0, lengthBytes.Length); var length = BitConverter.ToInt32(lengthBytes); @@ -192,7 +199,6 @@ public async Task EstablishAndCommunicateOverHSConnectionOnionStyle() } [Test] - [Retry(TestsRetryCount)] public void CanEstablishAndCommunicateOverHSConnectionOnionStyle() { Assert.DoesNotThrowAsync(EstablishAndCommunicateOverHSConnectionOnionStyle); diff --git a/NOnion.Tests/TorClientTests.cs b/NOnion.Tests/TorClientTests.cs index 04cab67a..c7cb3ef4 100644 --- a/NOnion.Tests/TorClientTests.cs +++ b/NOnion.Tests/TorClientTests.cs @@ -1,23 +1,40 @@ -using System; -using System.Collections.Generic; +using Microsoft.FSharp.Core; using System.IO; -using System.Text; using System.Threading.Tasks; using NUnit.Framework; -using NOnion.Directory; using NOnion.Client; -using Microsoft.FSharp.Core; - namespace NOnion.Tests { public class TorClientTests { + private async Task BootstrapWithGithub() + { + await TorClient.BootstrapWithGithubAsync(FSharpOption.None); + } + + [Test] + public void CanBootstrapWithGithub() + { + Assert.DoesNotThrowAsync(BootstrapWithGithub); + } + + private async Task BootstrapWithEmbeddedList() + { + await TorClient.BootstrapWithEmbeddedListAsync(FSharpOption.None); + } + + [Test] + public void CanBootstrapWithEmbeddedList() + { + Assert.DoesNotThrowAsync(BootstrapWithEmbeddedList); + } + private async Task CreateCircuit() { - var client = await TorClient.BootstrapWithEmbeddedListAsync(); + using TorClient client = await TorClient.BootstrapWithEmbeddedListAsync(FSharpOption.None); await client.CreateCircuitAsync(3, CircuitPurpose.Unknown, FSharpOption.None); } diff --git a/NOnion.Tests/TorDirectoryTests.cs b/NOnion.Tests/TorDirectoryTests.cs index f18a9a7c..749b9c79 100644 --- a/NOnion.Tests/TorDirectoryTests.cs +++ b/NOnion.Tests/TorDirectoryTests.cs @@ -67,4 +67,4 @@ public void CanReturnRandomRouter() Assert.DoesNotThrowAsync(ReturnRandomRouter); } } -} +} \ No newline at end of file diff --git a/NOnion/Client/TorClient.fs b/NOnion/Client/TorClient.fs index a5bd89f3..d4ceb055 100644 --- a/NOnion/Client/TorClient.fs +++ b/NOnion/Client/TorClient.fs @@ -41,17 +41,23 @@ type TorClient internal (directory: TorDirectory) = ) |> Seq.toList - static let BootstrapDirectory(ipEndPointList: List) = + static let BootstrapDirectory + (cachePath: Option) + (ipEndPointList: List) + = async { let rec tryBootstrap(ipEndPointList: List) = async { match ipEndPointList with | ipEndPoint :: tail -> try + let cacheDirectory = + match cachePath with + | None -> Path.GetTempPath() |> DirectoryInfo + | Some path -> path + let! directory = - TorDirectory.Bootstrap - ipEndPoint - (Path.GetTempPath() |> DirectoryInfo) + TorDirectory.Bootstrap ipEndPoint cacheDirectory return directory with @@ -62,33 +68,40 @@ type TorClient internal (directory: TorDirectory) = return! tryBootstrap ipEndPointList } - static let CreateClientFromFallbackString(fallbackListString: string) = + static let CreateClientFromFallbackString + (fallbackListString: string) + (cachePath: Option) + = async { let! directory = fallbackListString |> ConvertFallbackIncToList |> SelectRandomEndpoints - |> BootstrapDirectory + |> BootstrapDirectory cachePath return new TorClient(directory) } let guardsToDispose = ConcurrentBag() - static member AsyncBootstrapWithEmbeddedList() = + static member AsyncBootstrapWithEmbeddedList + (cachePath: Option) + = async { let fallbackListString = EmbeddedResourceUtility.ExtractEmbeddedResourceFileContents( "fallback_dirs.inc" ) - return! CreateClientFromFallbackString fallbackListString + return! CreateClientFromFallbackString fallbackListString cachePath } - static member BootstrapWithEmbeddedListAsync() = - TorClient.AsyncBootstrapWithEmbeddedList() |> Async.StartAsTask + static member BootstrapWithEmbeddedListAsync + (cachePath: Option) + = + TorClient.AsyncBootstrapWithEmbeddedList cachePath |> Async.StartAsTask - static member AsyncBootstrapWithGithub() = + static member AsyncBootstrapWithGithub(cachePath: Option) = async { let! fallbackListString = let urlToTorServerList = @@ -98,15 +111,15 @@ type TorClient internal (directory: TorDirectory) = httpClient.GetStringAsync urlToTorServerList |> Async.AwaitTask - return! CreateClientFromFallbackString fallbackListString + return! CreateClientFromFallbackString fallbackListString cachePath } - static member BootstrapWithGithubAsync() = - TorClient.AsyncBootstrapWithGithub() |> Async.StartAsTask + static member BootstrapWithGithubAsync(cachePath: Option) = + TorClient.AsyncBootstrapWithGithub cachePath |> Async.StartAsTask member __.Directory = directory - member __.CreateCircuit + member __.AsyncCreateCircuit (hopsCount: int) (purpose: CircuitPurpose) (extendByNodeOpt: Option) @@ -133,10 +146,7 @@ type TorClient internal (directory: TorDirectory) = let rec tryCreateCircuit(tryNumber: int) = async { if tryNumber > maximumExtendByNodeRetry then - return - raise - <| NOnionException - "Destination node can't be reached" + return raise <| DestinationNodeCantBeReachedException() else try let! guard, guardDetail = createNewGuard() @@ -204,7 +214,8 @@ type TorClient internal (directory: TorDirectory) = | :? NOnionException -> return raise - DestinationNodeCantBeReachedException + <| DestinationNodeCantBeReachedException + () | None -> () return circuit @@ -230,7 +241,8 @@ type TorClient internal (directory: TorDirectory) = purpose: CircuitPurpose, extendByNode: Option ) = - self.CreateCircuit hopsCount purpose extendByNode |> Async.StartAsTask + self.AsyncCreateCircuit hopsCount purpose extendByNode + |> Async.StartAsTask interface IDisposable with diff --git a/NOnion/Exceptions.fs b/NOnion/Exceptions.fs index 4bdf7096..757f34ab 100644 --- a/NOnion/Exceptions.fs +++ b/NOnion/Exceptions.fs @@ -54,4 +54,5 @@ type NOnionSocketException innerException ) -exception internal DestinationNodeCantBeReachedException +type DestinationNodeCantBeReachedException() = + inherit NOnionException("Destination node can't be reached") diff --git a/NOnion/Services/TorServiceClient.fs b/NOnion/Services/TorServiceClient.fs index 669d5207..074de3e3 100644 --- a/NOnion/Services/TorServiceClient.fs +++ b/NOnion/Services/TorServiceClient.fs @@ -22,7 +22,7 @@ open NOnion.Network type TorServiceClient = private { - RendezvousGuard: TorGuard + TorClient: TorClient RendezvousCircuit: TorCircuit Stream: TorStream } @@ -30,17 +30,16 @@ type TorServiceClient = member self.GetStream() = self.Stream - static member ConnectAsync (directory: TorDirectory) (url: string) = - TorServiceClient.Connect directory url |> Async.StartAsTask + static member ConnectAsync (client: TorClient) (url: string) = + TorServiceClient.Connect client url |> Async.StartAsTask - static member Connect (directory: TorDirectory) (url: string) = + static member Connect (client: TorClient) (url: string) = async { - let publicKey, port = HiddenServicesUtility.DecodeOnionUrl url let getIntroductionPointInfo() = async { - let! networkStatus = directory.GetLiveNetworkStatus() + let! networkStatus = client.Directory.GetLiveNetworkStatus() let periodNum, periodLength = networkStatus.GetTimePeriod() let srv = networkStatus.GetCurrentSRVForClient() @@ -51,7 +50,7 @@ type TorServiceClient = publicKey let! responsibleDirs = - directory.GetResponsibleHiddenServiceDirectories + client.Directory.GetResponsibleHiddenServiceDirectories blindedPublicKey srv periodNum @@ -66,15 +65,13 @@ type TorServiceClient = raise <| DescriptorDownloadFailedException() | hsDirectory :: tail -> try - use torClient = new TorClient(directory) - let! hsDirectoryNode = - directory.GetCircuitNodeDetailByIdentity + client.Directory.GetCircuitNodeDetailByIdentity hsDirectory try let! circuit = - torClient.CreateCircuit + client.AsyncCreateCircuit 2 CircuitPurpose.Unknown (Some hsDirectoryNode) @@ -378,18 +375,14 @@ type TorServiceClient = .Create() .GetNonZeroBytes randomGeneratedCookie - let! endpoint, guardnode = directory.GetRouter RouterType.Guard - let! _, rendezvousNode = directory.GetRouter RouterType.Normal - - let! rendezvousGuard = - TorGuard.NewClientWithIdentity - endpoint - (guardnode.GetIdentityKey() |> Some) + let! _, rendezvousNode = + client.Directory.GetRouter RouterType.Normal - let rendezvousCircuit = TorCircuit rendezvousGuard - - do! rendezvousCircuit.Create guardnode |> Async.Ignore - do! rendezvousCircuit.Extend rendezvousNode |> Async.Ignore + let! rendezvousCircuit = + client.AsyncCreateCircuit + 1 + CircuitPurpose.Unknown + (Some rendezvousNode) do! rendezvousCircuit.RegisterAsRendezvousPoint @@ -422,7 +415,7 @@ type TorServiceClient = ] } - let! networkStatus = directory.GetLiveNetworkStatus() + let! networkStatus = client.Directory.GetLiveNetworkStatus() let periodInfo = networkStatus.GetTimePeriod() let data, macKey = @@ -455,13 +448,11 @@ type TorServiceClient = macKey } - let introCircuit = TorCircuit rendezvousGuard - - do! introCircuit.Create guardnode |> Async.Ignore - - do! - introCircuit.Extend introductionPointNodeDetail - |> Async.Ignore + let! introCircuit = + client.AsyncCreateCircuit + 1 + Unknown + (Some introductionPointNodeDetail) let rendezvousJoin = rendezvousCircuit.WaitingForRendezvousJoin @@ -491,7 +482,7 @@ type TorServiceClient = return { - RendezvousGuard = rendezvousGuard + TorClient = client RendezvousCircuit = rendezvousCircuit Stream = serviceStream } @@ -499,7 +490,3 @@ type TorServiceClient = return failwith "Never happens. GetRouter never returns FastCreate" } - - interface IDisposable with - member self.Dispose() = - (self.RendezvousGuard :> IDisposable).Dispose() diff --git a/NOnion/Services/TorServiceHost.fs b/NOnion/Services/TorServiceHost.fs index 9e7207fa..c107fb52 100644 --- a/NOnion/Services/TorServiceHost.fs +++ b/NOnion/Services/TorServiceHost.fs @@ -37,9 +37,7 @@ type IntroductionPointInfo = type TorServiceHost ( - directory: TorDirectory, - maxDescriptorUploadRetryCount: int, - maxRendezvousConnectRetryCount: int, + client: TorClient, maybeMasterPrivateKey: Option ) = @@ -126,28 +124,18 @@ type TorServiceHost introEncPubKey = async { - let! endPoint, randomNodeDetails = - directory.GetRouter RouterType.Guard - - let! guard = - TorGuard.NewClientWithIdentity - endPoint - (randomNodeDetails.GetIdentityKey() |> Some) - - let rendezvousCircuit = - TorCircuit(guard, self.IncomingServiceStreamCallback) - - do! rendezvousCircuit.Create randomNodeDetails |> Async.Ignore - - do! - rendezvousCircuit.Extend( - CircuitNodeDetail.Create( - rendezvousEndpoint, - onionKey, - rendezvousFingerPrint - ) + let lastNodeDetails = + CircuitNodeDetail.Create( + rendezvousEndpoint, + onionKey, + rendezvousFingerPrint ) - |> Async.Ignore + + let! rendezvousCircuit = + client.AsyncCreateCircuit + 2 + CircuitPurpose.Unknown + (Some lastNodeDetails) do! rendezvousCircuit.Rendezvous @@ -182,7 +170,7 @@ type TorServiceHost introductionPointDetails.EncryptionKey.Private :?> X25519PrivateKeyParameters - let! networkStatus = directory.GetLiveNetworkStatus() + let! networkStatus = client.Directory.GetLiveNetworkStatus() let periodInfo = networkStatus.GetTimePeriod() let decryptedData, macKey = @@ -238,7 +226,7 @@ type TorServiceHost | Some linkSpecifier -> linkSpecifier.Data | None -> failwith "No rendezvous fingerprint found!" - let connectToRendezvousJob = + do! tryConnectingToRendezvous rendezvousEndpoint rendezvousFingerPrint @@ -249,11 +237,6 @@ type TorServiceHost introEncPrivKey introEncPubKey - do! - FSharpUtil.Retry - connectToRendezvousJob - maxRendezvousConnectRetryCount - return () } @@ -266,10 +249,10 @@ type TorServiceHost async { try let! guardEndPoint, guardNodeDetail = - directory.GetRouter RouterType.Guard + client.Directory.GetRouter RouterType.Guard let! _, introNodeDetail = - directory.GetRouter RouterType.Normal + client.Directory.GetRouter RouterType.Normal match introNodeDetail with | FastCreate -> @@ -369,65 +352,52 @@ type TorServiceHost member self.UploadDescriptor (directoryToUploadTo: string) (document: HiddenServiceFirstLayerDescriptorDocument) - (retry: int) = async { - if retry > maxDescriptorUploadRetryCount then - return () - else - try - let! hsDirectoryNode = - directory.GetCircuitNodeDetailByIdentity - directoryToUploadTo - - use torClient = new TorClient(directory) - - let! circuit = - torClient.CreateCircuit - 2 - CircuitPurpose.Unknown - (Some hsDirectoryNode) - - use dirStream = new TorStream(circuit) - do! dirStream.ConnectToDirectory() |> Async.Ignore - - let! _response = - TorHttpClient( - dirStream, - Constants.DefaultHttpHost - ) - .PostString - (sprintf - "/tor/hs/%i/publish" - Constants.HiddenServices.Version) - (document.ToString()) - - TorLogger.Log( - sprintf - "TorServiceHost: descriptor uploaded to node with identity %s" - directoryToUploadTo - ) - - return () - with - | :? UnsuccessfulHttpResponseException -> - // During testing, after migration to microdescriptor, we saw instances of - // 404 error msg when trying to publish our descriptors which mean for - // some reason we're trying to upload descriptor to a directory that - // is not a hidden service directory, there is no point in retrying here. - return () - | ex -> - TorLogger.Log( - sprintf - "TorServiceHost: hs descriptor upload failed, ex=%s" - (ex.ToString()) + try + let! hsDirectoryNode = + client.Directory.GetCircuitNodeDetailByIdentity + directoryToUploadTo + + let! circuit = + client.AsyncCreateCircuit + 2 + CircuitPurpose.Unknown + (Some hsDirectoryNode) + + use dirStream = new TorStream(circuit) + do! dirStream.ConnectToDirectory() |> Async.Ignore + + let! _response = + TorHttpClient( + dirStream, + Constants.DefaultHttpHost ) + .PostString + (sprintf + "/tor/hs/%i/publish" + Constants.HiddenServices.Version) + (document.ToString()) + + TorLogger.Log( + sprintf + "TorServiceHost: descriptor uploaded to node with identity %s" + directoryToUploadTo + ) - return! - self.UploadDescriptor - directoryToUploadTo - document - (retry + 1) + return () + with + | :? DestinationNodeCantBeReachedException + | :? UnsuccessfulHttpResponseException -> + // During testing, after migration to microdescriptor, we saw instances of + // 404 error msg when trying to publish our descriptors which mean for + // some reason we're trying to upload descriptor to a directory that + // is not a hidden service directory, there is no point in retrying here. + + // TorClient tries multiple times with different circuit to connect to + // the directory, if destination node can't be reached with any circuit + // we stop trying. + return () } member self.BuildAndUploadDescriptor @@ -443,7 +413,7 @@ type TorServiceHost (masterPublicKey.GetEncoded()) let! responsibleDirs = - directory.GetResponsibleHiddenServiceDirectories + client.Directory.GetResponsibleHiddenServiceDirectories blindedPublicKey srv periodNum @@ -691,7 +661,7 @@ type TorServiceHost let jobs = responsibleDirs - |> Seq.map(fun dir -> self.UploadDescriptor dir outerWrapper 1) + |> Seq.map(fun dir -> self.UploadDescriptor dir outerWrapper) do! Async.Parallel( @@ -749,7 +719,7 @@ type TorServiceHost //TODO: this should refresh every 60-120min member self.KeepDescriptorsUpToDate() = async { - let! networkStatus = directory.GetLiveNetworkStatus() + let! networkStatus = client.Directory.GetLiveNetworkStatus() let firstDescriptorBuildJob = self.UpdateFirstDescriptor networkStatus