forked from aarani/NOnion
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Client,Network,Services: add TorClient
- Loading branch information
Showing
7 changed files
with
1,366 additions
and
38 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
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 CreateCircuit() | ||
{ | ||
var client = await TorClient.BootstrapWithEmbeddedListAsync(); | ||
await client.CreateCircuitAsync(3, FSharpOption<Network.CircuitNodeDetail>.None); | ||
} | ||
|
||
[Test] | ||
public void CanCreateCircuit() | ||
{ | ||
Assert.DoesNotThrowAsync(CreateCircuit); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
namespace NOnion.Client | ||
|
||
open System | ||
open System.IO | ||
open System.Net | ||
open System.Net.Http | ||
open System.Text.RegularExpressions | ||
|
||
open NOnion.Directory | ||
open NOnion.Utility | ||
open NOnion | ||
open NOnion.Network | ||
|
||
type TorClient internal (directory: TorDirectory) = | ||
static let maximumBootstrapTries = 5 | ||
|
||
static let maximumExtendByNodeRetry = 5 | ||
|
||
static let ConvertFallbackIncToList(fallbackIncString: string) = | ||
let ipv4Pattern = "\"([0-9\\.]+)\\sorport=(\\S*)\\sid=(\\S*)\"" | ||
let matches = Regex.Matches(fallbackIncString, ipv4Pattern) | ||
|
||
matches | ||
|> Seq.cast | ||
|> Seq.map(fun (regMatch: Match) -> | ||
regMatch.Groups.[1].Value, int regMatch.Groups.[2].Value | ||
) | ||
|> Seq.toList | ||
|
||
static let SelectRandomEndpoints(fallbackList: List<string * int>) = | ||
fallbackList | ||
|> SeqUtils.TakeRandom maximumBootstrapTries | ||
|> Seq.map(fun (ipString, port) -> | ||
let ipAddress = IPAddress.Parse ipString | ||
IPEndPoint(ipAddress, port) | ||
) | ||
|> Seq.toList | ||
|
||
static let BootstrapDirectory(ipEndPointList: List<IPEndPoint>) = | ||
async { | ||
let rec tryBootstrap(ipEndPointList: List<IPEndPoint>) = | ||
async { | ||
match ipEndPointList with | ||
| ipEndPoint :: tail -> | ||
try | ||
let! directory = | ||
TorDirectory.Bootstrap | ||
ipEndPoint | ||
(Path.GetTempPath() |> DirectoryInfo) | ||
|
||
return directory | ||
with | ||
| :? NOnionException -> return! tryBootstrap tail | ||
| [] -> return failwith "Maximum bootstrap tries reached!" | ||
} | ||
|
||
return! tryBootstrap ipEndPointList | ||
} | ||
|
||
static let CreateClientFromFallbackString(fallbackListString: string) = | ||
async { | ||
let! directory = | ||
fallbackListString | ||
|> ConvertFallbackIncToList | ||
|> SelectRandomEndpoints | ||
|> BootstrapDirectory | ||
|
||
return new TorClient(directory) | ||
} | ||
|
||
//FIXME: lock this | ||
let mutable guardsToDispose: List<TorGuard> = List.empty | ||
|
||
static member BootstrapWithEmbeddedList() = | ||
async { | ||
let fallbackListString = | ||
EmbeddedResourceUtility.ExtractEmbeddedResourceFileContents( | ||
"fallback_dirs.inc" | ||
) | ||
|
||
return! CreateClientFromFallbackString fallbackListString | ||
} | ||
|
||
static member BootstrapWithEmbeddedListAsync() = | ||
TorClient.BootstrapWithEmbeddedList() |> Async.StartAsTask | ||
|
||
static member BootstrapWithGithub() = | ||
async { | ||
let! fallbackListString = | ||
let urlToTorServerList = | ||
"https://raw.githubusercontent.com/torproject/tor/main/src/app/config/fallback_dirs.inc" | ||
|
||
use httpClient = new HttpClient() | ||
|
||
httpClient.GetStringAsync urlToTorServerList |> Async.AwaitTask | ||
|
||
return! CreateClientFromFallbackString fallbackListString | ||
} | ||
|
||
member __.CreateCircuit | ||
(hopsCount: int) | ||
(extendByNodeOpt: Option<CircuitNodeDetail>) | ||
= | ||
async { | ||
let rec createNewGuard() = | ||
async { | ||
let! ipEndPoint, nodeDetail = | ||
directory.GetRouter RouterType.Guard | ||
|
||
try | ||
let! guard = | ||
TorGuard.NewClientWithIdentity | ||
ipEndPoint | ||
(nodeDetail.GetIdentityKey() |> Some) | ||
|
||
guardsToDispose <- guard :: guardsToDispose | ||
return guard, nodeDetail | ||
with | ||
| :? GuardConnectionFailedException -> | ||
return! createNewGuard() | ||
} | ||
|
||
let rec tryCreateCircuit(tryNumber: int) = | ||
async { | ||
if tryNumber > maximumExtendByNodeRetry then | ||
return | ||
raise | ||
<| NOnionException | ||
"Destination node can't be reached" | ||
else | ||
try | ||
let! guard, guardDetail = createNewGuard() | ||
let circuit = TorCircuit guard | ||
|
||
do! | ||
circuit.Create guardDetail | ||
|> Async.Ignore<uint16> | ||
|
||
let rec extend | ||
(numHopsToExtend: int) | ||
(nodesSoFar: List<CircuitNodeDetail>) | ||
= | ||
async { | ||
if numHopsToExtend > 0 then | ||
let rec findUnusedNode() = | ||
async { | ||
let! _ipEndPoint, nodeDetail = | ||
directory.GetRouter | ||
RouterType.Normal | ||
|
||
if (List.contains | ||
nodeDetail | ||
nodesSoFar) then | ||
return! findUnusedNode() | ||
else | ||
return nodeDetail | ||
} | ||
|
||
let! newUnusedNode = findUnusedNode() | ||
|
||
do! | ||
circuit.Extend newUnusedNode | ||
|> Async.Ignore<uint16> | ||
|
||
return! | ||
extend | ||
(numHopsToExtend - 1) | ||
(newUnusedNode :: nodesSoFar) | ||
else | ||
() | ||
} | ||
|
||
do! | ||
extend | ||
(hopsCount - 1) | ||
(List.singleton guardDetail) | ||
|
||
match extendByNodeOpt with | ||
| Some extendByNode -> | ||
try | ||
do! | ||
circuit.Extend extendByNode | ||
|> Async.Ignore<uint16> | ||
with | ||
| :? NOnionException -> | ||
return | ||
raise | ||
DestinationNodeCantBeReachedException | ||
| None -> () | ||
|
||
return circuit | ||
with | ||
| :? DestinationNodeCantBeReachedException -> | ||
return! tryCreateCircuit(tryNumber + 1) | ||
| ex -> | ||
match FSharpUtil.FindException<NOnionException> ex | ||
with | ||
| Some _nonionEx -> | ||
return! tryCreateCircuit tryNumber | ||
| None -> return raise <| FSharpUtil.ReRaise ex | ||
} | ||
|
||
let startTryNumber = 1 | ||
|
||
return! tryCreateCircuit startTryNumber | ||
} | ||
|
||
member self.CreateCircuitAsync | ||
( | ||
hopsCount: int, | ||
extendByNode: Option<CircuitNodeDetail> | ||
) = | ||
self.CreateCircuit hopsCount extendByNode |> Async.StartAsTask | ||
|
||
|
||
interface IDisposable with | ||
member __.Dispose() = | ||
for guard in guardsToDispose do | ||
(guard :> IDisposable).Dispose() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.