blob: 0316e37d7e42577657bfa7f9c9fd4d1ef3fd8cc5 [file] [log] [blame]
Chris Simpsona9b6c702018-04-08 07:11:37 -04001
2/*
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 */
20
21
22#if os(OSX) || os(iOS) || os(watchOS) || os(tvOS)
23 import Darwin
24#elseif os(Linux) || os(FreeBSD) || os(PS4) || os(Android)
25 import Glibc
26 import Dispatch
27#endif
28
29import Foundation
30import CoreFoundation
31
32private struct Sys {
33 #if os(Linux)
34 static let read = Glibc.read
35 static let write = Glibc.write
36 static let close = Glibc.close
37 #else
38 static let read = Darwin.read
39 static let write = Darwin.write
40 static let close = Darwin.close
41 #endif
42}
43
44extension in_addr {
45 public init?(hostent: hostent?) {
46 guard let host = hostent, host.h_addr_list != nil, host.h_addr_list.pointee != nil else {
47 return nil
48 }
49 self.init()
50 memcpy(&self, host.h_addr_list.pointee!, Int(host.h_length))
51
52 }
53}
54
55
56#if os(Linux)
57 /// TCFSocketTransport currently unavailable
58 /// remove comments and build to see why/fix
59 /// currently CF[Read|Write]Stream's can't cast to [Input|Output]Streams which breaks thigns
60#else
61extension Stream.PropertyKey {
62 static let SSLPeerTrust = Stream.PropertyKey(kCFStreamPropertySSLPeerTrust as String)
63}
64
65/// TCFSocketTransport, uses CFSockets and (NS)Stream's
66public class TCFSocketTransport: TStreamTransport {
67 public init?(hostname: String, port: Int, secure: Bool = false) {
68
69 var inputStream: InputStream
70 var outputStream: OutputStream
71
72 var readStream: Unmanaged<CFReadStream>?
73 var writeStream: Unmanaged<CFWriteStream>?
74 CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
75 hostname as CFString!,
76 UInt32(port),
77 &readStream,
78 &writeStream)
79
80 if let readStream = readStream?.takeRetainedValue(),
81 let writeStream = writeStream?.takeRetainedValue() {
82 CFReadStreamSetProperty(readStream, .shouldCloseNativeSocket, kCFBooleanTrue)
83 CFWriteStreamSetProperty(writeStream, .shouldCloseNativeSocket, kCFBooleanTrue)
84
85 if secure {
Chris Simpson2566ecd2018-08-29 14:40:44 -040086 CFReadStreamSetProperty(readStream, .socketSecurityLevel, StreamSocketSecurityLevel.negotiatedSSL.rawValue as CFString)
87 CFWriteStreamSetProperty(writeStream, .socketSecurityLevel, StreamSocketSecurityLevel.negotiatedSSL.rawValue as CFString)
Chris Simpsona9b6c702018-04-08 07:11:37 -040088 }
89
90 inputStream = readStream as InputStream
91 inputStream.schedule(in: .current, forMode: .defaultRunLoopMode)
92 inputStream.open()
93
94 outputStream = writeStream as OutputStream
95 outputStream.schedule(in: .current, forMode: .defaultRunLoopMode)
96 outputStream.open()
97
98 } else {
99
100 if readStream != nil {
101 readStream?.release()
102 }
103 if writeStream != nil {
104 writeStream?.release()
105 }
106 super.init(inputStream: nil, outputStream: nil)
107 return nil
108 }
109
110 super.init(inputStream: inputStream, outputStream: outputStream)
111
112 self.input?.delegate = self
113 self.output?.delegate = self
114 }
115}
116
117extension TCFSocketTransport: StreamDelegate { }
118#endif
119
120
121/// TSocketTransport, posix sockets. Supports IPv4 only for now
122public class TSocketTransport : TTransport {
123 public var socketDescriptor: Int32
124
125
126
127 /// Initialize from an already set up socketDescriptor.
128 /// Expects socket thats already bound/connected (i.e. from listening)
129 ///
130 /// - parameter socketDescriptor: posix socket descriptor (Int32)
131 public init(socketDescriptor: Int32) {
132 self.socketDescriptor = socketDescriptor
133 }
134
135
136 public convenience init(hostname: String, port: Int) throws {
137 guard let hp = gethostbyname(hostname.cString(using: .utf8)!)?.pointee,
138 let hostAddr = in_addr(hostent: hp) else {
139 throw TTransportError(error: .unknown, message: "Invalid address: \(hostname)")
140 }
141
142
143
144 #if os(Linux)
145 let sock = socket(AF_INET, Int32(SOCK_STREAM.rawValue), 0)
146 var addr = sockaddr_in(sin_family: sa_family_t(AF_INET),
147 sin_port: in_port_t(htons(UInt16(port))),
148 sin_addr: hostAddr,
149 sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
150 #else
151 let sock = socket(AF_INET, SOCK_STREAM, 0)
152
153 var addr = sockaddr_in(sin_len: UInt8(MemoryLayout<sockaddr_in>.size),
154 sin_family: sa_family_t(AF_INET),
155 sin_port: in_port_t(htons(UInt16(port))),
156 sin_addr: in_addr(s_addr: in_addr_t(0)),
157 sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
158
159 #endif
160
161 let addrPtr = withUnsafePointer(to: &addr){ UnsafePointer<sockaddr>(OpaquePointer($0)) }
162
163 let connected = connect(sock, addrPtr, UInt32(MemoryLayout<sockaddr_in>.size))
164 if connected != 0 {
165 throw TTransportError(error: .notOpen, message: "Error binding to host: \(hostname) \(port)")
166 }
167
168 self.init(socketDescriptor: sock)
169 }
170
171 deinit {
172 close()
173 }
174
175 public func readAll(size: Int) throws -> Data {
176 var out = Data()
177 while out.count < size {
178 out.append(try self.read(size: size))
179 }
180 return out
181 }
182
183 public func read(size: Int) throws -> Data {
184 var buff = Array<UInt8>.init(repeating: 0, count: size)
185 let readBytes = Sys.read(socketDescriptor, &buff, size)
186
187 return Data(bytes: buff[0..<readBytes])
188 }
189
190 public func write(data: Data) {
191 var bytesToWrite = data.count
192 var writeBuffer = data
193 while bytesToWrite > 0 {
194 let written = writeBuffer.withUnsafeBytes {
195 Sys.write(socketDescriptor, $0, writeBuffer.count)
196 }
197 writeBuffer = writeBuffer.subdata(in: written ..< writeBuffer.count)
198 bytesToWrite -= written
199 }
200 }
201
202 public func flush() throws {
203 // nothing to do
204 }
205
206 public func close() {
207 shutdown(socketDescriptor, Int32(SHUT_RDWR))
208 _ = Sys.close(socketDescriptor)
209 }
210}