BLIP/BLIPTest.m
author Jens Alfke <jens@mooseyard.com>
Wed Apr 22 16:45:39 2009 -0700 (2009-04-22)
changeset 26 cb9cdf247239
parent 22 8b883753394a
child 29 59689fbdcf77
permissions -rw-r--r--
* Added MYBonjourBrowser and MYBonjourService.
* Added MYPortMapper.
* Added -[TCPEndpoint setPeerToPeerIdentity:].
* Created a static-library target.
     1 //
     2 //  BLIPTest.m
     3 //  MYNetwork
     4 //
     5 //  Created by Jens Alfke on 5/13/08.
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #ifndef NDEBUG
    10 
    11 
    12 #import "BLIPRequest.h"
    13 #import "BLIPProperties.h"
    14 #import "BLIPConnection.h"
    15 
    16 #import "IPAddress.h"
    17 #import "Target.h"
    18 #import "CollectionUtils.h"
    19 #import "Logging.h"
    20 #import "Test.h"
    21 
    22 #import <Security/Security.h>
    23 #import <SecurityInterface/SFChooseIdentityPanel.h>
    24 
    25 @interface TCPEndpoint ()
    26 + (NSString*) describeCert: (SecCertificateRef)cert;
    27 + (NSString*) describeIdentity: (SecIdentityRef)identity;
    28 @end
    29 
    30 
    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
    39 
    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?
    44 
    45 
    46 static SecIdentityRef ChooseIdentity( NSString *prompt ) {
    47     NSMutableArray *identities = [NSMutableArray array];
    48     SecKeychainRef kc;
    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];
    55     Log(@"Found %u identities -- prompting '%@'", identities.count, prompt);
    56     if (identities.count > 0) {
    57         SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
    58         if ([panel runModalForIdentities: identities message: prompt] == NSOKButton) {
    59             Log(@"Using SSL identity: %@", panel.identity);
    60             return panel.identity;
    61         }
    62     }
    63     return NULL;
    64 }
    65 
    66 static SecIdentityRef GetClientIdentity(void) {
    67     return ChooseIdentity(@"Choose an identity for the BLIP Client Test:");
    68 }
    69 
    70 static SecIdentityRef GetListenerIdentity(void) {
    71     return ChooseIdentity(@"Choose an identity for the BLIP Listener Test:");
    72 }
    73 
    74 
    75 #pragma mark -
    76 #pragma mark CLIENT TEST:
    77 
    78 
    79 @interface BLIPConnectionTester : NSObject <BLIPConnectionDelegate>
    80 {
    81     BLIPConnection *_conn;
    82     NSMutableDictionary *_pending;
    83 }
    84 
    85 @end
    86 
    87 
    88 @implementation BLIPConnectionTester
    89 
    90 - (id) init
    91 {
    92     self = [super init];
    93     if (self != nil) {
    94         Log(@"** INIT %@",self);
    95         _pending = [[NSMutableDictionary alloc] init];
    96         IPAddress *addr = [[IPAddress alloc] initWithHostname: kListenerHost port: kListenerPort];
    97         _conn = [[BLIPConnection alloc] initToAddress: addr];
    98         if( ! _conn ) {
    99             [self release];
   100             return nil;
   101         }
   102         if( kClientUsesSSLCert ) {
   103             [_conn setPeerToPeerIdentity: GetClientIdentity()];
   104         } else if( kClientRequiresSSL ) {
   105             _conn.SSLProperties = $mdict({kTCPPropertySSLAllowsAnyRoot, $true},
   106                                         {(id)kCFStreamSSLPeerName, [NSNull null]});
   107         }
   108         _conn.delegate = self;
   109         Log(@"** Opening connection...");
   110         [_conn open];
   111     }
   112     return self;
   113 }
   114 
   115 - (void) dealloc
   116 {
   117     Log(@"** %@ closing",self);
   118     [_conn close];
   119     [_conn release];
   120     [super dealloc];
   121 }
   122 
   123 - (void) sendAMessage
   124 {
   125     if( _conn.status==kTCP_Open || _conn.status==kTCP_Opening ) {
   126         if(_pending.count<100) {
   127             Log(@"** Sending another %i messages...", kNBatchedMessages);
   128             for( int i=0; i<kNBatchedMessages; i++ ) {
   129                 size_t size = random() % 32768;
   130                 NSMutableData *body = [NSMutableData dataWithLength: size];
   131                 UInt8 *bytes = body.mutableBytes;
   132                 for( size_t i=0; i<size; i++ )
   133                     bytes[i] = i % 256;
   134                 
   135                 BLIPRequest *q = [_conn requestWithBody: body
   136                                              properties: $dict({@"Content-Type", @"application/octet-stream"},
   137                                                                {@"User-Agent", @"BLIPConnectionTester"},
   138                                                                {@"Date", [[NSDate date] description]},
   139                                                                {@"Size",$sprintf(@"%u",size)})];
   140                 Assert(q);
   141                 if( kUseCompression && (random()%2==1) )
   142                     q.compressed = YES;
   143                 if( random()%16 > 12 )
   144                     q.urgent = YES;
   145                 BLIPResponse *response = [q send];
   146                 Assert(response);
   147                 Assert(q.number>0);
   148                 Assert(response.number==q.number);
   149                 [_pending setObject: $object(size) forKey: $object(q.number)];
   150                 response.onComplete = $target(self,responseArrived:);
   151             }
   152         } else {
   153             Warn(@"There are %u pending messages; waiting for the listener to catch up...",_pending.count);
   154         }
   155         [self performSelector: @selector(sendAMessage) withObject: nil afterDelay: kSendInterval];
   156     }
   157 }
   158 
   159 - (void) responseArrived: (BLIPResponse*)response
   160 {
   161     Log(@"********** called responseArrived: %@",response);
   162 }
   163 
   164 - (void) connectionDidOpen: (TCPConnection*)connection
   165 {
   166     Log(@"** %@ didOpen",connection);
   167     [self sendAMessage];
   168 }
   169 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
   170 {
   171     Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]);
   172     return peerCert != nil;
   173 }
   174 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
   175 {
   176     Warn(@"** %@ failedToOpen: %@",connection,error);
   177     CFRunLoopStop(CFRunLoopGetCurrent());
   178 }
   179 - (void) connectionDidClose: (TCPConnection*)connection
   180 {
   181     if (connection.error)
   182         Warn(@"** %@ didClose: %@", connection,connection.error);
   183     else
   184         Log(@"** %@ didClose", connection);
   185     setObj(&_conn,nil);
   186     [NSObject cancelPreviousPerformRequestsWithTarget: self];
   187     CFRunLoopStop(CFRunLoopGetCurrent());
   188 }
   189 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
   190 {
   191     Log(@"***** %@ received %@",connection,request);
   192     [request respondWithData: request.body contentType: request.contentType];
   193 }
   194 
   195 - (void) connection: (BLIPConnection*)connection receivedResponse: (BLIPResponse*)response
   196 {
   197     Log(@"********** %@ received %@",connection,response);
   198     NSNumber *sizeObj = [_pending objectForKey: $object(response.number)];
   199 
   200     if( response.error )
   201         Warn(@"Got error response: %@",response.error);
   202     else {
   203         NSData *body = response.body;
   204         size_t size = body.length;
   205         Assert(size<32768);
   206         const UInt8 *bytes = body.bytes;
   207         for( size_t i=0; i<size; i++ )
   208             AssertEq(bytes[i],i % 256);
   209         AssertEq(size,sizeObj.unsignedIntValue);
   210     }
   211     Assert(sizeObj);
   212     [_pending removeObjectForKey: $object(response.number)];
   213     Log(@"Now %u replies pending", _pending.count);
   214 }
   215 
   216 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection
   217 {
   218     BOOL response = kClientAcceptCloseRequest;
   219     Log(@"***** %@ received a close request; returning %i",connection,response);
   220     return response;
   221 }
   222 
   223 
   224 @end
   225 
   226 
   227 TestCase(BLIPConnection) {
   228     SecKeychainSetUserInteractionAllowed(true);
   229     BLIPConnectionTester *tester = [[BLIPConnectionTester alloc] init];
   230     CAssert(tester);
   231     
   232     [[NSRunLoop currentRunLoop] run];
   233     
   234     Log(@"** Runloop stopped");
   235     [tester release];
   236 }
   237 
   238 
   239 
   240 
   241 #pragma mark LISTENER TEST:
   242 
   243 
   244 @interface BLIPTestListener : NSObject <TCPListenerDelegate, BLIPConnectionDelegate>
   245 {
   246     BLIPListener *_listener;
   247     int _nReceived;
   248 }
   249 
   250 @end
   251 
   252 
   253 @implementation BLIPTestListener
   254 
   255 - (id) init
   256 {
   257     self = [super init];
   258     if (self != nil) {
   259         _listener = [[BLIPListener alloc] initWithPort: kListenerPort];
   260         _listener.delegate = self;
   261         _listener.pickAvailablePort = YES;
   262         _listener.bonjourServiceType = @"_bliptest._tcp";
   263         if( kListenerUsesSSL ) {
   264             [_listener setPeerToPeerIdentity: GetListenerIdentity()];
   265             if (!kListenerRequiresClientCert)
   266                 [_listener setSSLProperty: $object(kTCPTryAuthenticate) 
   267                                    forKey: kTCPPropertySSLClientSideAuthentication];
   268         }
   269         Assert( [_listener open] );
   270         Log(@"%@ is listening...",self);
   271     }
   272     return self;
   273 }
   274 
   275 - (void) dealloc
   276 {
   277     Log(@"%@ closing",self);
   278     [_listener close];
   279     [_listener release];
   280     [super dealloc];
   281 }
   282 
   283 - (void) listener: (TCPListener*)listener didAcceptConnection: (TCPConnection*)connection
   284 {
   285     Log(@"** %@ accepted %@",self,connection);
   286     connection.delegate = self;
   287 }
   288 
   289 - (void) listener: (TCPListener*)listener failedToOpen: (NSError*)error
   290 {
   291     Log(@"** BLIPTestListener failed to open: %@",error);
   292 }
   293 
   294 - (void) listenerDidOpen: (TCPListener*)listener   {Log(@"** BLIPTestListener did open");}
   295 - (void) listenerDidClose: (TCPListener*)listener   {Log(@"** BLIPTestListener did close");}
   296 
   297 - (BOOL) listener: (TCPListener*)listener shouldAcceptConnectionFrom: (IPAddress*)address
   298 {
   299     Log(@"** %@ shouldAcceptConnectionFrom: %@",self,address);
   300     return YES;
   301 }
   302 
   303 
   304 - (void) connectionDidOpen: (TCPConnection*)connection
   305 {
   306     Log(@"** %@ didOpen [SSL=%@]",connection,connection.actualSecurityLevel);
   307     _nReceived = 0;
   308 }
   309 - (BOOL) connection: (TCPConnection*)connection authorizeSSLPeer: (SecCertificateRef)peerCert
   310 {
   311     Log(@"** %@ authorizeSSLPeer: %@",self, [TCPEndpoint describeCert:peerCert]);
   312     return peerCert != nil || ! kListenerRequiresClientCert;
   313 }
   314 - (void) connection: (TCPConnection*)connection failedToOpen: (NSError*)error
   315 {
   316     Log(@"** %@ failedToOpen: %@",connection,error);
   317 }
   318 - (void) connectionDidClose: (TCPConnection*)connection
   319 {
   320     if (connection.error)
   321         Warn(@"** %@ didClose: %@", connection,connection.error);
   322     else
   323         Log(@"** %@ didClose", connection);
   324     [connection release];
   325 }
   326 - (void) connection: (BLIPConnection*)connection receivedRequest: (BLIPRequest*)request
   327 {
   328     Log(@"***** %@ received %@",connection,request);
   329     NSData *body = request.body;
   330     size_t size = body.length;
   331     Assert(size<32768);
   332     const UInt8 *bytes = body.bytes;
   333     for( size_t i=0; i<size; i++ )
   334         AssertEq(bytes[i],i % 256);
   335     
   336     AssertEqual([request valueOfProperty: @"Content-Type"], @"application/octet-stream");
   337     Assert([request valueOfProperty: @"User-Agent"] != nil);
   338     AssertEq((size_t)[[request valueOfProperty: @"Size"] intValue], size);
   339 
   340     [request respondWithData: body contentType: request.contentType];
   341     
   342     if( ++ _nReceived == kListenerCloseAfter ) {
   343         Log(@"********** Closing BLIPTestListener after %i requests",_nReceived);
   344         [connection close];
   345     }
   346 }
   347 
   348 - (BOOL) connectionReceivedCloseRequest: (BLIPConnection*)connection;
   349 {
   350     Log(@"***** %@ received a close request",connection);
   351     return YES;
   352 }
   353 
   354 - (void) connection: (BLIPConnection*)connection closeRequestFailedWithError: (NSError*)error
   355 {
   356     Log(@"***** %@'s close request failed: %@",connection,error);
   357 }
   358 
   359 
   360 @end
   361 
   362 
   363 TestCase(BLIPListener) {
   364     EnableLogTo(BLIP,YES);
   365     EnableLogTo(PortMapper,YES);
   366     EnableLogTo(Bonjour,YES);
   367     SecKeychainSetUserInteractionAllowed(true);
   368     BLIPTestListener *listener = [[BLIPTestListener alloc] init];
   369     
   370     [[NSRunLoop currentRunLoop] run];
   371     
   372     [listener release];
   373 }
   374 
   375 
   376 #endif
   377 
   378 
   379 /*
   380  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   381  
   382  Redistribution and use in source and binary forms, with or without modification, are permitted
   383  provided that the following conditions are met:
   384  
   385  * Redistributions of source code must retain the above copyright notice, this list of conditions
   386  and the following disclaimer.
   387  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   388  and the following disclaimer in the documentation and/or other materials provided with the
   389  distribution.
   390  
   391  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   392  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   393  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   394  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   395  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   396   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   397  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   398  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   399  */