zhuhaow/NEKit

View on GitHub
src/Socket/AdapterSocket/Shadowsocks/ProtocolObfuscater.swift

Summary

Maintainability
C
1 day
Test Coverage
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)
            }
        }

    }
}