Fixed two CF memory leaks. (Fixes issue #5)
5 // Created by Jens Alfke on 5/13/08.
6 // Copyright 2008 Jens Alfke. All rights reserved.
12 #import "BLIPRequest.h"
13 #import "BLIPProperties.h"
14 #import "BLIPConnection.h"
18 #import "CollectionUtils.h"
22 #import <Security/Security.h>
23 #import <SecurityInterface/SFChooseIdentityPanel.h>
25 @interface TCPEndpoint ()
26 + (NSString*) describeCert: (SecCertificateRef)cert;
27 + (NSString*) describeIdentity: (SecIdentityRef)identity;
31 #define kListenerHost @"localhost"
32 #define kListenerPort 46353
33 #define kSendInterval 0.5
34 #define kNBatchedMessages 20
35 #define kUseCompression YES
36 #define kUrgentEvery 4
37 #define kListenerCloseAfter 50
38 #define kClientAcceptCloseRequest YES
40 #define kListenerUsesSSL YES // Does the listener (server) use an SSL connection?
41 #define kListenerRequiresClientCert YES // Does the listener require clients to have an SSL cert?
42 #define kClientRequiresSSL YES // Does the client require the listener to use SSL?
43 #define kClientUsesSSLCert YES // Does the client use an SSL cert?
46 static SecIdentityRef ChooseIdentity( NSString *prompt ) {
47 NSMutableArray *identities = [NSMutableArray array];
49 SecKeychainCopyDefault(&kc);
50 SecIdentitySearchRef search;
51 SecIdentitySearchCreate(kc, CSSM_KEYUSE_ANY, &search);
52 SecIdentityRef identity;
53 while (SecIdentitySearchCopyNext(search, &identity) == noErr)
54 [identities addObject: (id)identity];
56 Log(@"Found %u identities -- prompting '%@'", identities.count, prompt);
57 if (identities.count > 0) {
58 SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
59 if ([panel runModalForIdentities: identities message: prompt] == NSOKButton) {
60 Log(@"Using SSL identity: %@", panel.identity);
61 return panel.identity;
67 static SecIdentityRef GetClientIdentity(void) {
68 return ChooseIdentity(@"Choose an identity for the BLIP Client Test:");
71 static SecIdentityRef GetListenerIdentity(void) {
72 return ChooseIdentity(@"Choose an identity for the BLIP Listener Test:");
77 #pragma mark CLIENT TEST:
80 @interface BLIPConnectionTester : NSObject <BLIPConnectionDelegate>
82 BLIPConnection *_conn;
83 NSMutableDictionary *_pending;
89 @implementation BLIPConnectionTester
95 Log(@"** INIT %@",self);
96 _pending = [[NSMutableDictionary alloc] init];
97 IPAddress *addr = [[IPAddress alloc] initWithHostname: kListenerHost port: kListenerPort];
98 _conn = [[BLIPConnection alloc] initToAddress: addr];
103 if( kClientUsesSSLCert ) {
104 [_conn setPeerToPeerIdentity: GetClientIdentity()];
105 } else if( kClientRequiresSSL ) {
106 _conn.SSLProperties = $mdict({kTCPPropertySSLAllowsAnyRoot, $true},
107 {(id)kCFStreamSSLPeerName, [NSNull null]});
109 _conn.delegate = self;
110 Log(@"** Opening connection...");
118 Log(@"** %@ closing",self);
124 - (void) sendAMessage
126 if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) {
127 if(_pending.count<100) {
128 Log(@"** Sending another %i messages...", kNBatchedMessages);
129 for( int i=0; i<kNBatchedMessages; i++ ) {
130 size_t size = random() % 32768;
131 NSMutableData *body = [NSMutableData dataWithLength: size];
132 UInt8 *bytes = body.mutableBytes;
133 for( size_t i=0; i<size; i++ )
136 BLIPRequest *q = [_conn requestWithBody: body
137 properties: $dict({@"Content-Type", @"application/octet-stream"},
138 {@"User-Agent", @"BLIPConnectionTester"},
139 {@"Date", [[NSDate date] description]},
140 {@"Size",$sprintf(@"%u",size)})];
142 if( kUseCompression && (random()%2==1) )
144 if( random()%16 > 12 )
146 BLIPResponse *response = [q send];
149 Assert(response.number==q.number);
150 [_pending setObject: $object(size) forKey: $object(q.number)];
151 response.onComplete = $target(self,responseArrived:);
154 Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
156 [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
160 - (void) responseArrived: (BLIPResponse*)response
162 Log(@"********** called responseArrived: %@",response);
165 - (void) connectionDidOpen: (TCPConnection*)connection
167 Log(@"** %@ didOpen",connection);
170 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
172 Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]);
173 return peerCert != nil;
175 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
177 Warn(@"** %@ failedToOpen: %@",connection,error);
178 CFRunLoopStop(CFRunLoopGetCurrent());
180 - (void) connectionDidClose: (TCPConnection*)connection
182 if (connection.error)
183 Warn(@"** %@ didClose: %@", connection,connection.error);
185 Log(@"** %@ didClose", connection);
187 [NSObject cancelPreviousPerformRequestsWithTarget: self];
188 CFRunLoopStop(CFRunLoopGetCurrent());
190 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
192 Log(@"***** %@ received %@",connection,request);
193 [request respondWithData: request.body contentType: request.contentType];
196 - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response
198 Log(@"********** %@ received %@",connection,response);
199 NSNumber *sizeObj = [_pending objectForKey: $object(response.number)];
202 Warn(@"Got error response: %@",response.error);
204 NSData *body = response.body;
205 size_t size = body.length;
207 const UInt8 *bytes = body.bytes;
208 for( size_t i=0; i<size; i++ )
209 AssertEq(bytes[i],i % 256);
210 AssertEq(size,sizeObj.unsignedIntValue);
213 [_pending removeObjectForKey: $object(response.number)];
214 Log(@"Now %u replies pending", _pending.count);
217 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection
219 BOOL response = kClientAcceptCloseRequest;
220 Log(@"***** %@ received a close request; returning %i",connection,response);
228 TestCase(BLIPConnection) {
229 SecKeychainSetUserInteractionAllowed(true);
230 BLIPConnectionTester *tester = [[BLIPConnectionTester alloc] init];
233 [[NSRunLoop currentRunLoop] run];
235 Log(@"** Runloop stopped");
242 #pragma mark LISTENER TEST:
245 @interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
247 BLIPListener *_listener;
254 @implementation BLIPTestListener
260 _listener = [[BLIPListener alloc] initWithPort: kListenerPort];
261 _listener.delegate = self;
262 _listener.pickAvailablePort = YES;
263 _listener.bonjourServiceType = @"_bliptest._tcp";
264 if( kListenerUsesSSL ) {
265 [_listener setPeerToPeerIdentity: GetListenerIdentity()];
266 if (!kListenerRequiresClientCert)
267 [_listener setSSLProperty: $object(kTCPTryAuthenticate)
268 forKey: kTCPPropertySSLClientSideAuthentication];
270 Assert( [_listener open] );
271 Log(@"%@ is listening...",self);
278 Log(@"%@ closing",self);
284 - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection
286 Log(@"** %@ accepted %@",self,connection);
287 connection.delegate = self;
290 - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error
292 Log(@"** BLIPTestListener failed to open: %@",error);
295 - (void) listenerDidOpen: (TCPListener*)listener {Log(@"** BLIPTestListener did open");}
296 - (void) listenerDidClose: (TCPListener*)listener {Log(@"** BLIPTestListener did close");}
298 - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address
300 Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address);
305 - (void) connectionDidOpen: (TCPConnection*)connection
307 Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
310 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
312 Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]);
313 return peerCert != nil || ! kListenerRequiresClientCert;
315 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
317 Log(@"** %@ failedToOpen: %@",connection,error);
319 - (void) connectionDidClose: (TCPConnection*)connection
321 if (connection.error)
322 Warn(@"** %@ didClose: %@", connection,connection.error);
324 Log(@"** %@ didClose", connection);
325 [connection release];
327 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
329 Log(@"***** %@ received %@",connection,request);
330 NSData *body = request.body;
331 size_t size = body.length;
333 const UInt8 *bytes = body.bytes;
334 for( size_t i=0; i<size; i++ )
335 AssertEq(bytes[i],i % 256);
337 AssertEqual([request valueOfProperty: @"Content-Type"], @"application/octet-stream");
338 Assert([request valueOfProperty: @"User-Agent"] != nil);
339 AssertEq((size_t)[[request valueOfProperty: @"Size"] intValue], size);
341 [request respondWithData: body contentType: request.contentType];
343 if( ++ _nReceived == kListenerCloseAfter ) {
344 Log(@"********** Closing BLIPTestListener after %i requests",_nReceived);
349 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection;
351 Log(@"***** %@ received a close request",connection);
355 - (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error
357 Log(@"***** %@'s close request failed: %@",connection,error);
364 TestCase(BLIPListener) {
365 EnableLogTo(BLIP,YES);
366 EnableLogTo(PortMapper,YES);
367 EnableLogTo(Bonjour,YES);
368 SecKeychainSetUserInteractionAllowed(true);
369 BLIPTestListener *listener = [[BLIPTestListener alloc] init];
371 [[NSRunLoop currentRunLoop] run];
381 Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
383 Redistribution and use in source and binary forms, with or without modification, are permitted
384 provided that the following conditions are met:
386 * Redistributions of source code must retain the above copyright notice, this list of conditions
387 and the following disclaimer.
388 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
389 and the following disclaimer in the documentation and/or other materials provided with the
392 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
393 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
394 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
395 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
396 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
397 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
398 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
399 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.