]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | |
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 | ||
29 | import Foundation | |
30 | import CoreFoundation | |
31 | ||
32 | #if !swift(>=4.2) | |
33 | // Swift 3/4 compatibility | |
34 | fileprivate extension RunLoopMode { | |
35 | static let `default` = defaultRunLoopMode | |
36 | } | |
37 | #endif | |
38 | ||
39 | private struct Sys { | |
40 | #if os(Linux) | |
41 | static let read = Glibc.read | |
42 | static let write = Glibc.write | |
43 | static let close = Glibc.close | |
44 | #else | |
45 | static let read = Darwin.read | |
46 | static let write = Darwin.write | |
47 | static let close = Darwin.close | |
48 | #endif | |
49 | } | |
50 | ||
51 | extension in_addr { | |
52 | public init?(hostent: hostent?) { | |
53 | guard let host = hostent, host.h_addr_list != nil, host.h_addr_list.pointee != nil else { | |
54 | return nil | |
55 | } | |
56 | self.init() | |
57 | memcpy(&self, host.h_addr_list.pointee!, Int(host.h_length)) | |
58 | ||
59 | } | |
60 | } | |
61 | ||
62 | ||
63 | #if os(Linux) | |
64 | /// TCFSocketTransport currently unavailable | |
65 | /// remove comments and build to see why/fix | |
66 | /// currently CF[Read|Write]Stream's can't cast to [Input|Output]Streams which breaks thigns | |
67 | #else | |
68 | extension Stream.PropertyKey { | |
69 | static let SSLPeerTrust = Stream.PropertyKey(kCFStreamPropertySSLPeerTrust as String) | |
70 | } | |
71 | ||
72 | /// TCFSocketTransport, uses CFSockets and (NS)Stream's | |
73 | public class TCFSocketTransport: TStreamTransport { | |
74 | public init?(hostname: String, port: Int, secure: Bool = false) { | |
75 | ||
76 | var inputStream: InputStream | |
77 | var outputStream: OutputStream | |
78 | ||
79 | var readStream: Unmanaged<CFReadStream>? | |
80 | var writeStream: Unmanaged<CFWriteStream>? | |
81 | CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, | |
82 | hostname as CFString, | |
83 | UInt32(port), | |
84 | &readStream, | |
85 | &writeStream) | |
86 | ||
87 | if let readStream = readStream?.takeRetainedValue(), | |
88 | let writeStream = writeStream?.takeRetainedValue() { | |
89 | CFReadStreamSetProperty(readStream, .shouldCloseNativeSocket, kCFBooleanTrue) | |
90 | CFWriteStreamSetProperty(writeStream, .shouldCloseNativeSocket, kCFBooleanTrue) | |
91 | ||
92 | if secure { | |
93 | CFReadStreamSetProperty(readStream, .socketSecurityLevel, StreamSocketSecurityLevel.negotiatedSSL.rawValue as CFString) | |
94 | CFWriteStreamSetProperty(writeStream, .socketSecurityLevel, StreamSocketSecurityLevel.negotiatedSSL.rawValue as CFString) | |
95 | } | |
96 | ||
97 | inputStream = readStream as InputStream | |
98 | inputStream.schedule(in: .current, forMode: .default) | |
99 | inputStream.open() | |
100 | ||
101 | outputStream = writeStream as OutputStream | |
102 | outputStream.schedule(in: .current, forMode: .default) | |
103 | outputStream.open() | |
104 | ||
105 | } else { | |
106 | ||
107 | if readStream != nil { | |
108 | readStream?.release() | |
109 | } | |
110 | if writeStream != nil { | |
111 | writeStream?.release() | |
112 | } | |
113 | super.init(inputStream: nil, outputStream: nil) | |
114 | return nil | |
115 | } | |
116 | ||
117 | super.init(inputStream: inputStream, outputStream: outputStream) | |
118 | ||
119 | self.input?.delegate = self | |
120 | self.output?.delegate = self | |
121 | } | |
122 | } | |
123 | ||
124 | extension TCFSocketTransport: StreamDelegate { } | |
125 | #endif | |
126 | ||
127 | ||
128 | /// TSocketTransport, posix sockets. Supports IPv4 only for now | |
129 | public class TSocketTransport : TTransport { | |
130 | public var socketDescriptor: Int32 | |
131 | ||
132 | ||
133 | ||
134 | /// Initialize from an already set up socketDescriptor. | |
135 | /// Expects socket thats already bound/connected (i.e. from listening) | |
136 | /// | |
137 | /// - parameter socketDescriptor: posix socket descriptor (Int32) | |
138 | public init(socketDescriptor: Int32) { | |
139 | self.socketDescriptor = socketDescriptor | |
140 | } | |
141 | ||
142 | ||
143 | public convenience init(hostname: String, port: Int) throws { | |
144 | guard let hp = gethostbyname(hostname.cString(using: .utf8)!)?.pointee, | |
145 | let hostAddr = in_addr(hostent: hp) else { | |
146 | throw TTransportError(error: .unknown, message: "Invalid address: \(hostname)") | |
147 | } | |
148 | ||
149 | ||
150 | #if os(Linux) | |
151 | let sock = socket(AF_INET, Int32(SOCK_STREAM.rawValue), 0) | |
152 | var addr = sockaddr_in(sin_family: sa_family_t(AF_INET), | |
153 | sin_port: in_port_t(htons(UInt16(port))), | |
154 | sin_addr: hostAddr, | |
155 | sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) | |
156 | #else | |
157 | let sock = socket(AF_INET, SOCK_STREAM, 0) | |
158 | ||
159 | var addr = sockaddr_in(sin_len: UInt8(MemoryLayout<sockaddr_in>.size), | |
160 | sin_family: sa_family_t(AF_INET), | |
161 | sin_port: in_port_t(htons(UInt16(port))), | |
162 | sin_addr: in_addr(s_addr: in_addr_t(0)), | |
163 | sin_zero: (0, 0, 0, 0, 0, 0, 0, 0)) | |
164 | ||
165 | #endif | |
166 | ||
167 | let addrPtr = withUnsafePointer(to: &addr){ UnsafePointer<sockaddr>(OpaquePointer($0)) } | |
168 | ||
169 | let connected = connect(sock, addrPtr, UInt32(MemoryLayout<sockaddr_in>.size)) | |
170 | if connected != 0 { | |
171 | throw TTransportError(error: .notOpen, message: "Error binding to host: \(hostname) \(port)") | |
172 | } | |
173 | ||
174 | self.init(socketDescriptor: sock) | |
175 | } | |
176 | ||
177 | deinit { | |
178 | close() | |
179 | } | |
180 | ||
181 | public func readAll(size: Int) throws -> Data { | |
182 | var out = Data() | |
183 | while out.count < size { | |
184 | out.append(try self.read(size: size)) | |
185 | } | |
186 | return out | |
187 | } | |
188 | ||
189 | public func read(size: Int) throws -> Data { | |
190 | var buff = Array<UInt8>.init(repeating: 0, count: size) | |
191 | let readBytes = Sys.read(socketDescriptor, &buff, size) | |
192 | ||
193 | return Data(buff[0..<readBytes]) | |
194 | } | |
195 | ||
196 | public func write(data: Data) { | |
197 | var bytesToWrite = data.count | |
198 | var writeBuffer = data | |
199 | while bytesToWrite > 0 { | |
200 | let written = writeBuffer.withUnsafeBytes { | |
201 | Sys.write(socketDescriptor, $0, writeBuffer.count) | |
202 | } | |
203 | writeBuffer = writeBuffer.subdata(in: written ..< writeBuffer.count) | |
204 | bytesToWrite -= written | |
205 | } | |
206 | } | |
207 | ||
208 | public func flush() throws { | |
209 | // nothing to do | |
210 | } | |
211 | ||
212 | public func close() { | |
213 | shutdown(socketDescriptor, Int32(SHUT_RDWR)) | |
214 | _ = Sys.close(socketDescriptor) | |
215 | } | |
216 | } |