From 87e9935a0bdea7cdf60a9342c49f58ae2f8b3313 Mon Sep 17 00:00:00 2001 From: Andrew McCallum Date: Fri, 1 Mar 1996 16:06:57 +0000 Subject: [PATCH] New file. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@1022 72102866-910b-0410-8b05-ffd578937521 --- Headers/gnustep/base/TcpPort.h | 76 ++++ Headers/gnustep/base/UdpPort.h | 59 +++ Source/TcpPort.m | 632 +++++++++++++++++++++++++++++++++ Source/UdpPort.m | 423 ++++++++++++++++++++++ Source/objects/TcpPort.h | 76 ++++ Source/objects/UdpPort.h | 59 +++ 6 files changed, 1325 insertions(+) create mode 100644 Headers/gnustep/base/TcpPort.h create mode 100644 Headers/gnustep/base/UdpPort.h create mode 100644 Source/TcpPort.m create mode 100644 Source/UdpPort.m create mode 100644 Source/objects/TcpPort.h create mode 100644 Source/objects/UdpPort.h diff --git a/Headers/gnustep/base/TcpPort.h b/Headers/gnustep/base/TcpPort.h new file mode 100644 index 000000000..c9182423c --- /dev/null +++ b/Headers/gnustep/base/TcpPort.h @@ -0,0 +1,76 @@ +/* Interface for stream based on TCP sockets + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: February 1996 + + This file is part of the GNU Objective C Class 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __TcpPort_h__OBJECTS_INCLUDE +#define __TcpPort_h__OBJECTS_INCLUDE + +#include +#include +#include +#include +#include +#include + +@interface TcpPacket : Packet +@end + +@interface TcpInPort : InPort +{ + int _socket; + struct sockaddr_in _address; + fd_set active_fd_set; + NSMapTable *client_sock_2_out_port; + NSMapTable *client_sock_2_packet; +} + ++ newForReceivingFromPortNumber: (unsigned short)n; ++ newForReceivingFromRegisteredName: (id )name; + +/* Get a packet from the net and return it. If no packet is received + within MILLISECONDS, then return nil. The caller is responsible + for releasing the packet. */ +- receivePacketWithTimeout: (int)milliseconds; + +- (id ) connectedOutPorts; + +- (void) checkConnection; + +@end + +@interface TcpOutPort : OutPort +{ + int _socket; + struct sockaddr_in _address; + id connected_in_port; +} + ++ newForSendingToPortNumber: (unsigned short)n + onHost: (id )hostname; ++ newForSendingToRegisteredName: (id )name + onHost: (id )hostname; +- (BOOL) sendPacket: packet withTimeout: (int)milliseconds; + +@end + +#endif /* __TcpPort_h__OBJECTS_INCLUDE */ + diff --git a/Headers/gnustep/base/UdpPort.h b/Headers/gnustep/base/UdpPort.h new file mode 100644 index 000000000..e15878df7 --- /dev/null +++ b/Headers/gnustep/base/UdpPort.h @@ -0,0 +1,59 @@ +/* Interface for socket-based port object for use with Connection + Copyright (C) 1994, 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: July 1994 + + This file is part of the GNU Objective C Class 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SocketPort_h_INCLUDE_GNU +#define __SocketPort_h_INCLUDE_GNU + +#include +#include +#include +#ifndef WIN32 +# include +# include +#endif /* !WIN32 */ + +@interface UdpInPort : InPort +{ + int _socket; + struct sockaddr_in _address; +} + +- (int) portNumber; +- (int) socket; + +@end + +@interface UdpOutPort : OutPort +{ + struct sockaddr_in _address; +} + +- (int) portNumber; +- (id ) hostname; + +@end + +@interface UdpPacket : Packet +@end + +#endif /* __SocketPort_h_INCLUDE_GNU */ diff --git a/Source/TcpPort.m b/Source/TcpPort.m new file mode 100644 index 000000000..4ac0ec0c0 --- /dev/null +++ b/Source/TcpPort.m @@ -0,0 +1,632 @@ +/* Implementation of network port object based on TCP sockets + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: February 1996 + + This file is part of the GNU Objective C Class 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* TODO: + Make the sockets non-blocking. + */ + +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#include +#endif + +#define debug_tcp_port 1 + +@interface TcpInPort (Private) +- (int) _socket; +- (void) _addOutPort: p; +- (void) _connectedOutPortInvalidated: p; +@end + +@interface TcpOutPort (Private) +- (int) _socket; +- _initWithSocket: (int)s inPort: ip; ++ newWithAcceptedSocket: (int)s inPort: p; +@end + +@interface TcpPacket (Private) +- (int) _fillFromSocket: (int)s; +- (void) _writeToSocket: (int)s; ++ (int) readPacketSizeFromSocket: (int)s; +- _initForReceivingWithSize: (int)s replyPort: p; +@end + + +/* Our current, sad excuse for a name server. */ + +static unsigned short +name_2_port_number (const char *name) +{ + unsigned int ret = 0; + unsigned int ctr = 0; + + while (*name) + { + ret ^= *name++ << ctr; + ctr = (ctr + 1) % sizeof (void *); + } + return (ret % (65535 - IPPORT_USERRESERVED - 1)) + IPPORT_USERRESERVED; + /* return strlen (name) + IPPORT_USERRESERVED; */ +} + + + +/* Both TcpInPort's and TcpOutPort's are entered in this maptable. */ + +static NSMapTable *socket_2_port; +static void +init_socket_2_port () +{ + socket_2_port = + NSCreateMapTable (NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); +} + + + +@implementation TcpInPort + +static NSMapTable* port_number_2_port; + ++ (void) initialize +{ + if (self == [TcpInPort class]) + port_number_2_port = + NSCreateMapTable (NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); + if (!socket_2_port) + init_socket_2_port (); +} + +/* this is the designated initializer. */ ++ newForReceivingFromPortNumber: (unsigned short)n +{ + TcpInPort *p; + int sock; + + if ((p = (id) NSMapGet (port_number_2_port, (void*)((int)n)))) + return p; + + /* Create the socket. */ + sock = socket (AF_INET, SOCK_STREAM, 0); + if (sock < 0) + { + perror ("socket"); + abort (); + } + + /* Create the port object. */ + p = [[TcpInPort alloc] init]; + p->_socket = sock; + NSMapInsert (socket_2_port, (void*)sock, self); + + /* Give the socket a name. */ + p->_address.sin_family = AF_INET; + p->_address.sin_addr.s_addr = htonl (INADDR_ANY); + p->_address.sin_port = htons (n); + if (bind (sock, (struct sockaddr*) &(p->_address), sizeof (p->_address)) < 0) + { + perror ("bind"); + abort (); + } + + /* Set it up to accept connections, let 10 pending connections queue */ + if (listen (sock, 10) < 0) + { + perror ("listen"); + abort (); + } + + /* Initialize the set of active sockets. */ + FD_ZERO (&(p->active_fd_set)); + FD_SET (sock, &(p->active_fd_set)); + + /* Initializer the tables. */ + p->client_sock_2_out_port = + NSCreateMapTable (NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); + p->client_sock_2_packet = + NSCreateMapTable (NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); + + /* Record the new port in the table. */ + NSMapInsert (port_number_2_port, (void*)(int)n, p); + + return p; +} + ++ newForReceivingFromRegisteredName: (id )name +{ + return [self newForReceivingFromPortNumber: + name_2_port_number ([name cStringNoCopy])]; +} + ++ newForReceiving +{ + return [self newForReceivingFromPortNumber: 0]; +} + +- (id ) connectedOutPorts +{ + NSMapEnumerator me = NSEnumerateMapTable (client_sock_2_out_port); + int count = NSCountMapTable (client_sock_2_out_port); + int sock; + id out_port; + id out_ports[count]; + int i; + for (i = 0; + NSNextMapEnumeratorPair (&me, (void*)&sock, (void*)&out_port); + i++) + out_ports[i] = out_port; + return [[[Array alloc] initWithObjects: out_ports count: count] + autorelease]; +} + +- receivePacketWithTimeout: (int)milliseconds +{ + static int fd_index = 0; + fd_set read_fd_set; + struct sockaddr_in clientname; + struct timeval timeout; + void *select_timeout; + int sel_ret; + + /* If MILLISECONDS is less than 0, wait forever. */ + if (milliseconds >= 0) + { + timeout.tv_sec = milliseconds / 1000; + timeout.tv_usec = (milliseconds % 1000) * 1000; + select_timeout = &timeout; + } + else + select_timeout = NULL; + + for (;;) + { + read_fd_set = active_fd_set; + sel_ret = select (FD_SETSIZE, &read_fd_set, NULL, NULL, select_timeout); + if (sel_ret < 0) + { + perror ("select"); + abort (); + } + else if (sel_ret == 0) + return nil; + + for (fd_index = 0; fd_index < FD_SETSIZE; fd_index++) + if (FD_ISSET (fd_index, &read_fd_set)) + { + if (fd_index == _socket) + { + /* This is a connection request on the original socket. */ + int new; + int size; + + size = sizeof (clientname); + new = accept (_socket, (struct sockaddr*)&clientname, &size); + if (new < 0) + { + perror ("accept"); + abort (); + } + if (debug_tcp_port) + fprintf (stderr, + "Accepted connection from host %s, port %hd.\n", + inet_ntoa (clientname.sin_addr), + ntohs (clientname.sin_port)); + [self _addOutPort: [TcpOutPort newWithAcceptedSocket: new + inPort: self]]; + } + else + { + /* Data arriving on an already-connected socket. */ + TcpPacket *packet; + int remaining; + if (!(packet = NSMapGet (client_sock_2_packet, + (void*)fd_index))) + { + /* This is the beginning of a new packet on this socket. + Create a new Packet object for gathering the data. */ + + /* First, get the packet size, (which is encoded in + the first few bytes of the stream). */ + int packet_size = + [TcpPacket readPacketSizeFromSocket: fd_index]; + /* We got an EOF when trying to read the packet size; + invalidate the port, and keep on waiting for + incoming data on other sockets. */ + if (packet_size == EOF) + { + [(id) NSMapGet (client_sock_2_out_port, + (void*)fd_index) + invalidate]; + continue; + } + else + { + packet = [[TcpPacket alloc] + _initForReceivingWithSize: packet_size + replyPort: + NSMapGet (client_sock_2_out_port, + (void*)fd_index)]; + } + /* The packet has now been created with correct capacity */ + } + + /* Suck bytes from the socket into the packet; find out + how many more bytes are needed before packet will be + complete. */ + remaining = [packet _fillFromSocket: (int)fd_index]; + if (remaining == EOF) + { + /* We got an EOF when trying to read packet data; + release the packet and invalidate the corresponding + port. */ + [packet release]; + [(id) NSMapGet (client_sock_2_out_port, (void*)fd_index) + invalidate]; + } + else if (remaining == 0) + /* No bytes are remaining to be read for this packet; + the packet is complete; return it. */ + return packet; + } + } + } + return nil; +} + +- (void) _addOutPort: p +{ + int s = [p _socket]; + + FD_SET (s, &active_fd_set); + + NSMapInsert (client_sock_2_out_port, (void*)s, p); +} + +- (void) _connectedOutPortInvalidated: p +{ + id packet; + int s = [p _socket]; + + packet = NSMapGet (client_sock_2_packet, (void*)s); + if (packet) + { + NSMapRemove (client_sock_2_packet, (void*)s); + [packet release]; + } + NSMapRemove (client_sock_2_out_port, (void*)s); + FD_CLR(s, &active_fd_set); +} + +- (int) _socket +{ + return _socket; +} + +- (void) invalidate +{ + if (is_valid) + { + NSMapEnumerator me = NSEnumerateMapTable (client_sock_2_out_port); + int count = NSCountMapTable (client_sock_2_out_port); + id out_port; + int sock; + id out_ports[count]; + int i; + for (i = 0; + NSNextMapEnumeratorPair (&me, (void*)&sock, (void*)&out_port); + i++) + out_ports[i] = out_port; + for (i = 0; i < count; i++) + { + /* This will call [self _invalidateConnectedOutPort: for each. */ + [out_ports[i] invalidate]; + } + assert (!NSCountMapTable (client_sock_2_out_port)); + close (_socket); + [super invalidate]; + } +} + +- (void) dealloc +{ + [self invalidate]; + NSMapRemove (port_number_2_port, (void*)(int)ntohs(_address.sin_port)); + /* assert that these are empty? */ + NSFreeMapTable (client_sock_2_out_port); + NSFreeMapTable (client_sock_2_packet); + NSMapRemove (socket_2_port, (void*)_socket); + [super dealloc]; +} + +- (void) checkConnection +{ + [self notImplemented: _cmd]; +} + +- classForConnectedCoder: aRmc +{ + /* Make sure that Connection's always send us bycopy, + i.e. as our own class, not a Proxy class. */ + return [self class]; +} + +- (Class) packetClass +{ + return [TcpPacket class]; +} + +@end + + +@implementation TcpOutPort + ++ (void) initialize +{ + if (self == [TcpOutPort class]) + { + if (!socket_2_port) + init_socket_2_port (); + } +} + +/* This is the designated initializer. */ +- _initWithSocket: (int)s inPort: ip +{ + [super init]; + _socket = s; + NSMapInsert (socket_2_port, (void*)s, self); + connected_in_port = ip; + return self; +} + ++ newForSendingToPortNumber: (unsigned short)n + onHost: (id )hostname +{ + TcpOutPort *p; + int s; + + /* xxx If the TcpOutPort object already exists, just return it. */ + /* This needs to be judged according to the port number and host; + this is what will get sent when a TcpPort encodes itself. */ + + /* There isn't one already; create the TcpOutPort. */ + + /* Create the socket. */ + s = socket (AF_INET, SOCK_STREAM, 0); + if (s < 0) + { + perror ("socket (client)"); + abort (); + } + + /* Create a port object. */ + p = [[self alloc] _initWithSocket: s inPort: nil]; + + /* Initialize the address. */ + { + struct hostent *hostinfo; + p->_address.sin_family = AF_INET; + p->_address.sin_port = htons (n); + hostinfo = gethostbyname ([hostname cStringNoCopy]); + if (hostinfo == NULL) + { + fprintf (stderr, "Unknown host %s.\n", hostname); + abort (); + } + p->_address.sin_addr = *(struct in_addr *) hostinfo->h_addr; + } + + /* Connect to destination. */ + if (connect (s, (struct sockaddr*)&(p->_address), sizeof(p->_address)) < 0) + { + perror ("connect (client)"); + abort (); + } + + return p; +} + ++ newForSendingToRegisteredName: (id )name + onHost: (id )hostname +{ + return [self newForSendingToPortNumber: + name_2_port_number ([name cStringNoCopy]) + onHost: hostname];; +} + ++ newWithAcceptedSocket: (int)s inPort: p +{ + return [[self alloc] _initWithSocket: s inPort: p]; +} + +- (int) writeBytes: (const char*)b length: (int)len +{ + return write (_socket, b, len); +} + +- (BOOL) sendPacket: packet withTimeout: (int)milliseconds +{ + int c, l; + id reply_port = [packet replyPort]; + + if (connected_in_port == nil && reply_port != nil) + { + connected_in_port = reply_port; + [connected_in_port retain]; + [connected_in_port _addOutPort: self]; + /* xxx Register socket with the replyPort. */ + } + else if (connected_in_port != reply_port) + [self error:"TcpPort can't change reply port of an out port once set."]; + + [packet _writeToSocket: _socket]; + return YES; +} + +- (int) _socket +{ + return _socket; +} + +- (void) close +{ + [self invalidate]; +} + +- (void) invalidate +{ + if (is_valid) + { + if (close (_socket) < 0) + perror ("close, -invalidate"); + [connected_in_port _connectedOutPortInvalidated: self]; + [connected_in_port release]; + connected_in_port = nil; + [super invalidate]; + } +} + +- (void) dealloc +{ + if (is_valid) + [self invalidate]; + NSMapRemove (socket_2_port, (void*)_socket); +} + +- classForConnectedCoder: aRmc +{ + /* Make sure that Connection's always send us bycopy, + i.e. as our own class, not a Proxy class. */ + return [self class]; +} + +- (Class) packetClass +{ + return [TcpPacket class]; +} + +@end + + + +@implementation TcpPacket + +/* If you change this, you must change the use of ntohs() and htons() below. */ +#define PREFIX_TYPE unsigned short +#define PREFIX_LENGTH sizeof (PREFIX_TYPE) + +/* This is the designated initialzer. */ +/* xxx This will change; it doesn't set it's buffer size with a nice + interface */ +- _initForReceivingWithSize: (int)s replyPort: p +{ + [super _initOnMallocBuffer: (*objc_malloc) (s) + size: s + eofPosition: 0 + prefix: PREFIX_LENGTH + position: 0]; + assert (p); + reply_port = p; + return self; +} + +- initForSendingWithCapacity: (unsigned)c + replyPort: p +{ + [super _initOnMallocBuffer: (*objc_malloc)(c) + size: c + eofPosition: 0 + prefix: PREFIX_LENGTH + position: 0]; + reply_port = p; + return self; +} + +- (void) _writeToSocket: (int)s +{ + int write_len = eofPosition + 1; + int c, len = write_len - sizeof (unsigned short); + + /* Put the packet size in the first two bytes of the packet. + We use `-sizeof()' because the size does not include this + length-indicating prefix. */ + assert (prefix == PREFIX_LENGTH); + *(PREFIX_TYPE*)buffer = htons (len); + + /* Write the packet on the socket. */ + c = write (s, buffer, write_len); + if (c < 0) + { + perror ("write"); + abort (); + } + + /* Did we sucessfully write it all? */ + if (c != write_len) + [self error: "socket write failed"]; +} + ++ (int) readPacketSizeFromSocket: (int)s +{ + char size_buffer[PREFIX_LENGTH]; + int c; + int packet_size; + + c = read (s, size_buffer, PREFIX_LENGTH); + if (c == 0) + return EOF; + if (c != PREFIX_LENGTH) + [self error: "Failed to get packet size from socket."]; + + /* packet_size is the number of bytes in the packet, not including + this two-byte length header. */ + packet_size = ntohs (*(PREFIX_TYPE*) size_buffer); + assert (packet_size); + return packet_size; +} + +- (int) _fillFromSocket: (int)s +{ + int c; + int remaining; + + remaining = size - position; + c = read (s, buffer + position, remaining); + if (c == 0) + return EOF; + position += c; + eofPosition = position; + return remaining - c; +} + +@end + diff --git a/Source/UdpPort.m b/Source/UdpPort.m new file mode 100644 index 000000000..6dd5aa0c2 --- /dev/null +++ b/Source/UdpPort.m @@ -0,0 +1,423 @@ +/* Implementation of UDP port object for use with Connection + Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: July 1994 + + This file is part of the GNU Objective C Class 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if _AIX +#include +#endif /* _AIX */ +#ifndef WIN32 +#include +#include +#endif /* !WIN32 */ + +@interface UdpInPort (Private) +@end +@interface UdpOutPort (Private) ++ newForSendingToSockaddr: (struct sockaddr_in*)addr; +@end +@interface UdpPacket (Private) +- (void) _setReplyPort: p; +@end + +/* The maximum size of packet UdpPort's will send or recieve. */ +/* xxx What is the UDP maximum? */ +#define MAX_PACKET_SIZE 2048 + +/* Make this a hashtable? */ +static Lock* udp_port_gate = nil; + +static BOOL udp_port_debug = NO; + +/* xxx This function is just temporary. + Eventually we should write a real name server for sockets */ +static unsigned int +name_to_port_number (const char *name) +{ + unsigned int ret = 0; + unsigned int ctr = 0; + + /* xxx Limit the length? */ + while (*name) + { + ret ^= *name++ << ctr; + ctr = (ctr + 1) % sizeof (void *); + } + return ret % (65535 - IPPORT_USERRESERVED - 1); +} + +@implementation UdpInPort + +static NSMapTable *port_number_2_in_port = NULL; + ++ (void) initialize +{ + if (self == [UdpInPort class]) + { + port_number_2_in_port = + NSCreateMapTable (NSIntMapKeyCallBacks, + NSNonOwnedPointerMapValueCallBacks, 0); + } +} + ++ (void) setDebug: (BOOL)f +{ + udp_port_debug = f; +} + +/* This is the designated initializer. */ + +/* An argument of 0 say to let the system choose the port number. */ ++ newForReceivingFromPortNumber: (unsigned short)n +{ + UdpInPort* p; + int i, count; + + assert (n > IPPORT_USERRESERVED); + + [udp_port_gate lock]; + + /* See if there is already one created */ + if ((p = NSMapGet (port_number_2_in_port, (void*)(int)n))) + return p; + + /* No, create a new one */ + p = [[self alloc] init]; + p->_address.sin_family = AF_INET; +#if 1 + { + struct hostent *hp; + hp = gethostbyname ("localhost"); + assert (hp); + memcpy (&(p->_address.sin_addr), hp->h_addr, sizeof(p->_address.sin_addr)); + } +#else + p->_address.sin_addr.s_addr = INADDR_ANY; +#endif + p->_address.sin_port = htons (n); + if ((p->_socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) + { + perror("creating socket"); + abort (); + } + if (bind (p->_socket, (struct sockaddr*)&(p->_address), sizeof(p->_address))) + { + perror("bind"); + abort (); + } + if (udp_port_debug) + fprintf(stderr, "created new UdpInPort 0x%x, fd=%d port_number=%d\n", + (unsigned)p, p->_socket, -1); + + /* Record it. */ + NSMapInsert (port_number_2_in_port, (void*)(int)n, p); + [udp_port_gate unlock]; + + return p; +} + ++ newForReceivingFromRegisteredName: (id )name +{ + int n; + + n = name_to_port_number ([name cStringNoCopy]); + return [self newForReceivingFromPortNumber: n]; +} + +/* Returns nil on timeout. + Pass -1 for milliseconds to ignore timeout parameter and block indefinitely. +*/ + +- receivePacketWithTimeout: (int)milliseconds +{ + int r; + struct sockaddr_in remote_addr; + int remote_len; + UdpPacket *packet; + + if (udp_port_debug) + fprintf(stderr, "receiving from %d\n", [self portNumber]); + + if (milliseconds >= 0) + { + /* A timeout was requested; use select to ask if something is ready. */ + struct timeval timeout; + fd_set ready; + + timeout.tv_sec = milliseconds / 1000; + timeout.tv_usec = (milliseconds % 1000) * 1000; + FD_ZERO(&ready); + FD_SET(_socket, &ready); + if ((r = select(_socket + 1, &ready, 0, 0, &timeout)) < 0) + { + perror("select"); + abort (); + } + + if (r == 0) /* timeout */ + return nil; + if (!FD_ISSET(_socket, &ready)) + [self error:"select lied"]; + } + + /* There is a packet on the socket ready for us to receive. */ + + /* Create a packet. */ + packet = [[UdpPacket alloc] initWithCapacity: MAX_PACKET_SIZE]; + + /* Fill it with the UDP packet data. */ + remote_len = sizeof(remote_addr); + if (recvfrom (_socket, [packet streamBuffer], MAX_PACKET_SIZE, 0, + (struct sockaddr*)&remote_addr, &remote_len) + < 0) + { + perror("recvfrom"); + abort (); + } + + /* Set the packet's reply_port. */ + if (remote_len != sizeof(struct sockaddr_in)) + [self error:"remote address size mismatch"]; + [packet _setReplyPort: [[self class] newForSendingToSockaddr: &remote_addr]]; + + return packet; +} + +- (void) invalidate +{ + if (is_valid) + { + close (_socket); + [super invalidate]; + } +} + +- (void) dealloc +{ + [self invalidate]; + [super dealloc]; +} + +- (int) socket +{ + return _socket; +} + +- (int) portNumber +{ + return (int) ntohs (_address.sin_port); +} + +- (Class) packetClass +{ + return [UdpPacket class]; +} + +- (Class) classForConnectedCoder: aRmc +{ + /* Make sure that Connection's always send us bycopy, not a Proxy class. + Also, don't encode a "receive right" (ala Mach), encode a "send right". */ + return [UdpOutPort class]; +} + +- (void) encodeWithCoder: aCoder +{ + /* We are actually encoding a "send right" (ala Mach), + not a receive right. */ + [super encodeWithCoder: aCoder]; + [aCoder encodeValueOfCType: @encode(typeof(_address.sin_port)) + at: &_address.sin_port + withName: @"socket number"]; + [aCoder encodeValueOfCType: @encode(typeof(_address.sin_addr.s_addr)) + at: &_address.sin_addr.s_addr + withName: @"inet address"]; +} + ++ newWithCoder: aCoder +{ + /* An InPort cannot be created by decoding, only OutPort's. */ + [self shouldNotImplement: _cmd]; + return nil; +} + +@end + + + +@implementation UdpOutPort + +static Array *udp_out_port_array; + ++ (void) initialize +{ + if (self == [UdpOutPort class]) + { + udp_out_port_array = [Array new]; + } +} + +#define SOCKPORT_EQUAL(s1,s2) (s1->sin_port == s2->sin_port && s1->sin_addr.s_addr == s2->sin_addr.s_addr) +/* xxx Change to make INADDR_ANY and the localhost address be equal. */ +/* Assume that sin_family is equal */ +/* (!memcmp(s1, s2, sizeof(struct sockaddr_in))) + didn't work because sin_zero's differ. Does this matter? */ + + +/* This is the designated initializer. */ + ++ newForSendingToSockaddr: (struct sockaddr_in*)sockaddr +{ + UdpOutPort *p; + + /* See if there already exists a port for this sockaddr; + if so, just return it. */ + FOR_ARRAY (udp_out_port_array, p) + { + /* xxx Come up with a way to do this with a hashtable, not a list. */ + if (SOCKADDR_EQUAL (sockaddr, &(p->_address))) + return p; + } + END_FOR_ARRAY (udp_out_port_array); + + /* Create a new port. */ + p = [[self alloc] init]; + + /* Set the address. */ + memcpy (&(p->_address), sockaddr, sizeof(p->_address)); + + /* Remember it in the array. */ + /* xxx This will retain it; how will it ever get dealloc'ed? */ + [udp_out_port_array addObject: p]; + + return p; +} + + ++ newForSendingToPortNumber: (unsigned short)n + onHost: (id )hostname +{ + struct hostent *hp; + const char *host_cstring; + struct sockaddr_in addr; + + /* Look up the hostname. */ + if (!hostname || ![hostname length]) + host_cstring = "localhost"; + else + host_cstring = [hostname cStringNoCopy]; + hp = gethostbyname ((char*)host_cstring); + if (hp == 0) + [self error: "unknown host: \"%s\"", host_cstring]; + + /* Get the sockaddr_in address. */ + memcpy (&addr.sin_addr, hp->h_addr, hp->h_length); + addr.sin_family = AF_INET; + addr.sin_port = htons (n); + + return [self newForSendingToSockaddr: &addr]; +} + + +/* This currently ignores the timeout parameter */ + +- (BOOL) sendPacket: packet withTimeout: (int)milliseconds +{ + id reply_port = [packet replyPort]; + int len = [packet streamEofPosition]; + + assert (len < MAX_PACKET_SIZE); + + if ( ! [reply_port isKindOfClass: [UdpInPort class]]) + [self error:"Trying to send to a port that is not a UdpInPort"]; + if (udp_port_debug) + fprintf (stderr, "sending to %d\n", (int) ntohs (_address.sin_port)); + if (sendto ([reply_port socket], + [packet streamBuffer], len, 0, + (struct sockaddr*)&_address, sizeof (_address)) + < 0) + { + perror ("sendto"); + abort (); + } + return YES; +} + +- (int) portNumber +{ + return (int) ntohs (_address.sin_port); +} + +- (id ) hostname +{ + [self notImplemented: _cmd]; + return nil; +} + +- (Class) packetClass +{ + return [UdpPacket class]; +} + +- (void) encodeWithCoder: aCoder +{ + [super encodeWithCoder: aCoder]; + [aCoder encodeValueOfCType: @encode(typeof(_address.sin_port)) + at: &_address.sin_port + withName: @"socket number"]; + [aCoder encodeValueOfCType: @encode(typeof(_address.sin_addr.s_addr)) + at: &_address.sin_addr.s_addr + withName: @"inet address"]; +} + ++ newWithCoder: aCoder +{ + struct sockaddr_in addr; + + addr.sin_family = AF_INET; + [aCoder decodeValueOfCType: @encode(typeof(addr.sin_port)) + at: &addr.sin_port + withName: NULL]; + [aCoder decodeValueOfCType: @encode(typeof(addr.sin_addr.s_addr)) + at: &addr.sin_addr.s_addr + withName: NULL]; + return [UdpInPort newForSendingToSockaddr: &addr]; +} + +@end + + + +@implementation UdpPacket + +- (void) _setReplyPort: p +{ + reply_port = p; +} + +@end diff --git a/Source/objects/TcpPort.h b/Source/objects/TcpPort.h new file mode 100644 index 000000000..c9182423c --- /dev/null +++ b/Source/objects/TcpPort.h @@ -0,0 +1,76 @@ +/* Interface for stream based on TCP sockets + Copyright (C) 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: February 1996 + + This file is part of the GNU Objective C Class 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __TcpPort_h__OBJECTS_INCLUDE +#define __TcpPort_h__OBJECTS_INCLUDE + +#include +#include +#include +#include +#include +#include + +@interface TcpPacket : Packet +@end + +@interface TcpInPort : InPort +{ + int _socket; + struct sockaddr_in _address; + fd_set active_fd_set; + NSMapTable *client_sock_2_out_port; + NSMapTable *client_sock_2_packet; +} + ++ newForReceivingFromPortNumber: (unsigned short)n; ++ newForReceivingFromRegisteredName: (id )name; + +/* Get a packet from the net and return it. If no packet is received + within MILLISECONDS, then return nil. The caller is responsible + for releasing the packet. */ +- receivePacketWithTimeout: (int)milliseconds; + +- (id ) connectedOutPorts; + +- (void) checkConnection; + +@end + +@interface TcpOutPort : OutPort +{ + int _socket; + struct sockaddr_in _address; + id connected_in_port; +} + ++ newForSendingToPortNumber: (unsigned short)n + onHost: (id )hostname; ++ newForSendingToRegisteredName: (id )name + onHost: (id )hostname; +- (BOOL) sendPacket: packet withTimeout: (int)milliseconds; + +@end + +#endif /* __TcpPort_h__OBJECTS_INCLUDE */ + diff --git a/Source/objects/UdpPort.h b/Source/objects/UdpPort.h new file mode 100644 index 000000000..e15878df7 --- /dev/null +++ b/Source/objects/UdpPort.h @@ -0,0 +1,59 @@ +/* Interface for socket-based port object for use with Connection + Copyright (C) 1994, 1996 Free Software Foundation, Inc. + + Written by: R. Andrew McCallum + Created: July 1994 + + This file is part of the GNU Objective C Class 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SocketPort_h_INCLUDE_GNU +#define __SocketPort_h_INCLUDE_GNU + +#include +#include +#include +#ifndef WIN32 +# include +# include +#endif /* !WIN32 */ + +@interface UdpInPort : InPort +{ + int _socket; + struct sockaddr_in _address; +} + +- (int) portNumber; +- (int) socket; + +@end + +@interface UdpOutPort : OutPort +{ + struct sockaddr_in _address; +} + +- (int) portNumber; +- (id ) hostname; + +@end + +@interface UdpPacket : Packet +@end + +#endif /* __SocketPort_h_INCLUDE_GNU */