blob: db5a2b4aa31d365c850bad8f9fa8f945b8bfa4a2 [file] [log] [blame]
jfarrell9f154152014-04-04 11:41:15 -04001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19#import <Foundation/Foundation.h>
20#import <CoreFoundation/CoreFoundation.h>
21#import "TSSLSocketClient.h"
22#import "TSSLSocketException.h"
23#import "TObjective-C.h"
24#include <sys/socket.h>
25#include <netinet/in.h>
26#include <netdb.h>
27
28#if !TARGET_OS_IPHONE
29#import <CoreServices/CoreServices.h>
30#else
31#import <CFNetwork/CFNetwork.h>
32#endif
33
34@implementation TSSLSocketClient
35
36- (id) initWithHostname: (NSString *) hostname
37 port: (int) port
38{
39 sslHostname = hostname;
40 CFReadStreamRef readStream = NULL;
41 CFWriteStreamRef writeStream = NULL;
42
43
44 /* create a socket structure */
45 struct sockaddr_in pin;
46 struct hostent *hp = NULL;
47 for(int i = 0; i < 10; i++) {
48
49
50
51 if ((hp = gethostbyname([hostname UTF8String])) == NULL) {
52 NSLog(@"failed to resolve hostname %@", hostname);
53 herror("resolv");
54 if(i == 9) {
55 @throw [TSSLSocketException exceptionWithReason: @"failed to resolve hostname"];
56 }
57 [NSThread sleepForTimeInterval:0.2];
58 } else {
59 break;
60 }
61 }
62
63 memset (&pin, 0, sizeof(pin));
64 pin.sin_family = AF_INET;
65 pin.sin_addr.s_addr = ((struct in_addr *) (hp->h_addr))->s_addr;
66 pin.sin_port = htons (port);
67
68 /* create the socket */
69 if ((sd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
70 {
71 NSLog(@"failed to create socket for host %@:%d", hostname, port);
72 @throw [TSSLSocketException exceptionWithReason: @"failed to create socket"];
73 }
74
75 /* open a connection */
76 if (connect (sd, (struct sockaddr *) &pin, sizeof(pin)) == -1)
77 {
78 NSLog(@"failed to create conenct to host %@:%d", hostname, port);
79 @throw [TSSLSocketException exceptionWithReason: @"failed to connect"];
80 }
81 CFStreamCreatePairWithSocket(kCFAllocatorDefault, sd, &readStream, &writeStream);
82
83 CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
84 CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
85
86 if (readStream && writeStream) {
87 CFReadStreamSetProperty(readStream,
88 kCFStreamPropertySocketSecurityLevel,
89 kCFStreamSocketSecurityLevelTLSv1);
90
91 NSDictionary *settings =
92 [NSDictionary dictionaryWithObjectsAndKeys:
93 (id)kCFBooleanTrue, (id)kCFStreamSSLValidatesCertificateChain,
94 nil];
95
96 CFReadStreamSetProperty((CFReadStreamRef)readStream,
97 kCFStreamPropertySSLSettings,
98 (CFTypeRef)settings);
99 CFWriteStreamSetProperty((CFWriteStreamRef)writeStream,
100 kCFStreamPropertySSLSettings,
101 (CFTypeRef)settings);
102
103 inputStream = (bridge_stub NSInputStream *)readStream;
104 [inputStream retain_stub];
105 [inputStream setDelegate:self];
106 [inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
107 [inputStream open];
108
109 outputStream = (bridge_stub NSOutputStream *)writeStream;
110 [outputStream retain_stub];
111 [outputStream setDelegate:self];
112 [outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
113 [outputStream open];
114
115
116 CFRelease(readStream);
117 CFRelease(writeStream);
118 }
119
120
121
122 self = [super initWithInputStream: inputStream outputStream: outputStream];
123
124 return self;
125}
126
127#pragma mark -
128#pragma mark NSStreamDelegate
129- (void)stream:(NSStream *)aStream
130 handleEvent:(NSStreamEvent)eventCode {
131 switch (eventCode) {
132 case NSStreamEventNone:
133 break;
134 case NSStreamEventHasBytesAvailable:
135 break;
136 case NSStreamEventOpenCompleted:
137 break;
138 case NSStreamEventHasSpaceAvailable:
139 {
140 SecPolicyRef policy = SecPolicyCreateSSL(NO, (__bridge CFStringRef)(sslHostname));
141 SecTrustRef trust = NULL;
142 CFArrayRef streamCertificatesRef =
143 CFBridgingRetain((__bridge id)((__bridge CFArrayRef)([aStream propertyForKey:(NSString *) kCFStreamPropertySSLPeerCertificates])));
144 SecTrustCreateWithCertificates(CFBridgingRetain((__bridge id)(streamCertificatesRef)),
145 policy,
146 &trust);
147
148 SecTrustResultType trustResultType = kSecTrustResultInvalid;
149 SecTrustEvaluate(trust, &trustResultType);
150
151 BOOL proceed = NO;
152 switch (trustResultType) {
153 case kSecTrustResultProceed:
154 proceed = YES;
155 break;
156 case kSecTrustResultUnspecified:
157 NSLog(@"Trusted by OS");
158 proceed = YES;
159 break;
160 case kSecTrustResultRecoverableTrustFailure:
161 proceed = recoverFromTrustFailure(trust);
162 break;
163 case kSecTrustResultDeny:
164 NSLog(@"Deny");
165 break;
166 case kSecTrustResultFatalTrustFailure:
167 NSLog(@"FatalTrustFailure");
168 break;
169 case kSecTrustResultOtherError:
170 NSLog(@"OtherError");
171 break;
172 case kSecTrustResultInvalid:
173 NSLog(@"Invalid");
174 break;
175 default:
176 NSLog(@"Default");
177 break;
178 }
179
180 if (trust) {
181 CFRelease(trust);
182 }
183 if (policy) {
184 CFRelease(policy);
185 }
186 if (!proceed) {
187 NSLog(@"Cannot trust certificate. TrustResultType: %u", trustResultType);
188 [aStream close];
189 @throw [TSSLSocketException exceptionWithReason: @"Cannot trust certificate"];
190 }
191 }
192 break;
193 case NSStreamEventErrorOccurred:
194 {
195 NSError *theError = [aStream streamError];
196 NSLog(@"Error occured opening stream: %@", theError);
197// @throw [TSSLSocketException exceptionWithReason: @"Error occured opening stream" error: theError];
198 break;
199 }
200 case NSStreamEventEndEncountered:
201 break;
202 default:
203 break;
204 }
205}
206
207bool recoverFromTrustFailure(SecTrustRef myTrust)
208{
209
210 SecTrustResultType trustResult;
211 OSStatus status = SecTrustEvaluate(myTrust, &trustResult);
212
213 CFAbsoluteTime trustTime,currentTime,timeIncrement,newTime;
214 CFDateRef newDate;
215 if (trustResult == kSecTrustResultRecoverableTrustFailure) {
216 trustTime = SecTrustGetVerifyTime(myTrust);
217 timeIncrement = 31536000;
218 currentTime = CFAbsoluteTimeGetCurrent();
219 newTime = currentTime - timeIncrement;
220 if (trustTime - newTime){
221 newDate = CFDateCreate(NULL, newTime);
222 SecTrustSetVerifyDate(myTrust, newDate);
223 status = SecTrustEvaluate(myTrust, &trustResult);
224 }
225 }
226 if (trustResult != kSecTrustResultProceed) {
227 NSLog(@"Certificate trust failure");
228 return false;
229 }
230 return true;
231}
232
233- (void)close
234{
235 if(self.mInput) {
236 //Close and reset inputstream
237 CFReadStreamSetProperty((__bridge CFReadStreamRef)(self.mInput), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
238 [self.mInput setDelegate:nil];
239 [self.mInput close];
240 [self.mInput removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
241 self.mInput = nil;
242 }
243
244 if(self.mOutput) {
245 //Close and reset outputstream
246 CFReadStreamSetProperty((__bridge CFReadStreamRef)(self.mOutput), kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
247 [self.mOutput setDelegate:nil];
248 [self.mOutput close];
249 [self.mOutput removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
250 self.mOutput = nil;
251 }
252}
253
254- (BOOL) isOpen
255{
256 if(sd > 0)
257 return TRUE;
258 else
259 return FALSE;
260}
261
262@end
263