lpc4/lib/
lpc4/lib/doc/efun/
lpc4/lib/doc/lfun/
lpc4/lib/doc/operators/
lpc4/lib/doc/simul_efuns/
lpc4/lib/doc/types/
lpc4/lib/etc/
lpc4/lib/include/
lpc4/lib/include/arpa/
lpc4/lib/obj/d/
lpc4/lib/save/
lpc4/lib/secure/
lpc4/lib/std/
lpc4/lib/std/living/
/*
 * socket_efun.c -- socket efuns for MudOS.
 *    5-92 : Dwayne Fontenot (Jacques@TMI) : original coding.
 *   10-92 : Dave Richards (Cynosure) : less original coding.
 *    4-93 : Fredrik Hubinette (hubbe@lysator.liu.se) : adapted for functionpointers
 */
#include "global.h"

#include <sys/types.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#ifdef HAVE_SYS_SOCKETVAR_H
#include <sys/socketvar.h>
#endif
#ifdef _AIX
#include <sys/select.h>
#endif /* _AIX */
#include <fcntl.h>
#include <netdb.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include "comm.h"
#include "interpret.h"
#include "object.h"
#include "exec.h"
#include "debug.h"
#include "socket_efuns.h"
#include "socket_err.h"
#include "main.h"
#include "stralloc.h"
#include "dynamic_buffer.h"
#include "simulate.h"

extern int errno, d_flag;
extern int save_svalue_depth;
extern char *error_strings[];

struct lpc_socket lpc_socks[MAX_EFUN_SOCKS];
static int socket_name_to_sin PROT((char *, struct sockaddr_in *));

/*
 * Initialize the LPC efun socket array
 */
void init_sockets()
{
  int i;

  debug(8192,("init_sockets: initializing %d socket descriptor(s)\n",
	      MAX_EFUN_SOCKS));

  for (i = 0; i < MAX_EFUN_SOCKS; i++)
  {
    lpc_socks[i].fd = -1;
    lpc_socks[i].flags = 0;
    lpc_socks[i].mode = S_STREAM;
    lpc_socks[i].state = CLOSED;
    MEMSET((char *)&lpc_socks[i].l_addr, 0, sizeof (lpc_socks[i].l_addr));
    MEMSET((char *)&lpc_socks[i].r_addr, 0, sizeof (lpc_socks[i].r_addr));
    lpc_socks[i].owner_ob = NULL;
    lpc_socks[i].release_ob = NULL;

    SET_TO_ZERO(lpc_socks[i].read_callback);
    SET_TO_ZERO(lpc_socks[i].write_callback);
    SET_TO_ZERO(lpc_socks[i].close_callback);

    lpc_socks[i].r_buf = NULL;
    lpc_socks[i].r_off = 0;
    lpc_socks[i].r_len = 0;

    lpc_socks[i].w_buf = NULL;
    lpc_socks[i].w_off = 0;
    lpc_socks[i].w_len = 0;
  }
}

void check_svalue(struct svalue *s)
{
  if(s->type & (T_OBJECT | T_FUNCTION))
    if(s->u.ob->flags & O_DESTRUCTED)
      free_svalue(s);
}

/*
 * Create an LPC efun socket
 */
int socket_create(int mode,
              struct svalue *read_callback,
              struct svalue *close_callback)
{
  int type, i, fd, optval;

  switch (mode & S_MODE_MASK)
  {
  case S_STREAM:
    type = SOCK_STREAM;
    break;

  case S_DATAGRAM:
    type = SOCK_DGRAM;
    break;

  default:
    return EEMODENOTSUPP;
  }

  for (i = 0; i < MAX_EFUN_SOCKS; i++)
  {
    if (lpc_socks[i].state != CLOSED)
      continue;

    fd = socket(AF_INET, type, 0);
    if (fd == -1)
    {
      perror("socket_create: socket");
      return EESOCKET;
    }

    optval = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&optval,
		   sizeof (optval)) == -1)
    {
      perror("socket_create: setsockopt");
      close(fd);
      return EESETSOCKOPT;
    }

    if (set_socket_nonblocking(fd, 1) == -1)
    {
      perror("socket_create: set_socket_nonblocking");
      close(fd);
      return EENONBLOCK;
    }

    lpc_socks[i].fd = fd;
    lpc_socks[i].flags = S_HEADER;
    lpc_socks[i].mode = mode;
    lpc_socks[i].state = UNBOUND;
    MEMSET((char *)&lpc_socks[i].l_addr, 0, sizeof (lpc_socks[i].l_addr));
    MEMSET((char *)&lpc_socks[i].r_addr, 0, sizeof (lpc_socks[i].r_addr));
    lpc_socks[i].owner_ob = current_object;
    lpc_socks[i].release_ob = NULL;

    if(read_callback==NULL)
    {
      free_svalue(&lpc_socks[i].read_callback);
    }else{
      assign_svalue(&lpc_socks[i].read_callback,read_callback);
    }
    free_svalue(&lpc_socks[i].write_callback);

    if (type != SOCK_DGRAM && close_callback != NULL)
    {
      assign_svalue(&lpc_socks[i].close_callback,close_callback);
    }else{
      free_svalue(&lpc_socks[i].close_callback);
    }

    lpc_socks[i].r_buf = NULL;
    lpc_socks[i].r_off = 0;
    lpc_socks[i].r_len = 0;

    lpc_socks[i].w_buf = NULL;
    lpc_socks[i].w_off = 0;
    lpc_socks[i].w_len = 0;

    current_object->flags |= O_EFUN_SOCKET;

    debug(8192,("socket_create: created socket %d mode %d fd %d\n",
		i, mode, fd));

    return i;
  }

  return EENOSOCKS;
}
int socket_from_stdin()
{
  int i;
  
  for (i = 0; i < MAX_EFUN_SOCKS; i++)
    if (lpc_socks[i].state == CLOSED)
      break;

  if(i==MAX_EFUN_SOCKS)
    return EENOSOCKS;
  
  if (set_socket_nonblocking(0, 1) == -1)
  {
    perror("socket_create: set_socket_nonblocking");
    return EENONBLOCK;
  }

  lpc_socks[i].state = LISTEN;
  current_object->flags |= O_EFUN_SOCKET;
  lpc_socks[i].fd = 0;
  lpc_socks[i].flags = S_HEADER;
  lpc_socks[i].mode = S_STREAM;
  MEMSET((char *)&lpc_socks[i].l_addr, 0, sizeof (lpc_socks[i].l_addr));
  MEMSET((char *)&lpc_socks[i].r_addr, 0, sizeof (lpc_socks[i].r_addr));
  lpc_socks[i].owner_ob = current_object;
  lpc_socks[i].release_ob = NULL;
  
  free_svalue(&lpc_socks[i].read_callback);
  free_svalue(&lpc_socks[i].write_callback);
  free_svalue(&lpc_socks[i].close_callback);
  
  lpc_socks[i].r_buf = NULL;
  lpc_socks[i].r_off = 0;
  lpc_socks[i].r_len = 0;
  
  lpc_socks[i].w_buf = NULL;
  lpc_socks[i].w_off = 0;
  lpc_socks[i].w_len = 0;

  current_object->flags |= O_EFUN_SOCKET;
  
  return i;
}


/*
 * Bind an address to an LPC efun socket
 */
int socket_bind(int fd,int port)
{
  int len;
  struct sockaddr_in sin;

  if (fd < 0 || fd >= MAX_EFUN_SOCKS)
    return EEFDRANGE;
  if (lpc_socks[fd].state == CLOSED)
    return EEBADF;
  if (lpc_socks[fd].owner_ob != current_object)
    return EESECURITY;
  if (lpc_socks[fd].state != UNBOUND)
    return EEISBOUND;

  sin.sin_family = AF_INET;
  sin.sin_addr.s_addr = INADDR_ANY;
  sin.sin_port = htons((u_short)port);

  if (bind(lpc_socks[fd].fd, (struct sockaddr *)&sin, sizeof (sin)) == -1)
  {
    switch (errno) {
    case EADDRINUSE:
      return EEADDRINUSE;
    default:
      perror("socket_bind: bind");
      return EEBIND;
    }
  }

  len = sizeof (sin);
  if (getsockname(lpc_socks[fd].fd, (struct sockaddr *)&lpc_socks[fd].l_addr,
		  &len) == -1) {
    perror("socket_bind: getsockname");
    return EEGETSOCKNAME;
  }

  lpc_socks[fd].state = BOUND;

  debug(8192,("socket_bind: bound socket %d to %s.%d\n",
	      fd, inet_ntoa(lpc_socks[fd].l_addr.sin_addr),
	      ntohs(lpc_socks[fd].l_addr.sin_port)));

  return EESUCCESS;
}

/*
 * Listen for connections on an LPC efun socket
 */
int socket_listen(int fd,struct svalue *callback)
{
    if (fd < 0 || fd >= MAX_EFUN_SOCKS)
	return EEFDRANGE;
    if (lpc_socks[fd].state == CLOSED)
	return EEBADF;
    if (lpc_socks[fd].owner_ob != current_object)
	return EESECURITY;
    if ((lpc_socks[fd].mode & S_MODE_MASK) == S_DATAGRAM)
	return EEMODENOTSUPP;
    if (lpc_socks[fd].state == UNBOUND)
	return EENOADDR;
    if (lpc_socks[fd].state != BOUND)
	return EEISCONN;

    if (listen(lpc_socks[fd].fd, 5) == -1)
    {
	perror("socket_listen: listen");
	return EELISTEN;
    }

    lpc_socks[fd].state = LISTEN;
    assign_svalue(&lpc_socks[fd].read_callback,callback);
    current_object->flags |= O_EFUN_SOCKET;
    debug(8192,("socket_listen: listen on socket %d\n", fd));
    return EESUCCESS;
}

/*
 * Accept a connection on an LPC efun socket
 */
int socket_accept(int fd,
		  struct svalue *read_callback,
                  struct svalue * write_callback)
{
    int len, accept_fd, i;
    struct sockaddr_in sin;

    if (fd < 0 || fd >= MAX_EFUN_SOCKS)
	return EEFDRANGE;
    if (lpc_socks[fd].state == CLOSED)
	return EEBADF;
    if (lpc_socks[fd].owner_ob != current_object)
	return EESECURITY;
    if ((lpc_socks[fd].mode & S_MODE_MASK) == S_DATAGRAM)
	return EEMODENOTSUPP;
    if (lpc_socks[fd].state != LISTEN)
	return EENOTLISTN;

    lpc_socks[fd].flags &= ~S_WACCEPT;

    len = sizeof (sin);
    accept_fd = accept(lpc_socks[fd].fd, (struct sockaddr *)&sin, (int *)&len);
    if (accept_fd == -1) {
	perror("socket_accept: accept");
	switch (errno) {
	case EWOULDBLOCK:
	    return EEWOULDBLOCK;
	case EINTR:
	    return EEINTR;
	default:
	    perror("socket_accept: accept");
	    return EEACCEPT;
	}
    }

    for (i = 0; i < MAX_EFUN_SOCKS; i++)
    {
	if (lpc_socks[i].state != CLOSED)
	    continue;

	lpc_socks[i].fd = accept_fd;
	lpc_socks[i].flags = S_HEADER;
	lpc_socks[i].mode = lpc_socks[fd].mode;
	lpc_socks[i].state = DATA_XFER;
	lpc_socks[i].l_addr = lpc_socks[fd].l_addr;
	lpc_socks[i].r_addr = sin;
	lpc_socks[i].owner_ob = NULL;
	lpc_socks[i].release_ob = NULL;

	free_svalue(&lpc_socks[i].read_callback);
	free_svalue(&lpc_socks[i].write_callback);
	free_svalue(&lpc_socks[i].close_callback);

	lpc_socks[i].r_buf = NULL;
	lpc_socks[i].r_off = 0;
	lpc_socks[i].r_len = 0;

	lpc_socks[i].w_buf = NULL;
	lpc_socks[i].w_off = 0;
	lpc_socks[i].w_len = 0;

	lpc_socks[i].owner_ob = current_object;
        assign_svalue(&lpc_socks[i].read_callback,read_callback);
        assign_svalue(&lpc_socks[i].write_callback,write_callback);
        assign_svalue(&lpc_socks[i].close_callback,&lpc_socks[fd].close_callback);

	current_object->flags |= O_EFUN_SOCKET;

	debug(8192,("socket_accept: accept on socket %d\n", fd));
	debug(8192,("socket_accept: new socket %d on fd %d\n", i, accept_fd));

	return i;
    }

    close(accept_fd);

    return EENOSOCKS;
}

/*
 * Connect an LPC efun socket
 */
int socket_connect(int fd,
                   char *name,
                   struct svalue *read_callback,
                   struct svalue *write_callback)
{
    if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE;
    if (lpc_socks[fd].state == CLOSED) return EEBADF;
    if (lpc_socks[fd].owner_ob != current_object) return EESECURITY;
    if ((lpc_socks[fd].mode & S_MODE_MASK) == S_DATAGRAM)
       return EEMODENOTSUPP;

    switch (lpc_socks[fd].state)
    {
      case CLOSED:
      case UNBOUND:
      case BOUND:
        break;

      case LISTEN:    return EEISLISTEN;
      case DATA_XFER: return EEISCONN;
    }

    if (!socket_name_to_sin(name, &lpc_socks[fd].r_addr))
	return EEBADADDR;

    assign_svalue(&lpc_socks[fd].read_callback, read_callback);
    assign_svalue(&lpc_socks[fd].write_callback, write_callback);

    current_object->flags |= O_EFUN_SOCKET;

    if (connect(lpc_socks[fd].fd, (struct sockaddr *)&lpc_socks[fd].r_addr,
	sizeof (struct sockaddr_in)) == -1)
    {
	switch (errno) {
	case EINTR:
	    return EEINTR;
	case EADDRINUSE:
	    return EEADDRINUSE;
	case EALREADY:
	    return EEALREADY;
	case ECONNREFUSED:
	    return EECONNREFUSED;
	case EINPROGRESS:
	    break;
	default:
	    perror("socket_connect: connect");
	    return EECONNECT;
	}
    }

    lpc_socks[fd].state = DATA_XFER;
    lpc_socks[fd].flags |= S_BLOCKED;

    return EESUCCESS;
}

/*
 * Write a message on an LPC efun socket
 */
int socket_write(int fd,struct svalue *message,char *name)
{
  int len, off;
  char *buf;
  struct sockaddr_in sin;

  if (fd < 0 || fd >= MAX_EFUN_SOCKS) return EEFDRANGE;
  if (lpc_socks[fd].state == CLOSED) return EEBADF;
  if (lpc_socks[fd].owner_ob != current_object) return EESECURITY;

  switch (lpc_socks[fd].mode & S_MODE_MASK)
  {
  case S_STREAM:
    if (lpc_socks[fd].state != DATA_XFER) return EENOTCONN;
    if (name != NULL) return EEBADADDR;
    if(lpc_socks[fd].flags & S_BLOCKED)
    {
      if(lpc_socks[fd].mode & S_BUFFERED_OUTPUT)
      {
	len = my_strlen(message) + lpc_socks[fd].w_len;
	buf=(char *)malloc(len+1);
	if (buf == NULL) fatal("Out of memory");
	if(lpc_socks[fd].w_buf)
	  MEMCPY(buf,lpc_socks[fd].w_buf+lpc_socks[fd].w_off,lpc_socks[fd].w_len);
	MEMCPY(buf+lpc_socks[fd].w_len,strptr(message),my_strlen(message));
	buf[len]=0;
	if(lpc_socks[fd].w_buf)
	  free(lpc_socks[fd].w_buf);

	lpc_socks[fd].w_buf = buf;
	lpc_socks[fd].w_off = 0;
	lpc_socks[fd].w_len = len;
	return 0;
      }else{
	return EEALREADY;
      }
    }

    len = my_strlen(message);
    buf = (char *)xalloc(len + 1);
    if (buf == NULL) fatal("Out of memory");
    MEMCPY(buf, strptr(message),my_strlen(message));
    break;

  case S_DATAGRAM:
    if(name == NULL) return EENOADDR;
    if(!socket_name_to_sin(name, &sin)) return EEBADADDR;

    if (sendto(lpc_socks[fd].fd, strptr(message),
	       my_strlen(message) + 1, 0,
	       (struct sockaddr *)&sin, sizeof (sin)) == -1)
    {
      perror("socket_write: sendto");
      return EESENDTO;
    }
    return EESUCCESS;


  default:
    return EEMODENOTSUPP;
  }

  off = send(lpc_socks[fd].fd, buf,len, 0);
  if (off == -1)
  {
    switch (errno)
    {

    case EWOULDBLOCK:
      break;

    default:
      free(buf);
      perror("socket_write: send");
      return EESEND;
    }
  }

  if (off < len)
  {
    lpc_socks[fd].flags |= S_BLOCKED;
    lpc_socks[fd].w_buf = buf;
    lpc_socks[fd].w_off = off;
    lpc_socks[fd].w_len = len - off;
    return EECALLBACK;
  }

  free(buf);

  return EESUCCESS;
}

void parse_one_line(int fd)
{
  int e;
  char *s;

  if((lpc_socks[fd].mode & S_MODE_MASK) != S_STREAM) return;
  if(!(lpc_socks[fd].mode & S_BUFFERED_INPUT)) return;
  if(lpc_socks[fd].r_buf == NULL)
  {
    lpc_socks[fd].flags&=~S_MORE_LINES;
    return;
  }

  if(lpc_socks[fd].r_len>0 && lpc_socks[fd].r_buf)
  {
    s=lpc_socks[fd].r_buf+lpc_socks[fd].r_off;
    for(e=0;e<lpc_socks[fd].r_len;e++)
    {
      if(s[e]=='\n')
      {
	e++;
	break;
      }
    }
    lpc_socks[fd].r_off+=e;
    lpc_socks[fd].r_len-=e;
    push_number(fd);
    push_shared_string(make_shared_binary_string(s,e));
    lpc_socks[fd].flags |= S_MORE_LINES;
    debug(8192,("read_socket_handler: apply read callback\n"));
    check_svalue(&lpc_socks[fd].read_callback);
    apply_lambda(&lpc_socks[fd].read_callback, 2,1);
  }
  if(lpc_socks[fd].state==CLOSED || (lpc_socks[fd].flags & S_CLOSING))
    return;

  if(lpc_socks[fd].r_len<1)
  {
    lpc_socks[fd].flags &= ~S_MORE_LINES;
    if(lpc_socks[fd].r_buf)
      free(lpc_socks[fd].r_buf);
    lpc_socks[fd].r_buf=NULL;
  }
}

void handle_line_sockets()
{
  int i,s;
  do{
    s=0;
    for(i=0;i<MAX_EFUN_SOCKS;i++)
    {
      if((lpc_socks[i].mode & S_MODE_MASK) != S_STREAM) continue;
      if(!(lpc_socks[i].mode & S_BUFFERED_INPUT)) continue;
      if(lpc_socks[i].flags & S_MORE_LINES)
      {
        parse_one_line(i);
        s=1;
      }
    }
  }while(s);
}

/*
 * Handle LPC efun socket read select events
 */
void socket_read_select_handler(int fd)
{
  int cc=-1; /* make gcc happy */
  int addrlen;
  char buf[BUF_SIZE], addr[ADDR_BUF_SIZE];
  struct sockaddr_in sin;

  debug(8192,("read_socket_handler: fd %d state %d\n",
	      fd, lpc_socks[fd].state));

  switch (lpc_socks[fd].state)
  {
  case CLOSED:
  case UNBOUND:
    return;

  case BOUND:
    switch (lpc_socks[fd].mode & S_MODE_MASK)
    {
    case S_STREAM:
      break;

    case S_DATAGRAM:
      debug(8192,("read_socket_handler: DATA_XFER DATAGRAM\n"));
      addrlen = sizeof (sin);
      cc = recvfrom(lpc_socks[fd].fd, buf, sizeof (buf), 0,
		    (struct sockaddr *)&sin, &addrlen);
      if (cc <= 0)
	break;
      debug(8192,("read_socket_handler: read %d bytes\n", cc));
      sprintf(addr, "%s %d", inet_ntoa(sin.sin_addr),
	      ntohs(sin.sin_port));
      push_number(fd);
      push_shared_string(make_shared_binary_string(buf, cc));
      push_new_shared_string(addr);
      debug(8192,("read_socket_handler: apply\n"));
      check_svalue(&lpc_socks[fd].read_callback);
      apply_lambda(&lpc_socks[fd].read_callback, 3,1);
      return;
    }
    break;

  case LISTEN:
    debug(8192,("read_socket_handler: apply read callback\n"));
    lpc_socks[fd].flags |= S_WACCEPT;
    push_number(fd);
    check_svalue(&lpc_socks[fd].read_callback);
    apply_lambda(&lpc_socks[fd].read_callback, 1,1);
    return;

  case DATA_XFER:
    switch (lpc_socks[fd].mode & S_MODE_MASK)
    {

    case S_DATAGRAM:
      break;
            
    case S_STREAM:
      if(lpc_socks[fd].mode & S_BUFFERED_INPUT)
      { 
	char *s;
	int e;
    
	debug(8192,("read_socket_handler: DATA_XFER STREAM (buffered)\n"));
	cc = recv(lpc_socks[fd].fd, buf, sizeof (buf), 0);
	if (cc <= 0) break;
	debug(8192,("read_socket_handler: read %d bytes\n", cc));
    
	if(lpc_socks[fd].r_buf != NULL)
	{
	  e=lpc_socks[fd].r_len+cc;
	  s=(char *)malloc(e+1);
	  strncpy(s,lpc_socks[fd].r_buf+lpc_socks[fd].r_off,
		  lpc_socks[fd].r_len);
	  strncpy(s+lpc_socks[fd].r_len,buf,cc);
	  free(lpc_socks[fd].r_buf);
	  lpc_socks[fd].r_buf=s;
	  lpc_socks[fd].r_len=e;
	  lpc_socks[fd].r_off=0;
	}else{
	  s=(char *)malloc(cc);
	  strncpy(s,buf,cc);
	  lpc_socks[fd].r_buf=s;
	  lpc_socks[fd].r_len=cc;
	  lpc_socks[fd].r_off=0;
	}
	lpc_socks[fd].flags |=S_MORE_LINES;
	return;
      }
      debug(8192,("read_socket_handler: DATA_XFER STREAM\n"));
      cc = recv(lpc_socks[fd].fd, buf, sizeof (buf), 0);
      if (cc <= 0)
	break;
      debug(8192,("read_socket_handler: read %d bytes\n", cc));
      push_number(fd);
      push_shared_string(make_shared_binary_string(buf, cc));
      debug(8192,("read_socket_handler: apply read callback\n"));
      check_svalue(&lpc_socks[fd].read_callback);
      apply_lambda(&lpc_socks[fd].read_callback, 2,1);
      return;
    }
    break;
  }
  if(cc == -1)
  {
    switch(errno)
    {
    case EINTR:
    case EWOULDBLOCK:
      return;
    }
  }
  debug(8192,("read_socket_handler: apply close callback\n"));
  push_number(fd);
  check_svalue(&lpc_socks[fd].close_callback);
  safe_apply_lambda(&lpc_socks[fd].close_callback, 1);

  socket_close(fd,1);
}

/*
 * Handle LPC efun socket write select events
 */
void socket_write_select_handler(int fd)
{
  int cc;

  debug(8192,("write_socket_handler: fd %d state %d\n",
	      fd, lpc_socks[fd].state));

  if ((lpc_socks[fd].flags & S_BLOCKED) == 0)
    return;

  if (lpc_socks[fd].w_buf != NULL)
  {
    cc = send(lpc_socks[fd].fd, lpc_socks[fd].w_buf + lpc_socks[fd].w_off,
	      lpc_socks[fd].w_len, 0);
    if (cc == -1)
    {
#if 1
      switch(errno)
      {
      case EINTR:
      case EWOULDBLOCK:
	return;
      }
      debug(8192,("read_socket_handler: apply close callback\n"));
      push_number(fd);
      check_svalue(&lpc_socks[fd].close_callback);
      safe_apply_lambda(&lpc_socks[fd].close_callback, 1);

      socket_close(fd,2);
#endif
      return;
    }
    lpc_socks[fd].w_off += cc;
    lpc_socks[fd].w_len -= cc;
    if (lpc_socks[fd].w_len != 0)
      return;
    free(lpc_socks[fd].w_buf);
    lpc_socks[fd].w_buf = NULL;
    lpc_socks[fd].w_off = 0;
  }

  lpc_socks[fd].flags &= ~S_BLOCKED;

  if(lpc_socks[fd].flags & S_CLOSING)
  {
    socket_close(fd,2);
    return;
  }

  debug(8192,("write_socket_handler: apply write_callback\n"));

  push_number(fd);
  check_svalue(&lpc_socks[fd].write_callback);
  apply_lambda(&lpc_socks[fd].write_callback, 1,1);
}

/*
 * Close an LPC efun socket
 */
int socket_close(int fd,int force)
{
  if(d_flag>8)
    printf("so_close: %d\n",fd);
  if (fd < 0 || fd >= MAX_EFUN_SOCKS)
    return EEFDRANGE;
  if (lpc_socks[fd].state == CLOSED)
    return EEBADF;
  if (!force && lpc_socks[fd].owner_ob != current_object)
    return EESECURITY;

  if ((lpc_socks[fd].mode & S_MODE_MASK) != S_DATAGRAM)
  {
    if(!(lpc_socks[fd].flags & S_CLOSING))
      shutdown(lpc_socks[fd].fd, 0);
  }
  lpc_socks[fd].flags|=S_CLOSING;

  if((lpc_socks[fd].flags & S_BLOCKED) && force<2)
  {
    lpc_socks[fd].flags|=S_CLOSING;
    return EEALREADY;
  }

  while (close(lpc_socks[fd].fd) == -1 && errno == EINTR); /* empty while */
  lpc_socks[fd].state = CLOSED;

  if (lpc_socks[fd].r_buf != NULL) free(lpc_socks[fd].r_buf);
  lpc_socks[fd].r_buf=NULL;
  if (lpc_socks[fd].w_buf != NULL) free(lpc_socks[fd].w_buf);
  lpc_socks[fd].w_buf=NULL;

  free_svalue(&lpc_socks[fd].read_callback);
  free_svalue(&lpc_socks[fd].write_callback);
  free_svalue(&lpc_socks[fd].close_callback);

  debug(8192,("socket_close: closed fd %d\n", fd));
  return EESUCCESS;
}

/*
 * Release an LPC efun socket to another object
 */
int socket_release(int fd,struct svalue *callback)
{
    if (fd < 0 || fd >= MAX_EFUN_SOCKS)
	return EEFDRANGE;
    if (lpc_socks[fd].state == CLOSED)
	return EEBADF;
    if (lpc_socks[fd].owner_ob != current_object)
	return EESECURITY;
    if (lpc_socks[fd].flags & S_RELEASE)
	return EESOCKRLSD;

    lpc_socks[fd].flags |= S_RELEASE;
    lpc_socks[fd].release_ob = callback->u.ob;

    push_number(fd);
    push_object(callback->u.ob);
    safe_apply_lambda(callback, 2);

    if ((lpc_socks[fd].flags & S_RELEASE) == 0)
	return EESUCCESS;

    lpc_socks[fd].flags &= ~S_RELEASE;
    lpc_socks[fd].release_ob = NULL;

    return EESOCKNOTRLSD;
}

/*
 * Aquire an LPC efun socket from another object
 */
int socket_acquire(int fd,
	struct svalue *read_callback,
	struct svalue *write_callback,
	struct svalue *close_callback)
{
    if (fd < 0 || fd >= MAX_EFUN_SOCKS)
	return EEFDRANGE;
    if (lpc_socks[fd].state == CLOSED)
        return EEBADF;
    if ((lpc_socks[fd].flags & S_RELEASE) == 0)
	return EESOCKNOTRLSD;
    if (lpc_socks[fd].release_ob != current_object)
	return EESECURITY;

    lpc_socks[fd].flags &= ~S_RELEASE;
    lpc_socks[fd].owner_ob = current_object;
    lpc_socks[fd].release_ob = NULL;

    
    assign_svalue(&lpc_socks[fd].read_callback,read_callback);
    assign_svalue(&lpc_socks[fd].write_callback,write_callback);
    assign_svalue(&lpc_socks[fd].close_callback,close_callback);

    return EESUCCESS;
}

/*
 * Return the string representation of a socket error
 */
char *socket_error(int error)
{
    error = -(error + 1);
    if (error < 0 || error >= ERROR_STRINGS)
	return "socket_error: invalid error number";
    return error_strings[error];
}

/*
 * Return the current socket owner
 */
struct object *get_socket_owner(int fd)
{
  if (fd < 0 || fd >= MAX_EFUN_SOCKS)
    return (struct object *)NULL;
  if (lpc_socks[fd].state == CLOSED)
    return (struct object *)NULL;
  return lpc_socks[fd].owner_ob;
}

/*
 * Initialize a T_OBJECT svalue
 */
void assign_socket_owner(struct svalue *sv,struct object *ob)
{
    if (ob != NULL)
    {
      sv->type = T_OBJECT;
      sv->u.ob = ob;
      add_ref(ob, "assign_socket_owner");
    }else{
      SET_TO_ZERO(*sv);
    }
}

/*
 * Convert a string representation of an address to a sockaddr_in
 */
static int socket_name_to_sin(char *name,struct sockaddr_in *sin)
{
    int port;
    char *cp, addr[ADDR_BUF_SIZE];

    strncpy(addr, name, ADDR_BUF_SIZE);

    cp = STRCHR(addr, ' ');

    if (cp == NULL) return 0;

    *cp = '\0';
    port = atoi(cp + 1);

    sin->sin_family = AF_INET;
    sin->sin_port = htons((u_short)port);
    sin->sin_addr.s_addr = inet_addr(addr);

    return 1;
}

/*
 * Close any sockets owned by ob
 */
void close_referencing_sockets(struct object *ob)
{
  int i;
  struct object *save_current_object;

  save_current_object = current_object;

  current_object = ob;
  for (i = 0; i < MAX_EFUN_SOCKS; i++)
    if (lpc_socks[i].owner_ob == ob && lpc_socks[i].state != CLOSED)
      socket_close(i,1);

  current_object = save_current_object;
}

/*
 * Return the remote address for an LPC efun socket
 */
int get_socket_address(int fd,char *addr,int *port)
{
  if (fd < 0 || fd >= MAX_EFUN_SOCKS)
  {
    addr[0] = '\0';
    *port = 0;
    return EEFDRANGE;
  }
  *port = (int)ntohs(lpc_socks[fd].r_addr.sin_port);
  sprintf(addr, "%s", inet_ntoa(lpc_socks[fd].r_addr.sin_addr));
  return EESUCCESS;
}

/*
 * Return the string representation of a sockaddr_in
 */
static char *inet_address(struct sockaddr_in *sin)
{
  static char addr[50], port[7];

  if (ntohl(sin->sin_addr.s_addr) == INADDR_ANY)
  {
    strcpy(addr, "*");
  }else{
    strncpy(addr, inet_ntoa(sin->sin_addr),sizeof(addr)-7);
  }

  strcat(addr, ".");

  if (ntohs(sin->sin_port) == 0) strcpy(port, "*");
  else sprintf(port, "%d", ntohs(sin->sin_port));

  strcat(addr, port);
  return addr;
}



/*
 * Dump the LPC efun socket array
 */
char *dump_socket_status()
{
  int i;
  char b[100];

  init_buf();

  my_strcat("Fd    State      Mode       Local Address          Remote Address\n");
  my_strcat("--  ---------  --------  ---------------------  ---------------------\n");

  for(i = 0; i < MAX_EFUN_SOCKS; i++)
  {
    sprintf(b,"%2d  ", lpc_socks[i].fd);
    my_strcat(b);

    switch(lpc_socks[i].state){
    case CLOSED:
      my_strcat("CLOSED ");
      break;
    case UNBOUND:
      my_strcat("UNBOUND");
      break;
    case BOUND:
      my_strcat(" BOUND ");
      break;
    case LISTEN:
      my_strcat("LISTEN ");
      break;
    case DATA_XFER:
      my_strcat("DTXFER ");
      break;
    default:
      my_strcat("    ??    ");
      break;
    }
    my_putchar(' ');

    switch(lpc_socks[i].mode & S_MODE_MASK){
    case S_STREAM:
      my_strcat("STREAM");
      break;
    case S_DATAGRAM:
      my_strcat("DGRAM ");
      break;
    default:
      my_strcat("  ??  ");
      break;
    }
    my_putchar(' ');

    if(lpc_socks[i].mode & S_BUFFERED_INPUT)
    {
      my_putchar('I');
    }else{
      my_putchar('.');
    }

    if(lpc_socks[i].mode & S_BUFFERED_OUTPUT)
    {
      my_putchar('O');
    }else{
      my_putchar('.');
    }
    my_putchar(' ');

    sprintf(b,"%-21s  ", inet_address(&lpc_socks[i].l_addr));
    my_strcat(b);
    sprintf(b,"%-21s\n", inet_address(&lpc_socks[i].r_addr));
    my_strcat(b);
  }
  return free_buf();
}