Don't use snodes known to be unreliable for path building

pull/148/head
gmbnt 5 years ago
parent 029df5d329
commit 625a3b151b

@ -3,8 +3,8 @@ import PromiseKit
public extension LokiAPI { public extension LokiAPI {
private static var snodeVersion: [LokiAPITarget:String] = [:] private static var snodeVersion: [LokiAPITarget:String] = [:]
/// Only ever accessed from `LokiAPI.errorHandlingQueue` to avoid race conditions. /// Only ever modified from `LokiAPI.errorHandlingQueue` to avoid race conditions.
fileprivate static var failureCount: [LokiAPITarget:UInt] = [:] internal static var failureCount: [LokiAPITarget:UInt] = [:]
// MARK: Settings // MARK: Settings
private static let minimumSnodeCount = 2 private static let minimumSnodeCount = 2

@ -64,18 +64,18 @@ extension OnionRequestAPI {
} }
/// Encrypts the previous encryption result (i.e. that of the hop after this one) for this hop. Use this to build the layers of an onion request. /// Encrypts the previous encryption result (i.e. that of the hop after this one) for this hop. Use this to build the layers of an onion request.
internal static func encryptHop(from snode1: LokiAPITarget, to snode2: LokiAPITarget, using previousEncryptionResult: EncryptionResult) -> Promise<EncryptionResult> { internal static func encryptHop(from lhs: LokiAPITarget, to rhs: LokiAPITarget, using previousEncryptionResult: EncryptionResult) -> Promise<EncryptionResult> {
let (promise, seal) = Promise<EncryptionResult>.pending() let (promise, seal) = Promise<EncryptionResult>.pending()
getQueue().async { getQueue().async {
let parameters: JSON = [ let parameters: JSON = [
"ciphertext" : previousEncryptionResult.ciphertext.base64EncodedString(), "ciphertext" : previousEncryptionResult.ciphertext.base64EncodedString(),
"ephemeral_key" : previousEncryptionResult.ephemeralPublicKey.toHexString(), "ephemeral_key" : previousEncryptionResult.ephemeralPublicKey.toHexString(),
"destination" : snode2.publicKeySet!.ed25519Key "destination" : rhs.publicKeySet!.ed25519Key
] ]
do { do {
guard JSONSerialization.isValidJSONObject(parameters) else { return seal.reject(Error.invalidJSON) } guard JSONSerialization.isValidJSONObject(parameters) else { return seal.reject(Error.invalidJSON) }
let plaintext = try JSONSerialization.data(withJSONObject: parameters, options: []) let plaintext = try JSONSerialization.data(withJSONObject: parameters, options: [])
let result = try encrypt(plaintext, forSnode: snode1) let result = try encrypt(plaintext, forSnode: lhs)
seal.fulfill(result) seal.fulfill(result)
} catch (let error) { } catch (let error) {
seal.reject(error) seal.reject(error)

@ -6,10 +6,17 @@ internal enum OnionRequestAPI {
private static let urlSession = URLSession(configuration: .ephemeral, delegate: urlSessionDelegate, delegateQueue: nil) private static let urlSession = URLSession(configuration: .ephemeral, delegate: urlSessionDelegate, delegateQueue: nil)
private static let urlSessionDelegate = URLSessionDelegateImplementation() private static let urlSessionDelegate = URLSessionDelegateImplementation()
internal static var guardSnodes: Set<LokiAPITarget> = []
internal static var paths: Set<Path> = []
/// - Note: Exposed for testing purposes. /// - Note: Exposed for testing purposes.
internal static let workQueue = DispatchQueue(label: "OnionRequestAPI.workQueue", qos: .userInitiated) internal static let workQueue = DispatchQueue(label: "OnionRequestAPI.workQueue", qos: .userInitiated)
/// - Note: Must only be modified from `workQueue`.
internal static var guardSnodes: Set<LokiAPITarget> = []
/// - Note: Must only be modified from `workQueue`.
internal static var paths: Set<Path> = []
private static var snodePool: Set<LokiAPITarget> {
let unreliableSnodes = Set(LokiAPI.failureCount.keys)
return LokiAPI.randomSnodePool.subtracting(unreliableSnodes)
}
// MARK: Settings // MARK: Settings
private static let pathCount: UInt = 2 private static let pathCount: UInt = 2
@ -76,7 +83,7 @@ internal enum OnionRequestAPI {
if let parameters = parameters { if let parameters = parameters {
do { do {
guard JSONSerialization.isValidJSONObject(parameters) else { return seal.reject(Error.invalidJSON) } guard JSONSerialization.isValidJSONObject(parameters) else { return seal.reject(Error.invalidJSON) }
request.httpBody = try JSONSerialization.data(withJSONObject: parameters) request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: [])
} catch (let error) { } catch (let error) {
return seal.reject(error) return seal.reject(error)
} }
@ -144,7 +151,7 @@ internal enum OnionRequestAPI {
} else { } else {
print("[Loki] [Onion Request API] Populating guard snode cache.") print("[Loki] [Onion Request API] Populating guard snode cache.")
return LokiAPI.getRandomSnode().then(on: workQueue) { _ -> Promise<Set<LokiAPITarget>> in // Just used to populate the snode pool return LokiAPI.getRandomSnode().then(on: workQueue) { _ -> Promise<Set<LokiAPITarget>> in // Just used to populate the snode pool
var unusedSnodes = LokiAPI.randomSnodePool // Sync on workQueue var unusedSnodes = snodePool // Sync on workQueue
guard unusedSnodes.count >= guardSnodeCount else { throw Error.insufficientSnodes } guard unusedSnodes.count >= guardSnodeCount else { throw Error.insufficientSnodes }
func getGuardSnode() -> Promise<LokiAPITarget> { func getGuardSnode() -> Promise<LokiAPITarget> {
// randomElement() uses the system's default random generator, which is cryptographically secure // randomElement() uses the system's default random generator, which is cryptographically secure
@ -169,7 +176,6 @@ internal enum OnionRequestAPI {
private static func buildPaths() -> Promise<Set<Path>> { private static func buildPaths() -> Promise<Set<Path>> {
print("[Loki] [Onion Request API] Building onion request paths.") print("[Loki] [Onion Request API] Building onion request paths.")
return LokiAPI.getRandomSnode().then(on: workQueue) { _ -> Promise<Set<Path>> in // Just used to populate the snode pool return LokiAPI.getRandomSnode().then(on: workQueue) { _ -> Promise<Set<Path>> in // Just used to populate the snode pool
let snodePool = LokiAPI.randomSnodePool
return getGuardSnodes().map(on: workQueue) { guardSnodes in return getGuardSnodes().map(on: workQueue) { guardSnodes in
var unusedSnodes = snodePool.subtracting(guardSnodes) var unusedSnodes = snodePool.subtracting(guardSnodes)
let pathSnodeCount = guardSnodeCount * pathSize - guardSnodeCount let pathSnodeCount = guardSnodeCount * pathSize - guardSnodeCount
@ -204,6 +210,10 @@ internal enum OnionRequestAPI {
} }
} }
private static func dropPath(containing snode: LokiAPITarget) {
paths = paths.filter { !$0.contains(snode) }
}
/// Builds an onion around `payload` and returns the result. /// Builds an onion around `payload` and returns the result.
private static func buildOnion(around payload: JSON, targetedAt snode: LokiAPITarget) -> Promise<OnionBuildingResult> { private static func buildOnion(around payload: JSON, targetedAt snode: LokiAPITarget) -> Promise<OnionBuildingResult> {
var guardSnode: LokiAPITarget! var guardSnode: LokiAPITarget!

Loading…
Cancel
Save