PageRenderTime 4ms CodeModel.GetById 4ms app.highlight 56ms RepoModel.GetById 1ms app.codeStats 1ms

/vm/platforms/unix/plugins/SocketPlugin/sqUnixSocket.c

https://bitbucket.org/rmacnak/nsvm
C | 1420 lines | 994 code | 214 blank | 212 comment | 195 complexity | 7d4eda38e6f2a025ca10261b1d5e3eac MD5 | raw file
   1/* sqUnixSocket.c -- Unix socket support
   2 * 
   3 *   Copyright (C) 1996-2006 by Ian Piumarta and other authors/contributors
   4 *                              listed elsewhere in this file.
   5 *   All rights reserved.
   6 *   
   7 *   This file is part of Unix Squeak.
   8 * 
   9 *   Permission is hereby granted, free of charge, to any person obtaining a copy
  10 *   of this software and associated documentation files (the "Software"), to deal
  11 *   in the Software without restriction, including without limitation the rights
  12 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13 *   copies of the Software, and to permit persons to whom the Software is
  14 *   furnished to do so, subject to the following conditions:
  15 * 
  16 *   The above copyright notice and this permission notice shall be included in
  17 *   all copies or substantial portions of the Software.
  18 * 
  19 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  25 *   SOFTWARE.
  26 */
  27
  28/* Author: Ian.Piumarta@inria.fr
  29 * 
  30 * Last edited: 2006-10-18 10:08:57 by piumarta on emilia.local
  31 * 
  32 * Support for BSD-style "accept" primitives contributed by:
  33 *	Lex Spoon <lex@cc.gatech.edu>
  34 * 
  35 * Notes:
  36 * 	Sockets are completely asynchronous, but the resolver is still
  37 *	synchronous.
  38 * 
  39 * BUGS:
  40 *	Now that the image has real UDP primitives, the TCP/UDP duality in
  41 *	many of the connection-oriented functions should be removed and
  42 * 	cremated.
  43 */
  44
  45#include "sq.h"
  46#include "SocketPlugin/SocketPlugin.h"
  47#include "sqaio.h"
  48
  49#undef	AIO_DEBUG
  50#undef	DEBUG
  51
  52#ifdef ACORN
  53# include <time.h>
  54# define __time_t
  55# include <signal.h>
  56# include "inetlib.h"
  57# include "socklib.h"
  58# include "netdb.h"
  59# include "unixlib.h"
  60# include "sys/ioctl.h"
  61# include "sys/errno.h"
  62# define h_errno errno
  63# define MAXHOSTNAMELEN 256
  64# define socklen_t int
  65# define strncpy(dst, src, len) copyNCharsFromTo(len, src, dst)
  66
  67#else /* !ACORN */
  68
  69# ifdef NEED_GETHOSTNAME_P
  70    extern int gethostname();
  71# endif
  72# ifdef HAVE_SYS_TIME_H
  73#   include <sys/time.h>
  74# else
  75#   include <time.h>
  76# endif
  77# include <sys/param.h>
  78# include <sys/socket.h>
  79# include <netinet/in.h>
  80# include <netinet/udp.h>
  81# include <netinet/tcp.h>
  82# include <arpa/inet.h>
  83# include <netdb.h>
  84# include <errno.h>
  85# include <unistd.h>
  86  
  87#endif /* !ACORN */
  88
  89/* Solaris sometimes fails to define this in netdb.h */
  90#ifndef  MAXHOSTNAMELEN
  91# define MAXHOSTNAMELEN	256
  92#endif
  93
  94
  95/* debugging stuff. can probably be deleted */
  96
  97#ifdef DEBUG
  98# ifdef ACORN
  99#   define FPRINTF(s) \
 100    { \
 101      extern os_error privateErr; \
 102      extern void platReportError(os_error *e); \
 103      privateErr.errnum = (bits)0; \
 104      sprintf s; \
 105      platReportError((os_error *)&privateErr); \
 106    };
 107# else /* !ACORN */
 108    extern int aioLastTick, aioThisTick;
 109#   define FPRINTF(X) { aioThisTick= ioLowResMSecs();  fprintf(stderr, "%8d %8d ", aioThisTick, aioThisTick - aioLastTick);  aioLastTick= aioThisTick;  fprintf X; }
 110# endif
 111#else /* !DEBUG */
 112# define FPRINTF(X)
 113#endif
 114
 115
 116/*** Socket types ***/
 117
 118#define TCPSocketType	 	0
 119#define UDPSocketType	 	1
 120
 121
 122/*** Resolver states ***/
 123
 124#define ResolverUninitialised	0
 125#define ResolverSuccess		1
 126#define ResolverBusy		2
 127#define ResolverError		3
 128
 129
 130/*** TCP Socket states ***/
 131
 132#define Invalid			-1
 133#define Unconnected		 0
 134#define WaitingForConnection	 1
 135#define Connected		 2
 136#define OtherEndClosed		 3
 137#define ThisEndClosed		 4
 138
 139#define LINGER_SECS		 1
 140
 141static int thisNetSession= 0;
 142static int one= 1;
 143
 144static char   localHostName[MAXHOSTNAMELEN];
 145static u_long localHostAddress;	/* GROSS IPv4 ASSUMPTION! */
 146
 147typedef struct privateSocketStruct
 148{
 149  int s;			/* Unix socket */
 150  int connSema;			/* connection io notification semaphore */
 151  int readSema;			/* read io notification semaphore */
 152  int writeSema;		/* write io notification semaphore */
 153  int sockState;		/* connection + data state */
 154  int sockError;		/* errno after socket error */
 155  struct sockaddr_in peer;	/* default send/recv address for UDP */
 156  int multiListen;		/* whether to listen for multiple connections */
 157  int acceptedSock;		/* a connection that has been accepted */
 158} privateSocketStruct;
 159
 160#define CONN_NOTIFY	(1<<0)
 161#define READ_NOTIFY	(1<<1)
 162#define WRITE_NOTIFY	(1<<2)
 163
 164#define PING(S,EVT)						\
 165{								\
 166  interpreterProxy->signalSemaphoreWithIndex((S)->EVT##Sema);	\
 167  FPRINTF((stderr, "notify %d %s\n", (S)->s, #EVT));		\
 168}
 169
 170#define notify(SOCK,MASK)						\
 171{									\
 172  if ((MASK) & CONN_NOTIFY)  PING(SOCK,conn);				\
 173  if ((MASK) & READ_NOTIFY)  PING(SOCK,read);				\
 174  if ((MASK) & WRITE_NOTIFY) PING(SOCK,write);				\
 175}
 176
 177
 178/*** Accessors for private socket members from a Squeak socket pointer ***/
 179
 180#define _PSP(S)		(((S)->privateSocketPtr))
 181#define PSP(S)		((privateSocketStruct *)((S)->privateSocketPtr))
 182
 183#define SOCKET(S)	(PSP(S)->s)
 184#define SOCKETSTATE(S)	(PSP(S)->sockState)
 185#define SOCKETERROR(S)	(PSP(S)->sockError)
 186#define SOCKETPEER(S)	(PSP(S)->peer)
 187
 188
 189/*** Resolver state ***/
 190
 191static char lastName[MAXHOSTNAMELEN+1];
 192static int  lastAddr= 0;
 193static int  lastError= 0;
 194static int  resolverSema= 0;
 195
 196/*** Variables ***/
 197
 198extern struct VirtualMachine *interpreterProxy;
 199int setHookFn;
 200
 201
 202static void acceptHandler(int, void *, int);
 203static void connectHandler(int, void *, int);
 204static void dataHandler(int, void *, int);
 205static void closeHandler(int, void *, int);
 206
 207
 208
 209/* this MUST be turned on if DEBUG is turned on in aio.c  */
 210
 211#ifdef AIO_DEBUG
 212char *socketHandlerName(aioHandler h)
 213{
 214  if (h == acceptHandler)     return "acceptHandler";
 215  if (h == connectHandler)    return "connectHandler";
 216  if (h == dataHandler)       return "dataHandler";
 217  if (h == closeHandler)      return "closeHandler";
 218  return "***unknownHandler***";
 219}
 220#endif
 221
 222
 223/*** module initialisation/shutdown ***/
 224
 225
 226sqInt socketInit(void)
 227{
 228  return 1;
 229}
 230
 231sqInt socketShutdown(void)
 232{
 233  /* shutdown the network */
 234  sqNetworkShutdown();
 235  return 1;
 236}
 237
 238
 239/***      miscellaneous sundries           ***/
 240
 241/* set linger on a connected stream */
 242
 243static void setLinger(int fd, int flag)
 244{
 245  struct linger linger= { flag, flag * LINGER_SECS };
 246  setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *)&linger, sizeof(linger));
 247}
 248
 249/* answer the hostname for the given IP address */
 250
 251static const char *addrToName(int netAddress)
 252{
 253  u_long nAddr;
 254  struct hostent *he;
 255
 256  lastError= 0;			/* for the resolver */
 257  nAddr= htonl(netAddress);
 258  if ((he= gethostbyaddr((char *)&nAddr, sizeof(nAddr), AF_INET)))
 259    return he->h_name;
 260  lastError= h_errno;		/* ditto */
 261  return "";
 262}
 263
 264/* answer the IP address for the given hostname */
 265
 266static int nameToAddr(char *hostName)
 267{
 268  struct hostent *he;
 269
 270  lastError= 0;			/* ditto */
 271  if ((he= gethostbyname(hostName)))
 272    return ntohl(*(long *)(he->h_addr_list[0]));
 273  lastError= h_errno;		/* and one more ditto */
 274  return 0;
 275}
 276
 277/* answer whether the given socket is valid in this net session */
 278
 279static int socketValid(SocketPtr s)
 280{
 281  if (s && s->privateSocketPtr && thisNetSession && (s->sessionID == thisNetSession))
 282    return true;
 283  interpreterProxy->success(false);
 284  return false;
 285}
 286
 287/* answer 1 if the given socket is readable,
 288          0 if read would block, or
 289         -1 if the socket is no longer connected */
 290
 291static int socketReadable(int s)
 292{
 293  char buf[1];
 294  int n= recv(s, (void *)buf, 1, MSG_PEEK);
 295  if (n > 0) return 1;
 296  if ((n < 0) && (errno == EWOULDBLOCK)) return 0;
 297  return -1;	/* EOF */
 298}
 299
 300
 301/* answer whether the socket can be written without blocking */
 302
 303static int socketWritable(int s)
 304{
 305  struct timeval tv= { 0, 0 };
 306  fd_set fds;
 307  
 308  FD_ZERO(&fds);
 309  FD_SET(s, &fds);
 310  return select(s+1, 0, &fds, 0, &tv) > 0;
 311}
 312
 313/* answer the error condition on the given socket */
 314
 315static int socketError(int s)
 316{
 317  int error= 0;
 318  socklen_t errsz= sizeof(error);
 319  /* Solaris helpfuly returns -1 if there is an error on the socket, so
 320     we can't check the success of the getsockopt call itself.  Ho hum. */
 321  getsockopt(s, SOL_SOCKET, SO_ERROR, (void *)&error, &errsz);
 322  return error;
 323}
 324
 325
 326/***     asynchronous io handlers       ***/
 327
 328
 329/* accept() can now be performed for the socket: call accept(),
 330   and replace the server socket with the new client socket
 331   leaving the client socket unhandled
 332*/
 333static void acceptHandler(int fd, void *data, int flags)
 334{
 335  privateSocketStruct *pss= (privateSocketStruct *)data;
 336  FPRINTF((stderr, "acceptHandler(%d, %p ,%d)\n", fd, data, flags));
 337  if (flags & AIO_X) /* -- exception */
 338    {
 339      /* error during listen() */
 340      aioDisable(fd);
 341      pss->sockError= socketError(fd);
 342      pss->sockState= Invalid;
 343      pss->s= -1;
 344      close(fd);
 345      fprintf(stderr, "acceptHandler: aborting server %d pss=%p\n", fd, pss);
 346    }
 347  else /* (flags & AIO_R) -- accept() is ready */
 348    {
 349      int newSock= accept(fd, 0, 0);
 350      if (newSock < 0)
 351	{
 352	  if (errno == ECONNABORTED)
 353	    {
 354	      /* let's just pretend this never happened */
 355	      aioHandle(fd, acceptHandler, AIO_RX);
 356	      return;
 357	    }
 358	  /* something really went wrong */
 359	  pss->sockError= errno;
 360	  pss->sockState= Invalid;
 361	  perror("acceptHandler");
 362	  aioDisable(fd);
 363	  close(fd);
 364	  fprintf(stderr, "acceptHandler: aborting server %d pss=%p\n", fd, pss);
 365	}
 366      else /* newSock >= 0 -- connection accepted */
 367	{
 368	  pss->sockState= Connected;
 369	  setLinger(newSock, 1);
 370	  if (pss->multiListen)
 371	    {
 372	      pss->acceptedSock= newSock;
 373	    }
 374	  else /* traditional listen -- replace server with client in-place */
 375	    {
 376	      aioDisable(fd);
 377	      close(fd);
 378	      pss->s= newSock;
 379	      aioEnable(newSock, pss, 0);
 380	    }
 381	}
 382    }
 383  notify(pss, CONN_NOTIFY);
 384}
 385
 386
 387/* connect() has completed: check errors, leaving the socket unhandled */
 388
 389static void connectHandler(int fd, void *data, int flags)
 390{
 391  privateSocketStruct *pss= (privateSocketStruct *)data;
 392  FPRINTF((stderr, "connectHandler(%d, %p, %d)\n", fd, data, flags));
 393  if (flags & AIO_X) /* -- exception */
 394    {
 395      /* error during asynchronous connect() */
 396      aioDisable(fd);
 397      pss->sockError= socketError(fd);
 398      pss->sockState= Unconnected;
 399      perror("connectHandler");
 400    }
 401  else /* (flags & AIO_W) -- connect completed */
 402    {
 403      /* connect() has completed */
 404      int error= socketError(fd);
 405      if (error)
 406	{
 407	  FPRINTF((stderr, "connectHandler: error %d (%s)\n", error, strerror(error)));
 408	  pss->sockError= error;
 409	  pss->sockState= Unconnected;
 410	}
 411      else
 412	{
 413	  pss->sockState= Connected;
 414	  setLinger(pss->s, 1);
 415	}
 416    }
 417  notify(pss, CONN_NOTIFY);
 418}
 419
 420
 421/* read or write data transfer is now possible for the socket. */
 422
 423static void dataHandler(int fd, void *data, int flags)
 424{
 425  privateSocketStruct *pss= (privateSocketStruct *)data;
 426  FPRINTF((stderr, "dataHandler(%d=%d, %p, %d)\n", fd, pss->s, data, flags));
 427
 428  if (pss == NULL)
 429    {
 430      fprintf(stderr, "dataHandler: pss is NULL fd=%d data=%p flags=0x%x\n", fd, data, flags);
 431      return;
 432    }
 433
 434  if (flags & AIO_R)
 435    {
 436      int n= socketReadable(fd);
 437      if (n == 0)
 438	{
 439	  fprintf(stderr, "dataHandler: selected socket fd=%d flags=0x%x would block (why?)\n", fd, flags);
 440	}
 441      if (n != 1)
 442	{
 443	  pss->sockError= socketError(fd);
 444	  pss->sockState= OtherEndClosed;
 445	}
 446    }
 447  if (flags & AIO_X)
 448    {
 449      /* assume out-of-band data has arrived */
 450      /* NOTE: Squeak's socket interface is currently incapable of reading
 451       *       OOB data.  We have no choice but to discard it.  Ho hum. */
 452      char buf[1];
 453      int n= recv(fd, (void *)buf, 1, MSG_OOB);
 454      if (n == 1) fprintf(stderr, "socket: received OOB data: %02x\n", buf[0]);
 455    }
 456  if (flags & AIO_R) notify(pss, READ_NOTIFY);
 457  if (flags & AIO_W) notify(pss, WRITE_NOTIFY);
 458}
 459
 460
 461/* a non-blocking close() has completed -- finish tidying up */
 462
 463static void closeHandler(int fd, void *data, int flags)
 464{
 465  privateSocketStruct *pss= (privateSocketStruct *)data;
 466  aioDisable(fd);
 467  FPRINTF((stderr, "closeHandler(%d, %p, %d)\n", fd, data, flags));
 468  pss->sockState= Unconnected;
 469  pss->s= -1;
 470  notify(pss, CONN_NOTIFY);
 471}
 472
 473
 474/***     Squeak network functions        ***/
 475
 476
 477/* start a new network session */
 478
 479sqInt sqNetworkInit(sqInt resolverSemaIndex)
 480{
 481  if (0 != thisNetSession)
 482    return 0;  /* already initialised */
 483  gethostname(localHostName, MAXHOSTNAMELEN);
 484  localHostAddress= nameToAddr(localHostName);
 485  thisNetSession= clock() + time(0);
 486  if (0 == thisNetSession)
 487    thisNetSession= 1;  /* 0 => uninitialised */
 488  resolverSema= resolverSemaIndex;
 489  return 0;
 490}
 491
 492
 493/* terminate the current network session (invalidates all open sockets) */
 494
 495void sqNetworkShutdown(void)
 496{
 497  thisNetSession= 0;
 498  resolverSema= 0;
 499  aioFini();
 500}
 501
 502
 503
 504/***  Squeak Generic Socket Functions   ***/
 505
 506
 507/* create a new socket */
 508
 509void sqSocketCreateNetTypeSocketTypeRecvBytesSendBytesSemaID(SocketPtr s, sqInt netType, sqInt socketType, sqInt recvBufSize, sqInt sendBufSize, sqInt semaIndex)
 510{
 511  sqSocketCreateNetTypeSocketTypeRecvBytesSendBytesSemaIDReadSemaIDWriteSemaID(s, netType, socketType,recvBufSize, sendBufSize, semaIndex, semaIndex, semaIndex);
 512}
 513
 514void sqSocketCreateNetTypeSocketTypeRecvBytesSendBytesSemaIDReadSemaIDWriteSemaID(SocketPtr s, sqInt netType, sqInt socketType, sqInt recvBufSize, sqInt sendBufSize, sqInt semaIndex, sqInt readSemaIndex, sqInt writeSemaIndex)
 515{
 516  int newSocket= -1;
 517  privateSocketStruct *pss;
 518
 519  s->sessionID= 0;
 520  if (TCPSocketType == socketType)
 521    {
 522      /* --- TCP --- */
 523      newSocket= socket(AF_INET, SOCK_STREAM, 0);
 524    }
 525  else if (UDPSocketType == socketType)
 526    {
 527      /* --- UDP --- */
 528      newSocket= socket(AF_INET, SOCK_DGRAM, 0);
 529    }
 530  if (-1 == newSocket)
 531    {
 532      /* socket() failed, or incorrect socketType */
 533      interpreterProxy->success(false);
 534      return;
 535    }
 536  setsockopt(newSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one));
 537  /* private socket structure */
 538  pss= (privateSocketStruct *)calloc(1, sizeof(privateSocketStruct));
 539  if (pss == NULL)
 540    {
 541      fprintf(stderr, "acceptFrom: out of memory\n");
 542      interpreterProxy->success(false);
 543      return;
 544    }
 545  pss->s= newSocket;
 546  pss->connSema= semaIndex;
 547  pss->readSema= readSemaIndex;
 548  pss->writeSema= writeSemaIndex;
 549
 550  /* UDP sockets are born "connected" */
 551  if (UDPSocketType == socketType)
 552    {
 553      pss->sockState= Connected;
 554      aioEnable(pss->s, pss, 0);
 555    }
 556  else
 557    {
 558      pss->sockState= Unconnected;
 559    }
 560  pss->sockError= 0;
 561  /* initial UDP peer := wildcard */
 562  memset(&pss->peer, 0, sizeof(pss->peer));
 563  pss->peer.sin_family= AF_INET;
 564  pss->peer.sin_port= 0;
 565  pss->peer.sin_addr.s_addr= INADDR_ANY;
 566  /* Squeak socket */
 567  s->sessionID= thisNetSession;
 568  s->socketType= socketType;
 569  s->privateSocketPtr= pss;
 570  FPRINTF((stderr, "create(%d) -> %lx\n", SOCKET(s), (unsigned long)PSP(s)));
 571  /* Note: socket is in BLOCKING mode until aioEnable is called for it! */
 572}
 573
 574
 575/* return the state of a socket */
 576
 577sqInt sqSocketConnectionStatus(SocketPtr s)
 578{
 579  if (!socketValid(s))
 580    return Invalid;
 581  /* we now know that the net session is valid, so if state is Invalid... */
 582  if (SOCKETSTATE(s) == Invalid)	/* see acceptHandler() */
 583    {
 584      fprintf(stderr, "socketStatus: freeing invalidated pss=%p\n", PSP(s));
 585      /*free(PSP(s));*/	/* this almost never happens -- safer not to free()?? */
 586      _PSP(s)= 0;
 587      interpreterProxy->success(false);
 588      return Invalid;
 589    }
 590#if 0
 591  /* check for connection closed by peer */
 592  if (SOCKETSTATE(s) == Connected)
 593    {
 594      int fd= SOCKET(s);
 595      int n=  socketReadable(fd);
 596      if (n < 0)
 597	{
 598	  FPRINTF((stderr, "socketStatus(%d): detected other end closed\n", fd));
 599	  SOCKETSTATE(s)= OtherEndClosed;
 600	}
 601    }
 602#endif
 603  FPRINTF((stderr, "socketStatus(%d) -> %d\n", SOCKET(s), SOCKETSTATE(s)));
 604  return SOCKETSTATE(s);
 605}
 606
 607
 608
 609/* TCP => start listening for incoming connections.
 610 * UDP => associate the local port number with the socket.
 611 */
 612void sqSocketListenOnPort(SocketPtr s, sqInt port)
 613{
 614  sqSocketListenOnPortBacklogSize(s, port, 1);
 615}
 616
 617void sqSocketListenOnPortBacklogSizeInterface(SocketPtr s, sqInt port, sqInt backlogSize, sqInt addr)
 618{
 619  struct sockaddr_in saddr;
 620
 621  if (!socketValid(s))
 622    return;
 623
 624  /* only TCP sockets have a backlog */
 625  if ((backlogSize > 1) && (s->socketType != TCPSocketType))
 626    {
 627      interpreterProxy->success(false);
 628      return;
 629    }
 630
 631  PSP(s)->multiListen= (backlogSize > 1);
 632  FPRINTF((stderr, "listenOnPortBacklogSize(%d, %d)\n", SOCKET(s), backlogSize));
 633  memset(&saddr, 0, sizeof(saddr));
 634  saddr.sin_family= AF_INET;
 635  saddr.sin_port= htons((short)port);
 636  saddr.sin_addr.s_addr= htonl(addr);
 637  bind(SOCKET(s), (struct sockaddr*) &saddr, sizeof(saddr));
 638  if (TCPSocketType == s->socketType)
 639    {
 640      /* --- TCP --- */
 641      listen(SOCKET(s), backlogSize);
 642      SOCKETSTATE(s)= WaitingForConnection;
 643      aioEnable(SOCKET(s), PSP(s), 0);
 644      aioHandle(SOCKET(s), acceptHandler, AIO_RX); /* R => accept() */
 645    }
 646  else
 647    {
 648      /* --- UDP --- */
 649    }
 650}
 651
 652void sqSocketListenOnPortBacklogSize(SocketPtr s, sqInt port, sqInt backlogSize)
 653{
 654  sqSocketListenOnPortBacklogSizeInterface(s, port, backlogSize, INADDR_ANY);
 655}
 656
 657/* TCP => open a connection.
 658 * UDP => set remote address.
 659 */
 660void sqSocketConnectToPort(SocketPtr s, sqInt addr, sqInt port)
 661{
 662  struct sockaddr_in saddr;
 663
 664  if (!socketValid(s))
 665    return;
 666  FPRINTF((stderr, "connectTo(%d)\n", SOCKET(s)));
 667  memset(&saddr, 0, sizeof(saddr));
 668  saddr.sin_family= AF_INET;
 669  saddr.sin_port= htons((short)port);
 670  saddr.sin_addr.s_addr= htonl(addr);
 671  if (UDPSocketType == s->socketType)
 672    {
 673      /* --- UDP --- */
 674      if (SOCKET(s) >= 0)
 675	{
 676	  memcpy((void *)&SOCKETPEER(s), (void *)&saddr, sizeof(SOCKETPEER(s)));
 677	  SOCKETSTATE(s)= Connected;
 678	}
 679    }
 680  else
 681    {
 682      /* --- TCP --- */
 683      int result;
 684      aioEnable(SOCKET(s), PSP(s), 0);
 685      result= connect(SOCKET(s), (struct sockaddr *)&saddr, sizeof(saddr));
 686      FPRINTF((stderr, "connect() => %d\n", result));
 687      if (result == 0)
 688	{
 689	  /* connection completed synchronously */
 690	  SOCKETSTATE(s)= Connected;
 691	  notify(PSP(s), CONN_NOTIFY);
 692	  setLinger(SOCKET(s), 1);
 693	}
 694      else
 695	{
 696	  if (errno == EINPROGRESS || errno == EWOULDBLOCK)
 697	    {
 698	      /* asynchronous connection in progress */
 699	      SOCKETSTATE(s)= WaitingForConnection;
 700	      aioHandle(SOCKET(s), connectHandler, AIO_WX);  /* W => connect() */
 701	    }
 702	  else
 703	    {
 704	      /* connection error */
 705	      perror("sqConnectToPort");
 706	      SOCKETSTATE(s)= Unconnected;
 707	      SOCKETERROR(s)= errno;
 708	      notify(PSP(s), CONN_NOTIFY);
 709	    }
 710	}
 711    }
 712}
 713
 714
 715void sqSocketAcceptFromRecvBytesSendBytesSemaID(SocketPtr s, SocketPtr serverSocket, sqInt recvBufSize, sqInt sendBufSize, sqInt semaIndex)
 716{
 717  sqSocketAcceptFromRecvBytesSendBytesSemaIDReadSemaIDWriteSemaID(s, serverSocket, recvBufSize, sendBufSize, semaIndex, semaIndex, semaIndex);
 718}
 719
 720
 721void sqSocketAcceptFromRecvBytesSendBytesSemaIDReadSemaIDWriteSemaID(SocketPtr s, SocketPtr serverSocket, sqInt recvBufSize, sqInt sendBufSize, sqInt semaIndex, sqInt readSemaIndex, sqInt writeSemaIndex)
 722{
 723  /* The image has already called waitForConnection, so there is no
 724     need to signal the server's connection semaphore again. */
 725
 726  struct privateSocketStruct *pss;
 727
 728  FPRINTF((stderr, "acceptFrom(%p, %d)\n", s, SOCKET(serverSocket)));
 729
 730  /* sanity checks */
 731  if (!socketValid(serverSocket) || !PSP(serverSocket)->multiListen)
 732    {
 733      FPRINTF((stderr, "accept failed: (multi->%d)\n", PSP(serverSocket)->multiListen));
 734      interpreterProxy->success(false);
 735      return;
 736    }
 737
 738  /* check that a connection is there */
 739  if (PSP(serverSocket)->acceptedSock < 0)
 740    {
 741      fprintf(stderr, "acceptFrom: no socket available\n");
 742      interpreterProxy->success(false);
 743      return;
 744    }
 745
 746  /* got connection -- fill in the structure */
 747  s->sessionID= 0;
 748  pss= (privateSocketStruct *)calloc(1, sizeof(privateSocketStruct));
 749  if (pss == NULL)
 750    {
 751      fprintf(stderr, "acceptFrom: out of memory\n");
 752      interpreterProxy->success(false);
 753      return;
 754    }
 755
 756  _PSP(s)= pss;
 757  pss->s= PSP(serverSocket)->acceptedSock;
 758  PSP(serverSocket)->acceptedSock= -1;
 759  SOCKETSTATE(serverSocket)= WaitingForConnection;
 760  aioHandle(SOCKET(serverSocket), acceptHandler, AIO_RX);
 761  s->sessionID= thisNetSession;
 762  pss->connSema= semaIndex;
 763  pss->readSema= readSemaIndex;
 764  pss->writeSema= writeSemaIndex;
 765  pss->sockState= Connected;
 766  pss->sockError= 0;
 767  aioEnable(SOCKET(s), PSP(s), 0);
 768}
 769
 770
 771/* close the socket */
 772
 773void sqSocketCloseConnection(SocketPtr s)
 774{
 775  int result= 0;
 776
 777  if (!socketValid(s))
 778    return;
 779
 780  FPRINTF((stderr, "closeConnection(%d)\n", SOCKET(s)));
 781
 782  if (SOCKET(s) < 0)
 783    return;	/* already closed */
 784
 785  aioDisable(SOCKET(s));
 786  SOCKETSTATE(s)= ThisEndClosed;
 787  result= close(SOCKET(s));
 788  if ((result == -1) && (errno != EWOULDBLOCK))
 789    {
 790      /* error */
 791      SOCKETSTATE(s)= Unconnected;
 792      SOCKETERROR(s)= errno;
 793      notify(PSP(s), CONN_NOTIFY);
 794      perror("closeConnection");
 795    }
 796  else if (0 == result)
 797    {
 798      /* close completed synchronously */
 799      SOCKETSTATE(s)= Unconnected;
 800      FPRINTF((stderr, "closeConnection: disconnected\n"));
 801      SOCKET(s)= -1;
 802    }
 803  else
 804    {
 805      /* asynchronous close in progress */
 806      SOCKETSTATE(s)= ThisEndClosed;
 807      aioHandle(SOCKET(s), closeHandler, AIO_RWX);  /* => close() done */
 808      FPRINTF((stderr, "closeConnection: deferred [aioHandle is set]\n"));
 809    }
 810}
 811
 812
 813/* close the socket without lingering */
 814
 815void sqSocketAbortConnection(SocketPtr s)
 816{
 817  FPRINTF((stderr, "abortConnection(%d)\n", SOCKET(s)));
 818  if (!socketValid(s))
 819    return;
 820  setLinger(SOCKET(s), 0);
 821  sqSocketCloseConnection(s);
 822}
 823
 824
 825/* Release the resources associated with this socket. 
 826   If a connection is open, abort it. */
 827
 828void sqSocketDestroy(SocketPtr s)
 829{
 830  if (!socketValid(s))
 831    return;
 832
 833  FPRINTF((stderr, "destroy(%d)\n", SOCKET(s)));
 834
 835  if (SOCKET(s))
 836    sqSocketAbortConnection(s);		/* close if necessary */
 837
 838  if (PSP(s))
 839    free(PSP(s));			/* release private struct */
 840
 841  _PSP(s)= 0;
 842}
 843
 844
 845/* answer the OS error code for the last socket operation */
 846
 847sqInt sqSocketError(SocketPtr s)
 848{
 849  if (!socketValid(s))
 850    return -1;
 851  return SOCKETERROR(s);
 852}
 853
 854
 855/* return the local IP address bound to a socket */
 856
 857sqInt sqSocketLocalAddress(SocketPtr s)
 858{
 859  struct sockaddr_in saddr;
 860  socklen_t saddrSize= sizeof(saddr);
 861
 862  if (!socketValid(s))
 863    return -1;
 864  if (getsockname(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize)
 865      || (AF_INET != saddr.sin_family))
 866    return 0;
 867  return ntohl(saddr.sin_addr.s_addr);
 868}
 869
 870
 871/* return the peer's IP address */
 872
 873sqInt sqSocketRemoteAddress(SocketPtr s)
 874{
 875  struct sockaddr_in saddr;
 876  socklen_t saddrSize= sizeof(saddr);
 877
 878  if (!socketValid(s))
 879    return -1;
 880  if (TCPSocketType == s->socketType)
 881    {
 882      /* --- TCP --- */
 883      if (getpeername(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize)
 884	  || (AF_INET != saddr.sin_family))
 885	return 0;
 886      return ntohl(saddr.sin_addr.s_addr);
 887    }
 888  /* --- UDP --- */
 889  return ntohl(SOCKETPEER(s).sin_addr.s_addr);
 890}
 891
 892
 893/* return the local port number of a socket */
 894
 895sqInt sqSocketLocalPort(SocketPtr s)
 896{
 897  struct sockaddr_in saddr;
 898  socklen_t saddrSize= sizeof(saddr);
 899
 900  if (!socketValid(s))
 901    return -1;
 902  if (getsockname(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize)
 903      || (AF_INET != saddr.sin_family))
 904    return 0;
 905  return ntohs(saddr.sin_port);
 906}
 907
 908
 909/* return the peer's port number */
 910
 911sqInt sqSocketRemotePort(SocketPtr s)
 912{
 913  struct sockaddr_in saddr;
 914  socklen_t saddrSize= sizeof(saddr);
 915
 916  if (!socketValid(s))
 917    return -1;
 918  if (TCPSocketType == s->socketType)
 919    {
 920      /* --- TCP --- */
 921      if (getpeername(SOCKET(s), (struct sockaddr *)&saddr, &saddrSize)
 922	  || (AF_INET != saddr.sin_family))
 923	return 0;
 924      return ntohs(saddr.sin_port);
 925    }
 926  /* --- UDP --- */
 927  return ntohs(SOCKETPEER(s).sin_port);
 928}
 929
 930
 931/* answer whether the socket has data available for reading:
 932   if the socket is not connected, answer "false";
 933   if the socket is open and data can be read, answer "true".
 934   if the socket is open and no data is currently readable, answer "false";
 935   if the socket is closed by peer, change the state to OtherEndClosed
 936	and answer "false";
 937*/
 938sqInt sqSocketReceiveDataAvailable(SocketPtr s)
 939{
 940  if (!socketValid(s)) return false;
 941  if (SOCKETSTATE(s) == Connected)
 942    {
 943      int fd= SOCKET(s);
 944      int n=  socketReadable(fd);
 945      if (n > 0)
 946	{
 947	  FPRINTF((stderr, "receiveDataAvailable(%d) -> true\n", fd));
 948	  return true;
 949	}
 950      else if (n < 0)
 951	{
 952	  FPRINTF((stderr, "receiveDataAvailable(%d): other end closed\n", fd));
 953	  SOCKETSTATE(s)= OtherEndClosed;
 954	}
 955    }
 956  else /* (SOCKETSTATE(s) != Connected) */
 957    {
 958      FPRINTF((stderr, "receiveDataAvailable(%d): socket not connected\n", SOCKET(s)));
 959    }
 960  aioHandle(SOCKET(s), dataHandler, AIO_RX);
 961  FPRINTF((stderr, "receiveDataAvailable(%d) -> false [aioHandle is set]\n", SOCKET(s)));
 962  return false;
 963}
 964
 965
 966/* answer whether the socket has space to receive more data */
 967
 968sqInt sqSocketSendDone(SocketPtr s)
 969{
 970  if (!socketValid(s))
 971    return false;
 972  if (SOCKETSTATE(s) == Connected)
 973    {
 974      if (socketWritable(SOCKET(s))) return true;
 975      aioHandle(SOCKET(s), dataHandler, AIO_WX);
 976    }
 977  return false;
 978}
 979
 980
 981/* read data from the socket s into buf for at most bufSize bytes.
 982   answer the number actually read.  For UDP, fill in the peer's address
 983   with the approriate value.
 984*/
 985sqInt sqSocketReceiveDataBufCount(SocketPtr s, char *buf, sqInt bufSize)
 986{
 987  int nread= 0;
 988
 989  if (!socketValid(s))
 990    return -1;
 991  if (UDPSocketType == s->socketType)
 992    {
 993      /* --- UDP --- */
 994      socklen_t addrSize= sizeof(SOCKETPEER(s));
 995      if ((nread= recvfrom(SOCKET(s), buf, bufSize, 0, (struct sockaddr *)&SOCKETPEER(s), &addrSize)) <= 0)
 996	{
 997	  if ((nread == -1) && (errno == EWOULDBLOCK))
 998	    {
 999	      FPRINTF((stderr, "UDP receiveData(%d) < 1 [blocked]\n", SOCKET(s)));
1000	      return 0;
1001	    }
1002	  SOCKETERROR(s)= errno;
1003	  FPRINTF((stderr, "UDP receiveData(%d) < 1 [a:%d]\n", SOCKET(s), errno));
1004	  return 0;
1005	}
1006    }
1007  else
1008    {
1009      /* --- TCP --- */
1010      if ((nread= read(SOCKET(s), buf, bufSize)) <= 0)
1011	{
1012	  if ((nread == -1) && (errno == EWOULDBLOCK))
1013	    {
1014	      FPRINTF((stderr, "TCP receiveData(%d) < 1 [blocked]\n", SOCKET(s)));
1015	      return 0;
1016	    }
1017	  /* connection reset */
1018	  SOCKETSTATE(s)= OtherEndClosed;
1019	  SOCKETERROR(s)= errno;
1020	  FPRINTF((stderr, "TCP receiveData(%d) < 1 [b:%d]\n", SOCKET(s), errno));
1021	  notify(PSP(s), CONN_NOTIFY);
1022	  return 0;
1023	}
1024    }
1025  /* read completed synchronously */
1026  FPRINTF((stderr, "receiveData(%d) done = %d\n", SOCKET(s), nread));
1027  return nread;
1028}
1029
1030
1031/* write data to the socket s from buf for at most bufSize bytes.
1032   answer the number of bytes actually written.
1033*/ 
1034sqInt sqSocketSendDataBufCount(SocketPtr s, char *buf, sqInt bufSize)
1035{
1036  int nsent= 0;
1037
1038  if (!socketValid(s))
1039    return -1;
1040
1041  if (UDPSocketType == s->socketType)
1042    {
1043      /* --- UDP --- */
1044      FPRINTF((stderr, "UDP sendData(%d, %d)\n", SOCKET(s), bufSize));
1045      if ((nsent= sendto(SOCKET(s), buf, bufSize, 0, (struct sockaddr *)&SOCKETPEER(s), sizeof(SOCKETPEER(s)))) <= 0)
1046	{
1047	  if (errno == EWOULDBLOCK)	/* asynchronous write in progress */
1048	    return 0;
1049	  FPRINTF((stderr, "UDP send failed\n"));
1050	  SOCKETERROR(s)= errno;
1051	  return 0;
1052	}
1053    }
1054  else
1055    {
1056      /* --- TCP --- */
1057      FPRINTF((stderr, "TCP sendData(%d, %d)\n", SOCKET(s), bufSize));
1058      if ((nsent= write(SOCKET(s), buf, bufSize)) <= 0)
1059	{
1060	  if ((nsent == -1) && (errno == EWOULDBLOCK))
1061	    {
1062	      FPRINTF((stderr, "TCP sendData(%d, %d) -> %d [blocked]",
1063		       SOCKET(s), bufSize, nsent));
1064	      return 0;
1065	    }
1066	  else
1067	    {
1068	      /* error: most likely "connection closed by peer" */
1069	      SOCKETSTATE(s)= OtherEndClosed;
1070	      SOCKETERROR(s)= errno;
1071	      FPRINTF((stderr, "TCP write failed -> %d", errno));
1072	      return 0;
1073	    }
1074	}
1075    }
1076  /* write completed synchronously */
1077  FPRINTF((stderr, "sendData(%d) done = %d\n", SOCKET(s), nsent));
1078  return nsent;
1079}
1080
1081
1082/* read data from the UDP socket s into buf for at most bufSize bytes.
1083   answer the number of bytes actually read.
1084*/ 
1085sqInt sqSocketReceiveUDPDataBufCountaddressportmoreFlag(SocketPtr s, char *buf, sqInt bufSize,  sqInt *address,  sqInt *port, sqInt *moreFlag)
1086{
1087  if (socketValid(s) && (UDPSocketType == s->socketType))
1088    {
1089      struct sockaddr_in saddr;
1090      socklen_t addrSize= sizeof(saddr);
1091
1092      FPRINTF((stderr, "recvFrom(%d)\n", SOCKET(s)));
1093      memset(&saddr, 0, sizeof(saddr));
1094      { 
1095	int nread= recvfrom(SOCKET(s), buf, bufSize, 0, (struct sockaddr *)&saddr, &addrSize);
1096	if (nread >= 0)
1097	  {
1098	    *address= ntohl(saddr.sin_addr.s_addr);
1099	    *port= ntohs(saddr.sin_port);
1100	    return nread;
1101	  }
1102	if (errno == EWOULDBLOCK)	/* asynchronous read in progress */
1103	  return 0;
1104	SOCKETERROR(s)= errno;
1105	FPRINTF((stderr, "receiveData(%d)= %da\n", SOCKET(s), 0));
1106      }
1107    }
1108  interpreterProxy->success(false);
1109  return 0;
1110}
1111
1112
1113/* write data to the UDP socket s from buf for at most bufSize bytes.
1114 * answer the number of bytes actually written.
1115 */ 
1116sqInt sqSockettoHostportSendDataBufCount(SocketPtr s, sqInt address, sqInt port, char *buf, sqInt bufSize)
1117{
1118  if (socketValid(s) && (UDPSocketType == s->socketType))
1119    {
1120      struct sockaddr_in saddr;
1121
1122      FPRINTF((stderr, "sendTo(%d)\n", SOCKET(s)));
1123      memset(&saddr, 0, sizeof(saddr));
1124      saddr.sin_family= AF_INET;
1125      saddr.sin_port= htons((short)port);
1126      saddr.sin_addr.s_addr= htonl(address);
1127      {
1128	int nsent= sendto(SOCKET(s), buf, bufSize, 0, (struct sockaddr *)&saddr, sizeof(saddr));
1129	if (nsent >= 0)
1130	  return nsent;
1131	
1132	if (errno == EWOULDBLOCK)	/* asynchronous write in progress */
1133	  return 0;
1134	FPRINTF((stderr, "UDP send failed\n"));
1135	SOCKETERROR(s)= errno;
1136      }
1137    }
1138  interpreterProxy->success(false);
1139  return 0;
1140}
1141
1142
1143/*** socket options ***/
1144
1145
1146/* NOTE: we only support the portable options here as an incentive for
1147         people to write portable Squeak programs.  If you need
1148         non-portable socket options then go write yourself a plugin
1149         specific to your platform.  This decision is unilateral and
1150         non-negotiable.  - ikp
1151   NOTE: we only support the integer-valued options because the code
1152	 in SocketPlugin doesn't seem able to cope with the others.
1153	 (Personally I think that things like SO_SNDTIMEO et al would
1154	 by far more interesting than the majority of things on this
1155	 list, but there you go...)
1156   NOTE: if your build fails because of a missing option in this list,
1157	 simply DELETE THE OPTION (or comment it out) and then send
1158	 me mail (ian.piumarta@inria.fr) to let me know about it.
1159 */
1160
1161typedef struct
1162{
1163  char *name;		/* name as known to Squeak */
1164  int   optlevel;	/* protocol level */
1165  int   optname;	/* name as known to Unix */
1166} socketOption;
1167
1168#ifndef SOL_IP
1169# define SOL_IP IPPROTO_IP
1170#endif
1171
1172#ifndef SOL_UDP
1173# define SOL_UDP IPPROTO_UDP
1174#endif
1175
1176#ifndef SOL_TCP
1177# define SOL_TCP IPPROTO_TCP
1178#endif
1179
1180static socketOption socketOptions[]= {
1181  { "SO_DEBUG",				SOL_SOCKET,	SO_DEBUG },
1182  { "SO_REUSEADDR",			SOL_SOCKET,	SO_REUSEADDR },
1183  { "SO_DONTROUTE",			SOL_SOCKET,	SO_DONTROUTE },
1184  { "SO_BROADCAST",			SOL_SOCKET,	SO_BROADCAST },
1185  { "SO_SNDBUF",			SOL_SOCKET,	SO_SNDBUF },
1186  { "SO_RCVBUF",			SOL_SOCKET,	SO_RCVBUF },
1187  { "SO_KEEPALIVE",			SOL_SOCKET,	SO_KEEPALIVE },
1188  { "SO_OOBINLINE",			SOL_SOCKET,	SO_OOBINLINE },
1189  { "SO_LINGER",			SOL_SOCKET,	SO_LINGER },
1190  { "IP_TTL",				SOL_IP,		IP_TTL },
1191  { "IP_HDRINCL",			SOL_IP,		IP_HDRINCL },
1192  { "IP_MULTICAST_IF",			SOL_IP,		IP_MULTICAST_IF },
1193  { "IP_MULTICAST_TTL",			SOL_IP,		IP_MULTICAST_TTL },
1194  { "IP_MULTICAST_LOOP",		SOL_IP,		IP_MULTICAST_LOOP },
1195#ifdef IP_ADD_MEMBERSHIP
1196  { "IP_ADD_MEMBERSHIP",		SOL_IP,		IP_ADD_MEMBERSHIP },
1197  { "IP_DROP_MEMBERSHIP",		SOL_IP,		IP_DROP_MEMBERSHIP },
1198#endif
1199  { "TCP_MAXSEG",			SOL_TCP,	TCP_MAXSEG },
1200  { "TCP_NODELAY",			SOL_TCP,	TCP_NODELAY },
1201#ifdef SO_REUSEPORT
1202  { "SO_REUSEPORT",			SOL_SOCKET,	SO_REUSEPORT },
1203#endif
1204#if 0 /*** deliberately unsupported options -- do NOT enable these! ***/
1205  { "SO_PRIORITY",			SOL_SOCKET,	SO_PRIORITY },
1206  { "SO_RCVLOWAT",			SOL_SOCKET,	SO_RCVLOWAT },
1207  { "SO_SNDLOWAT",			SOL_SOCKET,	SO_SNDLOWAT },
1208  { "IP_RCVOPTS",			SOL_IP,		IP_RCVOPTS },
1209  { "IP_RCVDSTADDR",			SOL_IP,		IP_RCVDSTADDR },
1210  { "UDP_CHECKSUM",			SOL_UDP,	UDP_CHECKSUM },
1211  { "TCP_ABORT_THRESHOLD",		SOL_TCP,	TCP_ABORT_THRESHOLD },
1212  { "TCP_CONN_NOTIFY_THRESHOLD",	SOL_TCP,	TCP_CONN_NOTIFY_THRESHOLD },
1213  { "TCP_CONN_ABORT_THRESHOLD",		SOL_TCP,	TCP_CONN_ABORT_THRESHOLD },
1214  { "TCP_NOTIFY_THRESHOLD",		SOL_TCP,	TCP_NOTIFY_THRESHOLD },
1215  { "TCP_URGENT_PTR_TYPE",		SOL_TCP,	TCP_URGENT_PTR_TYPE },
1216#endif
1217  { (char *)0,				0,		0 }
1218};
1219
1220
1221static socketOption *findOption(char *name, size_t nameSize)
1222{
1223  if (nameSize < 32)
1224    {
1225      socketOption *opt= 0;
1226      char buf[32];
1227      buf[nameSize]= '\0';
1228      strncpy(buf, name, nameSize);
1229      for (opt= socketOptions; opt->name != 0; ++opt)
1230	if (!strcmp(buf, opt->name))
1231	  return opt;
1232      fprintf(stderr, "SocketPlugin: ignoring unknown option '%s'\n", buf);
1233    }
1234  return 0;
1235}
1236
1237
1238/* set the given option for the socket.  the option comes in as a
1239 * String.  (why on earth we might think this a good idea eludes me
1240 * ENTIRELY, so... if the string doesn't smell like an integer then we
1241 * copy it verbatim, assuming it's really a ByteArray pretending to be
1242 * a struct.  caveat hackor.)
1243 */
1244sqInt sqSocketSetOptionsoptionNameStartoptionNameSizeoptionValueStartoptionValueSizereturnedValue(SocketPtr s, char *optionName, sqInt optionNameSize, char *optionValue, sqInt optionValueSize, sqInt *result)
1245{
1246  if (socketValid(s))
1247    {
1248      socketOption *opt= findOption(optionName, (size_t)optionNameSize);
1249      if (opt != 0)
1250	{
1251	  int   val= 0;
1252	  char  buf[32];
1253	  char *endptr;
1254	  /* this is JUST PLAIN WRONG (I mean the design in the image rather
1255	     than the implementation here, which is probably correct
1256	     w.r.t. the broken design) */
1257	  if (optionValueSize > sizeof(buf) - 1)
1258	    goto barf;
1259
1260	  memset((void *)buf, 0, sizeof(buf));
1261	  memcpy((void *)buf, optionValue, optionValueSize);
1262	  if (optionValueSize == 1)	/* character `1' or `0' */
1263	    {
1264	      val= strtol(buf, &endptr, 0);
1265	      if (endptr != buf)
1266		{
1267		  memcpy((void *)buf, (void *)&val, sizeof(val));
1268		  optionValueSize= sizeof(val);
1269		}
1270	    }
1271	  if ((setsockopt(PSP(s)->s, opt->optlevel, opt->optname,
1272			  (const void *)buf, optionValueSize)) < 0)
1273	    {
1274	      perror("setsockopt");
1275	      goto barf;
1276	    }
1277	  /* it isn't clear what we're supposed to return here, since
1278	     setsockopt isn't supposed to have any value-result parameters
1279	     (go grok that `const' on the buffer argument if you don't
1280	     believe me).  the image says "the result of the negotiated
1281	     value".  what the fuck is there to negotiate?  either
1282	     setsockopt sets the value or it barfs.  and i'm not about to go
1283	     calling getsockopt just to see if the value got changed or not
1284	     (the image should send getOption: to the Socket if it really
1285	     wants to know).  if the following is wrong then I could
1286	     probably care (a lot) less...  fix the logic in the image and
1287	     then maybe i'll care about fixing the logic in here.  (i know
1288	     that isn't very helpful, but it's 05:47 in the morning and i'm
1289	     severely grumpy after fixing several very unpleasant bugs that
1290	     somebody introduced into this file while i wasn't looking.)  */
1291	  *result= val;
1292	  return 0;
1293	}
1294    }
1295 barf:
1296  interpreterProxy->success(false);
1297  return false;
1298}
1299
1300
1301/* query the socket for the given option.  */
1302sqInt sqSocketGetOptionsoptionNameStartoptionNameSizereturnedValue(SocketPtr s, char *optionName, sqInt optionNameSize, sqInt *result)
1303{
1304  if (socketValid(s))
1305    {
1306      socketOption *opt= findOption(optionName, (size_t)optionNameSize);
1307      if (opt != 0)
1308	{
1309	  int optval;	/* NOT sqInt */
1310	  socklen_t optlen= sizeof(optval);
1311	  if ((getsockopt(PSP(s)->s, opt->optlevel, opt->optname, (void *)&optval, &optlen)) < 0)
1312	    goto barf;
1313	  if (optlen != sizeof(optval))
1314	    goto barf;
1315	  *result= optval;
1316	  return 0;
1317	}
1318    }
1319 barf:
1320  interpreterProxy->success(false);
1321  return errno;
1322}
1323
1324void sqSocketBindToPort(SocketPtr s, int addr, int port)
1325{
1326  int result;
1327  struct sockaddr_in inaddr;
1328  privateSocketStruct *pss= PSP(s);
1329
1330  if (!socketValid(s)) return;
1331
1332  /* bind the socket */
1333  memset(&inaddr, 0, sizeof(inaddr));
1334  inaddr.sin_family= AF_INET;
1335  inaddr.sin_port= htons(port);
1336  inaddr.sin_addr.s_addr= htonl(addr);
1337
1338  if (bind(SOCKET(s), (struct sockaddr *)&inaddr, sizeof(struct sockaddr_in)) < 0)
1339    {
1340      pss->sockError= errno;
1341      interpreterProxy->success(false);
1342      return;
1343    }
1344}
1345
1346void sqSocketSetReusable(SocketPtr s)
1347{
1348  char optionValue[256];
1349  size_t bufSize;
1350  unsigned char buf[4];
1351  int err;
1352
1353  if (!socketValid(s)) return;
1354
1355  *(int *)buf= 1;
1356  bufSize= 4;
1357  if (setsockopt(SOCKET(s), SOL_SOCKET, SO_REUSEADDR, buf, bufSize) < 0)
1358    {
1359      PSP(s)->sockError= errno;
1360      interpreterProxy->success(false);
1361      return;
1362    }
1363}
1364
1365/*** Resolver functions ***/
1366
1367
1368/* Note: the Mac and Win32 implementations implement asynchronous lookups
1369 * in the DNS.  I can't think of an easy way to do this in Unix without
1370 * going totally ott with threads or somesuch.  If anyone knows differently,
1371 * please tell me about it. - Ian
1372 */
1373
1374
1375/*** irrelevancies ***/
1376
1377void sqResolverAbort(void) {}
1378
1379void sqResolverStartAddrLookup(sqInt address)
1380{
1381  const char *res;
1382  res= addrToName(address);
1383  strncpy(lastName, res, MAXHOSTNAMELEN);
1384  FPRINTF((stderr, "startAddrLookup %s\n", lastName));
1385}
1386
1387
1388sqInt sqResolverStatus(void)
1389{
1390  if(!thisNetSession)
1391    return ResolverUninitialised;
1392  if(lastError != 0)
1393    return ResolverError;
1394  return ResolverSuccess;
1395}
1396
1397/*** trivialities ***/
1398
1399sqInt sqResolverAddrLookupResultSize(void)	{ return strlen(lastName); }
1400sqInt sqResolverError(void)			{ return lastError; }
1401sqInt sqResolverLocalAddress(void)		{ return nameToAddr(localHostName); }
1402sqInt sqResolverNameLookupResult(void)		{ return lastAddr; }
1403
1404void sqResolverAddrLookupResult(char *nameForAddress, sqInt nameSize)
1405{
1406  memcpy(nameForAddress, lastName, nameSize);
1407}
1408
1409/*** name resolution ***/
1410
1411void sqResolverStartNameLookup(char *hostName, sqInt nameSize)
1412{
1413  int len= (nameSize < MAXHOSTNAMELEN) ? nameSize : MAXHOSTNAMELEN;
1414  memcpy(lastName, hostName, len);
1415  lastName[len]= lastError= 0;
1416  FPRINTF((stderr, "name lookup %s\n", lastName));
1417  lastAddr= nameToAddr(lastName);
1418  /* we're done before we even started */
1419  interpreterProxy->signalSemaphoreWithIndex(resolverSema);
1420}