source-code

This commit is contained in:
Thunder7yoshi
2020-06-09 23:38:02 +02:00
parent 3e3f9ab752
commit 06a8d21b98
346 changed files with 26230 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
//
// Server.swift
// AltStore
//
// Created by Riley Testut on 6/20/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Network
import AltKit
enum ConnectionError: LocalizedError
{
case serverNotFound
case connectionFailed
case connectionDropped
var failureReason: String? {
switch self
{
case .serverNotFound: return NSLocalizedString("Could not find AltServer.", comment: "")
case .connectionFailed: return NSLocalizedString("Could not connect to AltServer.", comment: "")
case .connectionDropped: return NSLocalizedString("The connection to AltServer was dropped.", comment: "")
}
}
}
extension Server
{
enum ConnectionType
{
case wireless
case wired
case local
}
}
struct Server: Equatable
{
var identifier: String? = nil
var service: NetService? = nil
var isPreferred = false
var connectionType: ConnectionType = .wireless
}
extension Server
{
// Defined in extension so we can still use the automatically synthesized initializer.
init?(service: NetService, txtData: Data)
{
let txtDictionary = NetService.dictionary(fromTXTRecord: txtData)
guard let identifierData = txtDictionary["serverID"], let identifier = String(data: identifierData, encoding: .utf8) else { return nil }
self.service = service
self.identifier = identifier
}
}

View File

@@ -0,0 +1,146 @@
//
// ServerConnection.swift
// AltStore
//
// Created by Riley Testut on 1/7/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Foundation
import Network
import AltKit
class ServerConnection
{
var server: Server
var connection: NWConnection
init(server: Server, connection: NWConnection)
{
self.server = server
self.connection = connection
}
func send<T: Encodable>(_ payload: T, prependSize: Bool = true, completionHandler: @escaping (Result<Void, Error>) -> Void)
{
do
{
let data: Data
if let payload = payload as? Data
{
data = payload
}
else
{
data = try JSONEncoder().encode(payload)
}
func process(_ error: Error?) -> Bool
{
if error != nil
{
completionHandler(.failure(ConnectionError.connectionDropped))
return false
}
else
{
return true
}
}
if prependSize
{
let requestSize = Int32(data.count)
let requestSizeData = withUnsafeBytes(of: requestSize) { Data($0) }
self.connection.send(content: requestSizeData, completion: .contentProcessed { (error) in
guard process(error) else { return }
self.connection.send(content: data, completion: .contentProcessed { (error) in
guard process(error) else { return }
completionHandler(.success(()))
})
})
}
else
{
connection.send(content: data, completion: .contentProcessed { (error) in
guard process(error) else { return }
completionHandler(.success(()))
})
}
}
catch
{
print("Invalid request.", error)
completionHandler(.failure(ALTServerError(.invalidRequest)))
}
}
func receiveResponse(completionHandler: @escaping (Result<ServerResponse, Error>) -> Void)
{
let size = MemoryLayout<Int32>.size
self.connection.receive(minimumIncompleteLength: size, maximumLength: size) { (data, _, _, error) in
do
{
let data = try self.process(data: data, error: error)
let expectedBytes = Int(data.withUnsafeBytes { $0.load(as: Int32.self) })
self.connection.receive(minimumIncompleteLength: expectedBytes, maximumLength: expectedBytes) { (data, _, _, error) in
do
{
let data = try self.process(data: data, error: error)
let response = try JSONDecoder().decode(ServerResponse.self, from: data)
completionHandler(.success(response))
}
catch
{
completionHandler(.failure(ALTServerError(error)))
}
}
}
catch
{
completionHandler(.failure(ALTServerError(error)))
}
}
}
}
private extension ServerConnection
{
func process(data: Data?, error: NWError?) throws -> Data
{
do
{
do
{
guard let data = data else { throw error ?? ALTServerError(.unknown) }
return data
}
catch let error as NWError
{
print("Error receiving data from connection \(connection)", error)
throw ALTServerError(.lostConnection)
}
catch
{
throw error
}
}
catch let error as ALTServerError
{
throw error
}
catch
{
preconditionFailure("A non-ALTServerError should never be thrown from this method.")
}
}
}

View File

@@ -0,0 +1,253 @@
//
// ServerManager.swift
// AltStore
//
// Created by Riley Testut on 5/30/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import Foundation
import Network
import AltKit
class ServerManager: NSObject
{
static let shared = ServerManager()
private(set) var isDiscovering = false
private(set) var discoveredServers = [Server]()
private let serviceBrowser = NetServiceBrowser()
private var services = Set<NetService>()
private let dispatchQueue = DispatchQueue(label: "io.altstore.ServerManager")
private var connectionListener: NWListener?
private var incomingConnections: [NWConnection]?
private var incomingConnectionsSemaphore: DispatchSemaphore?
private override init()
{
super.init()
self.serviceBrowser.delegate = self
self.serviceBrowser.includesPeerToPeer = false
}
}
extension ServerManager
{
func startDiscovering()
{
guard !self.isDiscovering else { return }
self.isDiscovering = true
self.serviceBrowser.searchForServices(ofType: ALTServerServiceType, inDomain: "")
self.startListeningForWiredConnections()
}
func stopDiscovering()
{
guard self.isDiscovering else { return }
self.isDiscovering = false
self.discoveredServers.removeAll()
self.services.removeAll()
self.serviceBrowser.stop()
self.stopListeningForWiredConnection()
}
func connect(to server: Server, completion: @escaping (Result<ServerConnection, Error>) -> Void)
{
DispatchQueue.global().async {
func finish(_ result: Result<ServerConnection, Error>)
{
completion(result)
}
func start(_ connection: NWConnection)
{
connection.stateUpdateHandler = { [unowned connection] (state) in
switch state
{
case .failed(let error):
print("Failed to connect to service \(server.service?.name ?? "").", error)
finish(.failure(ConnectionError.connectionFailed))
case .cancelled:
finish(.failure(OperationError.cancelled))
case .ready:
let connection = ServerConnection(server: server, connection: connection)
finish(.success(connection))
case .waiting: break
case .setup: break
case .preparing: break
@unknown default: break
}
}
connection.start(queue: self.dispatchQueue)
}
if let incomingConnectionsSemaphore = self.incomingConnectionsSemaphore, server.connectionType != .wireless
{
print("Waiting for incoming connection...")
let notificationCenter = CFNotificationCenterGetDarwinNotifyCenter()
switch server.connectionType
{
case .wired: CFNotificationCenterPostNotification(notificationCenter, .wiredServerConnectionStartRequest, nil, nil, true)
case .local: CFNotificationCenterPostNotification(notificationCenter, .localServerConnectionStartRequest, nil, nil, true)
case .wireless: break
}
_ = incomingConnectionsSemaphore.wait(timeout: .now() + 10.0)
if let connection = self.incomingConnections?.popLast()
{
start(connection)
}
else
{
finish(.failure(ALTServerError(.connectionFailed)))
}
}
else if let service = server.service
{
print("Connecting to service:", service)
let parameters = NWParameters.tcp
if server.connectionType == .local
{
// Prevent AltStore from initiating connections over multiple interfaces simultaneously 🤷
parameters.requiredInterfaceType = .loopback
}
let connection = NWConnection(to: .service(name: service.name, type: service.type, domain: service.domain, interface: nil), using: parameters)
start(connection)
}
}
}
}
private extension ServerManager
{
func addDiscoveredServer(_ server: Server)
{
var server = server
server.isPreferred = (server.identifier == UserDefaults.standard.preferredServerID)
guard !self.discoveredServers.contains(server) else { return }
self.discoveredServers.append(server)
}
func makeListener() -> NWListener
{
let listener = try! NWListener(using: .tcp, on: NWEndpoint.Port(rawValue: ALTDeviceListeningSocket)!)
listener.newConnectionHandler = { [weak self] (connection) in
self?.incomingConnections?.append(connection)
self?.incomingConnectionsSemaphore?.signal()
}
listener.stateUpdateHandler = { (state) in
switch state
{
case .ready: break
case .waiting, .setup: print("Listener socket waiting...")
case .cancelled: print("Listener socket cancelled.")
case .failed(let error): print("Listener socket failed:", error)
@unknown default: break
}
}
return listener
}
func startListeningForWiredConnections()
{
self.incomingConnections = []
self.incomingConnectionsSemaphore = DispatchSemaphore(value: 0)
self.connectionListener = self.makeListener()
self.connectionListener?.start(queue: self.dispatchQueue)
}
func stopListeningForWiredConnection()
{
self.connectionListener?.cancel()
self.connectionListener = nil
self.incomingConnections = nil
self.incomingConnectionsSemaphore = nil
}
}
extension ServerManager: NetServiceBrowserDelegate
{
func netServiceBrowserWillSearch(_ browser: NetServiceBrowser)
{
print("Discovering servers...")
}
func netServiceBrowserDidStopSearch(_ browser: NetServiceBrowser)
{
print("Stopped discovering servers.")
}
func netServiceBrowser(_ browser: NetServiceBrowser, didNotSearch errorDict: [String : NSNumber])
{
print("Failed to discovering servers.", errorDict)
}
func netServiceBrowser(_ browser: NetServiceBrowser, didFind service: NetService, moreComing: Bool)
{
service.delegate = self
if let txtData = service.txtRecordData(), let server = Server(service: service, txtData: txtData)
{
self.addDiscoveredServer(server)
}
else
{
service.resolve(withTimeout: 3)
self.services.insert(service)
}
}
func netServiceBrowser(_ browser: NetServiceBrowser, didRemove service: NetService, moreComing: Bool)
{
if let index = self.discoveredServers.firstIndex(where: { $0.service == service })
{
self.discoveredServers.remove(at: index)
}
self.services.remove(service)
}
}
extension ServerManager: NetServiceDelegate
{
func netServiceDidResolveAddress(_ service: NetService)
{
guard let data = service.txtRecordData(), let server = Server(service: service, txtData: data) else { return }
self.addDiscoveredServer(server)
}
func netService(_ sender: NetService, didNotResolve errorDict: [String : NSNumber])
{
print("Error resolving net service \(sender).", errorDict)
}
func netService(_ sender: NetService, didUpdateTXTRecord data: Data)
{
let txtDict = NetService.dictionary(fromTXTRecord: data)
print("Service \(sender) updated TXT Record:", txtDict)
}
}