blob: b70cc56a1dda28d70ca537dff08282e53b22a050 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
//
// AppMessageHandler.swift
// PacketTunnel
//
// Created by pronebird on 19/09/2023.
// Copyright © 2023 Mullvad VPN AB. All rights reserved.
//
import Foundation
import MullvadLogging
/**
Actor handling packet tunnel IPC (app) messages and patching them through to the right facility.
*/
public struct AppMessageHandler {
private let logger = Logger(label: "AppMessageHandler")
private let packetTunnelActor: PacketTunnelActorProtocol
private let urlRequestProxy: URLRequestProxyProtocol
public init(packetTunnelActor: PacketTunnelActorProtocol, urlRequestProxy: URLRequestProxyProtocol) {
self.packetTunnelActor = packetTunnelActor
self.urlRequestProxy = urlRequestProxy
}
/**
Handle app message received via packet tunnel IPC.
- Message data is expected to be a serialized `TunnelProviderMessage`.
- Reply is expected to be wrapped in `TunnelProviderReply`.
- Return `nil` in the event of error or when the call site does not expect any reply.
Calls to reconnect and notify actor when private key is changed are meant to run in parallel because those tasks are serialized in `TunnelManager` and await
the acknowledgment from IPC before starting next operation, hence it's critical to return as soon as possible.
(See `TunnelManager.reconnectTunnel()`, `SendTunnelProviderMessageOperation`)
*/
public func handleAppMessage(_ messageData: Data) async -> Data? {
guard let message = decodeMessage(messageData) else { return nil }
logger.debug("Received app message: \(message)")
switch message {
case let .sendURLRequest(request):
return await encodeReply(urlRequestProxy.sendRequest(request))
case let .cancelURLRequest(id):
urlRequestProxy.cancelRequest(identifier: id)
return nil
case .getTunnelStatus:
return await encodeReply(packetTunnelActor.observedState)
case .privateKeyRotation:
packetTunnelActor.notifyKeyRotation(date: Date())
return nil
case let .reconnectTunnel(nextRelay):
packetTunnelActor.reconnect(to: nextRelay, reconnectReason: ActorReconnectReason.userInitiated)
return nil
}
}
/// Deserialize `TunnelProviderMessage` or return `nil` on error. Errors are logged but ignored.
private func decodeMessage(_ data: Data) -> TunnelProviderMessage? {
do {
return try TunnelProviderMessage(messageData: data)
} catch {
logger.error(error: error, message: "Failed to decode the app message.")
return nil
}
}
/// Encode `TunnelProviderReply` or return `nil` on error. Errors are logged but ignored.
private func encodeReply<T: Codable>(_ reply: T) -> Data? {
do {
return try TunnelProviderReply(reply).encode()
} catch {
logger.error(error: error, message: "Failed to encode the app message reply.")
return nil
}
}
}
|