Cygwin: socketpair: Move socketpair creation inside fhandler_socket class

Add fhandler_socket::socketpair method

Deliberately disable AF_INET socketpairs for now

Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
Corinna Vinschen 2018-02-16 16:36:19 +01:00
parent cff85eaddc
commit 4e04751fc7
3 changed files with 202 additions and 179 deletions

View File

@ -588,6 +588,8 @@ class fhandler_socket: public fhandler_base
int getsockname (struct sockaddr *name, int *namelen);
int getpeername (struct sockaddr *name, int *namelen);
int getpeereid (pid_t *pid, uid_t *euid, gid_t *egid);
socketpair (int af, int type, int protocol, int flags,
fhandler_socket *fh_out);
int open (int flags, mode_t mode = 0);
void __reg3 read (void *ptr, size_t& len);

View File

@ -377,6 +377,130 @@ fhandler_socket::socket (int af, int type, int protocol, int flags)
return ret;
}
/* fhandler_socket::socketpair is called on the fhandler handling the
accepting socket, fh_out is the fhandler for the connecting socket. */
int
fhandler_socket::socketpair (int af, int type, int protocol, int flags,
fhandler_socket *fh_out)
{
SOCKET insock = INVALID_SOCKET;
SOCKET outsock = INVALID_SOCKET;
SOCKET sock = INVALID_SOCKET;
struct sockaddr_in sock_in, sock_out;
int len;
/* create listening socket */
sock = ::socket (AF_INET, type, 0);
if (sock == INVALID_SOCKET)
{
set_winsock_errno ();
goto err;
}
/* bind to unused port */
sock_in.sin_family = AF_INET;
sock_in.sin_port = 0;
sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
if (::bind (sock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
{
set_winsock_errno ();
goto err;
}
/* fetch socket name */
len = sizeof (sock_in);
if (::getsockname (sock, (struct sockaddr *) &sock_in, &len) < 0)
{
set_winsock_errno ();
goto err;
}
/* on stream sockets, create listener */
if (type == SOCK_STREAM && ::listen (sock, 2) < 0)
{
set_winsock_errno ();
goto err;
}
/* create connecting socket */
outsock = ::socket (AF_INET, type, 0);
if (outsock == INVALID_SOCKET)
{
set_winsock_errno ();
goto err;
}
/* on datagram sockets, bind connecting socket */
if (type == SOCK_DGRAM)
{
sock_out.sin_family = AF_INET;
sock_out.sin_port = 0;
sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
if (::bind (outsock, (struct sockaddr *) &sock_out,
sizeof (sock_out)) < 0)
{
set_winsock_errno ();
goto err;
}
/* ...and fetch name */
len = sizeof (sock_out);
if (::getsockname (outsock, (struct sockaddr *) &sock_out, &len) < 0)
{
set_winsock_errno ();
goto err;
}
}
sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
if (type == SOCK_DGRAM)
sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
/* connect */
if (::connect (outsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
{
set_winsock_errno ();
goto err;
}
if (type == SOCK_STREAM)
{
/* on stream sockets, accept connection and close listener */
len = sizeof (sock_in);
insock = ::accept (sock, (struct sockaddr *) &sock_in, &len);
if (insock == INVALID_SOCKET)
{
set_winsock_errno ();
goto err;
}
::closesocket (sock);
}
else
{
/* on datagram sockets, connect vice versa */
if (::connect (sock, (struct sockaddr *) &sock_out,
sizeof (sock_out)) < 0)
{
set_winsock_errno ();
goto err;
}
insock = sock;
}
sock = INVALID_SOCKET;
/* postprocessing */
connect_state (connected);
fh_out->connect_state (connected);
if (af == AF_LOCAL && type == SOCK_STREAM)
{
af_local_set_sockpair_cred ();
fh_out->af_local_set_sockpair_cred ();
}
if (set_socket_handle (insock, af, type, flags) < 0
|| fh_out->set_socket_handle (outsock, af, type, flags) < 0)
goto err;
return 0;
err:
if (sock != INVALID_SOCKET)
::closesocket (sock);
if (insock != INVALID_SOCKET)
::closesocket (insock);
if (outsock != INVALID_SOCKET)
::closesocket (outsock);
return -1;
}
void

View File

@ -2767,201 +2767,98 @@ cygwin_bindresvport (int fd, struct sockaddr_in *sin)
return cygwin_bindresvport_sa (fd, (struct sockaddr *) sin);
}
/* socketpair: standards? */
/* Win32 supports AF_INET only, so ignore domain and protocol arguments */
/* socketpair: POSIX.1-2001, POSIX.1-2008, 4.4BSD. */
extern "C" int
socketpair (int family, int type, int protocol, int *sb)
socketpair (int af, int type, int protocol, int *sb)
{
int res = -1;
SOCKET insock = INVALID_SOCKET;
SOCKET outsock = INVALID_SOCKET;
SOCKET newsock = INVALID_SOCKET;
struct sockaddr_in sock_in, sock_out;
int len;
const device *dev;
fhandler_socket *fh_in, *fh_out;
__try
int flags = type & _SOCK_FLAG_MASK;
type &= ~_SOCK_FLAG_MASK;
debug_printf ("socket (%d, %d (flags %y), %d)", af, type, flags, protocol);
switch (af)
{
int flags = type & _SOCK_FLAG_MASK;
type &= ~_SOCK_FLAG_MASK;
if (family != AF_LOCAL && family != AF_INET)
{
set_errno (EAFNOSUPPORT);
__leave;
}
case AF_LOCAL:
if (type != SOCK_STREAM && type != SOCK_DGRAM)
{
set_errno (EINVAL);
goto done;
}
if (protocol != 0)
{
set_errno (EPROTONOSUPPORT);
goto done;
}
dev = type == SOCK_STREAM ? stream_dev : dgram_dev;
break;
#if 0 /* FIXME: Given neither BSD nor Linux support anything other than AF_LOCAL
sockets, we deliberately disable AF_INIT socketpairs now and hope for
the best. */
case AF_INET:
if (type != SOCK_STREAM && type != SOCK_DGRAM)
{
set_errno (EINVAL);
goto done;
}
dev = type == SOCK_STREAM ? tcp_dev : udp_dev;
break;
#endif
default:
set_errno (EAFNOSUPPORT);
goto done;
}
if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0)
{
set_errno (EINVAL);
goto done;
}
{
cygheap_fdnew fd_in;
if (fd_in < 0)
goto done;
cygheap_fdnew fd_out (fd_in, false);
if (fd_out < 0)
{
set_errno (EPROTOTYPE);
__leave;
}
if ((flags & ~(SOCK_NONBLOCK | SOCK_CLOEXEC)) != 0)
{
set_errno (EINVAL);
__leave;
}
if ((family == AF_LOCAL && protocol != PF_UNSPEC && protocol != PF_LOCAL)
|| (family == AF_INET && protocol != PF_UNSPEC && protocol != PF_INET))
{
set_errno (EPROTONOSUPPORT);
__leave;
fd_in.release ();
goto done;
}
/* create the first socket */
newsock = socket (AF_INET, type, 0);
if (newsock == INVALID_SOCKET)
fh_in = (fhandler_socket *) build_fh_dev (*dev);
fh_out = (fhandler_socket *) build_fh_dev (*dev);
if (fh_in && fh_out
&& fh_in->socketpair (af, type, protocol, flags, fh_out) == 0)
{
debug_printf ("first socket call failed");
set_winsock_errno ();
__leave;
}
/* bind the socket to any unused port */
sock_in.sin_family = AF_INET;
sock_in.sin_port = 0;
sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
if (bind (newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
{
debug_printf ("bind failed");
set_winsock_errno ();
__leave;
}
len = sizeof (sock_in);
if (getsockname (newsock, (struct sockaddr *) &sock_in, &len) < 0)
{
debug_printf ("getsockname error");
set_winsock_errno ();
__leave;
}
/* For stream sockets, create a listener */
if (type == SOCK_STREAM)
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 ();
__leave;
}
/* For datagram sockets, bind the 2nd socket to an unused address, too */
if (type == SOCK_DGRAM)
{
sock_out.sin_family = AF_INET;
sock_out.sin_port = 0;
sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
if (bind (outsock, (struct sockaddr *) &sock_out, sizeof (sock_out)) < 0)
fd_in = fh_in;
fd_out = fh_out;
if (fd_in <= 2)
set_std_handle (fd_in);
if (fd_out <= 2)
set_std_handle (fd_out);
__try
{
debug_printf ("bind failed");
set_winsock_errno ();
__leave;
}
len = sizeof (sock_out);
if (getsockname (outsock, (struct sockaddr *) &sock_out, &len) < 0)
{
debug_printf ("getsockname error");
set_winsock_errno ();
__leave;
}
}
/* Force IP address to loopback */
sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
if (type == SOCK_DGRAM)
sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
/* Do a connect */
if (connect (outsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
{
debug_printf ("connect error");
set_winsock_errno ();
__leave;
}
if (type == SOCK_STREAM)
{
/* For stream sockets, accept the connection and close the listener */
len = sizeof (sock_in);
insock = accept (newsock, (struct sockaddr *) &sock_in, &len);
if (insock == INVALID_SOCKET)
{
debug_printf ("accept error");
set_winsock_errno ();
__leave;
}
closesocket (newsock);
newsock = INVALID_SOCKET;
}
else
{
/* For datagram sockets, connect the 2nd socket */
if (connect (newsock, (struct sockaddr *) &sock_out,
sizeof (sock_out)) < 0)
{
debug_printf ("connect error");
set_winsock_errno ();
__leave;
}
insock = newsock;
newsock = INVALID_SOCKET;
}
cygheap_fdnew sb0;
const device *dev;
if (family == AF_INET)
dev = (type == SOCK_STREAM ? tcp_dev : udp_dev);
else
dev = (type == SOCK_STREAM ? stream_dev : dgram_dev);
if (sb0 >= 0 && fdsock (sb0, dev, insock))
{
((fhandler_socket *) sb0)->set_addr_family (family);
((fhandler_socket *) sb0)->set_socket_type (type);
((fhandler_socket *) sb0)->connect_state (connected);
if (flags & SOCK_NONBLOCK)
((fhandler_socket *) sb0)->set_nonblocking (true);
if (flags & SOCK_CLOEXEC)
((fhandler_socket *) sb0)->set_close_on_exec (true);
if (family == AF_LOCAL && type == SOCK_STREAM)
((fhandler_socket *) sb0)->af_local_set_sockpair_cred ();
cygheap_fdnew sb1 (sb0, false);
if (sb1 >= 0 && fdsock (sb1, dev, outsock))
{
((fhandler_socket *) sb1)->set_addr_family (family);
((fhandler_socket *) sb1)->set_socket_type (type);
((fhandler_socket *) sb1)->connect_state (connected);
if (flags & SOCK_NONBLOCK)
((fhandler_socket *) sb1)->set_nonblocking (true);
if (flags & SOCK_CLOEXEC)
((fhandler_socket *) sb1)->set_close_on_exec (true);
if (family == AF_LOCAL && type == SOCK_STREAM)
((fhandler_socket *) sb1)->af_local_set_sockpair_cred ();
sb[0] = sb0;
sb[1] = sb1;
sb[0] = fd_in;
sb[1] = fd_out;
res = 0;
}
else
sb0.release ();
__except (EFAULT) {}
__endtry
}
else
{
fd_in.release ();
fd_out.release ();
}
}
__except (EFAULT) {}
__endtry
done:
syscall_printf ("%R = socketpair(...)", res);
if (res == -1)
{
if (insock != INVALID_SOCKET)
closesocket (insock);
if (outsock != INVALID_SOCKET)
closesocket (outsock);
if (newsock != INVALID_SOCKET)
closesocket (newsock);
}
return res;
}