/* net.cc: network-related routines.

   Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.

This file is part of Cygwin.

This software is a copyrighted work licensed under the terms of the
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
details. */

/* #define DEBUG_NEST_ON 1 */

#define  __INSIDE_CYGWIN_NET__

#define Win32_Winsock
#include "winsup.h"
#include <errno.h>
#include <sys/socket.h>
#include <sys/un.h>

#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <winsock2.h>
#include "cygheap.h"
#include "cygerrno.h"
#include "fhandler.h"
#include "path.h"
#include "dtable.h"
#include "sync.h"
#include "sigproc.h"
#include "pinfo.h"
#include "registry.h"

extern "C" {
int h_errno;

int __stdcall rcmd (char **ahost, unsigned short inport, char *locuser,
		    char *remuser, char *cmd, SOCKET *fd2p);
int __stdcall rexec (char **ahost, unsigned short inport, char *locuser,
		     char *password, char *cmd, SOCKET *fd2p);
int __stdcall rresvport (int *);
int sscanf (const char *, const char *, ...);
} /* End of "C" section */

static WSADATA wsadata;

/* Cygwin internal */
static SOCKET __stdcall
set_socket_inheritance (SOCKET sock)
{
  if (os_being_run == winNT)
    (void) SetHandleInformation ((HANDLE) sock, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
  else
    {
      SOCKET newsock;
      if (!DuplicateHandle (hMainProc, (HANDLE) sock, hMainProc, (HANDLE *) &newsock,
			    0, TRUE, DUPLICATE_SAME_ACCESS))
	small_printf ("DuplicateHandle failed %E");
      else
	{
	  closesocket (sock);
	  sock = newsock;
	}
    }
  return sock;
}

/* htonl: standards? */
extern "C" unsigned long int
htonl (unsigned long int x)
{
  return ((((x & 0x000000ffU) << 24) |
	   ((x & 0x0000ff00U) <<  8) |
	   ((x & 0x00ff0000U) >>  8) |
	   ((x & 0xff000000U) >> 24)));
}

/* ntohl: standards? */
extern "C" unsigned long int
ntohl (unsigned long int x)
{
  return htonl (x);
}

/* htons: standards? */
extern "C" unsigned short
htons (unsigned short x)
{
  return ((((x & 0x000000ffU) << 8) |
	   ((x & 0x0000ff00U) >> 8)));
}

/* ntohs: standards? */
extern "C" unsigned short
ntohs (unsigned short x)
{
  return htons (x);
}

/* Cygwin internal */
static void
dump_protoent (struct protoent *p)
{
  if (p)
    debug_printf ("protoent %s %x %x", p->p_name, p->p_aliases, p->p_proto);
}

/* exported as inet_ntoa: standards? */
extern "C" char *
cygwin_inet_ntoa (struct in_addr in)
{
  char *res = inet_ntoa (in);
  return res;
}

/* exported as inet_addr: standards? */
extern "C" unsigned long
cygwin_inet_addr (const char *cp)
{
  unsigned long res = inet_addr (cp);
  return res;
}

/* undocumented in wsock32.dll */
extern "C" unsigned int	WINAPI inet_network (const char *);

extern "C" unsigned int
cygwin_inet_network (const char *cp)
{
  unsigned int res = inet_network (cp);
  return res;
}

/* inet_netof is in the standard BSD sockets library.  It is useless
   for modern networks, since it assumes network values which are no
   longer meaningful, but some existing code calls it.  */

extern "C" unsigned long
inet_netof (struct in_addr in)
{
  unsigned long i, res;


  i = ntohl (in.s_addr);
  if (IN_CLASSA (i))
    res = (i & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT;
  else if (IN_CLASSB (i))
    res = (i & IN_CLASSB_NET) >> IN_CLASSB_NSHIFT;
  else
    res = (i & IN_CLASSC_NET) >> IN_CLASSC_NSHIFT;


  return res;
}

/* inet_makeaddr is in the standard BSD sockets library.  It is
   useless for modern networks, since it assumes network values which
   are no longer meaningful, but some existing code calls it.  */

extern "C" struct in_addr
inet_makeaddr (int net, int lna)
{
  unsigned long i;
  struct in_addr in;


  if (net < IN_CLASSA_MAX)
    i = (net << IN_CLASSA_NSHIFT) | (lna & IN_CLASSA_HOST);
  else if (net < IN_CLASSB_MAX)
    i = (net << IN_CLASSB_NSHIFT) | (lna & IN_CLASSB_HOST);
  else if (net < 0x1000000)
    i = (net << IN_CLASSC_NSHIFT) | (lna & IN_CLASSC_HOST);
  else
    i = net | lna;

  in.s_addr = htonl (i);


  return in;
}

struct tl
{
  int w;
  const char *s;
  int e;
};

static struct tl errmap[] =
{
 {WSAEWOULDBLOCK, "WSAEWOULDBLOCK", EWOULDBLOCK},
 {WSAEINPROGRESS, "WSAEINPROGRESS", EINPROGRESS},
 {WSAEALREADY, "WSAEALREADY", EALREADY},
 {WSAENOTSOCK, "WSAENOTSOCK", ENOTSOCK},
 {WSAEDESTADDRREQ, "WSAEDESTADDRREQ", EDESTADDRREQ},
 {WSAEMSGSIZE, "WSAEMSGSIZE", EMSGSIZE},
 {WSAEPROTOTYPE, "WSAEPROTOTYPE", EPROTOTYPE},
 {WSAENOPROTOOPT, "WSAENOPROTOOPT", ENOPROTOOPT},
 {WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT", EPROTONOSUPPORT},
 {WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT", ESOCKTNOSUPPORT},
 {WSAEOPNOTSUPP, "WSAEOPNOTSUPP", EOPNOTSUPP},
 {WSAEPFNOSUPPORT, "WSAEPFNOSUPPORT", EPFNOSUPPORT},
 {WSAEAFNOSUPPORT, "WSAEAFNOSUPPORT", EAFNOSUPPORT},
 {WSAEADDRINUSE, "WSAEADDRINUSE", EADDRINUSE},
 {WSAEADDRNOTAVAIL, "WSAEADDRNOTAVAIL", EADDRNOTAVAIL},
 {WSAENETDOWN, "WSAENETDOWN", ENETDOWN},
 {WSAENETUNREACH, "WSAENETUNREACH", ENETUNREACH},
 {WSAENETRESET, "WSAENETRESET", ENETRESET},
 {WSAECONNABORTED, "WSAECONNABORTED", ECONNABORTED},
 {WSAECONNRESET, "WSAECONNRESET", ECONNRESET},
 {WSAENOBUFS, "WSAENOBUFS", ENOBUFS},
 {WSAEISCONN, "WSAEISCONN", EISCONN},
 {WSAENOTCONN, "WSAENOTCONN", ENOTCONN},
 {WSAESHUTDOWN, "WSAESHUTDOWN", ESHUTDOWN},
 {WSAETOOMANYREFS, "WSAETOOMANYREFS", ETOOMANYREFS},
 {WSAETIMEDOUT, "WSAETIMEDOUT", ETIMEDOUT},
 {WSAECONNREFUSED, "WSAECONNREFUSED", ECONNREFUSED},
 {WSAELOOP, "WSAELOOP", ELOOP},
 {WSAENAMETOOLONG, "WSAENAMETOOLONG", ENAMETOOLONG},
 {WSAEHOSTDOWN, "WSAEHOSTDOWN", EHOSTDOWN},
 {WSAEHOSTUNREACH, "WSAEHOSTUNREACH", EHOSTUNREACH},
 {WSAENOTEMPTY, "WSAENOTEMPTY", ENOTEMPTY},
 {WSAEPROCLIM, "WSAEPROCLIM", EPROCLIM},
 {WSAEUSERS, "WSAEUSERS", EUSERS},
 {WSAEDQUOT, "WSAEDQUOT", EDQUOT},
 {WSAESTALE, "WSAESTALE", ESTALE},
 {WSAEREMOTE, "WSAEREMOTE", EREMOTE},
 {WSAEINVAL, "WSAEINVAL", EINVAL},
 {WSAEFAULT, "WSAEFAULT", EFAULT},
 {0, NULL, 0}
};

/* Cygwin internal */
void
__set_winsock_errno (const char *fn, int ln)
{
  int i;
  int why = WSAGetLastError ();
  for (i = 0; errmap[i].w != 0; ++i)
    if (why == errmap[i].w)
      break;

  if (errmap[i].w != 0)
    {
      syscall_printf ("%s:%d - %d (%s) -> %d", fn, ln, why, errmap[i].s, errmap[i].e);
      set_errno (errmap[i].e);
    }
  else
    {
      syscall_printf ("%s:%d - unknown error %d", fn, ln, why);
      set_errno (EPERM);
    }
}

/*
 * Since the member `s' isn't used for debug output we can use it
 * for the error text returned by herror and hstrerror.
 */
static struct tl host_errmap[] =
{
  {WSAHOST_NOT_FOUND, "Unknown host", HOST_NOT_FOUND},
  {WSATRY_AGAIN, "Host name lookup failure", TRY_AGAIN},
  {WSANO_RECOVERY, "Unknown server error", NO_RECOVERY},
  {WSANO_DATA, "No address associated with name", NO_DATA},
  {0, NULL, 0}
};

/* Cygwin internal */
static void
set_host_errno ()
{
  int i;

  int why = WSAGetLastError ();
  for (i = 0; host_errmap[i].w != 0; ++i)
    if (why == host_errmap[i].w)
      break;

  if (host_errmap[i].w != 0)
    h_errno = host_errmap[i].e;
  else
    h_errno = NETDB_INTERNAL;
}

/* exported as getprotobyname: standards? */
extern "C" struct protoent *
cygwin_getprotobyname (const char *p)
{
  struct protoent *res = getprotobyname (p);
  if (!res)
    set_winsock_errno ();

  dump_protoent (res);
  return res;
}

/* exported as getprotobynumber: standards? */
extern "C" struct protoent *
cygwin_getprotobynumber (int number)
{
  struct protoent *res = getprotobynumber (number);
  if (!res)
    set_winsock_errno ();

  dump_protoent (res);
  return res;
}

fhandler_socket *
fdsock (int fd, const char *name, SOCKET soc)
{
  if (wsadata.wVersion < 512) /* < Winsock 2.0 */
    soc = set_socket_inheritance (soc);
  fhandler_socket *fh = (fhandler_socket *) fdtab.build_fhandler (fd, FH_SOCKET, name);
  fh->set_io_handle ((HANDLE) soc);
  fh->set_flags (O_RDWR);
  fdtab.inc_need_fixup_before ();
  return fh;
}

/* exported as socket: standards? */
extern "C" int
cygwin_socket (int af, int type, int protocol)
{
  int res = -1;
  SetResourceLock (LOCK_FD_LIST, WRITE_LOCK|READ_LOCK, "socket");

  SOCKET soc;

  int fd = fdtab.find_unused_handle ();

  if (fd < 0)
    set_errno (EMFILE);
  else
    {
      debug_printf ("socket (%d, %d, %d)", af, type, protocol);

      soc = socket (AF_INET, type, 0);

      if (soc == INVALID_SOCKET)
	{
	  set_winsock_errno ();
	  goto done;
	}

      const char *name;
      if (af == AF_INET)
	name = (type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp");
      else
	name = (type == SOCK_STREAM ? "/dev/streamsocket" : "/dev/dgsocket");

      fdsock (fd, name, soc)->set_addr_family (af);
      res = fd;
    }

done:
  syscall_printf ("%d = socket (%d, %d, %d)", res, af, type, protocol);
  ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK|READ_LOCK, "socket");
  return res;
}

/* cygwin internal: map sockaddr into internet domain address */

static int get_inet_addr (const struct sockaddr *in, int inlen,
			   struct sockaddr_in *out, int *outlen)
{
  if (in->sa_family == AF_INET)
    {
      *out = * (sockaddr_in *)in;
      *outlen = inlen;
      return 1;
    }
  else if (in->sa_family == AF_UNIX)
    {
      sockaddr_in sin;
      char buf[32];

      memset (buf, 0, sizeof buf);
      int fd = open (in->sa_data, O_RDONLY);
      if (fd == -1)
	return 0;
      if (read (fd, buf, sizeof buf) == -1)
	return 0;
      sin.sin_family = AF_INET;
      sscanf (buf + strlen (SOCKET_COOKIE), "%hu", &sin.sin_port);
      sin.sin_port = htons (sin.sin_port);
      sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
      *out = sin;
      *outlen = sizeof sin;
      return 1;
    }
  else
    {
      set_errno (EAFNOSUPPORT);
      return 0;
    }
}

/* exported as sendto: standards? */
extern "C" int
cygwin_sendto (int fd,
		 const void *buf,
		 int len,
		 unsigned int flags,
		 const struct sockaddr *to,
		 int tolen)
{
  fhandler_socket *h = (fhandler_socket *) fdtab[fd];
  sockaddr_in sin;
  sigframe thisframe (mainthread);

  if (get_inet_addr (to, tolen, &sin, &tolen) == 0)
    return -1;

  int res = sendto (h->get_socket (), (const char *) buf, len,
		    flags, to, tolen);
  if (res == SOCKET_ERROR)
    {
      set_winsock_errno ();
      res = -1;
    }
  return res;
}

/* exported as recvfrom: standards? */
extern "C" int
cygwin_recvfrom (int fd,
		   char *buf,
		   int len,
		   int flags,
		   struct sockaddr *from,
		   int *fromlen)
{
  fhandler_socket *h = (fhandler_socket *) fdtab[fd];
  sigframe thisframe (mainthread);

  debug_printf ("recvfrom %d", h->get_socket ());

  int res = recvfrom (h->get_socket (), buf, len, flags, from, fromlen);
  if (res == SOCKET_ERROR)
    {
      set_winsock_errno ();
      res = -1;
    }

  return res;
}

/* Cygwin internal */
fhandler_socket *
get (int fd)
{
  if (fdtab.not_open (fd))
    {
      set_errno (EINVAL);
      return 0;
    }

  return fdtab[fd]->is_socket ();
}

/* exported as setsockopt: standards? */
extern "C" int
cygwin_setsockopt (int fd,
		     int level,
		     int optname,
		     const void *optval,
		     int optlen)
{
  fhandler_socket *h = get (fd);
  int res = -1;
  const char *name = "error";

  if (h)
    {
      /* For the following debug_printf */
      switch (optname)
	{
	case SO_DEBUG:
	  name="SO_DEBUG";
	  break;
	case SO_ACCEPTCONN:
	  name="SO_ACCEPTCONN";
	  break;
	case SO_REUSEADDR:
	  name="SO_REUSEADDR";
	  break;
	case SO_KEEPALIVE:
	  name="SO_KEEPALIVE";
	  break;
	case SO_DONTROUTE:
	  name="SO_DONTROUTE";
	  break;
	case SO_BROADCAST:
	  name="SO_BROADCAST";
	  break;
	case SO_USELOOPBACK:
	  name="SO_USELOOPBACK";
	  break;
	case SO_LINGER:
	  name="SO_LINGER";
	  break;
	case SO_OOBINLINE:
	  name="SO_OOBINLINE";
	  break;
	}

      res = setsockopt (h->get_socket (), level, optname,
				     (const char *) optval, optlen);

      if (optlen == 4)
	syscall_printf ("setsockopt optval=%x", *(long *) optval);

      if (res)
	set_winsock_errno ();
    }

  syscall_printf ("%d = setsockopt (%d, %d, %x (%s), %x, %d)",
		  res, fd, level, optname, name, optval, optlen);
  return res;
}

/* exported as getsockopt: standards? */
extern "C" int
cygwin_getsockopt (int fd,
		     int level,
		     int optname,
		     void *optval,
		     int *optlen)
{
  fhandler_socket *h = get (fd);
  int res = -1;
  const char *name = "error";
  if (h)
    {
      /* For the following debug_printf */
      switch (optname)
	{
	case SO_DEBUG:
	  name="SO_DEBUG";
	  break;
	case SO_ACCEPTCONN:
	  name="SO_ACCEPTCONN";
	  break;
	case SO_REUSEADDR:
	  name="SO_REUSEADDR";
	  break;
	case SO_KEEPALIVE:
	  name="SO_KEEPALIVE";
	  break;
	case SO_DONTROUTE:
	  name="SO_DONTROUTE";
	  break;
	case SO_BROADCAST:
	  name="SO_BROADCAST";
	  break;
	case SO_USELOOPBACK:
	  name="SO_USELOOPBACK";
	  break;
	case SO_LINGER:
	  name="SO_LINGER";
	  break;
	case SO_OOBINLINE:
	  name="SO_OOBINLINE";
	  break;
	}

      res = getsockopt (h->get_socket (), level, optname,
				       (char *) optval, (int *) optlen);

      if (res)
	set_winsock_errno ();
    }

  syscall_printf ("%d = getsockopt (%d, %d, %x (%s), %x, %d)",
		  res, fd, level, optname, name, optval, optlen);
  return res;
}

/* exported as connect: standards? */
extern "C" int
cygwin_connect (int fd,
		  const struct sockaddr *name,
		  int namelen)
{
  int res;
  fhandler_socket *sock = get (fd);
  sockaddr_in sin;
  sigframe thisframe (mainthread);

  if (get_inet_addr (name, namelen, &sin, &namelen) == 0)
    return -1;

  if (!sock)
    {
      res = -1;
    }
  else
    {
      res = connect (sock->get_socket (), (sockaddr *) &sin, namelen);
      if (res)
	set_winsock_errno ();
    }
  return res;
}

/* exported as getservbyname: standards? */
extern "C" struct servent *
cygwin_getservbyname (const char *name, const char *proto)
{
  struct servent *p = getservbyname (name, proto);
  if (!p)
    set_winsock_errno ();

  syscall_printf ("%x = getservbyname (%s, %s)", p, name, proto);
  return p;
}

/* exported as getservbyport: standards? */
extern "C" struct servent *
cygwin_getservbyport (int port, const char *proto)
{
  struct servent *p = getservbyport (port, proto);
  if (!p)
    set_winsock_errno ();

  syscall_printf ("%x = getservbyport (%d, %s)", p, port, proto);
  return p;
}

extern "C" int
cygwin_gethostname (char *name, size_t len)
{
  int PASCAL win32_gethostname (char*, int);

  if (wsock32_handle == NULL ||
      win32_gethostname (name, len) == SOCKET_ERROR)
    {
      DWORD local_len = len;

      if (!GetComputerNameA (name, &local_len))
	{
	  set_winsock_errno ();
	  return -1;
	}
    }
  debug_printf ("name %s\n", name);
  h_errno = 0;
  return 0;
}

/* exported as gethostbyname: standards? */
extern "C" struct hostent *
cygwin_gethostbyname (const char *name)
{
  static unsigned char tmp_addr[4];
  static struct hostent tmp;
  static char *tmp_aliases[1] = {0};
  static char *tmp_addr_list[2] = {0, 0};
  static int a, b, c, d;

  if (sscanf (name, "%d.%d.%d.%d", &a, &b, &c, &d) == 4)
    {
      /* In case you don't have DNS, at least x.x.x.x still works */
      memset (&tmp, 0, sizeof (tmp));
      tmp_addr[0] = a;
      tmp_addr[1] = b;
      tmp_addr[2] = c;
      tmp_addr[3] = d;
      tmp_addr_list[0] = (char *)tmp_addr;
      tmp.h_name = name;
      tmp.h_aliases = tmp_aliases;
      tmp.h_addrtype = 2;
      tmp.h_length = 4;
      tmp.h_addr_list = tmp_addr_list;
      return &tmp;
    }

  struct hostent *ptr = gethostbyname (name);
  if (!ptr)
    {
      set_winsock_errno ();
      set_host_errno ();
    }
  else
    {
      debug_printf ("h_name %s", ptr->h_name);
      h_errno = 0;
    }
  return ptr;
}

/* exported as accept: standards? */
extern "C" int
cygwin_accept (int fd, struct sockaddr *peer, int *len)
{
  int res = -1;
  sigframe thisframe (mainthread);

  fhandler_socket *sock = get (fd);
  if (sock)
    {
      /* accept on NT fails if len < sizeof (sockaddr_in)
       * some programs set len to
       * sizeof (name.sun_family) + strlen (name.sun_path) for UNIX domain
       */
      if (len && ((unsigned) *len < sizeof (struct sockaddr_in)))
	*len = sizeof (struct sockaddr_in);

      res = accept (sock->get_socket (), peer, len);  // can't use a blocking call inside a lock

      SetResourceLock (LOCK_FD_LIST, WRITE_LOCK|READ_LOCK, "accept");

      int res_fd = fdtab.find_unused_handle ();
      if (res_fd == -1)
	{
	  /* FIXME: what is correct errno? */
	  set_errno (EMFILE);
	  goto done;
	}
      if ((SOCKET) res == (SOCKET) INVALID_SOCKET)
	set_winsock_errno ();
      else
	{
	  fdsock (res_fd, sock->get_name (), res);
	  res = res_fd;
	}
    }
done:
  syscall_printf ("%d = accept (%d, %x, %x)", res, fd, peer, len);
  ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK|READ_LOCK, "accept");
  return res;
}

/* exported as bind: standards? */
extern "C" int
cygwin_bind (int fd, struct sockaddr *my_addr, int addrlen)
{
  int res = -1;

  fhandler_socket *sock = get (fd);
  if (sock)
    {
      if (my_addr->sa_family == AF_UNIX)
	{
#define un_addr ((struct sockaddr_un *) my_addr)
	  struct sockaddr_in sin;
	  int len = sizeof sin;
	  int fd;

	  if (strlen (un_addr->sun_path) >= UNIX_PATH_LEN)
	    {
	      set_errno (ENAMETOOLONG);
	      goto out;
	    }
	  sin.sin_family = AF_INET;
	  sin.sin_port = 0;
	  sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
	  if (bind (sock->get_socket (), (sockaddr *) &sin, len))
	    {
	      syscall_printf ("AF_UNIX: bind failed %d", get_errno ());
	      set_winsock_errno ();
	      goto out;
	    }
	  if (getsockname (sock->get_socket (), (sockaddr *) &sin, &len))
	    {
	      syscall_printf ("AF_UNIX: getsockname failed %d", get_errno ());
	      set_winsock_errno ();
	      goto out;
	    }

	  sin.sin_port = ntohs (sin.sin_port);
	  debug_printf ("AF_UNIX: socket bound to port %u", sin.sin_port);

	  /* bind must fail if file system socket object already exists
	     so _open () is called with O_EXCL flag. */
	  fd = _open (un_addr->sun_path,
		      O_WRONLY | O_CREAT | O_EXCL | O_BINARY,
		      0);
	  if (fd < 0)
	    {
	      if (get_errno () == EEXIST)
		set_errno (EADDRINUSE);
	      goto out;
	    }

	  char buf[sizeof (SOCKET_COOKIE) + 10];
	  __small_sprintf (buf, "%s%u", SOCKET_COOKIE, sin.sin_port);
	  len = strlen (buf) + 1;

	  /* Note that the terminating nul is written.  */
	  if (_write (fd, buf, len) != len)
	    {
	      save_errno here;
	      _close (fd);
	      _unlink (un_addr->sun_path);
	    }
	  else
	    {
	      _close (fd);
	      chmod (un_addr->sun_path,
		(S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO) & ~myself->umask);
	      res = 0;
	    }
#undef un_addr
	}
      else if (bind (sock->get_socket (), my_addr, addrlen))
	set_winsock_errno ();
      else
	res = 0;
    }

out:
  syscall_printf ("%d = bind (%d, %x, %d)", res, fd, my_addr, addrlen);
  return res;
}

/* exported as getsockname: standards? */
extern "C" int
cygwin_getsockname (int fd, struct sockaddr *addr, int *namelen)
{
  int res = -1;

  fhandler_socket *sock = get (fd);
  if (sock)
    {
      res = getsockname (sock->get_socket (), addr, namelen);
      if (res)
	set_winsock_errno ();

    }
  syscall_printf ("%d = getsockname (%d, %x, %d)", res, fd, addr, namelen);
  return res;
}

/* exported as gethostbyaddr: standards? */
extern "C" struct hostent *
cygwin_gethostbyaddr (const char *addr, int len, int type)
{
  struct hostent *ptr = gethostbyaddr (addr, len, type);
  if (!ptr)
    {
      set_winsock_errno ();
      set_host_errno ();
    }
  else
    {
      debug_printf ("h_name %s", ptr->h_name);
      h_errno = 0;
    }
  return ptr;
}

/* exported as listen: standards? */
extern "C" int
cygwin_listen (int fd, int backlog)
{
  int res = -1;


  fhandler_socket *sock = get (fd);
  if (sock)
    {
      res = listen (sock->get_socket (), backlog);
      if (res)
	set_winsock_errno ();
    }
  syscall_printf ("%d = listen (%d, %d)", res, fd, backlog);
  return res;
}

/* exported as shutdown: standards? */
extern "C" int
cygwin_shutdown (int fd, int how)
{
  int res = -1;
  sigframe thisframe (mainthread);

  fhandler_socket *sock = get (fd);
  if (sock)
    {
      res = shutdown (sock->get_socket (), how);
      if (res)
	set_winsock_errno ();
    }
  syscall_printf ("%d = shutdown (%d, %d)", res, fd, how);
  return res;
}

/* exported as hstrerror: BSD 4.3  */
extern "C" const char *
cygwin_hstrerror (int err)
{
  int i;

  for (i = 0; host_errmap[i].e != 0; ++i)
    if (err == host_errmap[i].e)
      break;

  return host_errmap[i].s;
}

/* exported as herror: BSD 4.3  */
extern "C" void
cygwin_herror (const char *s)
{
  if (fdtab.not_open (2))
    return;

  if (s)
    {
      write (2, s, strlen (s));
      write (2, ": ", 2);
    }

  const char *h_errstr = cygwin_hstrerror (h_errno);

  if (!h_errstr)
    switch (h_errno)
      {
      case NETDB_INTERNAL:
        h_errstr = "Resolver internal error";
        break;
      case NETDB_SUCCESS:
        h_errstr = "Resolver error 0 (no error)";
        break;
      default:
        h_errstr = "Unknown resolver error";
        break;
      }
  write (2, h_errstr, strlen (h_errstr));
  write (2, "\n", 1);
}

/* exported as getpeername: standards? */
extern "C" int
cygwin_getpeername (int fd, struct sockaddr *name, int *len)
{
  fhandler_socket *h = (fhandler_socket *) fdtab[fd];

  debug_printf ("getpeername %d", h->get_socket ());
  int res = getpeername (h->get_socket (), name, len);
  if (res)
    set_winsock_errno ();

  debug_printf ("%d = getpeername %d", res, h->get_socket ());
  return res;
}

/* exported as recv: standards? */
extern "C" int
cygwin_recv (int fd, void *buf, int len, unsigned int flags)
{
  fhandler_socket *h = (fhandler_socket *) fdtab[fd];
  sigframe thisframe (mainthread);

  int res = recv (h->get_socket (), (char *) buf, len, flags);
  if (res == SOCKET_ERROR)
    {
      set_winsock_errno ();
      res = -1;
    }

#if 0
  if (res > 0 && res < 200)
    for (int i=0; i < res; i++)
      system_printf ("%d %x %c", i, ((char *) buf)[i], ((char *) buf)[i]);
#endif

  syscall_printf ("%d = recv (%d, %x, %x, %x)", res, fd, buf, len, flags);

  return res;
}

/* exported as send: standards? */
extern "C" int
cygwin_send (int fd, const void *buf, int len, unsigned int flags)
{
  fhandler_socket *h = (fhandler_socket *) fdtab[fd];
  sigframe thisframe (mainthread);

  int res = send (h->get_socket (), (const char *) buf, len, flags);
  if (res == SOCKET_ERROR)
    {
      set_winsock_errno ();
      res = -1;
    }

  syscall_printf ("%d = send (%d, %x, %d, %x)", res, fd, buf, len, flags);

  return res;
}

/* getdomainname: standards? */
extern "C" int
getdomainname (char *domain, int len)
{
  /*
   * This works for Win95 only if the machine is configured to use MS-TCP.
   * If a third-party TCP is being used this will fail.
   * FIXME: On Win95, is there a way to portably check the TCP stack
   * in use and include paths for the Domain name in each ?
   * Punt for now and assume MS-TCP on Win95.
   */
  reg_key r (HKEY_LOCAL_MACHINE, KEY_READ,
	     (os_being_run != winNT) ? "System" : "SYSTEM",
	     "CurrentControlSet", "Services",
	     (os_being_run != winNT) ? "MSTCP" : "Tcpip",
	     NULL);

  /* FIXME: Are registry keys case sensitive? */
  if (r.error () || r.get_string ("Domain", domain, len, "") != ERROR_SUCCESS)
    {
      __seterrno ();
      return -1;
    }

  return 0;
}

/* Cygwin internal */
/* Fill out an ifconf struct. */

/*
 * IFCONF Windows 2000:
 * Look at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\
                                                        Parameters\Interfaces
 * Each subkey is an interface.
 */
static void
get_2k_ifconf (struct ifconf *ifc, int what)
{
  HKEY key;
  int cnt = 1;

  /* Union maps buffer to correct struct */
  struct ifreq *ifr = ifc->ifc_req;

  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                    "SYSTEM\\"
                    "CurrentControlSet\\"
                    "Services\\"
                    "Tcpip\\"
                    "Parameters\\"
                    "Interfaces",
                    0, KEY_READ, &key) == ERROR_SUCCESS)
    {
      HKEY ikey;
      unsigned long lip, lnp;
      struct sockaddr_in *sa = NULL;
      char name[256];
      DWORD size;
      char eth[2] = "/", ppp[2] = "/";

      for (int idx = 0;
           RegEnumKeyEx (key, idx, name, (size = 256, &size),
                         NULL, NULL, 0, NULL) == ERROR_SUCCESS;
           ++idx)
        {
          if (RegOpenKeyEx (key, name, 0, KEY_READ, &ikey) != ERROR_SUCCESS)
            continue;

          /* If the "NTEContextList" value not exists, the subkey
             is irrelevant. */
          if (RegQueryValueEx (ikey, "NTEContextList",
                               NULL, NULL, NULL, &size) != ERROR_SUCCESS)
            {
              RegCloseKey (ikey);
              continue;
            }

          if ((caddr_t) ++ifr > ifc->ifc_buf
              + ifc->ifc_len
              - sizeof (struct ifreq))
            {
              RegCloseKey (ikey);
              break;
            }

          char ipaddress[256], netmask[256];
          char dhcpaddress[256], dhcpnetmask[256];

          if (RegQueryValueEx (ikey, "IPAddress",
                               NULL, NULL,
                               (unsigned char *) ipaddress,
                               (size = 256, &size)) == ERROR_SUCCESS
              && RegQueryValueEx (ikey, "SubnetMask",
                                  NULL, NULL,
                                  (unsigned char *) netmask,
                                  (size = 256, &size)) == ERROR_SUCCESS)
            {
              /* ppp interfaces don't have the "AddressType" value. */
              if (RegQueryValueEx (ikey, "AddressType",
                                   NULL, NULL, NULL, &size) == ERROR_SUCCESS)
                {
                  ++*eth;
                  strcpy (ifr->ifr_name, "eth");
                  strcat (ifr->ifr_name, eth);
                }
              else
                {
                  ++*ppp;
                  strcpy (ifr->ifr_name, "ppp");
                  strcat (ifr->ifr_name, ppp);
                }

              memset (&ifr->ifr_addr, '\0', sizeof ifr->ifr_addr);
              if (cygwin_inet_addr (ipaddress) == 0L
                  && RegQueryValueEx (ikey, "DhcpIPAddress",
                                      NULL, NULL,
                                      (unsigned char *) dhcpaddress,
                                      (size = 256, &size))
                  == ERROR_SUCCESS
                  && RegQueryValueEx (ikey, "DhcpSubnetMask",
                                      NULL, NULL,
                                      (unsigned char *) dhcpnetmask,
                                      (size = 256, &size))
                  == ERROR_SUCCESS)
                {
                  switch (what)
                    {
                    case SIOCGIFCONF:
                    case SIOCGIFADDR:
                      sa = (struct sockaddr_in *) &ifr->ifr_addr;
                      sa->sin_addr.s_addr = cygwin_inet_addr (dhcpaddress);
                      break;
                    case SIOCGIFBRDADDR:
                      lip = cygwin_inet_addr (dhcpaddress);
                      lnp = cygwin_inet_addr (dhcpnetmask);
                      sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
                      sa->sin_addr.s_addr = lip & lnp | ~lnp;
                      break;
                    case SIOCGIFNETMASK:
                      sa = (struct sockaddr_in *) &ifr->ifr_netmask;
                      sa->sin_addr.s_addr =
                        cygwin_inet_addr (dhcpnetmask);
                      break;
                    }
                }
              else
                {
                  switch (what)
                    {
                    case SIOCGIFCONF:
                    case SIOCGIFADDR:
                      sa = (struct sockaddr_in *) &ifr->ifr_addr;
                      sa->sin_addr.s_addr = cygwin_inet_addr (ipaddress);
                      break;
                    case SIOCGIFBRDADDR:
                      lip = cygwin_inet_addr (ipaddress);
                      lnp = cygwin_inet_addr (netmask);
                      sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
                      sa->sin_addr.s_addr = lip & lnp | ~lnp;
                      break;
                    case SIOCGIFNETMASK:
                      sa = (struct sockaddr_in *) &ifr->ifr_netmask;
                      sa->sin_addr.s_addr = cygwin_inet_addr (netmask);
                      break;
                    }
                }
              sa->sin_family = AF_INET;
              sa->sin_port = 0;
              ++cnt;
            }

          RegCloseKey (ikey);
        }

      RegCloseKey (key);
    }

  /* Set the correct length */
  ifc->ifc_len = cnt * sizeof (struct ifreq);
}

/*
 * IFCONF Windows NT:
 * Look at the Bind value in
 * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Linkage\
 * This is a REG_MULTI_SZ with strings of the form:
 * \Device\<Netcard>, where netcard is the name of the net device.
 * Then look under:
 * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<NetCard>\
 *							Parameters\Tcpip
 * at the IPAddress, Subnetmask and DefaultGateway values for the
 * required values.
 */
static void
get_nt_ifconf (struct ifconf *ifc, int what)
{
  HKEY key;
  unsigned long lip, lnp;
  struct sockaddr_in *sa = NULL;
  DWORD size;
  int cnt = 1;
  char *binding = (char *) 0;

  /* Union maps buffer to correct struct */
  struct ifreq *ifr = ifc->ifc_req;

  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                    "SYSTEM\\"
                    "CurrentControlSet\\"
                    "Services\\"
                    "Tcpip\\"
                    "Linkage",
                    0, KEY_READ, &key) == ERROR_SUCCESS)
    {
      if (RegQueryValueEx (key, "Bind",
                           NULL, NULL,
                           NULL, &size) == ERROR_SUCCESS)
        {
          binding = (char *) alloca (size);
          if (RegQueryValueEx (key, "Bind",
                               NULL, NULL,
                               (unsigned char *) binding,
                               &size) != ERROR_SUCCESS)
            {
              binding = NULL;
            }
        }
      RegCloseKey (key);
    }

  if (binding)
    {
      char *bp, eth[2] = "/";
      char cardkey[256], ipaddress[256], netmask[256];

      for (bp = binding; *bp; bp += strlen (bp) + 1)
        {
          bp += strlen ("\\Device\\");
          strcpy (cardkey, "SYSTEM\\CurrentControlSet\\Services\\");
          strcat (cardkey, bp);
          strcat (cardkey, "\\Parameters\\Tcpip");

          if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, cardkey,
                            0, KEY_READ, &key) != ERROR_SUCCESS)
            continue;

          if (RegQueryValueEx (key, "IPAddress",
                               NULL, NULL,
                               (unsigned char *) ipaddress,
                               (size = 256, &size)) == ERROR_SUCCESS
              && RegQueryValueEx (key, "SubnetMask",
                                  NULL, NULL,
                                  (unsigned char *) netmask,
                                  (size = 256, &size)) == ERROR_SUCCESS)
            {
              char *ip, *np;
              char dhcpaddress[256], dhcpnetmask[256];

              for (ip = ipaddress, np = netmask;
                   *ip && *np;
                   ip += strlen (ip) + 1, np += strlen (np) + 1)
                {
                  if ((caddr_t) ++ifr > ifc->ifc_buf
                      + ifc->ifc_len
                      - sizeof (struct ifreq))
                    break;

                  if (! strncmp (bp, "NdisWan", 7))
                    {
                      strcpy (ifr->ifr_name, "ppp");
                      strcat (ifr->ifr_name, bp + 7);
                    }
                  else
                    {
                      ++*eth;
                      strcpy (ifr->ifr_name, "eth");
                      strcat (ifr->ifr_name, eth);
                    }
                  memset (&ifr->ifr_addr, '\0', sizeof ifr->ifr_addr);
                  if (cygwin_inet_addr (ip) == 0L
                      && RegQueryValueEx (key, "DhcpIPAddress",
                                          NULL, NULL,
                                          (unsigned char *) dhcpaddress,
                                          (size = 256, &size))
                      == ERROR_SUCCESS
                      && RegQueryValueEx (key, "DhcpSubnetMask",
                                          NULL, NULL,
                                          (unsigned char *) dhcpnetmask,
                                          (size = 256, &size))
                      == ERROR_SUCCESS)
                    {
                      switch (what)
                        {
                        case SIOCGIFCONF:
                        case SIOCGIFADDR:
                          sa = (struct sockaddr_in *) &ifr->ifr_addr;
                          sa->sin_addr.s_addr = cygwin_inet_addr (dhcpaddress);
                          break;
                        case SIOCGIFBRDADDR:
                          lip = cygwin_inet_addr (dhcpaddress);
                          lnp = cygwin_inet_addr (dhcpnetmask);
                          sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
                          sa->sin_addr.s_addr = lip & lnp | ~lnp;
                          break;
                        case SIOCGIFNETMASK:
                          sa = (struct sockaddr_in *) &ifr->ifr_netmask;
                          sa->sin_addr.s_addr =
                            cygwin_inet_addr (dhcpnetmask);
                          break;
                        }
                    }
                  else
                    {
                      switch (what)
                        {
                        case SIOCGIFCONF:
                        case SIOCGIFADDR:
                          sa = (struct sockaddr_in *) &ifr->ifr_addr;
                          sa->sin_addr.s_addr = cygwin_inet_addr (ip);
                          break;
                        case SIOCGIFBRDADDR:
                          lip = cygwin_inet_addr (ip);
                          lnp = cygwin_inet_addr (np);
                          sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
                          sa->sin_addr.s_addr = lip & lnp | ~lnp;
                          break;
                        case SIOCGIFNETMASK:
                          sa = (struct sockaddr_in *) &ifr->ifr_netmask;
                          sa->sin_addr.s_addr = cygwin_inet_addr (np);
                          break;
                        }
                    }
                  sa->sin_family = AF_INET;
                  sa->sin_port = 0;
                  ++cnt;
                }
            }
          RegCloseKey (key);
        }
    }

  /* Set the correct length */
  ifc->ifc_len = cnt * sizeof (struct ifreq);
}

/*
 * IFCONF Windows 9x:
 * HKLM/Enum/Network/MSTCP/"*"
 *         -> Value "Driver" enthält Subkey relativ zu
 *            HKLM/System/CurrentControlSet/Class/
 *         -> In Subkey "Bindings" die Values aufzählen
 *            -> Enthält Subkeys der Form "VREDIR\*"
 *               Das * ist ein Subkey relativ zu
 *               HKLM/System/CurrentControlSet/Class/Net/
 * HKLM/System/CurrentControlSet/Class/"Driver"
 *         -> Value "IPAddress"
 *         -> Value "IPMask"
 * HKLM/System/CurrentControlSet/Class/Net/"*"(aus "VREDIR\*")
 *         -> Wenn Value "AdapterName" == "MS$PPP" -> ppp interface
 *         -> Value "DriverDesc" enthält den Namen
 *
 */
static void
get_9x_ifconf (struct ifconf *ifc, int what)
{
  HKEY key;
  unsigned long lip, lnp;
  struct sockaddr_in *sa = NULL;
  FILETIME update;
  LONG res;
  DWORD size;
  int cnt = 1;
  char ifname[256];
  char eth[2] = "/";
  char ppp[2] = "/";

  /* Union maps buffer to correct struct */
  struct ifreq *ifr = ifc->ifc_req;

  if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Enum\\Network\\MSTCP",
                    0, KEY_READ, &key) != ERROR_SUCCESS)
    {
      /* Set the correct length */
      ifc->ifc_len = cnt * sizeof (struct ifreq);
      return;
    }

  for (int i = 0;
       (res = RegEnumKeyEx (key, i, ifname,
                            (size = sizeof ifname, &size),
                            0, 0, 0, &update)) != ERROR_NO_MORE_ITEMS;
       ++i)
    {
      HKEY ifkey, subkey;
      char driver[256], classname[256], bindname[256], netname[256];
      char adapter[256], ip[256], np[256];

      if (res != ERROR_SUCCESS
          || RegOpenKeyEx (key, ifname, 0,
                           KEY_READ, &ifkey) != ERROR_SUCCESS)
        continue;

      if (RegQueryValueEx (ifkey, "Driver", 0,
                           NULL, (unsigned char *) driver,
                           (size = sizeof driver, &size)) != ERROR_SUCCESS)
        {
          RegCloseKey (ifkey);
          continue;
        }

      strcpy (classname, "System\\CurrentControlSet\\Services\\Class\\");
      strcat (classname, driver);
      if ((res = RegOpenKeyEx (HKEY_LOCAL_MACHINE, classname,
                               0, KEY_READ, &subkey)) != ERROR_SUCCESS)
        {
          RegCloseKey (ifkey);
          continue;
        }

      if (RegQueryValueEx (subkey, "IPAddress", 0,
                           NULL, (unsigned char *) ip,
                           (size = sizeof ip, &size)) == ERROR_SUCCESS
          && RegQueryValueEx (subkey, "IPMask", 0,
                              NULL, (unsigned char *) np,
                              (size = sizeof np, &size)) == ERROR_SUCCESS)
        {
          if ((caddr_t)++ifr > ifc->ifc_buf
              + ifc->ifc_len
              - sizeof (struct ifreq))
            goto out;

          switch (what)
            {
            case SIOCGIFCONF:
            case SIOCGIFADDR:
              sa = (struct sockaddr_in *) &ifr->ifr_addr;
              sa->sin_addr.s_addr = cygwin_inet_addr (ip);
              break;
            case SIOCGIFBRDADDR:
              lip = cygwin_inet_addr (ip);
              lnp = cygwin_inet_addr (np);
              sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
              sa->sin_addr.s_addr = lip & lnp | ~lnp;
              break;
            case SIOCGIFNETMASK:
              sa = (struct sockaddr_in *) &ifr->ifr_netmask;
              sa->sin_addr.s_addr = cygwin_inet_addr (np);
              break;
            }
          sa->sin_family = AF_INET;
          sa->sin_port = 0;
        }

      RegCloseKey (subkey);

      if (RegOpenKeyEx (ifkey, "Bindings",
                         0, KEY_READ, &subkey) != ERROR_SUCCESS)
        {
          RegCloseKey (ifkey);
          --ifr;
          continue;
        }

      for (int j = 0;
           (res = RegEnumValue (subkey, j, bindname,
                                (size = sizeof bindname, &size),
                                0, NULL, NULL, NULL)) != ERROR_NO_MORE_ITEMS;
           ++j)
        if (!strncasecmp (bindname, "VREDIR\\", 7))
          break;

      RegCloseKey (subkey);

      if (res == ERROR_SUCCESS)
        {
          strcpy (netname, "System\\CurrentControlSet\\Services\\Class\\Net\\");
          strcat (netname, bindname + 7);

          if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, netname,
                            0, KEY_READ, &subkey) != ERROR_SUCCESS)
            {
              RegCloseKey (ifkey);
              --ifr;
              continue;
            }

          if (RegQueryValueEx (subkey, "AdapterName", 0,
                               NULL, (unsigned char *) adapter,
                               (size = sizeof adapter, &size)) == ERROR_SUCCESS
              && !strcasecmp (adapter, "MS$PPP"))
            {
              ++*ppp;
              strcpy (ifr->ifr_name, "ppp");
              strcat (ifr->ifr_name, ppp);
            }
          else
            {
              ++*eth;
              strcpy (ifr->ifr_name, "eth");
              strcat (ifr->ifr_name, eth);
            }

          RegCloseKey (subkey);

        }

      RegCloseKey (ifkey);

      ++cnt;
    }

out:

  RegCloseKey (key);

  /* Set the correct length */
  ifc->ifc_len = cnt * sizeof (struct ifreq);
}

int
get_ifconf (struct ifconf *ifc, int what)
{
  unsigned long lip, lnp;
  struct sockaddr_in *sa;

  /* Union maps buffer to correct struct */
  struct ifreq *ifr = ifc->ifc_req;

  /* Ensure we have space for two struct ifreqs, fail if not. */
  if (ifc->ifc_len < (int) (2 * sizeof (struct ifreq)))
    {
      set_errno (EFAULT);
      return -1;
    }

  /* Set up interface lo0 first */
  strcpy (ifr->ifr_name, "lo0");
  memset (&ifr->ifr_addr, '\0', sizeof (ifr->ifr_addr));
  switch (what)
    {
    case SIOCGIFCONF:
    case SIOCGIFADDR:
      sa = (struct sockaddr_in *) &ifr->ifr_addr;
      sa->sin_addr.s_addr = htonl (INADDR_LOOPBACK);
      break;
    case SIOCGIFBRDADDR:
      lip = htonl (INADDR_LOOPBACK);
      lnp = cygwin_inet_addr ("255.0.0.0");
      sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
      sa->sin_addr.s_addr = lip & lnp | ~lnp;
      break;
    case SIOCGIFNETMASK:
      sa = (struct sockaddr_in *) &ifr->ifr_netmask;
      sa->sin_addr.s_addr = cygwin_inet_addr ("255.0.0.0");
      break;
    default:
      set_errno (EINVAL);
      return -1;
    }
  sa->sin_family = AF_INET;
  sa->sin_port = 0;

  OSVERSIONINFO os_version_info;
  memset (&os_version_info, 0, sizeof os_version_info);
  os_version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
  GetVersionEx (&os_version_info);
  if (os_version_info.dwPlatformId != VER_PLATFORM_WIN32_NT)
    get_9x_ifconf (ifc, what);
  else if (os_version_info.dwMajorVersion <= 4)
    get_nt_ifconf (ifc, what);
  else
    get_2k_ifconf (ifc, what);

  return 0;
}

/* exported as rcmd: standards? */
extern "C" int
cygwin_rcmd (char **ahost, unsigned short inport, char *locuser,
	       char *remuser, char *cmd, int *fd2p)
{
  int res = -1;
  SOCKET fd2s;
  sigframe thisframe (mainthread);

  int res_fd = fdtab.find_unused_handle ();
  if (res_fd == -1)
    goto done;

  if (fd2p)
    {
      *fd2p = fdtab.find_unused_handle (res_fd + 1);
      if (*fd2p == -1)
	goto done;
    }

  res = rcmd (ahost, inport, locuser, remuser, cmd, fd2p? &fd2s: NULL);
  if (res == (int) INVALID_SOCKET)
    goto done;
  else
    {
      fdsock (res_fd, "/dev/tcp", res);
      res = res_fd;
    }
  if (fd2p)
    {
      fdsock (*fd2p, "/dev/tcp", fd2s);
    }
done:
  syscall_printf ("%d = rcmd (...)", res);
  return res;
}

/* exported as rresvport: standards? */
extern "C" int
cygwin_rresvport (int *port)
{
  int res = -1;
  sigframe thisframe (mainthread);

  int res_fd = fdtab.find_unused_handle ();
  if (res_fd == -1)
    goto done;
  res = rresvport (port);

  if (res == (int) INVALID_SOCKET)
    goto done;
  else
    {
      fdsock (res_fd, "/dev/tcp", res);
      res = res_fd;
    }
done:
  syscall_printf ("%d = rresvport (%d)", res, port ? *port : 0);
  return res;
}

/* exported as rexec: standards? */
extern "C" int
cygwin_rexec (char **ahost, unsigned short inport, char *locuser,
	       char *password, char *cmd, int *fd2p)
{
  int res = -1;
  SOCKET fd2s;
  sigframe thisframe (mainthread);

  int res_fd = fdtab.find_unused_handle ();
  if (res_fd == -1)
    goto done;
  if (fd2p)
    {
      *fd2p = fdtab.find_unused_handle (res_fd + 1);
      if (*fd2p == -1)
	goto done;
    }
  res = rexec (ahost, inport, locuser, password, cmd, fd2p ? &fd2s : NULL);
  if (res == (int) INVALID_SOCKET)
    goto done;
  else
    {
      fdsock (res_fd, "/dev/tcp", res);
      res = res_fd;
    }
  if (fd2p)
    {
      fd2s = set_socket_inheritance (fd2s);
      fdsock (*fd2p, "/dev/tcp", fd2s);
    }
done:
  syscall_printf ("%d = rexec (...)", res);
  return res;
}

/* socketpair: standards? */
/* Win32 supports AF_INET only, so ignore domain and protocol arguments */
extern "C" int
socketpair (int, int type, int, int *sb)
{
  int res = -1;
  SOCKET insock, outsock, newsock;
  struct sockaddr_in sock_in;
  int len = sizeof (sock_in);

  SetResourceLock (LOCK_FD_LIST, WRITE_LOCK|READ_LOCK, "socketpair");

  sb[0] = fdtab.find_unused_handle ();
  if (sb[0] == -1)
    {
      set_errno (EMFILE);
      goto done;
    }
  sb[1] = fdtab.find_unused_handle (sb[0] + 1);
  if (sb[1] == -1)
    {
      set_errno (EMFILE);
      goto done;
    }

  /* create a listening socket */
  newsock = socket (AF_INET, type, 0);
  if (newsock == INVALID_SOCKET)
    {
      debug_printf ("first socket call failed");
      set_winsock_errno ();
      goto done;
    }

  /* bind the socket to any unused port */
  sock_in.sin_family = AF_INET;
  sock_in.sin_port = 0;
  sock_in.sin_addr.s_addr = INADDR_ANY;

  if (bind (newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
    {
      debug_printf ("bind failed");
      set_winsock_errno ();
      closesocket (newsock);
      goto done;
    }

  if (getsockname (newsock, (struct sockaddr *) &sock_in, &len) < 0)
    {
      debug_printf ("getsockname error");
      set_winsock_errno ();
      closesocket (newsock);
      goto done;
    }

  listen (newsock, 2);

  /* create a connecting socket */
  outsock = socket (AF_INET, type, 0);
  if (outsock == INVALID_SOCKET)
    {
      debug_printf ("second socket call failed");
      set_winsock_errno ();
      closesocket (newsock);
      goto done;
    }

  sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);

  /* Do a connect and accept the connection */
  if (connect (outsock, (struct sockaddr *) &sock_in,
					   sizeof (sock_in)) < 0)
    {
      debug_printf ("connect error");
      set_winsock_errno ();
      closesocket (newsock);
      closesocket (outsock);
      goto done;
    }

  insock = accept (newsock, (struct sockaddr *) &sock_in, &len);
  if (insock == INVALID_SOCKET)
    {
      debug_printf ("accept error");
      set_winsock_errno ();
      closesocket (newsock);
      closesocket (outsock);
      goto done;
    }

  closesocket (newsock);
  res = 0;

  fdsock (sb[0], "/dev/tcp", insock);

  fdsock (sb[1], "/dev/tcp", outsock);

done:
  syscall_printf ("%d = socketpair (...)", res);
  ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK|READ_LOCK, "socketpair");
  return res;
}

/* sethostent: standards? */
extern "C" void
sethostent (int)
{
}

/* endhostent: standards? */
extern "C" void
endhostent (void)
{
}

extern "C" void
wsock_init ()
{
  int res = WSAStartup ((2<<8) | 2, &wsadata);

  debug_printf ("res %d", res);
  debug_printf ("wVersion %d", wsadata.wVersion);
  debug_printf ("wHighVersion %d", wsadata.wHighVersion);
  debug_printf ("szDescription %s", wsadata.szDescription);
  debug_printf ("szSystemStatus %s", wsadata.szSystemStatus);
  debug_printf ("iMaxSockets %d", wsadata.iMaxSockets);
  debug_printf ("iMaxUdpDg %d", wsadata.iMaxUdpDg);
  debug_printf ("lpVendorInfo %d", wsadata.lpVendorInfo);

  if (FIONBIO  != REAL_FIONBIO)
    debug_printf ("****************  FIONBIO  != REAL_FIONBIO");
}

