/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  socket.c,v 1.1.1.1 1994/04/04 04:29:41 amiga Exp
 *
 *  socket.c,v
 * Revision 1.1.1.1  1994/04/04  04:29:41  amiga
 * Initial CVS check in.
 *
 * Revision 1.2  1993/11/05  22:02:33  mwild
 * inet.library code, plus NOT YET WORKING code for "own" sockets
 *
 */

#define KERNEL
#include "ixemul.h"
#include "kprintf.h"

#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include "select.h"
#include <machine/param.h>

#define static
static struct file *getsock (int fdes);
static int soo_read  (struct file *fp, char *buf, int len);
static int soo_write (struct file *fp, char *buf, int len);
static int soo_ioctl (struct file *fp, int cmd, int inout, int arglen, caddr_t data);
static int soo_select(struct file *fp, int select_cmd, int io_mode);
static int soo_close (struct file *fp);

/* inet.library is just as weird as ixemul.library... it takes all parameters
   in parameter structures, addressed by d1 (a datareg taking an address..). */
#include "inet.h"

int
socket (int domain, int type, int protocol)
{
  struct a {
          short   domain;
          short   type;
          short   protocol;
          char    sigurg;
          char    sigio;
          short   err;
          struct socket *so;
  } uap;
  struct socket *so;
  struct file *fp;
  struct user *p = &u;
  int fd, err, ostat;

  if (domain == AF_INET && ! p->u_InetBase)
    {
      errno = ENOSYS;
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return -1;
    }

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  do
    {
      if (err = falloc (&fp, &fd))
        break;

#if defined (U_SOCKETS)
      if (domain == AF_INET)
	{
#endif
           uap.domain   = domain;
           uap.type     = type;
           uap.protocol = protocol;
           uap.sigurg   = u.u_sigurg;
           uap.sigio    = u.u_sigio;
           uap.err      = 0;
      
	   IN_socket(p->u_InetBase, &uap);
           fp->f_so = uap.so;
           err = uap.err;
#if defined (U_SOCKETS)
	}
      else
	{
	   err = socreate (domain, &so, type, protocol);
	}
#endif
      if (err)
	{
	  /* free the allocated fd */
	  p->u_ofile[fd] = 0;
	  fp->f_count = 0;
          break;
	}
    
      fp->f_stb.st_mode = 0666 | S_IFSOCK; /* not always, but.. */
      fp->f_stb.st_size = 128;    /* sizeof mbuf. */
      fp->f_stb.st_blksize = 128;

      fp->f_flags  = FREAD|FWRITE;
      fp->f_type   = domain == AF_INET ? DTYPE_SOCKET : DTYPE_USOCKET;
      fp->f_read   = soo_read;
      fp->f_write  = soo_write;
      fp->f_ioctl  = soo_ioctl;
      fp->f_close  = soo_close;
      fp->f_select = soo_select;
    }
  while (0);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (err == EINTR)
    setrun (FindTask (0));

  errno = err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return err ? -1 : fd;
}


int
bind (int s, caddr_t name, int namelen)
{
  struct a {
          struct socket	*so;
          caddr_t	name;
          short		namelen;
          short		err;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat, error;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
#if defined (U_SOCKETS)
  if (fp->f_type == DTYPE_SOCKET)
    {
#endif
      uap.so      = fp->f_so;
      uap.name    = name;
      uap.namelen = namelen;
      uap.err     = 0;
  
      IN_bind(p->u_InetBase, &uap);
      error = uap.err;
#if defined (U_SOCKETS)
    }
  else
    {
      struct mbuf *nam;
      error = sockargs (&nam, name, namelen, MT_SONAME);
      if (! error)
	{
          error = sobind (fp->f_so, nam);
          m_freem (nam);
        }
    }
#endif

  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (error == EINTR)
    setrun (FindTask (0));
  
  errno = error;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return error ? -1 : 0;
}

int
listen (int s, int backlog)
{
  struct a {
          struct socket *so;
          short         backlog;
          short         err;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat, error;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;

#if defined (U_SOCKETS)
  if (fp->f_type == DTYPE_SOCKET)
    {
#endif
      uap.so      = fp->f_so;
      uap.backlog = backlog;
      uap.err     = 0;
      IN_listen(p->u_InetBase, &uap);
      error = uap.err;
#if defined (U_SOCKETS)
    }
  else
    {
      error = solisten (fp->f_so, backlog);
    }
#endif
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (error == EINTR)
    setrun (FindTask (0));

  errno = error;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return error ? -1 : 0;
}

int
accept (int s, caddr_t name, int *namelen)
{
  struct a {
          struct socket *so;
          caddr_t name;
          short   namelen;
          short   err;
          struct socket *so2;
  } uap;
  struct file *fp = getsock (s), *fp2;
  struct user *p = &u;
  int err, fd2, ostat;
  
  if (! fp)
    return -1;
    
  ostat = p->p_stat;
  p->p_stat = SWAIT;
  do
    {
      /* first try to get a new descriptor. If that fails, don't even
         bother to call the library */
      if (err = falloc (&fp2, &fd2))
        break;
      
#if defined (U_SOCKETS)
      if (fp->f_type == DTYPE_SOCKET)
        {
#endif
          uap.so      = fp->f_so;
          uap.name    = name;
          uap.namelen = namelen ? *namelen : 0;
          uap.so2	  = 0;
          uap.err     = 0;
          IN_accept(p->u_InetBase, &uap);
          if (! uap.so2 && !uap.err)
            uap.err = EINTR;	/* XXX */
	  err = uap.err;
          if (namelen)
            *namelen = uap.namelen;

          fp2->f_so = uap.so2;
#if defined (U_SOCKETS)
	}
      else
        {
	  struct socket *so = fp->f_so;

          if ((so->so_options & SO_ACCEPTCONN) == 0) 
            err = EINVAL;
          else if ((so->so_state & SS_NBIO) && so->so_qlen == 0)
	    err = EWOULDBLOCK;
	  else
	    {
	      while (so->so_qlen == 0 && so->so_error == 0) 
	        {
		  if (so->so_state & SS_CANTRCVMORE) 
		    {
		      so->so_error = ECONNABORTED;
		      break;
		    }
		  if (err = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
		      netcon, 0))
		    {
		      so->so_error = err;
		      break;
		    }
	        }
	      if (so->so_error)
	        {
	          err = so->so_error;
	          so->so_error = 0;
		}
	      else
	        {
	          struct socket *aso = so->so_q;
	          struct mbuf *nam;
	          if (soqremque (aso, 1) == 0)
	            panic ("accept");
	          
	          fp2->f_so = aso;
	          nam = m_get (M_WAIT, MT_SONAME);
	          (void) soaccept (aso, nam);
	          if (name && namelen)
	            {
	              if (*namelen > nam->m_len)
	                *namelen = nam->m_len;
	              bcopy (mtod (nam, caddr_t), name, *namelen);
	            }
	          m_freem (nam);
	        }
	    }
        }
#endif

      if (err)
        {
          /* free the second file */
          u.u_ofile[fd2] = 0;
          fp2->f_count = 0;
          break;
        }

    
      fp2->f_stb.st_mode = 0666 | S_IFSOCK; /* not always, but.. */
      fp2->f_stb.st_size = 128;    /* sizeof mbuf.. */
      fp2->f_stb.st_blksize = 128;

      fp2->f_flags  = FREAD|FWRITE;
      fp2->f_type   = DTYPE_SOCKET;
      fp2->f_read   = soo_read;
      fp2->f_write  = soo_write;
      fp2->f_ioctl  = soo_ioctl;
      fp2->f_close  = soo_close;
      fp2->f_select = soo_select;
    }
  while (0);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (err == EINTR)
    setrun (FindTask (0));

  errno = err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return err ? -1 : fd2;
}


int
connect (int s, caddr_t name, int namelen)
{
  struct a {
          struct socket *so;
          caddr_t name;
          short   namelen;
          short   err;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat, error;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;

#if defined (U_SOCKETS)
  if (fp->f_type == DTYPE_SOCKET)
    {
#endif
      uap.so      = fp->f_so;
      uap.name    = name;
      uap.namelen = namelen;
      uap.err     = 0;
      IN_connect(p->u_InetBase, &uap);
      error = uap.err;
#if defined (U_SOCKETS)
    }
  else
    {
      struct socket *so = fp->f_so;
      struct mbuf *nam;

      if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING))
        {
	  error = EALREADY;
	  break;
	}
      if (error = sockargs(&nam, name, namelen, MT_SONAME))
	break;

      error = soconnect(so, nam);
      if (error)
	goto bad;
      if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) 
	{
	  m_freem(nam);
	  error = EINPROGRESS;
	}

      Forbid ();
      while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0)
	if (error = tsleep((caddr_t)&so->so_timeo, PSOCK | PCATCH,
		    	   netcon, 0))
	  break;
      if (error == 0) 
	{
	  error = so->so_error;
	  so->so_error = 0;
	}
      Permit ();
bad:
      so->so_state &= ~SS_ISCONNECTING;
      m_freem(nam);
      if (error == ERESTART)
	error = EINTR;
    }
#endif

  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (error == EINTR)
    setrun (FindTask (0));

  errno = error;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return error ? -1 : 0;
}

#if 0
/* This function is not available with Commodore sockets. */

int
socketpair (int domain, int type, int protocol, int sv[2])
{
  register struct file *fp1, *fp2;
  struct socket *so1, *so2;
  struct user *p = &u;
  int ostat, error;

  /* minimal test.. */
  if (!sv[0] || !sv[1])
    {
      errno = EFAULT;
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return -1;
    }

  /* no go with Commo-sockets */
  if (domain == AF_INET)
    {
      errno = EPFNOSUPPORT;
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return -1;
    }

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  do
    {
      /* first try to allocate two descriptors */
      if (error = falloc (&fp1, &sv[0]))
        break;
        
      if (error = falloc (&fp2, &sv[1]))
        {
free_first:
	  /* free first descriptor */
          u.u_ofile[sv[0]] = 0;
          fp1->f_count = 0;
          break;
        }

      if (error = socreate(domain, &so1, type, protocol))
        {
free_second:
          u.u_ofile[sv[1]] = 0;
          fp2->f_count = 0;
          goto free_first;
        }

      if (error = socreate(domain, &so2, type, protocol))
        {
close_so1:
	  soclose (so1);
          goto free_second;
        }
      
      if (error = soconnect2(so1, so2))
        {
close_so2:
	  soclose (so2);
	  goto close_so1;
	}
       
      if (type == SOCK_DGRAM) 
	{
	  /*
	   * Datagram socket connection is asymmetric.
	   */
	  if (error = soconnect2(so2, so1))
	    goto close_so2;
	}
    }
  while (0);
  p->p_stat = ostat;

  if (error == EINTR)
    setrun (FindTask (0));
  
  errno = error;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return error ? -1 : 0;
}
#endif


int
sendto (int s, caddr_t buf, int len, int flags, caddr_t to, int tolen)
{
  struct a {
          struct socket *so;
          caddr_t buf;
          int     len;
          short   flags;
          caddr_t to;
          short   tolen;
          short   err;
          int     rc;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so    = fp->f_so;
  uap.buf   = buf;
  uap.len   = len;
  uap.flags = flags;
  uap.to    = to;
  uap.tolen = tolen;
  uap.err   = 0;
  IN_sendto(p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;

  /* the library doesn't send this to us of course ;-) */
  if (uap.err == EPIPE)
    _psignal (FindTask (0), SIGPIPE);
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : uap.rc;
}


int
send (int s, caddr_t buf, int len, int flags)
{
  struct a {
          struct socket *so;
          caddr_t buf;
          int     len;
          short   flags;
          short   err;
          int     rc;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so    = fp->f_so;
  uap.buf   = buf;
  uap.len   = len;
  uap.flags = flags;
  uap.err   = 0;
  IN_send (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  
  /* the library doesn't send this to us of course ;-) */
  if (uap.err == EPIPE)
    _psignal (FindTask (0), SIGPIPE);
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : uap.rc;
}


int
sendmsg (int s, caddr_t msg, int flags)
{
  struct a {
          struct socket *so;
          caddr_t msg;
          short   flags;
          short   err;
          int     rc;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so    = fp->f_so;
  uap.msg   = msg;
  uap.flags = flags;
  uap.err   = 0;
  IN_sendmsg (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  
  /* the library doesn't send this to us of course ;-) */
  if (uap.err == EPIPE)
    _psignal (FindTask (0), SIGPIPE);
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : uap.rc;
}


int
recvfrom (int s, caddr_t buf, int len, int flags, caddr_t from, int *fromlen)
{
  struct a {
          struct socket *so;
          caddr_t buf;
          int     len;
          short   flags;
          caddr_t from;
          short   fromlen;
          short   err;
          int     rc;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so      = fp->f_so;
  uap.buf     = buf;
  uap.len     = len;
  uap.flags   = flags;
  uap.from    = from;
  uap.fromlen = fromlen ? *fromlen : 0;
  uap.err     = 0;
  IN_recvfrom (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  if (fromlen)
    *fromlen = uap.fromlen;
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : uap.rc;
}


int
recv (int s, caddr_t buf, int len, int flags)
{
  struct a {
          struct socket *so;
          caddr_t buf;
          int     len;
          short   flags;
          short   err;
          int     rc;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so    = fp->f_so;
  uap.buf   = buf;
  uap.len   = len;
  uap.flags = flags;
  uap.err   = 0;
  IN_recv (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : uap.rc;
}


int
recvmsg (int s, caddr_t msg, int flags)
{
  struct a {
          struct socket *so;
          caddr_t msg;
          short   flags;
          short   err;
          int     rc;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so    = fp->f_so;
  uap.msg   = msg;
  uap.flags = flags;
  uap.err   = 0;
  IN_recvmsg (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : uap.rc;
}


int
shutdown (int s, int how)
{
  struct a {
          struct socket *so;
          short    how;
          short    err;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so  = fp->f_so;
  uap.how = how;
  uap.err = 0;
  IN_shutdown (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : 0;
}


int
setsockopt (int s, int level, int name, caddr_t val, int valsize)
{
  struct a {
          struct socket *so;
          short   level;
          short   name;
          caddr_t val;
          short   valsize;
          short   err;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so      = fp->f_so;
  uap.level   = level;
  uap.name    = name;
  uap.val     = val;
  uap.valsize = valsize;
  uap.err     = 0;
  IN_setsockopt (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));

  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : 0;
}


int
getsockopt (int s, int level, int name, caddr_t val, int *valsize)
{
  struct a {
          struct socket *so;
          short   level;
          short   name;
          caddr_t val;
          short   valsize;
          short   err;
  } uap;
  register struct file *fp = getsock (s);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so      = fp->f_so;
  uap.level   = level;
  uap.name    = name;
  uap.val     = val;
  uap.valsize = valsize ? *valsize : 0;
  uap.err     = 0;
  IN_getsockopt (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  if (valsize)
    *valsize = uap.valsize;

  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : 0;
}


#if 0
/* same problem as with socketpair() */
pipe()
{
        register struct file *rf, *wf;
        struct socket *rso, *wso;
        int r;

        u.u_error = socreate(AF_UNIX, &rso, SOCK_STREAM, 0);
        if (u.u_error)
                return;
        u.u_error = socreate(AF_UNIX, &wso, SOCK_STREAM, 0);
        if (u.u_error)
                goto free;
        rf = falloc();
        if (rf == NULL)
                goto free2;
        r = u.u_r.r_val1;
        rf->f_flag = FREAD;
        rf->f_type = DTYPE_SOCKET;
        rf->f_ops = &socketops;
        rf->f_data = (caddr_t)rso;
        wf = falloc();
        if (wf == NULL)
                goto free3;
        wf->f_flag = FWRITE;
        wf->f_type = DTYPE_SOCKET;
        wf->f_ops = &socketops;
        wf->f_data = (caddr_t)wso;
        u.u_r.r_val2 = u.u_r.r_val1;
        u.u_r.r_val1 = r;
        if (u.u_error = unp_connect2(wso, rso))
                goto free4;
        wso->so_state |= SS_CANTRCVMORE;
        rso->so_state |= SS_CANTSENDMORE;
        return;
free4:
        wf->f_count = 0;
        u.u_ofile[u.u_r.r_val2] = 0;
free3:
        rf->f_count = 0;
        u.u_ofile[r] = 0;
free2:
        (void)soo_close(wso);
free:
        (void)soo_close(rso);
}
#endif


/*
 * Get socket name.
 */
int
getsockname (int fdes, caddr_t asa, int *alen)
{
  struct a {
          struct socket *so;
          caddr_t asa;
          short   alen;
          short   err;
  } uap;
  register struct file *fp = getsock (fdes);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so   = fp->f_so;
  uap.asa  = asa;
  uap.alen = alen ? *alen : 0;
  uap.err  = 0;
  IN_getsockname (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  if (alen)
    *alen = uap.alen;

  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : 0;
}

/*
 * Get name of peer for connected socket.
 */
int
getpeername (int fdes, caddr_t asa, int *alen)
{
  struct a {
          struct socket *so;
          caddr_t asa;
          short   alen;
          short   err;
  } uap;
  register struct file *fp = getsock (fdes);
  struct user *p = &u;
  int ostat;

  if (! fp)
    return -1;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so   = fp->f_so;
  uap.asa  = asa;
  uap.alen = alen ? *alen : 0;
  uap.err  = 0;
  IN_getpeername (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  if (alen)
    *alen = uap.alen;

  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : 0;
}

static struct file *
getsock (int fdes)
{
  register struct file *fp;
  register struct user *p = &u;

  if ((unsigned) fdes >= NOFILE)
    {
      errno = EBADF;
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return 0;
    }

  fp = p->u_ofile[fdes];
  if (fp == NULL)
    {
      errno = EBADF;
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return (0);
  }
  if (fp->f_type != DTYPE_SOCKET && fp->f_type != DTYPE_USOCKET) 
  {
      errno = ENOTSOCK;
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return (0);
    }
  if (fp->f_type == DTYPE_SOCKET && ! p->u_InetBase)
    {
      errno = EPIPE; /* ????? */
      KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
      return 0;
    }
  return (fp);
}


static int
soo_read (struct file *fp, char *buf, int len)
{
  struct a {
          struct socket *so;
          caddr_t buf;
          int     len;
          short   flags;
          short   err;
          int     rc;
  } uap;
  int ostat;
  struct user *p = &u;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so    = fp->f_so;
  uap.buf   = buf;
  uap.len   = len;
  uap.flags = 0;
  uap.err   = 0;
  IN_recv (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : uap.rc;
}


static int
soo_write (struct file *fp, char *buf, int len)
{
  struct a {
          struct socket *so;
          caddr_t buf;
          int     len;
          short   flags;
          short   err;
          int     rc;
  } uap;
  struct user *p = &u;
  int ostat;

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so    = fp->f_so;
  uap.buf   = buf;
  uap.len   = len;
  uap.flags = 0;
  uap.err   = 0;
  IN_send (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : uap.rc;
}



static int
soo_ioctl (struct file *fp, int cmd, int inout, int arglen, caddr_t data)
{
  struct a {
          struct socket *so;
          short	  cmd;
          caddr_t data;
          short   err;
  } uap;
  struct user *p = &u;
  int ostat;

  /* _SIGH_... they left almost everything neatly as it was in the BSD kernel
     code they used, but for whatever reason they decided they needed their
     own kind of ioctl encoding :-((
     
     Well then, here we go, and map `normal' cmds into CBM cmds: */

  switch (cmd)
   {
   case	SIOCADDRT	: cmd = ('r'<<8)|1; break;
   case	SIOCDELRT	: cmd = ('r'<<8)|2; break;
   case	SIOCSIFADDR	: cmd = ('i'<<8)|3; break;
   case	SIOCGIFADDR	: cmd = ('i'<<8)|4; break;
   case	SIOCSIFDSTADDR	: cmd = ('i'<<8)|5; break;
   case	SIOCGIFDSTADDR	: cmd = ('i'<<8)|6; break;
   case	SIOCSIFFLAGS	: cmd = ('i'<<8)|7; break;
   case	SIOCGIFFLAGS	: cmd = ('i'<<8)|8; break;
   case	SIOCGIFCONF	: cmd = ('i'<<8)|9; break;
   case	SIOCSIFMTU	: cmd = ('i'<<8)|10; break;
   case	SIOCGIFMTU	: cmd = ('i'<<8)|11; break;
   case	SIOCGIFBRDADDR	: cmd = ('i'<<8)|12; break;
   case	SIOCSIFBRDADDR	: cmd = ('i'<<8)|13; break;
   case	SIOCGIFNETMASK	: cmd = ('i'<<8)|14; break;
   case	SIOCSIFNETMASK	: cmd = ('i'<<8)|15; break;
   case	SIOCGIFMETRIC	: cmd = ('i'<<8)|16; break;
   case	SIOCSIFMETRIC	: cmd = ('i'<<8)|17; break;
   case SIOCSARP	: cmd = ('i'<<8)|18; break;
   case SIOCGARP	: cmd = ('i'<<8)|19; break;
   case SIOCDARP	: cmd = ('i'<<8)|20; break;
   case SIOCATMARK	: cmd = ('i'<<8)|21; break;
   case FIONBIO		: cmd = ('m'<<8)|22; break;
   case FIONREAD	: cmd = ('m'<<8)|23; break;
   case FIOASYNC	: cmd = ('m'<<8)|24; break;
   case SIOCSPGRP	: cmd = ('m'<<8)|25; break;
   case SIOCGPGRP	: cmd = ('m'<<8)|26; break;
   
   default:
     /* we really don't have to bother the library with cmds we can't even
        map over... */
     errno = EINVAL;
     KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
     return -1;
   }

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.so    = fp->f_so;
  uap.cmd   = cmd;
  uap.data  = data;
  uap.err   = 0;
  IN_ioctl (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
  if (uap.err == EINTR)
    setrun (FindTask (0));
  
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return uap.err ? -1 : 0;
}


static int
soo_select (struct file *fp, int select_cmd, int io_mode)
{
  struct a {
          struct socket **sotable;  /* just one socket used */
	  int	  *inp;
	  int	  *out;
	  int	  *exc;
	  void    *process;
	  short	  exec_sig;
	  short	  numfd;
	  short   err;
	  short   rc;
  } uap;
  int ostat;
  struct user *p = &u;
  int in, out, exc;

  switch (io_mode)
    {
    case SELMODE_IN:
      in = 1; out = 0; exc = 0;
      break;
    
    case SELMODE_OUT:
      in = 0; out = 1; exc = 0;
      break;

    case SELMODE_EXC:
      in = 0; out = 0; exc = 1;
      break;
    }

  ostat = p->p_stat;
  p->p_stat = SWAIT;
  uap.sotable = & fp->f_so;
  uap.inp = &in;
  uap.out = &out;
  uap.exc = &exc;
#if 0
  uap.process = FindTask (0);
  uap.exec_sig = p->u_sigio;
#else
  /* sigh, above doesn't work.. */
  uap.process = 0;
  uap.exec_sig = 0;
#endif
  uap.numfd = 1;
  uap.err = 0;
  IN_select (p->u_InetBase, &uap);
  if (CURSIG (p))
    SetSignal (0, SIGBREAKF_CTRL_C);
  p->p_stat = ostat;
      
  errno = uap.err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  if (select_cmd == SELCMD_PREPARE)
#if 0
    return 1 << p->u_sigio;
#else
    return 0;
#endif
  else
    return uap.rc == 1 ? 1 : 0;
}


static int
soo_close (struct file *fp)
{
  struct a {
          struct socket *so;
	  short   err;
  } uap;
  struct user *p = &u;
  int err = 0;

  ix_lock_base ();
  fp->f_count--;
  if (fp->f_count == 0)
    {
      /* don't have the base locked for IN_close, this MAY block!! */
      uap.so = fp->f_so;
      ix_unlock_base ();
      IN_close (p->u_InetBase, &uap);
      err = uap.err;
    }
  else
    ix_unlock_base ();

  errno = err;
  KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
  return err ? -1 : 0;
}
