jens@0: // jens@0: // BLIPTest.m jens@0: // MYNetwork jens@0: // jens@0: // Created by Jens Alfke on 5/13/08. jens@0: // Copyright 2008 Jens Alfke. All rights reserved. jens@0: // jens@0: jens@0: #ifndef NDEBUG jens@0: jens@0: jens@0: #import "BLIPRequest.h" jens@0: #import "BLIPProperties.h" jens@0: #import "BLIPConnection.h" jens@1: jens@0: #import "IPAddress.h" jens@0: #import "Target.h" jens@1: #import "CollectionUtils.h" jens@1: #import "Logging.h" jens@1: #import "Test.h" jens@0: jens@26: #import jens@26: #import jens@26: jens@26: @interface TCPEndpoint () jens@26: + (NSString*) describeCert: (SecCertificateRef)cert; jens@26: + (NSString*) describeIdentity: (SecIdentityRef)identity; jens@26: @end jens@0: jens@1: jens@11: #define kListenerHost @"localhost" jens@0: #define kListenerPort 46353 jens@0: #define kSendInterval 0.5 jens@0: #define kNBatchedMessages 20 jens@0: #define kUseCompression YES jens@0: #define kUrgentEvery 4 jens@18: #define kListenerCloseAfter 50 jens@22: #define kClientAcceptCloseRequest YES jens@0: jens@26: #define kListenerUsesSSL YES // Does the listener (server) use an SSL connection? jens@26: #define kListenerRequiresClientCert YES // Does the listener require clients to have an SSL cert? jens@26: #define kClientRequiresSSL YES // Does the client require the listener to use SSL? jens@26: #define kClientUsesSSLCert YES // Does the client use an SSL cert? jens@26: jens@26: jens@26: static SecIdentityRef ChooseIdentity( NSString *prompt ) { jens@26: NSMutableArray *identities = [NSMutableArray array]; jens@26: SecKeychainRef kc; jens@26: SecKeychainCopyDefault(&kc); jens@26: SecIdentitySearchRef search; jens@26: SecIdentitySearchCreate(kc, CSSM_KEYUSE_ANY, &search); jens@26: SecIdentityRef identity; danpreston@37: while (SecIdentitySearchCopyNext(search, &identity) == noErr) { jens@26: [identities addObject: (id)identity]; danpreston@37: CFRelease( identity ); danpreston@37: } jens@29: CFRelease(search); jens@26: Log(@"Found %u identities -- prompting '%@'", identities.count, prompt); jens@26: if (identities.count > 0) { jens@26: SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel]; jens@26: if ([panel runModalForIdentities: identities message: prompt] == NSOKButton) { jens@26: Log(@"Using SSL identity: %@", panel.identity); jens@26: return panel.identity; jens@26: } jens@26: } jens@26: return NULL; jens@26: } jens@0: jens@0: static SecIdentityRef GetClientIdentity(void) { jens@26: return ChooseIdentity(@"Choose an identity for the BLIP Client Test:"); jens@0: } jens@0: jens@0: static SecIdentityRef GetListenerIdentity(void) { jens@26: return ChooseIdentity(@"Choose an identity for the BLIP Listener Test:"); jens@0: } jens@0: jens@0: jens@0: #pragma mark - jens@0: #pragma mark CLIENT TEST: jens@0: jens@0: jens@0: @interface BLIPConnectionTester : NSObject jens@0: { jens@0: BLIPConnection *_conn; jens@0: NSMutableDictionary *_pending; jens@0: } jens@0: jens@0: @end jens@0: jens@0: jens@0: @implementation BLIPConnectionTester jens@0: jens@0: - (id) init jens@0: { jens@0: self = [super init]; jens@0: if (self != nil) { jens@0: Log(@"** INIT %@",self); jens@0: _pending = [[NSMutableDictionary alloc] init]; danpreston@35: IPAddress *addr = [[[IPAddress alloc] initWithHostname: kListenerHost port: kListenerPort] autorelease]; jens@0: _conn = [[BLIPConnection alloc] initToAddress: addr]; jens@0: if( ! _conn ) { jens@0: [self release]; jens@0: return nil; jens@0: } jens@26: if( kClientUsesSSLCert ) { jens@26: [_conn setPeerToPeerIdentity: GetClientIdentity()]; jens@26: } else if( kClientRequiresSSL ) { jens@26: _conn.SSLProperties = $mdict({kTCPPropertySSLAllowsAnyRoot, $true}, jens@26: {(id)kCFStreamSSLPeerName, [NSNull null]}); jens@0: } jens@0: _conn.delegate = self; jens@0: Log(@"** Opening connection..."); jens@0: [_conn open]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: - (void) dealloc jens@0: { jens@0: Log(@"** %@ closing",self); jens@0: [_conn close]; jens@0: [_conn release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: - (void) sendAMessage jens@0: { jens@18: if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) { jens@18: if(_pending.count<100) { jens@63: Log(@"** Sending a message that will fail to be handled..."); jens@63: BLIPRequest *q = [_conn requestWithBody: nil jens@63: properties: $dict({@"Profile", @"BLIPTest/DontHandleMe"}, jens@63: {@"User-Agent", @"BLIPConnectionTester"}, jens@63: {@"Date", [[NSDate date] description]})]; jens@63: BLIPResponse *response = [q send]; jens@63: Assert(response); jens@63: Assert(q.number>0); jens@63: Assert(response.number==q.number); jens@63: [_pending setObject: [NSNull null] forKey: $object(q.number)]; jens@63: response.onComplete = $target(self,responseArrived:); jens@63: jens@18: Log(@"** Sending another %i messages...", kNBatchedMessages); jens@18: for( int i=0; i 12 ) jens@18: q.urgent = YES; jens@18: BLIPResponse *response = [q send]; jens@18: Assert(response); jens@18: Assert(q.number>0); jens@18: Assert(response.number==q.number); jens@18: [_pending setObject: $object(size) forKey: $object(q.number)]; jens@18: response.onComplete = $target(self,responseArrived:); jens@18: } jens@18: } else { jens@18: Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count); jens@11: } jens@18: [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval]; jens@0: } jens@0: } jens@0: jens@0: - (void) responseArrived: (BLIPResponse*)response jens@0: { jens@0: Log(@"********** called responseArrived: %@",response); jens@0: } jens@0: jens@0: - (void) connectionDidOpen: (TCPConnection*)connection jens@0: { jens@0: Log(@"** %@ didOpen",connection); jens@0: [self sendAMessage]; jens@0: } jens@0: - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert jens@0: { jens@26: Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]); jens@0: return peerCert != nil; jens@0: } jens@0: - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error jens@0: { jens@26: Warn(@"** %@ failedToOpen: %@",connection,error); jens@0: CFRunLoopStop(CFRunLoopGetCurrent()); jens@0: } jens@0: - (void) connectionDidClose: (TCPConnection*)connection jens@0: { jens@26: if (connection.error) jens@26: Warn(@"** %@ didClose: %@", connection,connection.error); jens@26: else jens@26: Log(@"** %@ didClose", connection); jens@0: setObj(&_conn,nil); jens@0: [NSObject cancelPreviousPerformRequestsWithTarget: self]; jens@0: CFRunLoopStop(CFRunLoopGetCurrent()); jens@0: } jens@63: - (BOOL) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request jens@0: { jens@0: Log(@"***** %@ received %@",connection,request); jens@2: [request respondWithData: request.body contentType: request.contentType]; jens@63: return YES; jens@0: } jens@0: jens@0: - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response jens@0: { jens@0: Log(@"********** %@ received %@",connection,response); jens@63: id sizeObj = [_pending objectForKey: $object(response.number)]; jens@63: Assert(sizeObj); jens@63: jens@63: if (sizeObj == [NSNull null]) { jens@63: AssertEqual(response.error.domain, BLIPErrorDomain); jens@63: AssertEq(response.error.code, kBLIPError_NotFound); jens@63: } else { jens@63: if( response.error ) jens@63: Warn(@"Got error response: %@",response.error); jens@63: else { jens@63: NSData *body = response.body; jens@63: size_t size = body.length; jens@63: Assert(size<32768); jens@63: const UInt8 *bytes = body.bytes; jens@63: for( size_t i=0; i jens@0: { jens@0: BLIPListener *_listener; jens@18: int _nReceived; jens@0: } jens@0: jens@0: @end jens@0: jens@0: jens@0: @implementation BLIPTestListener jens@0: jens@0: - (id) init jens@0: { jens@0: self = [super init]; jens@0: if (self != nil) { jens@0: _listener = [[BLIPListener alloc] initWithPort: kListenerPort]; jens@0: _listener.delegate = self; jens@0: _listener.pickAvailablePort = YES; jens@0: _listener.bonjourServiceType = @"_bliptest._tcp"; jens@26: if( kListenerUsesSSL ) { jens@26: [_listener setPeerToPeerIdentity: GetListenerIdentity()]; jens@26: if (!kListenerRequiresClientCert) jens@26: [_listener setSSLProperty: $object(kTCPTryAuthenticate) jens@26: forKey: kTCPPropertySSLClientSideAuthentication]; jens@0: } jens@0: Assert( [_listener open] ); jens@0: Log(@"%@ is listening...",self); jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: - (void) dealloc jens@0: { jens@0: Log(@"%@ closing",self); jens@0: [_listener close]; jens@0: [_listener release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection jens@0: { jens@0: Log(@"** %@ accepted %@",self,connection); jens@0: connection.delegate = self; jens@0: } jens@0: jens@0: - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error jens@0: { jens@0: Log(@"** BLIPTestListener failed to open: %@",error); jens@0: } jens@0: jens@0: - (void) listenerDidOpen: (TCPListener*)listener {Log(@"** BLIPTestListener did open");} jens@0: - (void) listenerDidClose: (TCPListener*)listener {Log(@"** BLIPTestListener did close");} jens@0: jens@0: - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address jens@0: { jens@0: Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address); jens@0: return YES; jens@0: } jens@0: jens@0: jens@0: - (void) connectionDidOpen: (TCPConnection*)connection jens@0: { jens@0: Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel); jens@18: _nReceived = 0; jens@0: } jens@0: - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert jens@0: { jens@26: Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]); jens@0: return peerCert != nil || ! kListenerRequiresClientCert; jens@0: } jens@0: - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error jens@0: { jens@0: Log(@"** %@ failedToOpen: %@",connection,error); jens@0: } jens@0: - (void) connectionDidClose: (TCPConnection*)connection jens@0: { jens@26: if (connection.error) jens@26: Warn(@"** %@ didClose: %@", connection,connection.error); jens@26: else jens@26: Log(@"** %@ didClose", connection); jens@0: [connection release]; jens@0: } jens@63: - (BOOL) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request jens@0: { jens@0: Log(@"***** %@ received %@",connection,request); jens@0: jens@63: if ([request.profile isEqualToString: @"BLIPTest/EchoData"]) { jens@63: NSData *body = request.body; jens@63: size_t size = body.length; jens@63: Assert(size<32768); jens@63: const UInt8 *bytes = body.bytes; jens@63: for( size_t i=0; i. All rights reserved. jens@0: jens@0: Redistribution and use in source and binary forms, with or without modification, are permitted jens@0: provided that the following conditions are met: jens@0: jens@0: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@0: and the following disclaimer. jens@0: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions jens@0: and the following disclaimer in the documentation and/or other materials provided with the jens@0: distribution. jens@0: jens@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@0: */