<commit_before>//
// This source file is part of the MongoKitten open source project
//
// Copyright (c) 2016 - 2017 OpenKitten and the MongoKitten project authors
// Licensed under MIT
//
// See https://github.com/OpenKitten/MongoKitten/blob/mongokitten31/LICENSE.md for license information
// See https://github.com/OpenKitten/MongoKitten/blob/mongokitten31/CONTRIBUTORS.md for the list of MongoKitten project authors
//

import Foundation
import Dispatch

#if os(macOS) || os(iOS)
    import Security
    import Darwin
#else
    import KittenCTLS
    import Glibc
#endif

public final class MongoSocket: MongoTCP {
    public private(set) var plainClient: Int32
    
    #if os(macOS) || os(iOS)
    private let sslClient: SSLContext?
    #else
    fileprivate static var initialized: Bool = false
    private let sslClient: UnsafeMutablePointer<SSL>?
    private let sslMethod: UnsafePointer<SSL_METHOD>?
    private let sslContext: UnsafeMutablePointer<SSL_CTX>?
    
    deinit { SSL_CTX_free(sslContext) }
    #endif
    
    private var sslEnabled = false
    
    private static let socketQueue = DispatchQueue(label: "org.mongokitten.socketQueue", qos: DispatchQoS.userInteractive)
    
    public var onRead: ReadCallback
    public var onError: ErrorCallback
    
    private let operationLock = NSLock()
    
    private let readSource: DispatchSourceRead
    
    public init(address hostname: String, port: UInt16, options: [String: Any], onRead: @escaping ReadCallback, onError: @escaping ErrorCallback) throws {
        self.sslEnabled = options["sslEnabled"] as? Bool ?? false
        self.onRead = onRead
        self.onError = onError
        
        var criteria = addrinfo.init()
        
        let hostname = hostname == "localhost" ? "127.0.0.1" : hostname
        
        #if os(macOS) || os(iOS)
            criteria.ai_socktype = Int32(SOCK_STREAM)
        #else
            criteria.ai_socktype = Int32(SOCK_STREAM.rawValue)
        #endif
        
        criteria.ai_family = Int32(AF_UNSPEC)
        criteria.ai_flags = AI_PASSIVE
        criteria.ai_protocol = Int32(IPPROTO_TCP)
        
        var serverinfo: UnsafeMutablePointer<addrinfo>? = nil
        let ret = getaddrinfo(hostname, port.description, &criteria, &serverinfo)
        
        guard let addrList = serverinfo else { throw Error.ipAddressResolutionFailed }
        defer { freeaddrinfo(addrList) }
        
        guard let addrInfo = addrList.pointee.ai_addr else { throw Error.ipAddressResolutionFailed }
        
        let ptr = UnsafeMutablePointer<sockaddr_storage>.allocate(capacity: 1)
        ptr.initialize(to: sockaddr_storage())
        
        switch Int32(addrInfo.pointee.sa_family) {
        case Int32(AF_INET):
            let addr = UnsafeMutablePointer<sockaddr_in>.init(OpaquePointer(addrInfo))!
            let specPtr = UnsafeMutablePointer<sockaddr_in>(OpaquePointer(ptr))
            specPtr.assign(from: addr, count: 1)
        case Int32(AF_INET6):
            let addr = UnsafeMutablePointer<sockaddr_in6>(OpaquePointer(addrInfo))!
            let specPtr = UnsafeMutablePointer<sockaddr_in6>(OpaquePointer(ptr))
            specPtr.assign(from: addr, count: 1)
        default:
            throw Error.ipAddressResolutionFailed
        }
        
        #if os(macOS) || os(iOS)
            self.plainClient = socket(Int32(addrInfo.pointee.sa_family), SOCK_STREAM, Int32(IPPROTO_TCP))
        #else
            self.plainClient = socket(Int32(addrInfo.pointee.sa_family), Int32(SOCK_STREAM.rawValue), Int32(IPPROTO_TCP))
        #endif
        
        signal(SIGPIPE, SIG_IGN)
        
        guard plainClient >= 0 else {
            throw Error.cannotConnect
        }
        
        if Int32(addrInfo.pointee.sa_family) == Int32(AF_INET) {
            connect(self.plainClient, UnsafeMutablePointer<sockaddr>(OpaquePointer(ptr)), socklen_t(MemoryLayout<sockaddr_in>.size))
        } else {
            connect(self.plainClient, UnsafeMutablePointer<sockaddr>(OpaquePointer(ptr)), socklen_t(MemoryLayout<sockaddr_in6>.size))
        }
        
        
        #if os(macOS) || os(iOS)
            var val = 1
            setsockopt(self.plainClient, SOL_SOCKET, SO_NOSIGPIPE, &val, socklen_t(MemoryLayout<Int>.stride))
            
            if sslEnabled {
                guard let context = SSLCreateContext(nil, .clientSide, .streamType) else {
                    throw Error.cannotCreateContext
                }
                
                SSLSetIOFuncs(context, { context, data, length in
                    let context = context.bindMemory(to: Int32.self, capacity: 1).pointee
                    var read = Darwin.recv(context, data, length.pointee, 0)
                    
                    length.assign(from: &read, count: 1)
                    return 0
                }, { context, data, length in
                    let context = context.bindMemory(to: Int32.self, capacity: 1).pointee
                    var written = Darwin.send(context, data, length.pointee, 0)
                    
                    length.assign(from: &written, count: 1)
                    return 0
                })
                
                SSLSetConnection(context, &self.plainClient)
                
                if let path = options["CAFile"] as? String, let data = FileManager.default.contents(atPath: path) {
                    let bytes = [UInt8](data)
                    
                    if let certBytes = CFDataCreate(kCFAllocatorDefault, bytes, data.count), let cert = SecCertificateCreateWithData(kCFAllocatorDefault, certBytes) {
                        SSLSetCertificateAuthorities(context, cert, true)
                    }
                }
                
                _ = SSLHandshake(context)
                
                self.sslClient = context
            } else {
                self.sslClient = nil
            }
        #else
            if sslEnabled {
                let verifyCertificate = !(options["invalidCertificateAllowed"] as? Bool ?? false)
                let verifyHost = !(options["invalidHostNameAllowed"] as? Bool ?? false)
                
                if !MongoSocket.initialized {
                    SSL_library_init()<commit_msg>Comment unused variable<commit_after>//
// This source file is part of the MongoKitten open source project
//
// Copyright (c) 2016 - 2017 OpenKitten and the MongoKitten project authors
// Licensed under MIT
//
// See https://github.com/OpenKitten/MongoKitten/blob/mongokitten31/LICENSE.md for license information
// See https://github.com/OpenKitten/MongoKitten/blob/mongokitten31/CONTRIBUTORS.md for the list of MongoKitten project authors
//

import Foundation
import Dispatch

#if os(macOS) || os(iOS)
    import Security
    import Darwin
#else
    import KittenCTLS
    import Glibc
#endif

public final class MongoSocket: MongoTCP {
    public private(set) var plainClient: Int32
    
    #if os(macOS) || os(iOS)
    private let sslClient: SSLContext?
    #else
    fileprivate static var initialized: Bool = false
    private let sslClient: UnsafeMutablePointer<SSL>?
    private let sslMethod: UnsafePointer<SSL_METHOD>?
    private let sslContext: UnsafeMutablePointer<SSL_CTX>?
    
    deinit { SSL_CTX_free(sslContext) }
    #endif
    
    private var sslEnabled = false
    
    private static let socketQueue = DispatchQueue(label: "org.mongokitten.socketQueue", qos: DispatchQoS.userInteractive)
    
    public var onRead: ReadCallback
    public var onError: ErrorCallback
    
    private let operationLock = NSLock()
    
    private let readSource: DispatchSourceRead
    
    public init(address hostname: String, port: UInt16, options: [String: Any], onRead: @escaping ReadCallback, onError: @escaping ErrorCallback) throws {
        self.sslEnabled = options["sslEnabled"] as? Bool ?? false
        self.onRead = onRead
        self.onError = onError
        
        var criteria = addrinfo.init()
        
        let hostname = hostname == "localhost" ? "127.0.0.1" : hostname
        
        #if os(macOS) || os(iOS)
            criteria.ai_socktype = Int32(SOCK_STREAM)
        #else
            criteria.ai_socktype = Int32(SOCK_STREAM.rawValue)
        #endif
        
        criteria.ai_family = Int32(AF_UNSPEC)
        criteria.ai_flags = AI_PASSIVE
        criteria.ai_protocol = Int32(IPPROTO_TCP)
        
        var serverinfo: UnsafeMutablePointer<addrinfo>? = nil
        let ret = getaddrinfo(hostname, port.description, &criteria, &serverinfo)
        
        guard let addrList = serverinfo else { throw Error.ipAddressResolutionFailed }
        defer { freeaddrinfo(addrList) }
        
        guard let addrInfo = addrList.pointee.ai_addr else { throw Error.ipAddressResolutionFailed }
        
        let ptr = UnsafeMutablePointer<sockaddr_storage>.allocate(capacity: 1)
        ptr.initialize(to: sockaddr_storage())
        
        switch Int32(addrInfo.pointee.sa_family) {
        case Int32(AF_INET):
            let addr = UnsafeMutablePointer<sockaddr_in>.init(OpaquePointer(addrInfo))!
            let specPtr = UnsafeMutablePointer<sockaddr_in>(OpaquePointer(ptr))
            specPtr.assign(from: addr, count: 1)
        case Int32(AF_INET6):
            let addr = UnsafeMutablePointer<sockaddr_in6>(OpaquePointer(addrInfo))!
            let specPtr = UnsafeMutablePointer<sockaddr_in6>(OpaquePointer(ptr))
            specPtr.assign(from: addr, count: 1)
        default:
            throw Error.ipAddressResolutionFailed
        }
        
        #if os(macOS) || os(iOS)
            self.plainClient = socket(Int32(addrInfo.pointee.sa_family), SOCK_STREAM, Int32(IPPROTO_TCP))
        #else
            self.plainClient = socket(Int32(addrInfo.pointee.sa_family), Int32(SOCK_STREAM.rawValue), Int32(IPPROTO_TCP))
        #endif
        
        signal(SIGPIPE, SIG_IGN)
        
        guard plainClient >= 0 else {
            throw Error.cannotConnect
        }
        
        if Int32(addrInfo.pointee.sa_family) == Int32(AF_INET) {
            connect(self.plainClient, UnsafeMutablePointer<sockaddr>(OpaquePointer(ptr)), socklen_t(MemoryLayout<sockaddr_in>.size))
        } else {
            connect(self.plainClient, UnsafeMutablePointer<sockaddr>(OpaquePointer(ptr)), socklen_t(MemoryLayout<sockaddr_in6>.size))
        }
        
        
        #if os(macOS) || os(iOS)
            var val = 1
            setsockopt(self.plainClient, SOL_SOCKET, SO_NOSIGPIPE, &val, socklen_t(MemoryLayout<Int>.stride))
            
            if sslEnabled {
                guard let context = SSLCreateContext(nil, .clientSide, .streamType) else {
                    throw Error.cannotCreateContext
                }
                
                SSLSetIOFuncs(context, { context, data, length in
                    let context = context.bindMemory(to: Int32.self, capacity: 1).pointee
                    var read = Darwin.recv(context, data, length.pointee, 0)
                    
                    length.assign(from: &read, count: 1)
                    return 0
                }, { context, data, length in
                    let context = context.bindMemory(to: Int32.self, capacity: 1).pointee
                    var written = Darwin.send(context, data, length.pointee, 0)
                    
                    length.assign(from: &written, count: 1)
                    return 0
                })
                
                SSLSetConnection(context, &self.plainClient)
                
                if let path = options["CAFile"] as? String, let data = FileManager.default.contents(atPath: path) {
                    let bytes = [UInt8](data)
                    
                    if let certBytes = CFDataCreate(kCFAllocatorDefault, bytes, data.count), let cert = SecCertificateCreateWithData(kCFAllocatorDefault, certBytes) {
                        SSLSetCertificateAuthorities(context, cert, true)
                    }
                }
                
                _ = SSLHandshake(context)
                
                self.sslClient = context
            } else {
                self.sslClient = nil
            }
        #else
            if sslEnabled {
                let verifyCertificate = !(options["invalidCertificateAllowed"] as? Bool ?? false)
//                let verifyHost = !(options["invalidHostNameAllowed"] as? Bool ?? false)
                
                if !MongoSocket.initialized {
                    SSL_library_init()
{'fraction_non_alphanumeric': 0.08601978933929141, 'fraction_numerical': 0.01149058410469199, 'mean_word_length': 2.0620571707793793, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 33, 'https://': 4, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '499300', 'n_tokens_mistral': 3646, 'n_tokens_neox': 3443, 'n_words': 767}