Fixed: The -connection:failedToOpen: delegate method wasn't being called.
     5 //  Created by Jens Alfke on 5/10/08.
 
     6 //  Copyright 2008 Jens Alfke. All rights reserved.
 
     7 //  Portions based on TCPServer class from Apple's "CocoaEcho" sample code.
 
     9 #import "TCPListener.h"
 
    10 #import "TCPConnection.h"
 
    14 #import "ExceptionUtils.h"
 
    17 #include <sys/socket.h>
 
    18 #include <netinet/in.h>
 
    22 static void TCPListenerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, 
 
    23                                       CFDataRef address, const void *data, void *info);
 
    25 @interface TCPListener()
 
    26 @property BOOL bonjourPublished;
 
    27 @property NSInteger bonjourError;
 
    28 - (void) _updateTXTRecord;
 
    32 @implementation TCPListener
 
    35 - (id) initWithPort: (UInt16)port
 
    48     LogTo(TCP,@"DEALLOC %@",self);
 
    53 @synthesize delegate=_delegate, port=_port, useIPv6=_useIPv6,
 
    54             bonjourServiceType=_bonjourServiceType, bonjourServiceName=_bonjourServiceName,
 
    55             bonjourPublished=_bonjourPublished, bonjourError=_bonjourError,
 
    56             pickAvailablePort=_pickAvailablePort;
 
    59 - (NSString*) description
 
    61     return $sprintf(@"%@[port %hu]",self.class,_port);
 
    65 // Stores the last error from CFSocketCreate or CFSocketSetAddress into *ouError.
 
    66 static void* getLastCFSocketError( NSError **outError ) {
 
    68         *outError = [NSError errorWithDomain: NSPOSIXErrorDomain code: errno userInfo: nil];
 
    72 // Closes a socket (if it's not already NULL), and returns NULL to assign to it.
 
    73 static CFSocketRef closeSocket( CFSocketRef socket ) {
 
    75         CFSocketInvalidate(socket);
 
    81 // opens a socket of a given protocol, either ipv4 or ipv6.
 
    82 - (CFSocketRef) _openProtocol: (SInt32) protocolFamily 
 
    83                       address: (struct sockaddr*)address
 
    84                         error: (NSError**)error
 
    86     CFSocketContext socketCtxt = {0, self, NULL, NULL, NULL};
 
    87     CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP,
 
    88                                         kCFSocketAcceptCallBack, &TCPListenerAcceptCallBack, &socketCtxt);
 
    90         return getLastCFSocketError(error);   // CFSocketCreate leaves error code in errno
 
    93     setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
 
    95     NSData *addressData = [NSData dataWithBytes:address length:address->sa_len];
 
    96     if (kCFSocketSuccess != CFSocketSetAddress(socket, (CFDataRef)addressData)) {
 
    97         getLastCFSocketError(error);
 
    98         return closeSocket(socket);
 
   100     // set up the run loop source for the socket
 
   101     CFRunLoopRef cfrl = CFRunLoopGetCurrent();
 
   102     CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
 
   103     CFRunLoopAddSource(cfrl, source, kCFRunLoopCommonModes);
 
   108 - (BOOL) _failedToOpen: (NSError*)error
 
   110     LogTo(TCP,@"%@ failed to open: %@",self,error);
 
   111     [self tellDelegate: @selector(listener:failedToOpen:) withObject: error];
 
   116 - (BOOL) open: (NSError**)outError 
 
   118     // set up the IPv4 endpoint; if port is 0, this will cause the kernel to choose a port for us
 
   120         struct sockaddr_in addr4;
 
   121         memset(&addr4, 0, sizeof(addr4));
 
   122         addr4.sin_len = sizeof(addr4);
 
   123         addr4.sin_family = AF_INET;
 
   124         addr4.sin_port = htons(_port);
 
   125         addr4.sin_addr.s_addr = htonl(INADDR_ANY);
 
   128         _ipv4socket = [self _openProtocol: PF_INET address: (struct sockaddr*)&addr4 error: &error];
 
   129         if( ! _ipv4socket ) {
 
   130             if( error.code==EADDRINUSE && _pickAvailablePort && _port<0xFFFF ) {
 
   131                 LogTo(BLIPVerbose,@"%@: port busy, trying %hu...",self,_port+1);
 
   132                 self.port += 1;        // try the next port
 
   134                 if( outError ) *outError = error;
 
   135                 return [self _failedToOpen: error];
 
   138     }while( ! _ipv4socket );
 
   141         // now that the binding was successful, we get the port number 
 
   142         NSData *addr = [(NSData *)CFSocketCopyAddress(_ipv4socket) autorelease];
 
   143         const struct sockaddr_in *addr4 = addr.bytes;
 
   144         self.port = ntohs(addr4->sin_port);
 
   148         // set up the IPv6 endpoint
 
   149         struct sockaddr_in6 addr6;
 
   150         memset(&addr6, 0, sizeof(addr6));
 
   151         addr6.sin6_len = sizeof(addr6);
 
   152         addr6.sin6_family = AF_INET6;
 
   153         addr6.sin6_port = htons(_port);
 
   154         memcpy(&(addr6.sin6_addr), &in6addr_any, sizeof(addr6.sin6_addr));
 
   156         _ipv6socket = [self _openProtocol: PF_INET6 address: (struct sockaddr*)&addr6 error: outError];
 
   157         if( ! _ipv6socket ) {
 
   158             _ipv4socket = closeSocket(_ipv4socket);
 
   159             return [self _failedToOpen: *outError];
 
   164     if( _bonjourServiceType && !_netService) {
 
   165         // instantiate the NSNetService object that will advertise on our behalf.
 
   166         _netService = [[NSNetService alloc] initWithDomain: @"local." 
 
   167                                                       type: _bonjourServiceType
 
   168                                                       name: _bonjourServiceName ?:@""
 
   171             [_netService setDelegate:self];
 
   172             if( _bonjourTXTRecord )
 
   173                 [self _updateTXTRecord];
 
   174             [_netService publish];
 
   176             self.bonjourError = -1;
 
   177             Warn(@"%@: Failed to create NSNetService",self);
 
   181     LogTo(TCP,@"%@ is open",self);
 
   182     [self tellDelegate: @selector(listenerDidOpen:) withObject: nil];
 
   188     return [self open: nil];
 
   197             [_netService release];
 
   199             self.bonjourPublished = NO;
 
   201         self.bonjourError = 0;
 
   203         _ipv4socket = closeSocket(_ipv4socket);
 
   204         _ipv6socket = closeSocket(_ipv6socket);
 
   206         LogTo(BLIP,@"%@ is closed",self);
 
   207         [self tellDelegate: @selector(listenerDidClose:) withObject: nil];
 
   214     return _ipv4socket != NULL;
 
   219 #pragma mark ACCEPTING CONNECTIONS:
 
   222 @synthesize connectionClass = _connectionClass;
 
   225 - (BOOL) acceptConnection: (CFSocketNativeHandle)socket
 
   227     IPAddress *addr = [IPAddress addressOfSocket: socket];
 
   230     if( [_delegate respondsToSelector: @selector(listener:shouldAcceptConnectionFrom:)]
 
   231        && ! [_delegate listener: self shouldAcceptConnectionFrom: addr] )
 
   234     Assert(_connectionClass);
 
   235     TCPConnection *conn = [[self.connectionClass alloc] initIncomingFromSocket: socket
 
   240     if( _sslProperties ) {
 
   241         conn.SSLProperties = _sslProperties;
 
   242         [conn setSSLProperty: $true forKey: (id)kCFStreamSSLIsServer];
 
   245     [self tellDelegate: @selector(listener:didAcceptConnection:) withObject: conn];
 
   250 static void TCPListenerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) 
 
   252     TCPListener *server = (TCPListener *)info;
 
   253     if (kCFSocketAcceptCallBack == type) { 
 
   254         CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
 
   257             accepted = [server acceptConnection: nativeSocketHandle];
 
   258         }catchAndReport(@"TCPListenerAcceptCallBack");
 
   260             close(nativeSocketHandle);
 
   266 #pragma mark BONJOUR:
 
   269 - (NSDictionary*) bonjourTXTRecord
 
   271     return _bonjourTXTRecord;
 
   274 - (void) setBonjourTXTRecord: (NSDictionary*)txt
 
   276     if( ifSetObj(&_bonjourTXTRecord,txt) )
 
   277         [self _updateTXTRecord];
 
   280 - (void) _updateTXTRecord
 
   284         if( _bonjourTXTRecord ) {
 
   285             data = [NSNetService dataFromTXTRecordDictionary: _bonjourTXTRecord];
 
   287                 LogTo(BLIP,@"%@: Set %u-byte TXT record", self,data.length);
 
   289                 Warn(@"TCPListener: Couldn't convert txt dict to data: %@",_bonjourTXTRecord);
 
   292         [_netService setTXTRecordData: data];
 
   297 - (void)netServiceWillPublish:(NSNetService *)sender
 
   299     LogTo(BLIP,@"%@: Advertising %@",self,sender);
 
   300     self.bonjourPublished = YES;
 
   303 - (void)netService:(NSNetService *)sender didNotPublish:(NSDictionary *)errorDict
 
   305     self.bonjourError = [[errorDict objectForKey:NSNetServicesErrorCode] intValue];
 
   306     LogTo(BLIP,@"%@: Failed to advertise %@: error %i",self,sender,self.bonjourError);
 
   307     [_netService release];
 
   311 - (void)netServiceDidStop:(NSNetService *)sender
 
   313     LogTo(BLIP,@"%@: Stopped advertising %@",self,sender);
 
   314     self.bonjourPublished = NO;
 
   315     [_netService release];
 
   325  Copyright (c) 2008, Jens Alfke <jens@mooseyard.com>. All rights reserved.
 
   327  Redistribution and use in source and binary forms, with or without modification, are permitted
 
   328  provided that the following conditions are met:
 
   330  * Redistributions of source code must retain the above copyright notice, this list of conditions
 
   331  and the following disclaimer.
 
   332  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
 
   333  and the following disclaimer in the documentation and/or other materials provided with the
 
   336  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
 
   337  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
 
   338  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
 
   339  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 
   340  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 
   341   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
 
   342  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
 
   343  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.