/** Implementation of a port based on BSD sockets Copyright (C) 2002 Free Software Foundation, Inc. Written by: Jonathan Gapen Created: December 2002 This file is part of the GNUstep Base Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. NSSocketPort class reference $Date$ $Revision$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef __MINGW__ #include #include #include #include #include #include #else #include #include #define close closesocket #endif /* __MINGW__ */ /** *

* NSSocketPort on MacOS X(tm) is a concrete subclass of * NSPort which implements Distributed Objects communication between * hosts on a network. However, the GNUstep distributed objects system's * NSPort class uses TCP/IP for all of its communication. The GNUstep * NSSocketPort, then, is useful as a convenient method to * create and encapsulate BSD sockets: *

* * int fileDesc; * NSFileHandle *smtpHandle; * * fileDesc = [[NSSocketPort alloc] initRemoteWithTCPPort: 25 * host: @"mail.example.com"]; * smtpHandle = [[NSFileHandle alloc] initWithFileDescriptor: fileDesc]; * */ @implementation NSSocketPort + (void) initialize { if (self == [NSSocketPort class]) { [self setVersion: 1]; } } /** * Initialize the receiver with a local socket to accept TCP connections * on a non-conflicting port number chosen by the system. */ - (id) init { return [self initWithTCPPort: 0]; } - (void) dealloc { if (_socket > -1) { NSDebugMLLog(@"NSSocketPort", @"closing socket descriptor %d", _socket); close(_socket); } } /** * Initialize the receiver as a local socket to accept connections on * TCP port portNumber. If portNumber is zero, * the system will chose a non-conflicting port number.
* NOTE: This method currently does not support IPv6 connections. */ - (id) initWithTCPPort: (unsigned short)portNumber { struct sockaddr_in sa; NSData *saData; /* Clear memory, as recommended. */ memset(&sa, 0, sizeof(struct sockaddr_in)); sa.sin_len = sizeof(struct sockaddr_in); sa.sin_family = PF_INET; sa.sin_port = htons(portNumber); sa.sin_addr.s_addr = INADDR_ANY; saData = [NSData dataWithBytes: &sa length: sizeof(struct sockaddr_in)]; if (saData == nil) { RELEASE(self); return nil; } return [self initWithProtocolFamily: PF_INET socketType: SOCK_STREAM protocol: 0 address: saData]; } /** * Initialize the receiver as a local socket to accept connections on a * socket of type with the protocol from the protocol * family family. The addrData should contain a copy * of the protocol family-specific address data in an NSData object. */ - (id) initWithProtocolFamily: (int)family socketType: (int)type protocol: (int)protocol address: (NSData *)addrData { int s = -1; if (addrData == nil) { NSDebugMLLog(@"NSSocketPort", @"Nil value passed for address."); goto iWPFAFailed; } s = socket(family, type, protocol); if (s == -1) { NSLog(@"socket: %s", GSLastErrorStr(errno)); goto iWPFAFailed; } if (bind(s, (struct sockaddr *)[addrData bytes], [addrData length]) == -1) { NSLog(@"bind: %s", GSLastErrorStr(errno)); goto iWPFAFailed; } if (listen(s, SOMAXCONN) == -1) { NSLog(@"listen: %s", GSLastErrorStr(errno)); goto iWPFAFailed; } return [self initWithProtocolFamily: family socketType: type protocol: protocol socket: (NSSocketNativeHandle)s]; iWPFAFailed: if (s > -1) close(s); RELEASE(self); return nil; } /** * Initialize the receiver with socket, the platform-native handle * to a previously initialized listen-mode socket of type type * with the protocol protocol from the protocol family * family.
* The receiver will close the socket upon deallocation. */ - (id) initWithProtocolFamily: (int)family socketType: (int)type protocol: (int)protocol socket: (NSSocketNativeHandle)socket { _protocolFamily = family; _socketType = type; _protocol = protocol; _socket = socket; return self; } /** * Initialize the receiver to connect to a remote TCP socket on port * portNumber of host hostname. The receiver delays * initiation of the connection until it has data to send.
* NOTE: This method currently does not support IPv6 connections. */ - (id) initRemoteWithTCPPort: (unsigned short)portNumber host: (NSString *)hostname { struct sockaddr_in sa; const char *address; NSData *addrData; address = [[[NSHost hostWithName: hostname] address] cString]; if (address == NULL) { RELEASE(self); return nil; } /* Clear memory, as recommended. */ memset(&sa, 0, sizeof(struct sockaddr_in)); sa.sin_len = sizeof(struct sockaddr_in); sa.sin_family = PF_INET; sa.sin_port = htons(portNumber); sa.sin_addr.s_addr = inet_addr(address); addrData = [NSData dataWithBytes: &sa length: sizeof(struct sockaddr_in)]; if (addrData == nil) { RELEASE(self); return nil; } return [self initRemoteWithProtocolFamily: PF_INET socketType: SOCK_STREAM protocol: 0 address: addrData]; } /** * Initialize the receiver to connect to a remote socket of type * with protocol from the protocol family family. The * addrData should contain a copy of the protocol family-specific * address data in an NSData object. */ - (id) initRemoteWithProtocolFamily: (int)family socketType: (int)type protocol: (int)protocol address: (NSData *)addrData { if (addrData == nil) { NSDebugMLLog(@"NSSocketPort", @"Nil value passed for address."); RELEASE(self); return nil; } _socket = socket(family, type, protocol); if (_socket == -1) { NSLog(@"socket: %s", GSLastErrorStr(errno)); RELEASE(self); return nil; } _protocolFamily = family; _socketType = type; _protocol = protocol; _remoteAddrData = RETAIN(addrData); return self; } /** * Return the protocol family-specific socket address in an NSData object. */ - (NSData *) address { char sa[SOCK_MAXADDRLEN]; int len = SOCK_MAXADDRLEN; if (_remoteAddrData != nil) { return _remoteAddrData; } else if (getsockname(_socket, (struct sockaddr *)&sa, &len) == 0) { return [NSData dataWithBytes: &sa length: len]; } else { NSLog(@"getsockname: %s", GSLastErrorStr(errno)); return nil; } } /** * Return the socket protocol. */ - (int) protocol { return _protocol; } /** * Return the socket protocol family. */ - (int) protocolFamily { return _protocolFamily; } /** * Return the platform-native socket handle. */ - (NSSocketNativeHandle) socket { return _socket; } /** * Return the socket type. */ - (int) socketType { return _socketType; } /* Concrete NSPort method implementations. */ - (void) invalidate { /* Sockets don't close when the connection drops, they time out. Invalidation is not possible; the caller must notice the error. */ return; } /* Experimentation */ - (void) doesNotRecognizeSelector: (SEL)aSelector { NSDebugLog(@"NSSocketPort", @"NSSocketPort does not recognize selector %@\n", NSStringFromSelector(aSelector)); } /* NSCopying */ /** * FIXME: The Apple documentation does not explain what it means to copy an * NSSocketPort and I do not have access to a MacOS X system to check. */ - (id) copyWithZone: (NSZone *)zone { if (zone == NULL) zone = NSDefaultMallocZone(); if (NSShouldRetainWithZone(self, zone) == YES) return RETAIN(self); else { NSSocketPort *copy = NSAllocateObject([self class], 0, zone); if (copy != nil) { /* Insulate against NSPort changes. */ [copy setDelegate: [self delegate]]; copy->_socket = dup(_socket); copy->_protocolFamily = _protocolFamily; copy->_socketType = _socketType; copy->_protocol = _protocol; _remoteAddrData = [_remoteAddrData copyWithZone: zone]; } return RETAIN(copy); } } /* NSCoding */ - (void) encodeWithCoder: (NSCoder *)encoder { NSParameterAssert([encoder isKindOfClass: [NSPortCoder class]]); NSDebugMLLog(@"NSSocketPort", @"called"); } - (id) initWithCoder: (NSCoder *)decoder { NSParameterAssert([decoder isKindOfClass: [NSPortCoder class]]); NSDebugMLLog(@"NSSocketPort", @"called"); return nil; } @end