ftp.nice.ch/pub/next/developer/languages/smalltalk/squeak-2.0-0.3d109.s.tar.gz#/squeak-2.0/src/sqUnixNetwork.c

This is sqUnixNetwork.c in view mode; [Download] [Up]

/* Unix socket support.
 *
 * Author: Ian Piumarta (ian.piumarta@inria.fr)
 *
 * Last edited: Mon Feb  2 17:02:20 1998 by piumarta (Ian Piumarta) on fricotin
 *
 * Notes:
 *
 * 1. 	UDP support is fully implemented here, but is missing in the
 * 	image.
 *
 * 2.	Sockets are completely asynchronous, but the resolver is still
 *	synchronous.
 *
 * 3.	Due to bugs in Solaris and HP-UX (and possibly others) the SIGIO
 *	mechanism is no longer used.  Instead aioPollForIO() is periodically
 *	called from HandleEvents in sqXWindow.c in order to check for I/O
 *	completion using select().
 *
 * $Log: sqUnixNetwork.c,v $
 * Revision 1.2  1997/09/30 04:59:19  piumarta
 * added asynchronous support
 *
 * Revision 1.1  1997/08/02 01:37:12  piumarta
 * Initial revision
 */

static char rcsid[]=
  "$Id: sqUnixNetwork.c,v 1.2 1997/09/30 05:00:23 piumarta Exp piumarta $";

#include "sq.h"

#include <signal.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <errno.h>

#ifdef NEED_FILIO
# include <sys/filio.h>
#endif

#ifdef NEED_SELECT
# include <sys/select.h>
#endif

/* debugging stuff; can probably be deleted */

#ifdef DEBUG
# define FPRINTF(X) fprintf X
  char *ticks= "-\\|/";
  char *ticker= "";
# define DO_TICK() \
    { fprintf(stderr, "\r%c\r", *ticker); if (!*ticker++) ticker= ticks; }
#else
# define FPRINTF(X)
# define DO_TICK()
#endif


/*** Socket types ***/

#define TCPSocketType	 	0
#define UDPSocketType	 	1


/*** Resolver states ***/

#define ResolverUninitialized	0
#define ResolverSuccess		1
#define ResolverBusy		2
#define ResolverError		3


/*** TCP Socket states ***/

#define Unconnected		0x00
#define WaitingForConnection	0x01
#define Connected		0x02
#define OtherEndClosed		0x03
#define ThisEndClosed		0x04

static int thisNetSession= 0;
static int one= 1;

static char   localHostName[MAXHOSTNAMELEN];
static u_long localHostAddress;		/* WARNING: GROSS IPv4 ASSUMPTION!!! */

typedef struct privateSocketStruct {
  int s;				/* Unix socket */
  int sema;				/* io notification semaphore */
  int sockState;			/* connection + data state */
  int sockError;			/* errno after socket error */
  struct sockaddr_in peer;		/* default send/recv address for UDP */
} privateSocketStruct;


/*** Accessors for private socket members from a Squeak socket pointer ***/

#define PSP(S)		((privateSocketStruct *)((S)->privateSocketPtr))

#define SOCKET(S)	(PSP(S)->s)
#define SOCKETSEMA(S)	(PSP(S)->sema)
#define SOCKETSTATE(S)	(PSP(S)->sockState)
#define SOCKETERROR(S)	(PSP(S)->sockError)
#define SOCKETPEER(S)	(PSP(S)->peer)

#define SIGNAL(PSS)	signalSemaphoreWithIndex((PSS)->sema)

/*** Resolver state ***/

static char lastName[MAXHOSTNAMELEN+1];
static int  lastAddr= 0;
static int  lastError= 0;

static int  resolverSema;


/*** asynchronous i/o support ***/

/* handler flags */
#define AIO_EX	(0<<0)
#define AIO_RD	(1<<0)
#define AIO_WR	(1<<1)
#define AIO_RW	(AIO_RD | AIO_WR)

typedef void (*AioHandler)(privateSocketStruct *pss, int errorFlag);

static privateSocketStruct *sockets[FD_SETSIZE];
static AioHandler	    handler[FD_SETSIZE];

static int	lastSocket;
static fd_set	readMask;
static fd_set	writeMask;
static fd_set	exceptionMask;

static void nullHandler(privateSocketStruct *, int);
static void acceptHandler(privateSocketStruct *, int);
static void connectHandler(privateSocketStruct *, int);
static void dataHandler(privateSocketStruct *, int);
static void closeHandler(privateSocketStruct *, int);


#ifdef DEBUG
static char *handlerName(AioHandler h) {
  if (h == nullHandler)    return "nullHandler";
  if (h == acceptHandler)  return "acceptHandler";
  if (h == connectHandler) return "connectHandler";
  if (h == dataHandler)    return "dataHandler";
  if (h == closeHandler)   return "closeHandler";
  return "***unknownHandler***";
}
#endif

void aioPollForIO(int microSeconds, int extraFd);

/* poll for io activity and call the appropriate handler(s) *
 *
 * Note: this can be called from ioProcessEvents with a zero timeout
 *       and from ioRelinquishProcessor with a non-zero timeout.
 *
 *	 "extraFd" is a file descriptor that is polled for reading but
 *	 never handled -- this allows a relinquished CPU to return
 *	 early if there is mouse or keyboard input activity.  Essential
 *	 for (e.g.) handling keyboard interrupts during i/o wait.
 */
void aioPollForIO(int microSeconds, int extraFd)
{
  int fd;
  fd_set rd, wr, ex;
  struct timeval tv;

  DO_TICK();

  /* get out early if there is no pending i/o and no need to relinquish cpu */
  if (!lastSocket && !microSeconds) return;

  rd= readMask;
  wr= writeMask;
  ex= exceptionMask;
  if (extraFd) FD_SET(extraFd, &rd);
  tv.tv_sec=  microSeconds / 1000000;
  tv.tv_usec= microSeconds % 1000000;

  {
    int limit= (extraFd > lastSocket ? extraFd : lastSocket) + 1;
    int result= select(limit, &rd, &wr, &ex, &tv);
    if (result < 0)
      perror("select");
    if (result != 0)
      for (fd= 0; fd < lastSocket + 1; ++fd) {
	if (FD_ISSET(fd, &ex))
	  handler[fd](sockets[fd], 1);
	else if (FD_ISSET(fd, &rd) || FD_ISSET(fd, &wr))
	  if (fd != extraFd)
	    handler[fd](sockets[fd], 0);
      }
  }
}


/* enable asynchronous notification for a socket */
static void aioEnable(privateSocketStruct *pss)
{
  int fd= pss->s;

  FPRINTF((stderr, "aioEnable(%d)\n", fd));

  sockets[fd]= pss;
  handler[fd]= nullHandler;
#ifdef FIOSNBIO	/* HP-UKes again */
  if (ioctl(fd, FIOSNBIO, (char *)&one) < 0) perror("ioctl(FIOSNBIO,1)");
#else
  if (ioctl(fd, FIONBIO, (char *)&one) < 0) perror("ioctl(FIONBIO,1)");
#endif
}

/* install/change the handler for a socket */
static void aioHandle(privateSocketStruct *pss,
		      AioHandler handlerFn,
		      int flags)
{
  int fd= pss->s;

  FPRINTF((stderr, "aioHandle(%d,%s,%d)\n",
	   fd, handlerName(handlerFn), flags));

  if (sockets[fd] != pss) {
    fprintf(stderr, "aioHandle: bad match\n");
    sockets[fd]= pss;
  }
  handler[fd]= handlerFn;
  if (fd > lastSocket) lastSocket= fd;
  if (flags & AIO_RD) FD_SET(fd, &readMask);
  if (flags & AIO_WR) FD_SET(fd, &writeMask);
  FD_SET(fd, &exceptionMask);
}


/* temporarily suspend asynchronous notification for a socket */
static void aioSuspend(privateSocketStruct *pss)
{
  int fd= pss->s;

  FPRINTF((stderr, "aioSuspend(%d)\n", fd));

  if (fd) {
    FD_CLR(fd, &readMask);
    FD_CLR(fd, &writeMask);
/*  FD_CLR(fd, &exceptionMask); */
    handler[fd]= nullHandler;
    /* keep lastSocket accurate (reduces to zero if no more sockets) */
    while (lastSocket && !FD_ISSET(lastSocket, &exceptionMask))
      --lastSocket;
  }
}


/* definitively disable asynchronous notification for a socket */
static void aioDisable(privateSocketStruct *pss)
{
  int fd= pss->s;

  FPRINTF((stderr, "aioDisable(%d)\n", fd));

  if (fd) {
    FD_CLR(fd, &exceptionMask);
    aioSuspend(pss);
    sockets[fd]= 0;
    handler[fd]= 0;
  }
}


/* initialise asynchronous i/o handlers */
static void aioInit()
{
  int i;

  for (i= 0; i < FD_SETSIZE; i++) {
    sockets[i]= 0;
    handler[i]= 0;
  }
  FD_ZERO(&readMask);
  FD_ZERO(&writeMask);
  FD_ZERO(&exceptionMask);
  lastSocket= 0;
  signal(SIGPIPE, SIG_IGN);
}


/* disable handlers and close all sockets */
static void aioShutdown()
{
  int i;

  for (i= 0; i < lastSocket+1; i++)
    if (sockets[i]) {
      aioDisable(sockets[i]);
      close(sockets[i]->s);
    }
}


/*** Miscellaneous sundries ***/

/* answer the hostname for the given IP address */
static const char *addrToName(int netAddress)
{
  u_long nAddr;
  struct hostent *he;

  lastError= 0;			/* for the resolver */
  nAddr= htonl(netAddress);
  if ((he= gethostbyaddr((char *)&nAddr, sizeof(nAddr), AF_INET)))
    return he->h_name;
  lastError= h_errno;		/* ditto */
  return "";
}

/* answer the IP address for the given hostname */
static int nameToAddr(char *hostName)
{
  struct hostent *he;

  lastError= 0;			/* ditto */
  if ((he= gethostbyname(hostName)))
    return ntohl(*(long *)(he->h_addr_list[0]));
  lastError= h_errno;		/* and one more ditto */
  return 0;
}

/* answer whether the given socket is valid in this net session */
static int socketValid(SocketPtr s)
{
  if ((s != 0) && (s->privateSocketPtr != 0) && (s->sessionID == thisNetSession))
    return true;
  success(false);
  return false;
}

/* answer whether the socket can be read (or accept()ed) without blocking */
static int socketReadable(int s)
{
  struct timeval tv= { 0, 0 };
  fd_set fds;

  FD_ZERO(&fds);
  FD_SET(s, &fds);
  return select(s+1, &fds, 0, 0, &tv) > 0;
}

/* answer whether the socket can be written without blocking */
static int socketWritable(int s)
{
  struct timeval tv= { 0, 0 };
  fd_set fds;

  FD_ZERO(&fds);
  FD_SET(s, &fds);
  return select(s+1, 0, &fds, 0, &tv) > 0;
}


/*** aio handlers ***/

/* Each handler must at least:
 *   - suspend further handling for the associated socket
 *   - signal its semaphore to indicate that the operation is complete
 */

/* this handler should never normally be invoked */
static void nullHandler(privateSocketStruct *pss, int errFlag)
{
  fprintf(stderr, "nullHandler(%d,%d)\n", pss->s, errFlag);
  aioSuspend(pss);
  if (errFlag)
    pss->sockState= OtherEndClosed;
  SIGNAL(pss);	/* operation complete */
}

/* accept() can now be performed for the socket: call accept(),
   and replace the server socket with the new client socket
   leaving the client socket unhandled */
static void acceptHandler(privateSocketStruct *pss, int errFlag)
{
  FPRINTF((stderr, "acceptHandler(%d,%d)\n", pss->s, errFlag));
  aioSuspend(pss);
  if (errFlag) {
    /* error during listen() */
    close(pss->s);
    pss->sockState= Unconnected;
    pss->sockError= EBADF;	/* educated guess */
  } else {
    /* accept() is ready */
    int newSock= accept(pss->s, 0, 0);
    aioDisable(pss);
    close(pss->s);
    if (newSock < 0) {
      /* error during accept() */
      pss->sockError= errno;
      pss->sockState= Unconnected;
      perror("acceptHandler");
    } else {
      /* connection accepted */
      pss->s= newSock;
      pss->sockState= Connected;
      aioEnable(pss);
    }
  }
  SIGNAL(pss);	/* operation complete */
}

/* connect() has completed: check errors, leaving the socket unhandled */
static void connectHandler(privateSocketStruct *pss, int errFlag)
{
  FPRINTF((stderr, "connectHandler(%d,%d)\n", pss->s, errFlag));
  aioSuspend(pss);
  if (errFlag) {
    /* error during asynchronous connect() */
    close(pss->s);
    pss->sockError= errno;
    pss->sockState= Unconnected;
  } else {
    /* connect() has completed */
    pss->sockState= Connected;
  }
  SIGNAL(pss);	/* operation complete */
}

/* read or write data transfer is now possible for the socket */
static void dataHandler(privateSocketStruct *pss, int errFlag)
{
  FPRINTF((stderr, "dataHandler(%d,%d)\n", pss->s, errFlag));
  aioSuspend(pss);
  if (errFlag)
    /* error: almost certainly "connection closed by peer" */
    pss->sockState= OtherEndClosed;
  SIGNAL(pss);	/* operation complete */
}

/* a non-blocking close() has completed -- finish tidying up */
static void closeHandler(privateSocketStruct *pss, int errFlag)
{
  FPRINTF((stderr, "closeHandler(%d,%d)\n", pss->s, errFlag));
  aioDisable(pss);
  pss->sockState= Unconnected;
  pss->s= 0;
  SIGNAL(pss);	/* operation complete */
}


/*** Squeak network functions ***/

/* start a new network session */
int sqNetworkInit(int resolverSemaIndex)
{
  if (0 != thisNetSession) return 0;  /* already initialised */
  gethostname(localHostName, MAXHOSTNAMELEN);
  localHostAddress= nameToAddr(localHostName);
  thisNetSession= clock() + time(0);
  if (0 == thisNetSession) thisNetSession= 1;  /* 0 => uninitialised */
  resolverSema= resolverSemaIndex;
  aioInit();
  return 0;
}

/* terminate the current network session (invalidates all open sockets) */
void sqNetworkShutdown(void)
{
  thisNetSession= 0;
  resolverSema= 0;
  aioShutdown();
}


/*** Squeak Generic Socket Functions ***/

/* create a new socket */
void sqSocketCreateNetTypeSocketTypeRecvBytesSendBytesSemaID(
        SocketPtr s, int netType, int socketType,
        int recvBufSize, int sendBufSize, int semaIndex)
{
  int newSocket= -1;
  privateSocketStruct *pss;

  s->sessionID= 0;
  if (TCPSocketType == socketType)
    /* --- TCP --- */
    newSocket= socket(AF_INET, SOCK_STREAM, 0);
  else if (UDPSocketType == socketType)
    /* --- UDP --- */
    newSocket= socket(AF_INET, SOCK_DGRAM, 0);
  if (-1 == newSocket) { /* socket() failed, or incorrect socketType */
    success(false);
    return;
  }
  setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
  /* private socket structure */
  pss= (privateSocketStruct *)calloc(1, sizeof(privateSocketStruct));
  pss->s= newSocket;
  pss->sema= semaIndex;
  /* UDP sockets are born "connected" */
  pss->sockState= (UDPSocketType == socketType) ? Connected : Unconnected;
  pss->sockError= 0;
  /* initial UDP peer := wildcard */
  memset(&pss->peer, 0, sizeof(pss->peer));
  pss->peer.sin_family= AF_INET;
  pss->peer.sin_port= 0;
  pss->peer.sin_addr.s_addr= INADDR_ANY;
  /* Squeak socket */
  s->sessionID= thisNetSession;
  s->socketType= socketType;
  s->privateSocketPtr= pss;
  FPRINTF((stderr, "create(%d) -> %lx\n", SOCKET(s), PSP(s)));
  /* Note: socket is in BLOCKING mode until aioEnable is called for it! */
}

/* return the state of a socket */
int sqSocketConnectionStatus(SocketPtr s)
{
  if (!socketValid(s)) return -1;

  return SOCKETSTATE(s);
}

/* TCP => start listening for incoming connections.
 * UDP => associate the local port number with the socket.
 */
void sqSocketListenOnPort(SocketPtr s, int port)
{
  struct sockaddr_in saddr;

  if (!socketValid(s)) return;

  FPRINTF((stderr, "listenOnPort(%d)\n", SOCKET(s)));

  memset(&saddr, 0, sizeof(saddr));
  saddr.sin_family= AF_INET;
  saddr.sin_port= htons((short)port);
  saddr.sin_addr.s_addr= INADDR_ANY;
  bind(SOCKET(s), (struct sockaddr*) &saddr, sizeof(saddr));
  if (TCPSocketType == s->socketType) {
    /* --- TCP --- */
    /* set backlog to 1, since Squeak server sockets only ever accept a single
       connection.  (This is unforgivable, but that's the way things are.) */
    listen(SOCKET(s), 1);
    SOCKETSTATE(s)= WaitingForConnection;
    aioEnable(PSP(s));
    aioHandle(PSP(s), acceptHandler, AIO_RD); /* => accept() possible */
  } else {
    /* --- UDP --- */
  }
}

/* TCP => open a connection.
 * UDP => set remote address.
 */
void sqSocketConnectToPort(SocketPtr s, int addr, int port)
{
  struct sockaddr_in saddr;

  if (!socketValid(s)) return;

  FPRINTF((stderr, "connectTo(%d)\n", SOCKET(s)));

  memset(&saddr, 0, sizeof(saddr));
  saddr.sin_family= AF_INET;
  saddr.sin_port= htons((short)port);
  saddr.sin_addr.s_addr= htonl(addr);

  if (UDPSocketType == s->socketType) {
    /* --- UDP --- */
    memcpy((void *)&SOCKETPEER(s), (void *)&saddr, sizeof(SOCKETPEER(s)));
    SOCKETSTATE(s)= Connected;
  } else {
    /* --- TCP --- */
    int result;
    aioEnable(PSP(s));
    result= connect(SOCKET(s), (struct sockaddr *)&saddr, sizeof(saddr));
    FPRINTF((stderr, "connect() => %d\n", result));
    if (result == 0) {
      /* connection completed synchronously */
      SOCKETSTATE(s)= Connected;
      SIGNAL(PSP(s));	/* operation complete */
    } else {
      if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
	/* asynchronous connection in progress */
	SOCKETSTATE(s)= WaitingForConnection;
	aioHandle(PSP(s), connectHandler, AIO_WR);  /* => connect() done */
      } else {
	/* connection error */
	perror("sqConnectToPort");
	SOCKETSTATE(s)= Unconnected;
	SOCKETERROR(s)= errno;
	SIGNAL(PSP(s));	/* operation complete */
      }
    }
  }
}

/* close the socket */
void sqSocketCloseConnection(SocketPtr s)
{
  int result;

  if (!socketValid(s)) return;

  FPRINTF((stderr, "closeConnection(%d)\n", SOCKET(s)));

  aioDisable(PSP(s));
  SOCKETSTATE(s)= ThisEndClosed;
  result= close(SOCKET(s));
  if (result == -1 && errno != EWOULDBLOCK) {
    /* error */
    SOCKETSTATE(s)= Unconnected;
    SOCKETERROR(s)= errno;
    SIGNAL(PSP(s));	/* operation complete */
  } else if (0 == result) {
    /* close completed synchronously */
    SOCKETSTATE(s)= Unconnected;
    SOCKET(s)= 0;
    SIGNAL(PSP(s));	/* operation complete */
  } else {
    /* asynchronous close in progress */
    SOCKETSTATE(s)= ThisEndClosed;
    aioHandle(PSP(s), closeHandler, AIO_EX);  /* => close() done */
  }
}

/* close the socket without lingering */
void sqSocketAbortConnection(SocketPtr s)
{
  struct linger linger= { 0, 0 };

  FPRINTF((stderr, "abortConnection(%d)\n", SOCKET(s)));

  if (!socketValid(s)) return;
  setsockopt(SOCKET(s), SOL_SOCKET, SO_LINGER,
	     (char *)&linger, sizeof(linger));
  sqSocketCloseConnection(s);
}

/* Release the resources associated with this socket. 
   If a connection is open, abort it.*/
void sqSocketDestroy(SocketPtr s)
{
  if (!socketValid(s)) return;

  FPRINTF((stderr, "destroy(%d)\n", SOCKET(s)));

  if (SOCKET(s)) sqSocketAbortConnection(s);	/* close if necessary */
  if (PSP(s)) free(PSP(s));			/* release private struct */
  /* PSP(s)= 0;*/	/* HP-UKes cannot cope with this */
  s->privateSocketPtr= 0;
}

/* answer the OS error code for the last socket operation */
int sqSocketError(SocketPtr s)
{
  if (!socketValid(s)) return -1;

  return SOCKETERROR(s);
}

/* return the local IP address bound to a socket */
int sqSocketLocalAddress(SocketPtr s)
{
  struct sockaddr_in saddr;
  int saddrSize= sizeof(saddr);

  if (!socketValid(s)) return -1;

  if (getsockname(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize) ||
      AF_INET != saddr.sin_family)
    return 0;
  return ntohl(saddr.sin_addr.s_addr);
}

/* return the peer's IP address */
int sqSocketRemoteAddress(SocketPtr s)
{
  struct sockaddr_in saddr;
  int saddrSize;

  if (!socketValid(s)) return -1;

  if (TCPSocketType == s->socketType) {
    /* --- TCP --- */
    saddrSize= sizeof(saddr);
    if (getpeername(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize) ||
	AF_INET != saddr.sin_family)
      return 0;
    return ntohl(saddr.sin_addr.s_addr);
  }
  /* --- UDP --- */
  return ntohl(SOCKETPEER(s).sin_addr.s_addr);
}

/* return the local port number of a socket */
int sqSocketLocalPort(SocketPtr s)
{
  struct sockaddr_in saddr;
  int saddrSize= sizeof(saddr);

  if (!socketValid(s)) return -1;

  if (getsockname(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize) ||
      AF_INET != saddr.sin_family)
    return 0;
  return ntohs(saddr.sin_port);
}

/* return the peer's port number */
int sqSocketRemotePort(SocketPtr s)
{
  struct sockaddr_in saddr;
  int saddrSize;

  if (!socketValid(s)) return -1;

  if (TCPSocketType == s->socketType) {
    /* --- TCP --- */
    if (getpeername(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize) ||
	AF_INET != saddr.sin_family)
      return 0;
    return ntohs(saddr.sin_port);
  }
  /* --- UDP --- */
  return ntohs(SOCKETPEER(s).sin_port);
}

/* answer whether the socket has data available for reading */
int sqSocketReceiveDataAvailable(SocketPtr s)
{
  if (!socketValid(s)) return -1;
  if (SOCKETSTATE(s) == Connected) {
    if (socketReadable(SOCKET(s))) return true;
    aioHandle(PSP(s), dataHandler, AIO_RW);
  }
  return false;
}

/* answer whether the socket has space to receive more data */
int sqSocketSendDone(SocketPtr s)
{
  if (!socketValid(s)) return -1;
  if (SOCKETSTATE(s) == Connected) {
    if (socketWritable(SOCKET(s))) return true;
    aioHandle(PSP(s), dataHandler, AIO_RW);
  }
  return false;
}

/* read data from the socket s into buf for at most bufSize bytes.
   answer the number actually read.  For UDP, fill in the peer's address
   with the approriate value. */
int sqSocketReceiveDataBufCount(SocketPtr s, int buf, int bufSize)
{
  int nread;

  if (!socketValid(s)) return -1;

  if (UDPSocketType == s->socketType) {
    /* --- UDP --- */
    int addrSize= sizeof(SOCKETPEER(s));
    if ((nread= recvfrom(SOCKET(s), (void*)buf, bufSize, 0,
			 (struct sockaddr *)&SOCKETPEER(s),
			 &addrSize)) <= 0) {
      addrSize= sizeof(SOCKETPEER(s));
      if (socketReadable(SOCKET(s)) &&
	  ((nread= recvfrom(SOCKET(s), (void*)buf, bufSize, 0,
			    (struct sockaddr *)&SOCKETPEER(s),
			    &addrSize)) <= 0)) {
	SOCKETERROR(s)= errno;
	FPRINTF((stderr, "receiveData(%d) = %da\n", SOCKET(s), 0));
	return 0;
      }
    }
  } else {
    /* --- TCP --- */
    if ((nread= read(SOCKET(s), (void*)buf, bufSize)) <= 0) {
      if (errno == EWOULDBLOCK) {
	/* asynchronous read in progress */
	aioHandle(PSP(s), dataHandler, AIO_RW);	/* => retry */
	return 0;
      } else {
	/* error: most probably "connection closed by peer" */
	SOCKETSTATE(s)= OtherEndClosed;
	SOCKETERROR(s)= errno;
	FPRINTF((stderr, "receiveData(%d) = %db\n", SOCKET(s), 0));
	return 0;
      }
    }
  }
  /* read completed synchronously */
  FPRINTF((stderr, "receiveData(%d) = %d\n", SOCKET(s), nread));
  aioSuspend(PSP(s));
  return nread;
}

/* write data to the socket s from buf for at most bufSize bytes.
   answer the number of bytes actually written. */ 
int sqSocketSendDataBufCount(SocketPtr s, int buf, int bufSize)
{
  int nsent;

  if (!socketValid(s)) return -1;

  FPRINTF((stderr, "sendData(%d,%d)\n", SOCKET(s), bufSize));

  if (UDPSocketType == s->socketType) {
    /* --- UDP --- */
    if ((nsent= sendto(SOCKET(s), (void *)buf, bufSize, 0,
		       (struct sockaddr *)&SOCKETPEER(s),
		       sizeof(SOCKETPEER(s)))) <= 0) {
      if (socketWritable(SOCKET(s)) &&
	  ((nsent= sendto(SOCKET(s), (void *)buf, bufSize, 0,
			  (struct sockaddr *)&SOCKETPEER(s),
			  sizeof(SOCKETPEER(s)))) <= 0)) {
	SOCKETERROR(s)= errno;
	return 0;
      }
    }
  } else {
    /* --- TCP --- */
    if ((nsent= write(SOCKET(s), (char *)buf, bufSize)) <= 0) {
      if (errno == EWOULDBLOCK) {
	/* asynchronous write in progress */
	aioHandle(PSP(s), dataHandler, AIO_RW);	/* => data sent */
	return 0;
      } else {
	/* error: most likely "connection closed by peer" */
	SOCKETSTATE(s)= OtherEndClosed;
	SOCKETERROR(s)= errno;
	return 0;
      }
    }
  }
  /* write completed synchronously */
  FPRINTF((stderr, "sendData(%d) = %d\n", SOCKET(s), nsent));
  aioSuspend(PSP(s));
  return nsent;
}



/*** Resolver functions ***/

/* Note: the Mac and Win32 implementations implement asynchronous lookups
 * in the DNS.  I can't think of an easy way to do this in Unix without
 * going totally ott with threads or somesuch.  If anyone knows differently,
 * please tell me about it. - Ian
 */

/*** irrelevancies ***/

void sqResolverAbort(void) {}

void sqResolverStartAddrLookup(int address)
{
  const char *res;
  res= addrToName(address);
  strncpy(lastName, res, MAXHOSTNAMELEN);
}

int sqResolverStatus(void)
{
  if(!thisNetSession) return ResolverUninitialized;
  if(lastError) return ResolverError;
  return ResolverSuccess;
}

/*** trivialities ***/

int sqResolverAddrLookupResultSize(void)	{ return strlen(lastName); }
int sqResolverError(void)			{ return lastError; }
int sqResolverLocalAddress(void)		{ return nameToAddr(localHostName); }
int sqResolverNameLookupResult(void)		{ return lastAddr; }

void sqResolverAddrLookupResult(char *nameForAddress, int nameSize)
{
  memcpy(nameForAddress, lastName, nameSize);
}

/*** name resolution ***/

void sqResolverStartNameLookup(char *hostName, int nameSize)
{
  int len= (nameSize < MAXHOSTNAMELEN) ? nameSize : MAXHOSTNAMELEN;
  memcpy(lastName, hostName, len);
  lastName[len]= lastError= 0;
  lastAddr= nameToAddr(lastName);
  /* we're done before we even started */
  signalSemaphoreWithIndex(resolverSema);
}

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