1
/** Implementation for GSUnixSSLHandle for GNUStep
2
Copyright (C) 1997-1999 Free Software Foundation, Inc.
4
Written by: Richard Frith-Macdonald <richard@brainstorm.co.uk>
7
This file is part of the GNUstep Base Library.
9
This library is free software; you can redistribute it and/or
10
modify it under the terms of the GNU Library General Public
11
License as published by the Free Software Foundation; either
12
version 2 of the License, or (at your option) any later version.
14
This library is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17
Library General Public License for more details.
19
You should have received a copy of the GNU Library General Public
20
License along with this library; if not, write to the Free
21
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
27
#if defined(__WIN32__) || defined(_WIN32) || defined(__MS_WIN32__)
42
#if defined(__WIN32__)
44
#define GNUSTEP_BASE_SOCKET_MESSAGE (WM_USER + 1)
47
/* Because openssl uses `id' as variable name sometime,
48
while it is an Objective-C reserved keyword. */
50
#include <openssl/ssl.h>
54
#include <Foundation/Foundation.h>
56
#include <gnustep/base/UnixFileHandle.h>
58
#if defined(__MINGW__)
63
#include <sys/param.h>
64
#include <sys/socket.h>
65
#include <netinet/in.h>
66
#include <arpa/inet.h>
68
#endif /* __MINGW__ */
72
#include <sys/fcntl.h>
73
#include <sys/ioctl.h>
75
#include <sys/filio.h>
84
// Maximum data in single I/O operation
85
#define NETBUF_SIZE 4096
87
// Key to info dictionary for operation mode.
88
static NSString* NotificationKey = @"NSFileHandleNotificationKey";
92
@interface GSUnixSSLHandle : UnixFileHandle <GCFinalization>
99
- (void) sslDisconnect;
100
- (void) sslSetCertificate: (NSString*)certFile
101
privateKey: (NSString*)privateKey
102
PEMpasswd: (NSString*)PEMpasswd;
105
@implementation GSUnixSSLHandle
108
if (self == [GSUnixSSLHandle class])
114
- (NSData*) availableData
116
char buf[NETBUF_SIZE];
121
if (isNonBlocking == YES)
122
[self setNonBlocking: NO];
123
d = [NSMutableData dataWithCapacity: 0];
126
while ((len = read(descriptor, buf, sizeof(buf))) > 0)
128
[d appendBytes: buf length: len];
135
if ((len = SSL_read(ssl, buf, sizeof(buf))) > 0)
137
[d appendBytes: buf length: len];
142
if ((len = read(descriptor, buf, sizeof(buf))) > 0)
144
[d appendBytes: buf length: len];
150
[NSException raise: NSFileHandleOperationException
151
format: @"unable to read from descriptor - %s",
152
GSLastErrorStr(errno)];
159
[self sslDisconnect];
165
[self sslDisconnect];
169
- (NSData*) readDataOfLength: (unsigned)len
175
if (isNonBlocking == YES)
176
[self setNonBlocking: NO];
181
buf = NSZoneMalloc(NSDefaultMallocZone(), len);
182
d = [NSMutableData dataWithBytesNoCopy: buf length: len];
183
if ((got = SSL_read(ssl, [d mutableBytes], len)) < 0)
185
[NSException raise: NSFileHandleOperationException
186
format: @"unable to read from descriptor - %s",
187
GSLastErrorStr(errno)];
193
char buf[NETBUF_SIZE];
195
d = [NSMutableData dataWithCapacity: 0];
198
int chunk = len > sizeof(buf) ? sizeof(buf) : len;
202
got = SSL_read(ssl, buf, chunk);
206
got = read(descriptor, buf, chunk);
210
[d appendBytes: buf length: got];
215
[NSException raise: NSFileHandleOperationException
216
format: @"unable to read from descriptor - %s",
217
GSLastErrorStr(errno)];
220
while (len > 0 && got > 0);
225
- (NSData*) readDataToEndOfFile
227
char buf[NETBUF_SIZE];
232
if (isNonBlocking == YES)
233
[self setNonBlocking: NO];
234
d = [NSMutableData dataWithCapacity: 0];
237
while ((len = SSL_read(ssl, buf, sizeof(buf))) > 0)
239
[d appendBytes: buf length: len];
244
while ((len = read(descriptor, buf, sizeof(buf))) > 0)
246
[d appendBytes: buf length: len];
251
[NSException raise: NSFileHandleOperationException
252
format: @"unable to read from descriptor - %s",
253
GSLastErrorStr(errno)];
258
- (void) receivedEvent: (void*)data
259
type: (RunLoopEventType)type
261
forMode: (NSString*)mode
265
if (isNonBlocking == NO)
266
[self setNonBlocking: YES];
267
if (type == ET_RDESC)
269
operation = [readInfo objectForKey: NotificationKey];
270
if (operation == NSFileHandleConnectionAcceptedNotification)
272
struct sockaddr_in buf;
274
int blen = sizeof(buf);
276
desc = accept(descriptor, (struct sockaddr*)&buf, &blen);
281
s = [NSString stringWithFormat: @"Accept attempt failed - %s",
282
GSLastErrorStr(errno)];
283
[readInfo setObject: s forKey: GSFileHandleNotificationError];
286
{ // Accept attempt completed.
288
struct sockaddr_in sin;
289
int size = sizeof(sin);
291
h = [[GSUnixSSLHandle alloc] initWithFileDescriptor: desc
292
closeOnDealloc: YES];
293
getpeername(desc, (struct sockaddr*)&sin, &size);
295
[readInfo setObject: h
296
forKey: NSFileHandleNotificationFileHandleItem];
299
[self postReadNotification];
301
else if (operation == NSFileHandleDataAvailableNotification)
303
[self postReadNotification];
310
char buf[NETBUF_SIZE];
312
item = [readInfo objectForKey: NSFileHandleNotificationDataItem];
313
length = [item length];
317
received = SSL_read(ssl, buf, sizeof(buf));
321
received = read(descriptor, buf, sizeof(buf));
324
{ // Read up to end of file.
325
[self postReadNotification];
327
else if (received < 0)
329
if (errno != EAGAIN && errno != EINTR)
333
s = [NSString stringWithFormat: @"Read attempt failed - %s",
334
GSLastErrorStr(errno)];
335
[readInfo setObject: s forKey: GSFileHandleNotificationError];
336
[self postReadNotification];
341
[item appendBytes: buf length: received];
342
if (operation == NSFileHandleReadCompletionNotification)
344
// Read a single chunk of data
345
[self postReadNotification];
350
else if (type == ET_WDESC)
352
NSMutableDictionary *info;
354
info = [writeInfo objectAtIndex: 0];
355
operation = [info objectForKey: NotificationKey];
356
if (operation == GSFileHandleWriteCompletionNotification)
362
item = [info objectForKey: NSFileHandleNotificationDataItem];
363
length = [item length];
365
if (writePos < length)
371
written = SSL_write(ssl, (char*)ptr + writePos,
376
written = write(descriptor, (char*)ptr + writePos,
381
if (written < 0 && errno != EAGAIN && errno != EINTR)
385
s = [NSString stringWithFormat:
386
@"Write attempt failed - %s", GSLastErrorStr(errno)];
387
[info setObject: s forKey: GSFileHandleNotificationError];
388
[self postWriteNotification];
396
if (writePos >= length)
397
{ // Write operation completed.
398
[self postWriteNotification];
402
{ // Connection attempt completed.
404
int len = sizeof(result);
406
if (getsockopt(descriptor, SOL_SOCKET, SO_ERROR,
407
(char*)&result, &len) == 0 && result != 0)
411
s = [NSString stringWithFormat: @"Connect attempt failed - %s",
412
GSLastErrorStr(result)];
413
[info setObject: s forKey: GSFileHandleNotificationError];
421
[self postWriteNotification];
432
if (connected == YES)
434
return YES; /* Already connected. */
436
if (isStandardFile == YES)
438
NSLog(@"Attempt to make ssl connection to a standard file");
443
* Ensure we have a context and handle to connect with.
447
ctx = SSL_CTX_new(SSLv23_client_method());
454
ret = SSL_set_fd(ssl, descriptor);
455
loop = [NSRunLoop currentRunLoop];
456
[loop runUntilDate: [NSDate dateWithTimeIntervalSinceNow: 0.01]];
457
ret = SSL_connect(ssl);
463
NSTimeInterval last = 0.0;
464
NSTimeInterval limit = 0.1;
466
final = [[NSDate alloc] initWithTimeIntervalSinceNow: 20.0];
467
when = [NSDate alloc];
469
err = SSL_get_error(ssl, ret);
470
while ((err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE)
471
&& [final timeIntervalSinceNow] > 0.0)
473
NSTimeInterval tmp = limit;
477
when = [when initWithTimeIntervalSinceNow: limit];
478
[loop runUntilDate: when];
479
ret = SSL_connect(ssl);
483
err = SSL_get_error(ssl, ret);
487
err = SSL_ERROR_NONE;
492
if (err != SSL_ERROR_NONE)
499
str = @"No error: really helpful";
501
case SSL_ERROR_ZERO_RETURN:
502
str = @"Zero Return error";
504
case SSL_ERROR_WANT_READ:
505
str = @"Want Read Error";
507
case SSL_ERROR_WANT_WRITE:
508
str = @"Want Write Error";
510
case SSL_ERROR_WANT_X509_LOOKUP:
511
str = @"Want X509 Lookup Error";
513
case SSL_ERROR_SYSCALL:
514
str = [NSString stringWithFormat: @"Syscall error %d - %s",
515
e, GSLastErrorStr(e)];
518
str = @"SSL Error: really helpful";
521
str = @"Standard Unix Error: really helpful";
524
NSLog(@"unable to make SSL connection to %@:%@ - %@",
525
address, service, str);
533
- (void) sslDisconnect
537
if (connected == YES)
553
- (void) sslSetCertificate: (NSString*)certFile
554
privateKey: (NSString*)privateKey
555
PEMpasswd: (NSString*)PEMpasswd
557
if (isStandardFile == YES)
559
NSLog(@"Attempt to set ssl certificate for a standard file");
563
* Ensure we have a context to set the certificate for.
567
ctx = SSL_CTX_new(SSLv23_client_method());
569
if ([PEMpasswd length] > 0)
571
SSL_CTX_set_default_passwd_cb_userdata(ctx, (char*)[PEMpasswd cString]);
573
if ([certFile length] > 0)
575
SSL_CTX_use_certificate_file(ctx, [certFile cString], X509_FILETYPE_PEM);
577
if ([privateKey length] > 0)
579
SSL_CTX_use_PrivateKey_file(ctx, [privateKey cString], X509_FILETYPE_PEM);
583
- (void) writeData: (NSData*)item
586
const void *ptr = [item bytes];
587
unsigned int len = [item length];
588
unsigned int pos = 0;
591
if (isNonBlocking == YES)
593
[self setNonBlocking: NO];
597
int toWrite = len - pos;
599
if (toWrite > NETBUF_SIZE)
601
toWrite = NETBUF_SIZE;
605
rval = SSL_write(ssl, (char*)ptr+pos, toWrite);
609
rval = write(descriptor, (char*)ptr+pos, toWrite);
613
if (errno == EAGAIN == errno == EINTR)
626
[NSException raise: NSFileHandleOperationException
627
format: @"unable to write to descriptor - %s",
628
GSLastErrorStr(errno)];