ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/gstep-base-0.2.7.tgz#/gstep-base-0.2.7/src/UdpPort.m

This is UdpPort.m in view mode; [Download] [Up]

/* Implementation of UDP port object for use with Connection
   Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
   
   Written by:  Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
   Created: July 1994
   
   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., 675 Mass Ave, Cambridge, MA 02139, USA.
   */ 

/* xxx I should also look into SOCK_RDM and SOCK_SEQPACKET. */

#include <gnustep/base/UdpPort.h>
#include <gnustep/base/Lock.h>
#include <gnustep/base/Connection.h>
#include <gnustep/base/Coder.h>
#include <gnustep/base/Array.h>
#include <gnustep/base/ConnectedCoder.h>
#include <gnustep/base/String.h>
#include <assert.h>
#ifndef __WIN32__
#include <sys/param.h>		/* for MAXHOSTNAMELEN */
#endif /* !__WIN32__ */
#if _AIX
#include <sys/select.h>
#endif /* _AIX */
#ifndef __WIN32__
#include <netdb.h>
#include <sys/time.h>
#endif /* !__WIN32__ */

@interface UdpInPort (Private)
@end
@interface UdpOutPort (Private)
+ newForSendingToSockaddr: (struct sockaddr_in*)addr;
@end
@interface UdpInPacket (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;


/* 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; */
}

@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);
    }
}

/* This is the designated initializer.
   If N is zero, it will choose a port number for you. */

+ newForReceivingFromPortNumber: (unsigned short)n 
{
  UdpInPort* p;

  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 port object */
  p = [[self alloc] init];

  /* Make a new socket for the port object */
  if ((p->_socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
    {
      perror("[UdpInPort +newForReceivingFromPortNumber:] socket()");
      abort ();
    }

  /* Give the socket a name using bind */
  {
    struct hostent *hp;
    char hostname[MAXHOSTNAMELEN];
    int len = MAXHOSTNAMELEN;
    if (gethostname (hostname, len) < 0)
      {
	perror ("[UdpInPort +newForReceivingFromPortNumber:] gethostname()");
	abort ();
      }
    hp = gethostbyname (hostname);
    if (!hp)
      /* xxx This won't work with port connections on a network, though.
         Fix this.  Perhaps there is a better way of getting the address
	 of the local host. */
      hp = gethostbyname ("localhost");
    assert (hp);
    /* Use host's address, and not INADDR_ANY, so that went we
       encode our _address for a D.O. operation, they get
       our unique host address that can identify us across the network. */
    memcpy (&(p->_address.sin_addr), hp->h_addr, hp->h_length);
    p->_address.sin_family = AF_INET;
    p->_address.sin_port = htons (n);
    /* N may be zero, in which case bind() will choose a port number
       for us. */
    if (bind (p->_socket,
	      (struct sockaddr*) &(p->_address),
	      sizeof (p->_address)) 
	< 0)
      {
	perror ("[UdpInPort +newForReceivingFromPortNumber] bind()");
	abort ();
      }
  }

  /* If the caller didn't specify a port number, it was chosen for us.
     Here, find out what number was chosen. */
  if (!n)
    /* xxx Perhaps I should do this unconditionally? */
    {
      int size = sizeof (p->_address);
      if (getsockname (p->_socket,
		       (struct sockaddr*)&(p->_address),
		       &size)
	  < 0)
	{
	  perror ("[UdpInPort +newForReceivingFromPortNumber] getsockname()");
	  abort ();
	}
      assert (p->_address.sin_port);
    }

  /* Record it in UdpInPort's map table. */
  NSMapInsert (port_number_2_in_port, (void*)(int)n, p);
  [udp_port_gate unlock];

  if (udp_port_debug)
    fprintf(stderr, "created new UdpInPort 0x%x, fd=%d port_number=%d\n",
	   (unsigned)p, p->_socket, htons(p->_address.sin_port));

  return p;
}

+ newForReceivingFromRegisteredName: (id <String>)name
{
  int n;

  n = name_2_port_number ([name cStringNoCopy]);
  return [self newForReceivingFromPortNumber: n];
}

/* Usually, you would run the run loop to get packets, but if you
   want to wait for one directly from a port, you can use this method. */
- newPacketReceivedBeforeDate: date
{
  return nil;
}


/* 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;
  UdpInPacket *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 = [[UdpInPacket 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 [UdpInPacket 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.
     These values must match those expected by [TcpOutPort +newWithCoder] */
  [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;
}

+ (void) setDebug: (BOOL)f
{
  udp_port_debug = f;
}

@end



@implementation UdpOutPort

static Array *udp_out_port_array;

+ (void) initialize
{
  if (self == [UdpOutPort class])
    {
      udp_out_port_array = [Array new];
    }
}

#define SOCKADDR_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 <String>)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 <String>) hostname
{
  [self notImplemented: _cmd];
  return nil;
}

- (Class) packetClass
{
  return [UdpInPacket 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 [UdpOutPort newForSendingToSockaddr: &addr];
}

@end



@implementation UdpInPacket

- (void) _setReplyPort: p
{
  [self notImplemented: _cmd];
}

@end

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.