Files
ichmagmaus111andClaude Opus 5.5 0622057a4f Ghostgram: open-source release
Ghostgram for iOS, rebuilt from scratch on Telegram iOS 13.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 17:57:09 +01:00

136 lines
5.3 KiB
Swift

import Foundation
public enum WebProxyProtocol {
public static let headerSize = 8
public static let initialWindow = 4 * 1024 * 1024
public static let maximumPayload = 1024 * 1024
public static let maximumDataPayload = 64 * 1024
public static let defaultBatchSize = 2 * 1024 * 1024
public static let maximumBatchFrames = 4096
public static let maximumQueuedBytes = 32 * 1024 * 1024
public static let maximumQueuedItems = 16384
public static let tombstoneCount = 4096
public static let maximumStreamId: UInt32 = 0x00ff_ffff
}
public enum WebProxyFrameType: UInt8, CaseIterable {
case open = 0x01
case data = 0x02
case close = 0x03
case window = 0x04
case ping = 0x05
case pong = 0x06
case hello = 0x10
case welcome = 0x11
case bye = 0x1f
}
public struct WebProxyFrame: Equatable {
public let type: WebProxyFrameType
public let streamId: UInt32
public let payload: Data
public init(type: WebProxyFrameType, streamId: UInt32, payload: Data = Data()) {
self.type = type
self.streamId = streamId
self.payload = payload
}
public func validated() throws -> WebProxyFrame {
guard self.streamId <= WebProxyProtocol.maximumStreamId,
self.payload.count <= WebProxyProtocol.maximumPayload else {
throw WebProxyFrameError.invalidFrame
}
switch self.type {
case .open, .close:
guard self.streamId != 0 && self.payload.isEmpty else { throw WebProxyFrameError.invalidFrame }
case .data:
guard self.streamId != 0 && !self.payload.isEmpty else { throw WebProxyFrameError.invalidFrame }
case .window:
guard self.streamId != 0, self.payload.count == 4, self.windowDelta != 0 else { throw WebProxyFrameError.invalidFrame }
case .ping, .pong:
guard self.streamId == 0 else { throw WebProxyFrameError.invalidFrame }
case .hello:
guard self.streamId == 0 && self.payload == Data([1]) else { throw WebProxyFrameError.invalidFrame }
case .welcome:
guard self.streamId == 0 && self.payload.isEmpty else { throw WebProxyFrameError.invalidFrame }
case .bye:
guard self.streamId == 0 else { throw WebProxyFrameError.invalidFrame }
}
return self
}
public var windowDelta: UInt32? {
guard self.payload.count == 4 else { return nil }
return self.payload.reduce(UInt32(0)) { ($0 << 8) | UInt32($1) }
}
public static func window(streamId: UInt32, delta: UInt32) -> WebProxyFrame {
return WebProxyFrame(type: .window, streamId: streamId, payload: Data([
UInt8((delta >> 24) & 0xff), UInt8((delta >> 16) & 0xff),
UInt8((delta >> 8) & 0xff), UInt8(delta & 0xff)
]))
}
}
public enum WebProxyFrameError: Error, Equatable {
case invalidFrame
case unknownType
case bufferLimitExceeded
}
public enum WebProxyFrameEncoder {
public static func encode(_ frame: WebProxyFrame) throws -> Data {
let frame = try frame.validated()
let length = UInt32(frame.payload.count)
var data = Data(capacity: WebProxyProtocol.headerSize + frame.payload.count)
data.append(frame.type.rawValue)
data.append(UInt8((frame.streamId >> 16) & 0xff))
data.append(UInt8((frame.streamId >> 8) & 0xff))
data.append(UInt8(frame.streamId & 0xff))
data.append(UInt8((length >> 24) & 0xff))
data.append(UInt8((length >> 16) & 0xff))
data.append(UInt8((length >> 8) & 0xff))
data.append(UInt8(length & 0xff))
data.append(frame.payload)
return data
}
}
public final class WebProxyFrameDecoder {
private var buffer = Data()
public init() {
}
public func append(_ data: Data) throws -> [WebProxyFrame] {
let maximumBufferedSize = WebProxyProtocol.maximumQueuedBytes
guard self.buffer.count <= maximumBufferedSize,
data.count <= maximumBufferedSize - self.buffer.count else {
throw WebProxyFrameError.bufferLimitExceeded
}
self.buffer.append(data)
var result: [WebProxyFrame] = []
while self.buffer.count >= WebProxyProtocol.headerSize {
guard let type = WebProxyFrameType(rawValue: self.buffer[0]) else {
throw WebProxyFrameError.unknownType
}
let streamId = (UInt32(self.buffer[1]) << 16) | (UInt32(self.buffer[2]) << 8) | UInt32(self.buffer[3])
let length = (UInt32(self.buffer[4]) << 24) | (UInt32(self.buffer[5]) << 16) | (UInt32(self.buffer[6]) << 8) | UInt32(self.buffer[7])
guard length <= UInt32(WebProxyProtocol.maximumPayload) else {
throw WebProxyFrameError.invalidFrame
}
let frameLength = WebProxyProtocol.headerSize + Int(length)
guard self.buffer.count >= frameLength else { break }
let payload = self.buffer.subdata(in: WebProxyProtocol.headerSize ..< frameLength)
let frame = try WebProxyFrame(type: type, streamId: streamId, payload: payload).validated()
result.append(frame)
guard result.count <= WebProxyProtocol.maximumBatchFrames else {
throw WebProxyFrameError.bufferLimitExceeded
}
self.buffer.removeSubrange(0 ..< frameLength)
}
return result
}
}