/* Socket interprocess communication syscalls
   Copyright (C) 1991 Free Software Foundation

This file is part of the GNU Hurd.

The GNU Hurd is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.

The GNU Hurd 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with the GNU Hurd; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/* Written by Michael I. Bushnell.  */

#include "mach.h"
#include <cthreads.h>
#include <gnu/types.h>
#include <gnu/param.h>
#include <gnu/errno.h>
#include <gnu/socket.h>
#include <gnu/un.h>
#include <gnu/signal.h>
#include <hurd/hurd_types.h>

#include "socket.h"
#include "fs.h"
#include "name.h"

#include "alix.h"
#include "alix_io.h"
#include "alix_name.h"
#include <machine/alix_machdep.h>

/* Missing: sendmsg, recvmsg */

static mach_port_t find_server (int);
static int fetch_sockaddr (socket_t, void *, int, addr_port_t *, int);

/* SYSCALL: Create a socket of specified type, domain, and protocol.  */
int
socket (void *ap, int *ret1, int *ret2)
{
  struct 
    {
      int domain;
      int type;
      int protocol;
    }
  *args = ap;
  int d;
  int err;
  socket_t server;

  server = find_server (args->domain);
  if (server == 0)
    return EAFNOSUPPORT;
  
  mutex_lock (&iolock);
  d = falloc ();
  if (d == -1)
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }
  
  err = h2ae (socket_create (server, args->type, 
			     args->protocol, &dtable[d].ioserver));
  
  if (!err)
    *ret1 = d;
  
  mutex_unlock (&iolock);
  return err;
}

/* SYSCALL: Bind a socket to an address.  */
int
bind (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int s;
      struct sockaddr *name;
      int namelen;
    }
  *args = ap;
  addr_port_t addr;
  int err;

  mutex_lock (&iolock);

  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      err = EBADF;
      goto bad;
    }
  
  if (err = fetch_sockaddr (dtable[args->s].ioserver,
			    args->name, args->namelen, &addr, 1))
    goto bad;
  
  err = h2ae (socket_bind (dtable[args->s].ioserver, addr));
  
  mach_port_deallocate (mach_task_self (), addr);
  
 bad:
  mutex_unlock (&iolock);
  return err;
}

int
#ifdef COMPAT_43
getsockname1 (void *ap, int *ret1, int *ret2, int compat43)
#else
getsockname (void *ap, int *ret1, int *ret2)
#endif
{
  struct 
    {
      int s;
      void *addr;
      int *addrlenp;
    }
  *args = ap;
  struct sockaddr *sa;
  u_int addrlen;
  addr_port_t addrport;
  int err;

  if (err = copyin (args->addrlenp, &addrlen, sizeof (int)))
    return err;
  
  if (addrlen > MAXSOCKADDR)
    return EINVAL;
  sa = alloca (addrlen);
  
  mutex_lock (&iolock);
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }

  if (err = h2ae (socket_name (dtable[args->s].ioserver, &addrport)))
    {
      mutex_unlock (&iolock);
      return err;
    }
  
  err = h2ae (socket_whatis_address (addrport, sa, &addrlen));
  mutex_unlock (&iolock);
  mach_port_deallocate (mach_task_self (), addrport);
  
  if (!err)
    {
#ifdef COMPAT_43
      if (compat43)
	((struct osockaddr *)sa)->sa_family = sa->sa_family;
#endif      
      err = copyout (sa, args->addr, addrlen);
      if (!err)
	err = copyout (&addrlen, args->addrlenp, sizeof (int));
    }

  return err;
}

#ifdef COMPAT_43
int
getsockname (void *ap, int *ret1, int *ret2)
{
  return getsockname1 (ap, ret1, ret2, 0);
}

int
getsockname_compat43 (void *ap, int *ret1, int *ret2)
{
  return getsockname1 (ap, ret1, ret2, 1);
}
#endif

int
#ifdef COMPAT_43
getpeername1 (void *ap, int *ret1, int *ret2, int compat43)
#else
getpeername (void *ap, int *ret1, int *ret2)
#endif
{
  struct 
    {
      int s;
      void *addr;
      int *addrlenp;
    }
  *args = ap;
  struct sockaddr *sa;
  u_int addrlen;
  addr_port_t addrport;
  int err;

  if (err = copyin (args->addrlenp, &addrlen, sizeof (int)))
    return err;
  
  if (addrlen > MAXSOCKADDR)
    return EINVAL;
  sa = alloca (addrlen);
  
  mutex_lock (&iolock);
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }

  /* XXX security hole -- this should verify that we are talking
     to the real server and not just trust it... */
  if (err = h2ae (socket_peername (dtable[args->s].ioserver, &addrport)))
    {
      mutex_unlock (&iolock);
      return err;
    }
  
  err = h2ae (socket_whatis_address (addrport, sa, &addrlen));
  mutex_unlock (&iolock);
  mach_port_deallocate (mach_task_self (), addrport);
  
  if (!err)
    {
#ifdef COMPAT_43
      if (compat43)
	((struct osockaddr *)sa)->sa_family = sa->sa_family;
#endif      
      err = copyout (sa, args->addr, addrlen);
      if (!err)
	err = copyout (&addrlen, args->addrlenp, sizeof (int));
    }
  return err;
}

#ifdef COMPAT_43
int
getpeername (void *ap, int *ret1, int *ret2)
{
  return getpeername1 (ap, ret1, ret2, 0);
}

int
getpeername_compat43 (void *ap, int *ret1, int *ret2)
{
  return getpeername1 (ap, ret1, ret2, 1);
}
#endif

/* SYSCALL: Prepare a socket to receive connections.  */
int
listen (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int s;
      int backlog;
    }
  *args = ap;
  int err;
  
  mutex_lock (&iolock);
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }
  
  err = h2ae (socket_listen (dtable[args->s].ioserver, args->backlog));
  mutex_unlock (&iolock);
  return err;
}

/* SYSCALL: Accept a connection from a previously listen-ed socket.  */
  
int
#ifdef COMPAT_43
accept1 (void *ap, int *ret1, int *ret2, int compat43)
#else
accept (void *ap, int *ret1, int *ret2)
#endif
{
  struct 
    {
      int s;
      struct sockaddr *addr;
      int *addrlenp;
    }
  *args = ap;
  mach_port_t addr;
  int newd;
  mach_port_t conport;
  struct sockaddr *buf = 0;
  u_int addrlen;
  int err;

  if (args->addr)
    {
      if (err = copyin (args->addrlenp, &addrlen, sizeof (int)))
	return err;
      if (addrlen > MAXSOCKADDR)
	return EINVAL;
      buf = alloca (addrlen);
    }

  mutex_lock (&iolock);

  newd = falloc ();
  if (newd == -1)
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }
  
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }

  if (err = h2ae (socket_accept (dtable[args->s].ioserver, &conport, &addr)))
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }

  if (args->addr)
    {
      err = h2ae (socket_whatis_address (addr, buf, &addrlen));
      mach_port_deallocate (mach_task_self (), addr);
      if (err)
	{
	  mutex_unlock (&iolock);
	  mach_port_deallocate (mach_task_self (), conport);
	  return err;
	}
      
#ifdef COMPAT_43
      if (compat43)
	((struct osockaddr *)buf)->sa_family = buf->sa_family;
#endif
      err = copyout (buf, args->addr, addrlen);
      if (!err)
	err = copyout (&addrlen, args->addrlenp, sizeof (int));
      if (err)
	{
	  mutex_unlock (&iolock);
	  mach_port_deallocate (mach_task_self (), conport);
	  return err;
	}
    }
  else
    mach_port_deallocate (mach_task_self (), addr);
  
  dtable[newd].ioserver = conport;
  mutex_unlock (&iolock);
  *ret1 = newd;
  return 0;
}

#ifdef COMPAT_43
int
accept (void *ap, int *ret1, int *ret2)
{
  return accept1 (ap, ret1, ret2, 0);
}

int
accept_compat43 (void *ap, int *ret1, int *ret2)
{
  return accept1 (ap, ret1, ret2, 1);
}
#endif

/* SYSCALL:  Connect a socket to a foreign address.  */
int
connect (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int s;
      struct sockaddr *addr;
      int namelen;
    }
  *args = ap;
  int err;
  mach_port_t addr;
  
  mutex_lock (&iolock);
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }
  
  err = fetch_sockaddr (dtable[args->s].ioserver, args->addr,
			args->namelen, &addr, 0);
  if (!err)
    {
      err = h2ae (socket_connect (dtable[args->s].ioserver, addr));
      mach_port_deallocate (mach_task_self (), addr);
    }
  
  mutex_unlock (&iolock);
  return err;
}

/* SYSCALL: Create a pair of streams already connected in the
   specified domain, of the specified type and protocol.  */
int
socketpair (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int domain;
      int type;
      int protocol;
      int *sv;
    }
  *args = ap;
  int sv[2];
  int err;
  socket_t s1, s2, server;

  server = find_server (args->domain);
  if (server == 0)
    return EAFNOSUPPORT;
  
  mutex_lock (&iolock);
  
  sv[0] = falloc ();
  if (sv[0] == -1)
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }
  dtable[sv[0]].ioserver = 1;		/* mark it used */
  sv[1] = falloc ();
  dtable[sv[0]].ioserver = MACH_PORT_NULL;
  if (sv[1] == -1)
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }
  
  if (err = h2ae (socket_create (server, args->type, args->protocol, &s1)))
    {
      mutex_unlock (&iolock);
      return err;
    }
  if (err = h2ae (socket_create (server, args->type, args->protocol, &s2)))
    {
      mutex_unlock (&iolock);
      mach_port_deallocate (mach_task_self (), s1);
      return err;
    }
  if (err = h2ae (socket_connect2 (s1, s2)))
    {
      mutex_unlock (&iolock);
      mach_port_deallocate (mach_task_self (), s1);
      mach_port_deallocate (mach_task_self (), s2);
      return err;
    }
  
  if (err = copyout (sv, args->sv, sizeof (int) * 2))
    {
      mutex_unlock (&iolock);
      mach_port_deallocate (mach_task_self (), s1);
      mach_port_deallocate (mach_task_self (), s2);
      return err;
    }

  dtable[sv[0]].ioserver = s1;
  dtable[sv[1]].ioserver = s2;
  mutex_unlock (&iolock);
  *ret1 = sv[0];		/* XXX ??? */
  *ret1 = sv[1];		/* XXX ??? */
  return 0;
}

/* SYSCALL: Create a one-way linked pair of file descriptors.  */
int
pipe (void *ap, int *ret1, int *ret2)
{
  int rd, wr;
  socket_t rdpt, wrpt, server;
  int err;
  
  server = find_server (AF_LOCAL);
  if (server == 0)
    return EAFNOSUPPORT;
  
  mutex_lock (&iolock);
  rd = falloc ();
  if (rd == -1)
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }
  dtable[rd].ioserver = 1;
  wr = falloc ();
  dtable[rd].ioserver = 0;
  if (wr == -1)
    {
      mutex_unlock (&iolock);
      return EMFILE;
    }
  
  if (err = h2ae (socket_create (server, SOCK_STREAM, 0, &rdpt)))
    {
      mutex_unlock (&iolock);
      return err;
    }
  if (err = h2ae (socket_create (server, SOCK_STREAM, 0, &wrpt)))
    {
      mutex_unlock (&iolock);
      mach_port_deallocate (mach_task_self (), rdpt);
      return err;
    }
  if (err = h2ae (socket_connect2 (rdpt, wrpt)))
    {
      mutex_unlock (&iolock);
      mach_port_deallocate (mach_task_self (), rdpt);
      mach_port_deallocate (mach_task_self (), wrpt);
      return err;
    }
  
  if (err = h2ae (socket_shutdown (rdpt, 1)))
    {
      mutex_unlock (&iolock);
      mach_port_deallocate (mach_task_self (), rdpt);
      mach_port_deallocate (mach_task_self (), wrpt);
      return err;
    }
  if (err = h2ae (socket_shutdown (wrpt, 0)))
    {
      mutex_unlock (&iolock);
      mach_port_deallocate (mach_task_self (), rdpt);
      mach_port_deallocate (mach_task_self (), wrpt);
      return err;
    }

  dtable[rd].ioserver = rdpt;
  dtable[wr].ioserver = wrpt;
  mutex_unlock (&iolock);

  *ret1 = rd;
  *ret2 = wr;
  return 0;
}

/* Shutdown one half or both of a file descriptor.  */
int
shutdown (void *ap, int *ret1, int *ret2)
{
  struct 
    {
      int s;
      int how;
    }
  *args = ap;
  int err;
  
  mutex_lock (&iolock);
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }
  
  err = h2ae (socket_shutdown (dtable[args->s].ioserver, args->how));
  mutex_unlock (&iolock);
  return err;
}

int
getsockopt (void *ap, int *ret1, int *ret2)
{
  struct 
    {
      int s;
      int level;
      int option;
      void *optval;
      int *optlen;
    }
  *args = ap;
  void *buf;
  u_int len;
  int err;
  
  if (args->optval)
    {
      if (err = copyin (args->optlen, &len, sizeof (int)))
	return err;
      if (len > MAXSOCKOPT)
	return EINVAL;
      buf = alloca (len);
    }
  else
    {
      len = 0;
      buf = 0;
    }
  
  mutex_lock (&iolock);
  
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }
  
  err = h2ae (socket_getopt (dtable[args->s].ioserver, args->level,
			     args->option, buf, &len));
  if (args->optval)
    {
      if (!err)
	err = copyout (buf, args->optval, len);
      if (!err)
	err = copyout (&len, args->optlen, sizeof (len));
    }

  mutex_unlock (&iolock);
  return err;
}

/* SYSCALL: Set an option on a socket.  */
int
setsockopt (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int s;
      int level;
      int option;
      void *optval;
      int optlen;
    }
  *args = ap;
  void *buf;
  int err;
  
  if (args->optval)
    {
      if (args->optlen > MAXSOCKOPT)
	return EINVAL;
      buf = alloca (args->optlen);
      if (err = copyin (args->optval, buf, args->optlen))
	return err;
    }
  else
    {
      buf = 0;
      args->optlen = 0;
    }
  
  mutex_lock (&iolock);
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }
  
  err = socket_setopt (dtable[args->s].ioserver, args->level, args->option,
		       buf, args->optlen);
  mutex_unlock (&iolock);
  return err;
}

int
sendto (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int s;
      void *buf;
      int len;
      int flags;
      struct sockaddr *addr;
      int namelen;
    }
  *args = ap;
  int err;
  mach_port_t addr;
  int cc;
	    
  mutex_lock (&iolock);
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }
  
  if (args->addr)
    {
      err = fetch_sockaddr (dtable[args->s].ioserver, args->addr, 
			    args->namelen, &addr, 0);
      if (err)
	{
	  mutex_unlock (&iolock);
	  return err;
	}
    }
  else
    addr = MACH_PORT_NULL;
  
  if (args->len <= INBAND_MAX_DATA)
    err = h2ae (socket_send_inband (dtable[args->s].ioserver, addr,
				    args->flags, args->buf, args->len,
				    0, 0, 0, 0, &cc));
  else
    err = h2ae (socket_send_outofband (dtable[args->s].ioserver, addr, 
				       args->flags, args->buf, args->len,
				       0, 0, 0, 0, &cc));

  mutex_unlock (&iolock);
  if (addr)
    mach_port_deallocate (mach_task_self (), addr);
  
  *ret1 = cc;
  return err;
}

#ifdef COMPAT_43
int
send_43compat (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int s;
      void *buf;
      int len;
      int flags;
    }
  *args = ap;
  
  struct
    {
      int s;
      void *buf;
      int len;
      int flags;
      struct sockaddr *to;
      int tolen;
    }
  newargs;
  
  newargs.s = args->s;
  newargs.buf = args->buf;
  newargs.len = args->len;
  newargs.flags = args->flags;
  newargs.to = 0;
  newargs.tolen = 0;

  return sendto (&newargs, ret1, ret2);
}
#endif

int
#ifdef COMPAT_43
recvfrom1 (void *ap, int *ret1, int *ret2, int compat43)
#else
recvfrom (void *ap, int *ret1, int *ret2)
#endif
{
  struct 
    {
      int s;
      void *buf;
      int len;
      int flags;
      struct sockaddr *from;
      int *fromlen;
    }
  *args = ap;
  char *buf;
  addr_port_t addr;
  u_int cc;
  u_int addrlen;
  struct sockaddr *sa;
  int err;
  int outflags;
  
  if (args->from)
    {
      err = copyin (args->fromlen, &addrlen, sizeof (int));
      if (err)
	return err;
    }
  if (addrlen > MAXSOCKADDR)
    return EINVAL;
  sa = alloca (addrlen);

  mutex_lock (&iolock);
  if (args->s < 0 || args->s >= dtablesize || !dtable[args->s].ioserver)
    {
      mutex_unlock (&iolock);
      return EBADF;
    }

  if (args->len <= INBAND_MAX_DATA)
    err = h2ae (socket_recv_inband (dtable[args->s].ioserver, &addr,
				    args->flags, args->buf, &cc, 0, 0,
				    0, 0, &outflags, args->len));
  else
    {
      err = h2ae (socket_recv_outofband (dtable[args->s].ioserver, &addr,
					 args->flags, &buf, &cc, 0, 0,
					 0, 0, &outflags, args->len));
      if (!err)
	err = copyout (buf, args->buf, cc);
      vm_deallocate (mach_task_self (), (int)buf, cc);
    }
  mutex_unlock (&iolock);
  
  if (!err && args->from)
    {
      err = h2ae (socket_whatis_address (addr, sa, &addrlen));
#ifdef COMPAT_43
      if (compat43)
	((struct osockaddr *)sa)->sa_family = sa->sa_family;
#endif      
      if (!err)
	err = copyout (sa, args->from, addrlen);
      if (!err)
	err = copyout (&addrlen, args->fromlen, sizeof (int));
    }

  mach_port_deallocate (mach_task_self (), addr);
  *ret1 = cc;
  return err;
}

#ifdef COMPAT_43
int
recv_compat43 (void *ap, int *ret1, int *ret2)
{
  struct
    {
      int s;
      void *buf;
      int len;
      int flags;
    }
  *args = ap;
  
  struct
    {
      int s;
      void *buf;
      int len;
      int flags;
      struct sockaddr *from;
      int *fromlen;
    }
  newargs;
  
  newargs.s = args->s;
  newargs.buf = args->buf;
  newargs.len = args->len;
  newargs.flags = args->flags;
  newargs.from = 0;
  newargs.fromlen = 0;
  
  return recvfrom1 (&newargs, ret1, ret2, 1);
}

int
recvfrom (void *ap, int *ret1, int *ret2)
{
  return recvfrom1 (ap, ret1, ret2, 0);
}

int 
recvfrom_compat43 (void *ap, int *ret1, int *ret2)
{
  return recvfrom1 (ap, ret1, ret2, 1);
}
#endif

int
fetch_sockaddr (socket_t server,
		void *usrbuf, 
		int buflen, 
		addr_port_t *port,
		int binding)
{
  int err;
  struct sockaddr *sa;
  
  if (buflen > MAXSOCKADDR)
    return EINVAL;
  sa = alloca (buflen);
  if (err = copyin (usrbuf, sa, buflen))
    return err;

#if defined (COMPAT_43) && BYTE_ORDER != BIG_ENDIAN
  if (sa->sa_family == 0 && sa->sa_len < AF_MAX)
    sa->sa_family = sa->sa_len;
#endif

  sa->sa_len = buflen;

  if (sa->sa_family == AF_LOCAL)
    {
      struct sockaddr_un *sun = (struct sockaddr_un *)sa;
      extern int path_split (char *, file_t *, char **);
      extern int path_lookup (char *, int, int, file_t *);
      file_t dir, file;
      char *name;
      addr_port_t addr;
      if (binding)
	{
	  if (err = h2ae (socket_fabricate_address (server, &addr)))
	    return err;
	  err = path_split (sun->sun_path, &dir, &name);
	  if (!err)
	    err = h2ae (dir_makesock (dir, name, strlen (name) + 1, addr));
	  mach_port_deallocate (mach_task_self (), dir);
	  if (err)
	    mach_port_deallocate (mach_task_self (), addr);
	  else
	    *port = addr;
	  return err;
	}
      else
	{
	  if (err = path_lookup (sun->sun_path, 0, 0, &file))
	    return err;
	  err = h2ae (file_getsockaddr (file, &addr));
	  mach_port_deallocate (mach_task_self (), file);
	  if (!err)
	    *port = addr;
	  return err;
	}
    }
  else
    {
      err = h2ae (socket_create_address (server, sa, sa->sa_len, 
					 port, binding));
      return err;
    }
}


/* This is an array of server names indexed by protocol family.  */
struct mutex serverlock;

static struct {
  char *name;
  mach_port_t port;
}
servertab[] = 
{
  {"pf-local", 0},		/* AF_UNSPEC */
  {"pf-local", 0},		/* local to host */
  {"pf-inet", 0},		/* internet: UCP, TCP, etc. */
  {"pf-implink", 0},		/* arpanet imp layer */
  {"pf-pup", 0},		/* pup protocols: e.g. BSP */
  {"pf-chaos", 0},		/* MIT CHAOS net */
  {"pf-ns", 0},			/* Xerox NS */
  {"pf-iso", 0},		/* ISO protocols */
  {"pf-ecma", 0},		/* European Computer Manufacturers */
  {"pf-datakit", 0},		/* datakit protocols */
  {"pf-ccitt", 0},		/* CCITT protocols, X.25, etc */
  {"pf-sna", 0},		/* IBM SNA */
  {"pf-decnet", 0},		/* DECnet */
  {"pf-dli", 0},		/* DEC Direct data link interface */
  {"pf-lat", 0},		/* LAT */
  {"pf-hylink", 0},		/* NSC Hyperchannel */
  {"pf-appletalk", 0},		/* don't ask */
  {"pf-route", 0},		/* Internal Routing Protocol */
  {"pf-link", 0},		/* Link layer interface */
  {"pf-xtp", 0},		/* Express Transfer Protocol */
};

mach_port_t
find_server (int domain)
{
  mach_port_t ret;
  
  if (domain < 0 || domain > PF_MAX)
    return 0;

  mutex_lock (&serverlock);
  
  if (servertab[domain].port)
    ret = servertab[domain].port;
  else
    {
      if (name_getport (nameserver, servertab[domain].name, 
			strlen (servertab[domain].name) + 1,
			MACH_PORT_RIGHT_SEND, &servertab[domain].port))
	ret = 0;
      else
	ret = servertab[domain].port;
    }
  mutex_unlock (&serverlock);
  return ret;
}

