Hi,

A request for comment for a socketpair for windows.

Please comment.

In order to support AF_UNIX we will need to do the same thing that with dirfd.

Bastien
/* Copyright (C) 2011 Free Software Foundation, Inc.

   This program 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 3 of the License, or
   (at your option) any later version.

   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */

#include <config.h>

/* Specification.  */
#include "socketpair.h"

#include <errno.h>
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

#include <sys/socket.h>
#include <netdb.h>

#include <netinet/in.h>
#include <netinet/ip.h>

#include "cloexec.h"

#if !defined HAVE_SOCKETPAIR

static int
filtererrno (int e)
{
  switch (e)
    {
    case EAFNOSUPPORT:
    case EMFILE:
    case ENFILE:
    case EOPNOTSUPP:
    case EPROTONOSUPPORT:
    case EPROTOTYPE:
    case EACCES:
    case EFAULT:
    case ENOBUFS:
    case ENOMEM:
      return e;
    default:
      return ENOMEM;
    }
}

/* filter out supported domain, type, protocol */
static bool
socketpairsupported (int domain, int type, int protocol)
{
  (void) protocol;
  (void) type;
  if (domain == AF_INET || domain == AF_INET6)
    return true;
  return false;
}

/* 
   The socketpair() call creates an unnamed pair of connected sockets 
   in the specified domain, 
   of the specified type, and using the optionally specified protocol.
   
   NOTE: raw variant does not try to emulate AF_UNIX
*/
static int
raw_socketpair (int domain, int rawtype, int protocol, int sv[2])
{
#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
  struct sockaddr_storage serverfd_addr, outsock_addr;
  socklen_t addr_len = sizeof (struct sockaddr_storage);
  struct addrinfo hints, *res;
  int getaddrinfo_r;
  int serverfd;
  int saved_errno;
  int insock, outsock;
  int type;
  int cloexecflag;

  /* filter out cloexec flag */
  type = type & ~SOCK_CLOEXEC;
  cloexecflag = type & SOCK_CLOEXEC;


  /* filter out protocol */
  if (!socketpairsupported (domain, type, protocol))
    {
      errno = EOPNOTSUPP;
      return -1;
    }

  /* get loopback address */
  memset (&hints, 0, sizeof (hints));
  hints.ai_family = domain;
  hints.ai_socktype = type;
  hints.ai_protocol = protocol;
  hints.ai_flags = 0;

  getaddrinfo_r = getaddrinfo (NULL, "0", &hints, &res);
  /* fake errno */
  switch (getaddrinfo_r)
    {
    case 0:
      break;
    case EAI_FAMILY:
      errno = EAFNOSUPPORT;
      return -1;
    case EAI_MEMORY:
      errno = ENOMEM;
      return -1;
    default:
      errno = EIO;
      return -1;
    }

  serverfd = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
  if (-1 == serverfd)
    goto out_bind_fail;

  if (-1 == bind (serverfd, res->ai_addr, res->ai_addrlen))
    goto out_close_serverfd;

  if (-1 ==
      getsockname (serverfd, (struct sockaddr *) &serverfd_addr, &addr_len))
    goto out_close_serverfd;

  if (type != SOCK_DGRAM)
    if (-1 == listen (serverfd, 1))
      goto out_close_serverfd;

  outsock = socket (res->ai_family, res->ai_socktype, res->ai_protocol);
  if (-1 == outsock)
    goto out_close_serverfd;

  if (type == SOCK_DGRAM)
    {
      if (-1 == bind (outsock, res->ai_addr, res->ai_addrlen))
	goto out_close_outsock;
      if (-1 ==
	  getsockname (outsock, (struct sockaddr *) &outsock_addr, &addr_len))
	goto out_close_outsock;
    }

  if (-1 == connect (outsock, (struct sockaddr *) &serverfd_addr, addr_len))
    goto out_close_outsock;

  if (type != SOCK_DGRAM)
    {
      insock = accept (serverfd, NULL, NULL);
      if (-1 == outsock)
	goto out_close_insock;
      /* do not check error, at most we leak serverfd */
      (void) close (serverfd);
    }
  else
    {
      if (-1 ==
	  connect (serverfd, (struct sockaddr *) &outsock_addr, addr_len))
	goto out_close_outsock;
      insock = serverfd;
    }

  sv[0] = insock;
  sv[1] = outsock;
  freeaddrinfo (res);
  return 0;
out_close_insock:
  saved_errno = errno;
  (void) close (outsock);
  errno = saved_errno;
out_close_outsock:
  saved_errno = errno;
  (void) close (insock);
  errno = saved_errno;
out_close_serverfd:
  saved_errno = errno;
  (void) close (serverfd);
  errno = saved_errno;
out_bind_fail:
  errno = filtererrno (errno);
  freeaddrinfo (res);
  return -1;
#else
  errno = ENOSYS;
  return -1;
#endif
}

/* 
   The socketpair() call creates an unnamed pair of connected sockets 
   in the specified domain, 
   of the specified type, and using the optionally specified protocol.
*/
int
socketpair (int domain, int type, int protocol, int sv[2])
{
  return raw_socketpair (int domain, int type, int protocol, int sv[2]);
}

#endif

Reply via email to