Handle incorrect clock setting

This commit is contained in:
Niels Andriesse 2021-07-23 13:42:13 +10:00
parent ac730e00b4
commit 8a29469eb5
3 changed files with 23 additions and 17 deletions

View File

@ -200,7 +200,8 @@ public final class MessageSender : NSObject {
}
// Send the result
let base64EncodedData = wrappedMessage.base64EncodedString()
let snodeMessage = SnodeMessage(recipient: message.recipient!, data: base64EncodedData, ttl: message.ttl, timestamp: message.sentTimestamp!)
let timestamp = UInt64(Int64(message.sentTimestamp!) + SnodeAPI.clockOffset)
let snodeMessage = SnodeMessage(recipient: message.recipient!, data: base64EncodedData, ttl: message.ttl, timestamp: timestamp)
SnodeAPI.sendMessage(snodeMessage).done(on: DispatchQueue.global(qos: .userInitiated)) { promises in
var isSuccess = false
let promiseCount = promises.count

View File

@ -310,7 +310,7 @@ public enum OnionRequestAPI {
}
/// Sends an onion request to `server`. Builds new paths as needed.
public static func sendOnionRequest(_ request: NSURLRequest, to server: String, target: String = "/loki/v3/lsrpc", using x25519PublicKey: String, isJSONRequired: Bool = true) -> Promise<JSON> {
public static func sendOnionRequest(_ request: NSURLRequest, to server: String, target: String = "/loki/v3/lsrpc", using x25519PublicKey: String) -> Promise<JSON> {
var rawHeaders = request.allHTTPHeaderFields ?? [:]
rawHeaders.removeValue(forKey: "User-Agent")
var headers: JSON = rawHeaders.mapValues { value in
@ -352,14 +352,14 @@ public enum OnionRequestAPI {
"headers" : headers
]
let destination = Destination.server(host: host, target: target, x25519PublicKey: x25519PublicKey, scheme: scheme, port: port)
let promise = sendOnionRequest(with: payload, to: destination, isJSONRequired: isJSONRequired)
let promise = sendOnionRequest(with: payload, to: destination)
promise.catch2 { error in
SNLog("Couldn't reach server: \(url) due to error: \(error).")
}
return promise
}
public static func sendOnionRequest(with payload: JSON, to destination: Destination, isJSONRequired: Bool = true) -> Promise<JSON> {
public static func sendOnionRequest(with payload: JSON, to destination: Destination) -> Promise<JSON> {
let (promise, seal) = Promise<JSON>.pending()
var guardSnode: Snode?
Threading.workQueue.async { // Avoid race conditions on `guardSnodes` and `paths`
@ -386,28 +386,28 @@ public enum OnionRequestAPI {
let ivAndCiphertext = Data(base64Encoded: base64EncodedIVAndCiphertext), ivAndCiphertext.count >= AESGCM.ivSize else { return seal.reject(HTTP.Error.invalidJSON) }
do {
let data = try AESGCM.decrypt(ivAndCiphertext, with: destinationSymmetricKey)
// The old open group server and file server implementations put the status code in the JSON under the "status"
// key, whereas the new implementations put it under the "status_code" key
guard let json = try JSONSerialization.jsonObject(with: data, options: [ .fragmentsAllowed ]) as? JSON,
let statusCode = json["status_code"] as? Int ?? json["status"] as? Int else { return seal.reject(HTTP.Error.invalidJSON) }
if statusCode == 406 { // Clock out of sync
SNLog("The user's clock is out of sync with the service node network.")
seal.reject(SnodeAPI.Error.clockOutOfSync)
} else if let bodyAsString = json["body"] as? String {
// This clause is only used by the old open group and file server implementations. The new implementations will
// always go to the next clause.
let body: JSON
if !isJSONRequired {
body = [ "result" : bodyAsString ]
} else {
guard let bodyAsData = bodyAsString.data(using: .utf8),
let b = try JSONSerialization.jsonObject(with: bodyAsData, options: [ .fragmentsAllowed ]) as? JSON else { return seal.reject(HTTP.Error.invalidJSON) }
body = b
guard let bodyAsData = bodyAsString.data(using: .utf8),
let body = try JSONSerialization.jsonObject(with: bodyAsData, options: [ .fragmentsAllowed ]) as? JSON else { return seal.reject(HTTP.Error.invalidJSON) }
if let timestamp = body["t"] as? Int64 {
let offset = timestamp - Int64(NSDate.millisecondTimestamp())
if abs(offset) > 20 * 1000 { // If we're off by more than 20 seconds
SnodeAPI.clockOffset = offset
}
}
guard 200...299 ~= statusCode else {
return seal.reject(Error.httpRequestFailedAtDestination(statusCode: UInt(statusCode), json: body, destination: destination))
}
guard 200...299 ~= statusCode else { return seal.reject(Error.httpRequestFailedAtDestination(statusCode: UInt(statusCode), json: body, destination: destination)) }
seal.fulfill(body)
} else {
guard 200...299 ~= statusCode else { return seal.reject(Error.httpRequestFailedAtDestination(statusCode: UInt(statusCode), json: json, destination: destination)) }
guard 200...299 ~= statusCode else {
return seal.reject(Error.httpRequestFailedAtDestination(statusCode: UInt(statusCode), json: json, destination: destination))
}
seal.fulfill(json)
}
} catch {

View File

@ -13,6 +13,11 @@ public final class SnodeAPI : NSObject {
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
internal static var snodePool: Set<Snode> = []
/// The offset between the user's clock and the Service Node's clock. Used in cases where the
/// user's clock is incorrect.
///
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
public static var clockOffset: Int64 = 0
/// - Note: Should only be accessed from `Threading.workQueue` to avoid race conditions.
public static var swarmCache: [String:Set<Snode>] = [:]