/* 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 . */ #include /* Specification. */ #include "socketpair.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #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