src/Socket/AdapterSocket/Shadowsocks/ProtocolObfuscater.swift
import Foundation
extension ShadowsocksAdapter {
public struct ProtocolObfuscater {
public class Factory {
public init() {}
public func build() -> ProtocolObfuscaterBase {
return ProtocolObfuscaterBase()
}
}
public class ProtocolObfuscaterBase {
public weak var inputStreamProcessor: CryptoStreamProcessor!
public weak var outputStreamProcessor: ShadowsocksAdapter!
public func start() {}
public func input(data: Data) throws {}
public func output(data: Data) {}
public func didWrite() {}
}
public class OriginProtocolObfuscater: ProtocolObfuscaterBase {
public class Factory: ProtocolObfuscater.Factory {
public override init() {}
public override func build() -> ShadowsocksAdapter.ProtocolObfuscater.ProtocolObfuscaterBase {
return OriginProtocolObfuscater()
}
}
public override func start() {
outputStreamProcessor.becomeReadyToForward()
}
public override func input(data: Data) throws {
try inputStreamProcessor.input(data: data)
}
public override func output(data: Data) {
outputStreamProcessor.output(data: data)
}
}
public class HTTPProtocolObfuscater: ProtocolObfuscaterBase {
public class Factory: ProtocolObfuscater.Factory {
let method: String
let hosts: [String]
let customHeader: String?
public init(method: String = "GET", hosts: [String], customHeader: String?) {
self.method = method
self.hosts = hosts
self.customHeader = customHeader
}
public override func build() -> ShadowsocksAdapter.ProtocolObfuscater.ProtocolObfuscaterBase {
return HTTPProtocolObfuscater(method: method, hosts: hosts, customHeader: customHeader)
}
}
static let headerLength = 30
static let userAgent = ["Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0",
"Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/44.0",
"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Ubuntu/11.10 Chromium/27.0.1453.93 Chrome/27.0.1453.93 Safari/537.36",
"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:35.0) Gecko/20100101 Firefox/35.0",
"Mozilla/5.0 (compatible; WOW64; MSIE 10.0; Windows NT 6.2)",
"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)",
"Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko",
"Mozilla/5.0 (Linux; Android 4.4; Nexus 5 Build/BuildID) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/30.0.0.0 Mobile Safari/537.36",
"Mozilla/5.0 (iPad; CPU OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3"]
let method: String
let hosts: [String]
let customHeader: String?
var readingFakeHeader = false
var sendHeader = false
var remaining = false
var buffer = Buffer(capacity: 8192)
public init(method: String = "GET", hosts: [String], customHeader: String?) {
self.method = method
self.hosts = hosts
self.customHeader = customHeader
}
private func generateHeader(encapsulating data: Data) -> String {
let ind = Int(arc4random_uniform(UInt32(hosts.count)))
let host = outputStreamProcessor.port == 80 ? hosts[ind] : "\(hosts[ind]):\(outputStreamProcessor.port)"
var header = "\(method) /\(hexlify(data: data)) HTTP/1.1\r\nHost: \(host)\r\n"
if let customHeader = customHeader {
header += customHeader
} else {
let ind = Int(arc4random_uniform(UInt32(HTTPProtocolObfuscater.userAgent.count)))
header += "User-Agent: \(HTTPProtocolObfuscater.userAgent[ind])\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\nAccept-Language: en-US,en;q=0.8\r\nAccept-Encoding: gzip, deflate\r\nDNT: 1\r\nConnection: keep-alive"
}
header += "\r\n\r\n"
return header
}
private func hexlify(data: Data) -> String {
var result = ""
for i in data {
result = result.appendingFormat("%%%02x", i)
}
return result
}
public override func start() {
readingFakeHeader = true
outputStreamProcessor.becomeReadyToForward()
}
public override func input(data: Data) throws {
if readingFakeHeader {
buffer.append(data: data)
if buffer.get(to: Utils.HTTPData.DoubleCRLF) != nil {
readingFakeHeader = false
if let remainData = buffer.get() {
try inputStreamProcessor.input(data: remainData)
return
}
}
try inputStreamProcessor.input(data: Data())
return
}
try inputStreamProcessor.input(data: data)
}
public override func output(data: Data) {
if sendHeader {
outputStreamProcessor.output(data: data)
} else {
var fakeRequestDataLength = inputStreamProcessor.key.count + HTTPProtocolObfuscater.headerLength
if data.count - fakeRequestDataLength > 64 {
fakeRequestDataLength += Int(arc4random_uniform(64))
} else {
fakeRequestDataLength = data.count
}
var outputData = generateHeader(encapsulating: data.subdata(in: 0 ..< fakeRequestDataLength)).data(using: .utf8)!
outputData.append(data.subdata(in: fakeRequestDataLength ..< data.count))
sendHeader = true
outputStreamProcessor.output(data: outputData)
}
}
}
public class TLSProtocolObfuscater: ProtocolObfuscaterBase {
public class Factory: ProtocolObfuscater.Factory {
let hosts: [String]
public init(hosts: [String]) {
self.hosts = hosts
}
public override func build() -> ShadowsocksAdapter.ProtocolObfuscater.ProtocolObfuscaterBase {
return TLSProtocolObfuscater(hosts: hosts)
}
}
let hosts: [String]
let clientID: Data = {
var id = Data(count: 32)
Utils.Random.fill(data: &id)
return id
}()
private var status = 0
private var buffer = Buffer(capacity: 1024)
init(hosts: [String]) {
self.hosts = hosts
}
public override func start() {
handleStatus0()
outputStreamProcessor.socket.readDataTo(length: 129)
}
public override func input(data: Data) throws {
switch status {
case 8:
try handleInput(data: data)
case 1:
outputStreamProcessor.becomeReadyToForward()
default:
break
}
}
public override func output(data: Data) {
switch status {
case 8:
handleStatus8(data: data)
return
case 1:
handleStatus1(data: data)
return
default:
break
}
}
private func authData() -> Data {
var time = UInt32(Date.init().timeIntervalSince1970).bigEndian
var output = Data(count: 32)
var key = inputStreamProcessor.key
key.append(clientID)
withUnsafeBytes(of: &time) {
output.replaceSubrange(0 ..< 4, with: $0)
}
Utils.Random.fill(data: &output, from: 4, length: 18)
output.withUnsafeBytes {
output.replaceSubrange(22 ..< 32, with: HMAC.final(value: $0.baseAddress!, length: 22, algorithm: .SHA1, key: key).subdata(in: 0..<10))
}
return output
}
private func pack(data: Data) -> Data {
var output = Data()
var left = data.count
while left > 0 {
let blockSize = UInt16(min(Int(arc4random_uniform(UInt32(UInt16.max))) % 4096 + 100, left))
var blockSizeBE = blockSize.bigEndian
output.append(contentsOf: [0x17, 0x03, 0x03])
withUnsafeBytes(of: &blockSizeBE) {
output.append($0.baseAddress!.assumingMemoryBound(to: UInt8.self), count: $0.count)
}
output.append(data.subdata(in: data.count - left ..< data.count - left + Int(blockSize)))
left -= Int(blockSize)
}
return output
}
private func handleStatus8(data: Data) {
outputStreamProcessor.output(data: pack(data: data))
}
private func handleStatus0() {
status = 1
var outData = Data()
outData.append(contentsOf: [0x03, 0x03])
outData.append(authData())
outData.append(0x20)
outData.append(clientID)
outData.append(contentsOf: [0x00, 0x1c, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x9c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0x0a])
outData.append("0100".data(using: .utf8)!)
var extData = Data()
extData.append(contentsOf: [0xff, 0x01, 0x00, 0x01, 0x00])
let hostData = hosts[Int(arc4random_uniform(UInt32(hosts.count)))].data(using: .utf8)!
var sniData = Data(capacity: hosts.count + 2 + 1 + 2 + 2 + 2)
sniData.append(contentsOf: [0x00, 0x00])
var _lenBE = UInt16(hostData.count + 5).bigEndian
withUnsafeBytes(of: &_lenBE) {
sniData.append($0.baseAddress!.assumingMemoryBound(to: UInt8.self), count: $0.count)
}
_lenBE = UInt16(hostData.count + 3).bigEndian
withUnsafeBytes(of: &_lenBE) {
sniData.append($0.baseAddress!.assumingMemoryBound(to: UInt8.self), count: $0.count)
}
sniData.append(0x00)
_lenBE = UInt16(hostData.count).bigEndian
withUnsafeBytes(of: &_lenBE) {
sniData.append($0.baseAddress!.assumingMemoryBound(to: UInt8.self), count: $0.count)
}
sniData.append(hostData)
extData.append(sniData)
extData.append(contentsOf: [0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0xd0])
var randomData = Data(count: 208)
Utils.Random.fill(data: &randomData)
extData.append(randomData)
extData.append(contentsOf: [0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x06, 0x01, 0x06, 0x03, 0x05, 0x01, 0x05, 0x03, 0x04, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x03, 0x02, 0x01, 0x02, 0x03])
extData.append(contentsOf: [0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00])
extData.append(contentsOf: [0x00, 0x12, 0x00, 0x00])
extData.append(contentsOf: [0x75, 0x50, 0x00, 0x00])
extData.append(contentsOf: [0x00, 0x0b, 0x00, 0x02, 0x01, 0x00])
extData.append(contentsOf: [0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18])
_lenBE = UInt16(extData.count).bigEndian
withUnsafeBytes(of: &_lenBE) {
outData.append($0.baseAddress!.assumingMemoryBound(to: UInt8.self), count: $0.count)
}
outData.append(extData)
var outputData = Data(capacity: outData.count + 9)
outputData.append(contentsOf: [0x16, 0x03, 0x01])
_lenBE = UInt16(outData.count + 4).bigEndian
withUnsafeBytes(of: &_lenBE) {
outputData.append($0.baseAddress!.assumingMemoryBound(to: UInt8.self), count: $0.count)
}
outputData.append(contentsOf: [0x01, 0x00])
_lenBE = UInt16(outData.count).bigEndian
withUnsafeBytes(of: &_lenBE) {
outputData.append($0.baseAddress!.assumingMemoryBound(to: UInt8.self), count: $0.count)
}
outputData.append(outData)
outputStreamProcessor.output(data: outputData)
}
private func handleStatus1(data: Data) {
status = 8
var outputData = Data()
outputData.append(contentsOf: [0x14, 0x03, 0x03, 0x00, 0x01, 0x01, 0x16, 0x03, 0x03, 0x00, 0x20])
var random = Data(count: 22)
Utils.Random.fill(data: &random)
outputData.append(random)
var key = inputStreamProcessor.key
key.append(clientID)
outputData.withUnsafeBytes {
outputData.append(HMAC.final(value: $0.baseAddress!, length: outputData.count, algorithm: .SHA1, key: key).subdata(in: 0..<10))
}
outputData.append(pack(data: data))
outputStreamProcessor.output(data: outputData)
}
private func handleInput(data: Data) throws {
buffer.append(data: data)
var unpackedData = Data()
while buffer.left > 5 {
buffer.skip(3)
var length: Int = 0
buffer.withUnsafeBytes { (ptr: UnsafePointer<UInt16>) in
length = Int(ptr.pointee.byteSwapped)
}
buffer.skip(2)
if buffer.left >= length {
unpackedData.append(buffer.get(length: length)!)
continue
} else {
buffer.setBack(length: 5)
break
}
}
buffer.squeeze()
try inputStreamProcessor.input(data: unpackedData)
}
}
}
}