summaryrefslogtreecommitdiff
path: root/winsup/cygwin/fhandler_socket.cc
diff options
context:
space:
mode:
Diffstat (limited to 'winsup/cygwin/fhandler_socket.cc')
-rw-r--r--winsup/cygwin/fhandler_socket.cc1694
1 files changed, 1694 insertions, 0 deletions
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
new file mode 100644
index 00000000000..51d72fc8f90
--- /dev/null
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -0,0 +1,1694 @@
+/* fhandler_socket.cc. See fhandler.h for a description of the fhandler classes.
+
+ Copyright 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc.
+
+ 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__
+
+#include "winsup.h"
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <asm/byteorder.h>
+
+#include <stdlib.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock2.h>
+#include <iphlpapi.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "cygwin/version.h"
+#include "perprocess.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include "cygthread.h"
+#include "select.h"
+#include "wininfo.h"
+#include <unistd.h>
+#include <sys/acl.h>
+#include "cygtls.h"
+#include "cygwin/in6.h"
+
+#define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT)
+
+extern bool fdsock (cygheap_fdmanip& fd, const device *, SOCKET soc);
+extern "C" {
+int sscanf (const char *, const char *, ...);
+} /* End of "C" section */
+
+fhandler_dev_random* entropy_source;
+
+/* cygwin internal: map sockaddr into internet domain address */
+static int
+get_inet_addr (const struct sockaddr *in, int inlen,
+ struct sockaddr_storage *out, int *outlen,
+ int *type = NULL, int *secret = NULL)
+{
+ int secret_buf [4];
+ int* secret_ptr = (secret ? : secret_buf);
+
+ if (in->sa_family == AF_INET || in->sa_family == AF_INET6)
+ {
+ memcpy (out, in, inlen);
+ *outlen = inlen;
+ return 1;
+ }
+ else if (in->sa_family == AF_LOCAL)
+ {
+ path_conv pc (in->sa_data, PC_SYM_FOLLOW);
+ if (pc.error)
+ {
+ set_errno (pc.error);
+ return 0;
+ }
+ if (!pc.exists ())
+ {
+ set_errno (ENOENT);
+ return 0;
+ }
+ if (!pc.issocket ())
+ {
+ set_errno (EBADF);
+ return 0;
+ }
+ HANDLE fh = CreateFile (pc, GENERIC_READ, wincap.shared (), &sec_none,
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if (fh == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return 0;
+ }
+ int ret = 0;
+ DWORD len = 0;
+ char buf[128];
+ memset (buf, 0, sizeof buf);
+ if (ReadFile (fh, buf, 128, &len, 0))
+ {
+ struct sockaddr_in sin;
+ char ctype;
+ sin.sin_family = AF_INET;
+ sscanf (buf + strlen (SOCKET_COOKIE), "%hu %c %08x-%08x-%08x-%08x",
+ &sin.sin_port,
+ &ctype,
+ secret_ptr, secret_ptr + 1, secret_ptr + 2, secret_ptr + 3);
+ sin.sin_port = htons (sin.sin_port);
+ sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ memcpy (out, &sin, sizeof sin);
+ *outlen = sizeof sin;
+ if (type)
+ *type = (ctype == 's' ? SOCK_STREAM :
+ ctype == 'd' ? SOCK_DGRAM
+ : 0);
+ ret = 1;
+ }
+ else
+ __seterrno ();
+ CloseHandle (fh);
+ return ret;
+ }
+ else
+ {
+ set_errno (EAFNOSUPPORT);
+ return 0;
+ }
+}
+
+/**********************************************************************/
+/* fhandler_socket */
+
+fhandler_socket::fhandler_socket () :
+ fhandler_base (),
+ sun_path (NULL),
+ status ()
+{
+ need_fork_fixup (true);
+ prot_info_ptr = (LPWSAPROTOCOL_INFOA) cmalloc (HEAP_BUF,
+ sizeof (WSAPROTOCOL_INFOA));
+#if 0
+ if (pc.is_fs_special ())
+ {
+ fhandler_socket * fhs = (fhandler_socket *) fh;
+ fhs->set_addr_family (AF_LOCAL);
+ fhs->set_sun_path (posix_path);
+ }
+#endif
+}
+
+fhandler_socket::~fhandler_socket ()
+{
+ if (prot_info_ptr)
+ cfree (prot_info_ptr);
+ if (sun_path)
+ cfree (sun_path);
+}
+
+char *
+fhandler_socket::get_proc_fd_name (char *buf)
+{
+ __small_sprintf (buf, "socket:[%d]", get_socket ());
+ return buf;
+}
+
+int
+fhandler_socket::open (int flags, mode_t mode)
+{
+ set_errno (ENXIO);
+ return 0;
+}
+
+void
+fhandler_socket::af_local_set_sockpair_cred ()
+{
+ sec_pid = sec_peer_pid = getpid ();
+ sec_uid = sec_peer_uid = geteuid32 ();
+ sec_gid = sec_peer_gid = getegid32 ();
+}
+
+void
+fhandler_socket::af_local_setblocking (bool &async, bool &nonblocking)
+{
+ async = async_io ();
+ nonblocking = is_nonblocking ();
+ if (async || nonblocking)
+ WSAAsyncSelect (get_socket (), winmsg, 0, 0);
+ unsigned long p = 0;
+ ioctlsocket (get_socket (), FIONBIO, &p);
+ set_nonblocking (false);
+ async_io (false);
+}
+
+void
+fhandler_socket::af_local_unsetblocking (bool async, bool nonblocking)
+{
+ if (nonblocking)
+ {
+ unsigned long p = 1;
+ ioctlsocket (get_socket (), FIONBIO, &p);
+ set_nonblocking (true);
+ }
+ if (async)
+ {
+ WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK);
+ async_io (true);
+ }
+}
+
+bool
+fhandler_socket::af_local_recv_secret ()
+{
+ int out[4] = { 0, 0, 0, 0 };
+ int rest = sizeof out;
+ char *ptr = (char *) out;
+ while (rest > 0)
+ {
+ int ret = recvfrom (ptr, rest, 0, NULL, NULL);
+ if (ret <= 0)
+ break;
+ rest -= ret;
+ ptr += ret;
+ }
+ if (rest == 0)
+ {
+ debug_printf ("Received af_local secret: %08x-%08x-%08x-%08x",
+ out[0], out[1], out[2], out[3]);
+ if (out[0] != connect_secret[0] || out[1] != connect_secret[1]
+ || out[2] != connect_secret[2] || out[3] != connect_secret[3])
+ {
+ debug_printf ("Receiving af_local secret mismatch");
+ return false;
+ }
+ }
+ else
+ debug_printf ("Receiving af_local secret failed");
+ return rest == 0;
+}
+
+bool
+fhandler_socket::af_local_send_secret ()
+{
+ int rest = sizeof connect_secret;
+ char *ptr = (char *) connect_secret;
+ while (rest > 0)
+ {
+ int ret = sendto (ptr, rest, 0, NULL, 0);
+ if (ret <= 0)
+ break;
+ rest -= ret;
+ ptr += ret;
+ }
+ debug_printf ("Sending af_local secret %s", rest == 0 ? "succeeded"
+ : "failed");
+ return rest == 0;
+}
+
+bool
+fhandler_socket::af_local_recv_cred ()
+{
+ struct ucred out = { (pid_t) 0, (__uid32_t) -1, (__gid32_t) -1 };
+ int rest = sizeof out;
+ char *ptr = (char *) &out;
+ while (rest > 0)
+ {
+ int ret = recvfrom (ptr, rest, 0, NULL, NULL);
+ if (ret <= 0)
+ break;
+ rest -= ret;
+ ptr += ret;
+ }
+ if (rest == 0)
+ {
+ debug_printf ("Received eid credentials: pid: %d, uid: %d, gid: %d",
+ out.pid, out.uid, out.gid);
+ sec_peer_pid = out.pid;
+ sec_peer_uid = out.uid;
+ sec_peer_gid = out.gid;
+ }
+ else
+ debug_printf ("Receiving eid credentials failed");
+ return rest == 0;
+}
+
+bool
+fhandler_socket::af_local_send_cred ()
+{
+ struct ucred in = { sec_pid, sec_uid, sec_gid };
+ int rest = sizeof in;
+ char *ptr = (char *) &in;
+ while (rest > 0)
+ {
+ int ret = sendto (ptr, rest, 0, NULL, 0);
+ if (ret <= 0)
+ break;
+ rest -= ret;
+ ptr += ret;
+ }
+ if (rest == 0)
+ debug_printf ("Sending eid credentials succeeded");
+ else
+ debug_printf ("Sending eid credentials failed");
+ return rest == 0;
+}
+
+int
+fhandler_socket::af_local_connect ()
+{
+ /* This keeps the test out of select. */
+ if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM)
+ return 0;
+
+ debug_printf ("af_local_connect called");
+ bool orig_async_io, orig_is_nonblocking;
+ af_local_setblocking (orig_async_io, orig_is_nonblocking);
+ if (!af_local_send_secret () || !af_local_recv_secret ()
+ || !af_local_send_cred () || !af_local_recv_cred ())
+ {
+ debug_printf ("accept from unauthorized server");
+ ::shutdown (get_socket (), SD_BOTH);
+ WSASetLastError (WSAECONNREFUSED);
+ return -1;
+ }
+ af_local_unsetblocking (orig_async_io, orig_is_nonblocking);
+ return 0;
+}
+
+int
+fhandler_socket::af_local_accept ()
+{
+ debug_printf ("af_local_accept called");
+ bool orig_async_io, orig_is_nonblocking;
+ af_local_setblocking (orig_async_io, orig_is_nonblocking);
+ if (!af_local_recv_secret () || !af_local_send_secret ()
+ || !af_local_recv_cred () || !af_local_send_cred ())
+ {
+ debug_printf ("connect from unauthorized client");
+ ::shutdown (get_socket (), SD_BOTH);
+ ::closesocket (get_socket ());
+ WSASetLastError (WSAECONNABORTED);
+ return -1;
+ }
+ af_local_unsetblocking (orig_async_io, orig_is_nonblocking);
+ return 0;
+}
+
+void
+fhandler_socket::af_local_set_cred ()
+{
+ sec_pid = getpid ();
+ sec_uid = geteuid32 ();
+ sec_gid = getegid32 ();
+ sec_peer_pid = (pid_t) 0;
+ sec_peer_uid = (__uid32_t) -1;
+ sec_peer_gid = (__gid32_t) -1;
+}
+
+void
+fhandler_socket::af_local_copy (fhandler_socket *sock)
+{
+ sock->connect_secret[0] = connect_secret[0];
+ sock->connect_secret[1] = connect_secret[1];
+ sock->connect_secret[2] = connect_secret[2];
+ sock->connect_secret[3] = connect_secret[3];
+ sock->sec_pid = sec_pid;
+ sock->sec_uid = sec_uid;
+ sock->sec_gid = sec_gid;
+ sock->sec_peer_pid = sec_peer_pid;
+ sock->sec_peer_uid = sec_peer_uid;
+ sock->sec_peer_gid = sec_peer_gid;
+}
+
+void
+fhandler_socket::af_local_set_secret (char *buf)
+{
+ if (!entropy_source)
+ {
+ void *buf = malloc (sizeof (fhandler_dev_random));
+ entropy_source = new (buf) fhandler_dev_random ();
+ entropy_source->dev () = *urandom_dev;
+ }
+ if (entropy_source &&
+ !entropy_source->open (O_RDONLY))
+ {
+ delete entropy_source;
+ entropy_source = NULL;
+ }
+ if (entropy_source)
+ {
+ size_t len = sizeof (connect_secret);
+ entropy_source->read (connect_secret, len);
+ if (len != sizeof (connect_secret))
+ bzero ((char*) connect_secret, sizeof (connect_secret));
+ }
+ __small_sprintf (buf, "%08x-%08x-%08x-%08x",
+ connect_secret [0], connect_secret [1],
+ connect_secret [2], connect_secret [3]);
+}
+
+void
+fhandler_socket::fixup_before_fork_exec (DWORD win_proc_id)
+{
+ if (!WSADuplicateSocketA (get_socket (), win_proc_id, prot_info_ptr))
+ debug_printf ("WSADuplicateSocket went fine, sock %p, win_proc_id %d, prot_info_ptr %p",
+ get_socket (), win_proc_id, prot_info_ptr);
+ else
+ {
+ debug_printf ("WSADuplicateSocket error, sock %p, win_proc_id %d, prot_info_ptr %p",
+ get_socket (), win_proc_id, prot_info_ptr);
+ set_winsock_errno ();
+ }
+}
+
+void
+fhandler_socket::fixup_after_fork (HANDLE parent)
+{
+ SOCKET new_sock;
+
+ debug_printf ("WSASocket begin, dwServiceFlags1=%d",
+ prot_info_ptr->dwServiceFlags1);
+
+ if ((new_sock = WSASocketA (FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ prot_info_ptr, 0, 0)) == INVALID_SOCKET)
+ {
+ debug_printf ("WSASocket error");
+ set_io_handle ((HANDLE)INVALID_SOCKET);
+ set_winsock_errno ();
+ }
+ else
+ {
+ debug_printf ("WSASocket went fine new_sock %p, old_sock %p", new_sock, get_socket ());
+
+ /* Go figure! Even though the original socket was not inheritable,
+ the duplicated socket is inheritable again. This can lead to all
+ sorts of trouble, apparently. Note that there's no way to prevent
+ this on 9x, not even by trying to reset socket inheritance using
+ DuplicateHandle and closing the original socket. */
+ if (wincap.has_set_handle_information ())
+ SetHandleInformation ((HANDLE) new_sock, HANDLE_FLAG_INHERIT, 0);
+
+ set_io_handle ((HANDLE) new_sock);
+ }
+}
+
+void
+fhandler_socket::fixup_after_exec ()
+{
+ if (!close_on_exec ())
+ fixup_after_fork (NULL);
+}
+
+int
+fhandler_socket::dup (fhandler_base *child)
+{
+ HANDLE nh;
+
+ debug_printf ("here");
+ fhandler_socket *fhs = (fhandler_socket *) child;
+ fhs->addr_family = addr_family;
+ fhs->set_socket_type (get_socket_type ());
+ if (get_addr_family () == AF_LOCAL)
+ {
+ fhs->set_sun_path (get_sun_path ());
+ if (get_socket_type () == SOCK_STREAM)
+ {
+ fhs->sec_pid = sec_pid;
+ fhs->sec_uid = sec_uid;
+ fhs->sec_gid = sec_gid;
+ fhs->sec_peer_pid = sec_peer_pid;
+ fhs->sec_peer_uid = sec_peer_uid;
+ fhs->sec_peer_gid = sec_peer_gid;
+ }
+ }
+ fhs->connect_state (connect_state ());
+
+ /* Since WSADuplicateSocket() fails on NT systems when the process
+ is currently impersonating a non-privileged account, we revert
+ to the original account before calling WSADuplicateSocket() and
+ switch back afterwards as it's also in fork().
+ If WSADuplicateSocket() still fails for some reason, we fall back
+ to DuplicateHandle(). */
+ WSASetLastError (0);
+ cygheap->user.deimpersonate ();
+ fhs->set_io_handle (get_io_handle ());
+ fhs->fixup_before_fork_exec (GetCurrentProcessId ());
+ cygheap->user.reimpersonate ();
+ if (!WSAGetLastError ())
+ {
+ fhs->fixup_after_fork (hMainProc);
+ if (fhs->get_io_handle() != (HANDLE) INVALID_SOCKET)
+ {
+ cygheap->fdtab.inc_need_fixup_before ();
+ return 0;
+ }
+ }
+ debug_printf ("WSADuplicateSocket failed, trying DuplicateHandle");
+
+ /* We don't call fhandler_base::dup here since that requires
+ having winsock called from fhandler_base and it creates only
+ inheritable sockets which is wrong for winsock2. */
+
+ if (!DuplicateHandle (hMainProc, get_io_handle (), hMainProc, &nh, 0,
+ FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("!DuplicateHandle(%x) failed, %E", get_io_handle ());
+ __seterrno ();
+ return -1;
+ }
+ VerifyHandle (nh);
+ fhs->set_io_handle (nh);
+ cygheap->fdtab.inc_need_fixup_before ();
+ return 0;
+}
+
+int __stdcall
+fhandler_socket::fstat (struct __stat64 *buf)
+{
+ int res;
+ if (get_device () == FH_UNIX)
+ {
+ res = fhandler_base::fstat_fs (buf);
+ if (!res)
+ {
+ buf->st_mode = (buf->st_mode & ~S_IFMT) | S_IFSOCK;
+ }
+ }
+ else
+ {
+ res = fhandler_base::fstat (buf);
+ if (!res)
+ {
+ buf->st_dev = 0;
+ buf->st_ino = (__ino64_t) ((DWORD) get_handle ());
+ buf->st_mode = S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO;
+ }
+ }
+ return res;
+}
+
+int
+fhandler_socket::fchmod (mode_t mode)
+{
+ if (get_device () == FH_UNIX)
+ {
+ fhandler_disk_file fh (pc);
+ fh.get_device () = FH_FS;
+ int ret = fh.fchmod (mode);
+ SetFileAttributes (pc, GetFileAttributes (pc) | FILE_ATTRIBUTE_SYSTEM);
+ return ret;
+ }
+ return 0;
+}
+
+int
+fhandler_socket::fchown (__uid32_t uid, __gid32_t gid)
+{
+ if (get_device () == FH_UNIX)
+ {
+ fhandler_disk_file fh (pc);
+ return fh.fchown (uid, gid);
+ }
+ return 0;
+}
+
+int
+fhandler_socket::facl (int cmd, int nentries, __aclent32_t *aclbufp)
+{
+ if (get_device () == FH_UNIX)
+ {
+ fhandler_disk_file fh (pc);
+ return fh.facl (cmd, nentries, aclbufp);
+ }
+ return fhandler_base::facl (cmd, nentries, aclbufp);
+}
+
+int
+fhandler_socket::link (const char *newpath)
+{
+ if (get_device () == FH_UNIX)
+ {
+ fhandler_disk_file fh (pc);
+ return fh.link (newpath);
+ }
+ return fhandler_base::link (newpath);
+}
+
+static inline bool
+address_in_use (struct sockaddr_in *addr)
+{
+ PMIB_TCPTABLE tab;
+ PMIB_TCPROW entry;
+ DWORD size = 0, i;
+
+ if (GetTcpTable (NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER)
+ {
+ tab = (PMIB_TCPTABLE) alloca (size);
+ if (!GetTcpTable (tab, &size, FALSE))
+ {
+ for (i = tab->dwNumEntries, entry = tab->table; i > 0; --i, ++entry)
+ if (entry->dwLocalAddr == addr->sin_addr.s_addr
+ && entry->dwLocalPort == addr->sin_port
+ && entry->dwState >= MIB_TCP_STATE_LISTEN
+ && entry->dwState <= MIB_TCP_STATE_LAST_ACK)
+ return true;
+ }
+ }
+ return false;
+}
+
+int
+fhandler_socket::bind (const struct sockaddr *name, int namelen)
+{
+ int res = -1;
+
+ if (name->sa_family == AF_LOCAL)
+ {
+#define un_addr ((struct sockaddr_un *) name)
+ struct sockaddr_in sin;
+ int len = sizeof sin;
+
+ 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 (get_socket (), (sockaddr *) &sin, len))
+ {
+ syscall_printf ("AF_LOCAL: bind failed %d", get_errno ());
+ set_winsock_errno ();
+ goto out;
+ }
+ if (::getsockname (get_socket (), (sockaddr *) &sin, &len))
+ {
+ syscall_printf ("AF_LOCAL: getsockname failed %d", get_errno ());
+ set_winsock_errno ();
+ goto out;
+ }
+
+ sin.sin_port = ntohs (sin.sin_port);
+ debug_printf ("AF_LOCAL: socket bound to port %u", sin.sin_port);
+
+ path_conv pc (un_addr->sun_path, PC_SYM_FOLLOW);
+ if (pc.error)
+ {
+ set_errno (pc.error);
+ goto out;
+ }
+ if (pc.exists ())
+ {
+ set_errno (EADDRINUSE);
+ goto out;
+ }
+ mode_t mode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~cygheap->umask;
+ DWORD attr = FILE_ATTRIBUTE_SYSTEM;
+ if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+ attr |= FILE_ATTRIBUTE_READONLY;
+ SECURITY_ATTRIBUTES sa = sec_none;
+ security_descriptor sd;
+ if (allow_ntsec && pc.has_acls ())
+ set_security_attribute (mode, &sa, sd);
+ HANDLE fh = CreateFile (pc, GENERIC_WRITE, 0, &sa, CREATE_NEW, attr, 0);
+ if (fh == INVALID_HANDLE_VALUE)
+ {
+ if (GetLastError () == ERROR_ALREADY_EXISTS)
+ set_errno (EADDRINUSE);
+ else
+ __seterrno ();
+ }
+
+ char buf[sizeof (SOCKET_COOKIE) + 80];
+ __small_sprintf (buf, "%s%u %c ", SOCKET_COOKIE, sin.sin_port, get_socket_type () == SOCK_STREAM ? 's' : get_socket_type () == SOCK_DGRAM ? 'd' : '-');
+ af_local_set_secret (strchr (buf, '\0'));
+ DWORD blen = strlen (buf) + 1;
+ if (!WriteFile (fh, buf, blen, &blen, 0))
+ {
+ __seterrno ();
+ CloseHandle (fh);
+ DeleteFile (pc);
+ }
+ else
+ {
+ CloseHandle (fh);
+ set_sun_path (un_addr->sun_path);
+ res = 0;
+ }
+#undef un_addr
+ }
+ else
+ {
+ /* If the application didn't explicitely call setsockopt (SO_REUSEADDR),
+ enforce exclusive local address use using the SO_EXCLUSIVEADDRUSE
+ socket option, to emulate POSIX socket behaviour more closely.
+
+ KB 870562: Note that this option is only available since NT4 SP4.
+ Also note that a bug in Win2K SP1-3 and XP up to SP1 only enables
+ this option for users in the local administrators group. */
+ if (wincap.has_exclusiveaddruse ())
+ {
+ if (!saw_reuseaddr ())
+ {
+ int on = 1;
+ int ret = ::setsockopt (get_socket (), SOL_SOCKET,
+ ~(SO_REUSEADDR),
+ (const char *) &on, sizeof on);
+ debug_printf ("%d = setsockopt (SO_EXCLUSIVEADDRUSE), %E", ret);
+ }
+ else
+ {
+ debug_printf ("SO_REUSEADDR set");
+ /* There's a bug in SO_REUSEADDR handling in WinSock.
+ Per standards, we must not be able to reuse a complete
+ duplicate of a local TCP address (same IP, same port),
+ even if SO_REUSEADDR has been set. That's unfortunately
+ possible in WinSock. So we're testing here if the local
+ address is already in use and don't bind, if so. This
+ only works for OSes with IP Helper support. */
+ if (get_socket_type () == SOCK_STREAM
+ && wincap.has_ip_helper_lib ()
+ && address_in_use ((struct sockaddr_in *) name))
+ {
+ debug_printf ("Local address in use, don't bind");
+ set_errno (EADDRINUSE);
+ goto out;
+ }
+ }
+ }
+ if (::bind (get_socket (), name, namelen))
+ set_winsock_errno ();
+ else
+ res = 0;
+ }
+
+out:
+ return res;
+}
+
+int
+fhandler_socket::connect (const struct sockaddr *name, int namelen)
+{
+ int res = -1;
+ bool in_progress = false;
+ struct sockaddr_storage sst;
+ DWORD err;
+ int type;
+
+ if (!get_inet_addr (name, namelen, &sst, &namelen, &type, connect_secret))
+ return -1;
+
+ if (get_addr_family () == AF_LOCAL && get_socket_type () != type)
+ {
+ WSASetLastError (WSAEPROTOTYPE);
+ set_winsock_errno ();
+ return -1;
+ }
+
+ res = ::connect (get_socket (), (struct sockaddr *) &sst, namelen);
+
+ if (!res)
+ err = 0;
+ else
+ {
+ err = WSAGetLastError ();
+ /* Special handling for connect to return the correct error code
+ when called on a non-blocking socket. */
+ if (is_nonblocking ())
+ {
+ if (err == WSAEWOULDBLOCK || err == WSAEALREADY)
+ in_progress = true;
+
+ if (err == WSAEWOULDBLOCK)
+ WSASetLastError (err = WSAEINPROGRESS);
+ }
+ if (err == WSAEINVAL)
+ WSASetLastError (err = WSAEISCONN);
+ set_winsock_errno ();
+ }
+
+ if (get_addr_family () == AF_LOCAL && (!res || in_progress))
+ set_sun_path (name->sa_data);
+
+ if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
+ {
+ af_local_set_cred (); /* Don't move into af_local_connect since
+ af_local_connect is called from select,
+ possibly running under another identity. */
+ if (!res && af_local_connect ())
+ {
+ set_winsock_errno ();
+ return -1;
+ }
+ }
+
+ if (err == WSAEINPROGRESS || err == WSAEALREADY)
+ connect_state (connect_pending);
+ else if (err)
+ connect_state (connect_failed);
+ else
+ connect_state (connected);
+
+ return res;
+}
+
+int
+fhandler_socket::listen (int backlog)
+{
+ int res = ::listen (get_socket (), backlog);
+ if (res && WSAGetLastError () == WSAEINVAL)
+ {
+ /* It's perfectly valid to call listen on an unbound INET socket.
+ In this case the socket is automatically bound to an unused
+ port number, listening on all interfaces. On Winsock, listen
+ fails with WSAEINVAL when it's called on an unbound socket.
+ So we have to bind manually here to have POSIX semantics. */
+ if (get_addr_family () == AF_INET)
+ {
+ struct sockaddr_in sin;
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ if (!::bind (get_socket (), (struct sockaddr *) &sin, sizeof sin))
+ res = ::listen (get_socket (), backlog);
+ }
+ else if (get_addr_family () == AF_INET6)
+ {
+ struct sockaddr_in6 sin6 =
+ {
+ sin6_family: AF_INET6,
+ sin6_port: 0,
+ sin6_flowinfo: 0,
+ sin6_addr: IN6ADDR_ANY_INIT,
+ sin6_scope_id: 0
+ };
+ if (!::bind (get_socket (), (struct sockaddr *) &sin6, sizeof sin6))
+ res = ::listen (get_socket (), backlog);
+ }
+ }
+ if (!res)
+ {
+ if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
+ af_local_set_cred ();
+ connect_state (connected);
+ }
+ else
+ set_winsock_errno ();
+ return res;
+}
+
+int
+fhandler_socket::accept (struct sockaddr *peer, int *len)
+{
+ int res = -1;
+
+ /* Allows NULL peer and len parameters. */
+ struct sockaddr_in peer_dummy;
+ int len_dummy;
+ if (!peer)
+ peer = (struct sockaddr *) &peer_dummy;
+ if (!len)
+ {
+ len_dummy = sizeof (struct sockaddr_in);
+ len = &len_dummy;
+ }
+
+ /* 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 (get_socket (), peer, len);
+
+ if (res == (int) INVALID_SOCKET)
+ set_winsock_errno ();
+ else
+ {
+ cygheap_fdnew res_fd;
+ if (res_fd >= 0 && fdsock (res_fd, &dev (), res))
+ {
+ fhandler_socket *sock = (fhandler_socket *) res_fd;
+ sock->set_addr_family (get_addr_family ());
+ sock->set_socket_type (get_socket_type ());
+ sock->async_io (async_io ());
+ sock->set_nonblocking (is_nonblocking ());
+ if (get_addr_family () == AF_LOCAL)
+ {
+ sock->set_sun_path (get_sun_path ());
+ if (get_socket_type () == SOCK_STREAM)
+ {
+ /* Don't forget to copy credentials from accepting
+ socket to accepted socket and start transaction
+ on accepted socket! */
+ af_local_copy (sock);
+ res = sock->af_local_accept ();
+ if (res == -1)
+ {
+ res_fd.release ();
+ set_winsock_errno ();
+ goto out;
+ }
+ }
+ }
+ sock->connect_state (connected);
+ res = res_fd;
+ }
+ else
+ {
+ closesocket (res);
+ res = -1;
+ }
+ }
+
+out:
+ debug_printf ("res %d", res);
+ return res;
+}
+
+int
+fhandler_socket::getsockname (struct sockaddr *name, int *namelen)
+{
+ int res = -1;
+
+ if (get_addr_family () == AF_LOCAL)
+ {
+ struct sockaddr_un *sun = (struct sockaddr_un *) name;
+ memset (sun, 0, *namelen);
+ sun->sun_family = AF_LOCAL;
+
+ if (!get_sun_path ())
+ sun->sun_path[0] = '\0';
+ else
+ /* According to SUSv2 "If the actual length of the address is
+ greater than the length of the supplied sockaddr structure, the
+ stored address will be truncated." We play it save here so
+ that the path always has a trailing 0 even if it's truncated. */
+ strncpy (sun->sun_path, get_sun_path (),
+ *namelen - sizeof *sun + sizeof sun->sun_path - 1);
+
+ *namelen = sizeof *sun - sizeof sun->sun_path
+ + strlen (sun->sun_path) + 1;
+ res = 0;
+ }
+ else
+ {
+ res = ::getsockname (get_socket (), name, namelen);
+ if (res)
+ set_winsock_errno ();
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::getpeername (struct sockaddr *name, int *namelen)
+{
+ int res = ::getpeername (get_socket (), name, namelen);
+ if (res)
+ set_winsock_errno ();
+
+ return res;
+}
+
+bool
+fhandler_socket::prepare (HANDLE &event, long event_mask)
+{
+ WSASetLastError (0);
+ closed (false);
+ if ((event = WSACreateEvent ()) == WSA_INVALID_EVENT)
+ {
+ debug_printf ("WSACreateEvent, %E");
+ return false;
+ }
+ if (WSAEventSelect (get_socket (), event, event_mask) == SOCKET_ERROR)
+ {
+ debug_printf ("WSAEventSelect(evt), %d", WSAGetLastError ());
+ return false;
+ }
+ return true;
+}
+
+int
+fhandler_socket::wait (HANDLE event, int flags, DWORD timeout)
+{
+ int ret = SOCKET_ERROR;
+ int wsa_err = 0;
+ WSAEVENT ev[2] = { event, signal_arrived };
+ WSANETWORKEVENTS evts;
+
+/* If WSAWaitForMultipleEvents is interrupted by a signal, and the signal
+ has the SA_RESTART flag set, return to this label and... restart. */
+sa_restart:
+
+ switch (WSAWaitForMultipleEvents (2, ev, FALSE, timeout, FALSE))
+ {
+ case WSA_WAIT_TIMEOUT:
+ ret = 0;
+ break;
+ case WSA_WAIT_EVENT_0:
+ if (!WSAEnumNetworkEvents (get_socket (), event, &evts))
+ {
+ if (!evts.lNetworkEvents)
+ {
+ ret = 0;
+ break;
+ }
+ if (evts.lNetworkEvents & FD_OOB)
+ {
+ if (evts.iErrorCode[FD_OOB_BIT])
+ wsa_err = evts.iErrorCode[FD_OOB_BIT];
+ else if (flags & MSG_OOB)
+ ret = 0;
+ else
+ {
+ raise (SIGURG);
+ WSASetLastError (WSAEINTR);
+ break;
+ }
+ }
+ if (evts.lNetworkEvents & FD_ACCEPT)
+ {
+ if (evts.iErrorCode[FD_ACCEPT_BIT])
+ wsa_err = evts.iErrorCode[FD_ACCEPT_BIT];
+ else
+ ret = 0;
+ }
+ if (evts.lNetworkEvents & FD_CONNECT)
+ {
+ if (evts.iErrorCode[FD_CONNECT_BIT])
+ wsa_err = evts.iErrorCode[FD_CONNECT_BIT];
+ else
+ ret = 0;
+ }
+ else if (evts.lNetworkEvents & FD_READ)
+ {
+ if (evts.iErrorCode[FD_READ_BIT])
+ wsa_err = evts.iErrorCode[FD_READ_BIT];
+ else
+ ret = 0;
+ }
+ else if (evts.lNetworkEvents & FD_WRITE)
+ {
+ if (evts.iErrorCode[FD_WRITE_BIT])
+ wsa_err = evts.iErrorCode[FD_WRITE_BIT];
+ else
+ ret = 0;
+ }
+ if (evts.lNetworkEvents & FD_CLOSE)
+ {
+ closed (true);
+ if (!wsa_err)
+ {
+ if (evts.iErrorCode[FD_CLOSE_BIT])
+ wsa_err = evts.iErrorCode[FD_CLOSE_BIT];
+ else
+ ret = 0;
+ }
+ }
+ if (wsa_err)
+ WSASetLastError (wsa_err);
+ }
+ break;
+ case WSA_WAIT_EVENT_0 + 1:
+ if (_my_tls.call_signal_handler ())
+ {
+ sig_dispatch_pending ();
+ goto sa_restart;
+ }
+ WSASetLastError (WSAEINTR);
+ break;
+ default:
+ WSASetLastError (WSAEFAULT);
+ break;
+ }
+ return ret;
+}
+
+void
+fhandler_socket::release (HANDLE event)
+{
+ int last_err = WSAGetLastError ();
+ /* KB 168349: NT4 fails if the event parameter is not NULL. */
+ if (WSAEventSelect (get_socket (), NULL, 0) == SOCKET_ERROR)
+ debug_printf ("WSAEventSelect(NULL), %d", WSAGetLastError ());
+ WSACloseEvent (event);
+ unsigned long non_block = 0;
+ if (ioctlsocket (get_socket (), FIONBIO, &non_block))
+ debug_printf ("return to blocking failed: %d", WSAGetLastError ());
+ else
+ WSASetLastError (last_err);
+}
+
+int
+fhandler_socket::readv (const struct iovec *const iov, const int iovcnt,
+ ssize_t tot)
+{
+ struct msghdr msg =
+ {
+ msg_name: NULL,
+ msg_namelen: 0,
+ msg_iov: (struct iovec *) iov, // const_cast
+ msg_iovlen: iovcnt,
+ msg_control: NULL,
+ msg_controllen: 0,
+ msg_flags: 0
+ };
+
+ return recvmsg (&msg, 0, tot);
+}
+
+int
+fhandler_socket::recvfrom (void *ptr, size_t len, int flags,
+ struct sockaddr *from, int *fromlen)
+{
+ int res = SOCKET_ERROR;
+ DWORD ret = 0;
+
+ WSABUF wsabuf = { len, (char *) ptr };
+
+ if (is_nonblocking () || closed () || async_io ())
+ {
+ DWORD lflags = (DWORD) (flags & MSG_WINMASK);
+ res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret,
+ &lflags, from, fromlen, NULL, NULL);
+ }
+ else
+ {
+ HANDLE evt;
+ if (prepare (evt, FD_CLOSE | FD_READ | (owner () ? FD_OOB : 0)))
+ {
+ do
+ {
+ DWORD lflags = (DWORD) (flags & MSG_WINMASK);
+ res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, &lflags,
+ from, fromlen, NULL, NULL);
+ }
+ while (res == SOCKET_ERROR
+ && WSAGetLastError () == WSAEWOULDBLOCK
+ && !closed ()
+ && !(res = wait (evt, flags)));
+ release (evt);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ /* According to SUSv3, errno isn't set in that case and no error
+ condition is returned. */
+ if (WSAGetLastError () == WSAEMSGSIZE)
+ return len;
+
+ /* ESHUTDOWN isn't defined for recv in SUSv3. Simply EOF is returned
+ in this case. */
+ if (WSAGetLastError () == WSAESHUTDOWN)
+ return 0;
+
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+
+ return res;
+}
+
+int
+fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot)
+{
+ if (CYGWIN_VERSION_CHECK_FOR_USING_ANCIENT_MSGHDR)
+ ((struct OLD_msghdr *) msg)->msg_accrightslen = 0;
+ else
+ {
+ msg->msg_controllen = 0;
+ msg->msg_flags = 0;
+ }
+ if (get_addr_family () == AF_LOCAL)
+ {
+ /* On AF_LOCAL sockets the (fixed-size) name of the shared memory
+ area used for descriptor passing is transmitted first.
+ If this string is empty, no descriptors are passed and we can
+ go ahead recv'ing the normal data blocks. Otherwise start
+ special handling for descriptor passing. */
+ /*TODO*/
+ }
+
+ struct iovec *const iov = msg->msg_iov;
+ const int iovcnt = msg->msg_iovlen;
+
+ struct sockaddr *from = (struct sockaddr *) msg->msg_name;
+ int *fromlen = from ? &msg->msg_namelen : NULL;
+
+ int res = SOCKET_ERROR;
+
+ WSABUF wsabuf[iovcnt];
+ unsigned long len = 0L;
+
+ const struct iovec *iovptr = iov + iovcnt;
+ WSABUF *wsaptr = wsabuf + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ wsaptr -= 1;
+ len += wsaptr->len = iovptr->iov_len;
+ wsaptr->buf = (char *) iovptr->iov_base;
+ }
+ while (wsaptr != wsabuf);
+
+ DWORD ret = 0;
+
+ if (is_nonblocking () || closed () || async_io ())
+ {
+ DWORD lflags = (DWORD) (flags & MSG_WINMASK);
+ res = WSARecvFrom (get_socket (), wsabuf, iovcnt, &ret,
+ &lflags, from, fromlen, NULL, NULL);
+ }
+ else
+ {
+ HANDLE evt;
+ if (prepare (evt, FD_CLOSE | FD_READ | (owner () ? FD_OOB : 0)))
+ {
+ do
+ {
+ DWORD lflags = (DWORD) (flags & MSG_WINMASK);
+ res = WSARecvFrom (get_socket (), wsabuf, iovcnt, &ret,
+ &lflags, from, fromlen, NULL, NULL);
+ }
+ while (res == SOCKET_ERROR
+ && WSAGetLastError () == WSAEWOULDBLOCK
+ && !closed ()
+ && !(res = wait (evt, flags)));
+ release (evt);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ /* According to SUSv3, errno isn't set in that case and no error
+ condition is returned. */
+ if (WSAGetLastError () == WSAEMSGSIZE)
+ return len;
+
+ /* ESHUTDOWN isn't defined for recv in SUSv3. Simply EOF is returned
+ in this case. */
+ if (WSAGetLastError () == WSAESHUTDOWN)
+ return 0;
+
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+
+ return res;
+}
+
+int
+fhandler_socket::writev (const struct iovec *const iov, const int iovcnt,
+ ssize_t tot)
+{
+ struct msghdr msg =
+ {
+ msg_name: NULL,
+ msg_namelen: 0,
+ msg_iov: (struct iovec *) iov, // const_cast
+ msg_iovlen: iovcnt,
+ msg_control: NULL,
+ msg_controllen: 0,
+ msg_flags: 0
+ };
+
+ return sendmsg (&msg, 0, tot);
+}
+
+int
+fhandler_socket::sendto (const void *ptr, size_t len, int flags,
+ const struct sockaddr *to, int tolen)
+{
+ struct sockaddr_storage sst;
+
+ if (to && !get_inet_addr (to, tolen, &sst, &tolen))
+ return SOCKET_ERROR;
+
+ int res = SOCKET_ERROR;
+ DWORD ret = 0;
+
+ WSABUF wsabuf = { len, (char *) ptr };
+
+ if (is_nonblocking () || closed () || async_io ())
+ res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sst : NULL), tolen,
+ NULL, NULL);
+ else
+ {
+ HANDLE evt;
+ if (prepare (evt, FD_CLOSE | FD_WRITE | (owner () ? FD_OOB : 0)))
+ {
+ do
+ {
+ res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sst : NULL),
+ tolen, NULL, NULL);
+ }
+ while (res == SOCKET_ERROR
+ && WSAGetLastError () == WSAEWOULDBLOCK
+ && !(res = wait (evt, 0))
+ && !closed ());
+ release (evt);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ else
+ res = ret;
+
+ /* Special handling for EPIPE and SIGPIPE.
+
+ EPIPE is generated if the local end has been shut down on a connection
+ oriented socket. In this case the process will also receive a SIGPIPE
+ unless MSG_NOSIGNAL is set. */
+ if (res == SOCKET_ERROR && get_errno () == ESHUTDOWN
+ && get_socket_type () == SOCK_STREAM)
+ {
+ set_errno (EPIPE);
+ if (! (flags & MSG_NOSIGNAL))
+ raise (SIGPIPE);
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::sendmsg (const struct msghdr *msg, int flags, ssize_t tot)
+{
+ if (get_addr_family () == AF_LOCAL)
+ {
+ /* For AF_LOCAL/AF_UNIX sockets, if descriptors are given, start
+ the special handling for descriptor passing. Otherwise just
+ transmit an empty string to tell the receiver that no
+ descriptor passing is done. */
+ /*TODO*/
+ }
+
+ struct iovec *const iov = msg->msg_iov;
+ const int iovcnt = msg->msg_iovlen;
+
+ int res = SOCKET_ERROR;
+
+ WSABUF wsabuf[iovcnt];
+
+ const struct iovec *iovptr = iov + iovcnt;
+ WSABUF *wsaptr = wsabuf + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ wsaptr -= 1;
+ wsaptr->len = iovptr->iov_len;
+ wsaptr->buf = (char *) iovptr->iov_base;
+ }
+ while (wsaptr != wsabuf);
+
+ DWORD ret = 0;
+
+ if (is_nonblocking () || closed () || async_io ())
+ res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret,
+ flags & MSG_WINMASK, (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen, NULL, NULL);
+ else
+ {
+ HANDLE evt;
+ if (prepare (evt, FD_CLOSE | FD_WRITE | (owner () ? FD_OOB : 0)))
+ {
+ do
+ {
+ res = WSASendTo (get_socket (), wsabuf, iovcnt,
+ &ret, flags & MSG_WINMASK,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen, NULL, NULL);
+ }
+ while (res == SOCKET_ERROR
+ && WSAGetLastError () == WSAEWOULDBLOCK
+ && !(res = wait (evt, 0))
+ && !closed ());
+ release (evt);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ else
+ res = ret;
+
+ /* Special handling for EPIPE and SIGPIPE.
+
+ EPIPE is generated if the local end has been shut down on a connection
+ oriented socket. In this case the process will also receive a SIGPIPE
+ unless MSG_NOSIGNAL is set. */
+ if (res == SOCKET_ERROR && get_errno () == ESHUTDOWN
+ && get_socket_type () == SOCK_STREAM)
+ {
+ set_errno (EPIPE);
+ if (! (flags & MSG_NOSIGNAL))
+ raise (SIGPIPE);
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::shutdown (int how)
+{
+ int res = ::shutdown (get_socket (), how);
+
+ if (res)
+ set_winsock_errno ();
+ else
+ switch (how)
+ {
+ case SHUT_RD:
+ saw_shutdown_read (true);
+ break;
+ case SHUT_WR:
+ saw_shutdown_write (true);
+ break;
+ case SHUT_RDWR:
+ saw_shutdown_read (true);
+ saw_shutdown_write (true);
+ break;
+ }
+ return res;
+}
+
+int
+fhandler_socket::close ()
+{
+ int res = 0;
+
+ /* HACK to allow a graceful shutdown even if shutdown() hasn't been
+ called by the application. Note that this isn't the ultimate
+ solution but it helps in many cases. */
+ struct linger linger;
+ linger.l_onoff = 1;
+ linger.l_linger = 240; /* secs. default 2MSL value according to MSDN. */
+ setsockopt (get_socket (), SOL_SOCKET, SO_LINGER,
+ (const char *)&linger, sizeof linger);
+
+ while ((res = closesocket (get_socket ())) != 0)
+ {
+ if (WSAGetLastError () != WSAEWOULDBLOCK)
+ {
+ set_winsock_errno ();
+ res = -1;
+ break;
+ }
+ if (WaitForSingleObject (signal_arrived, 10) == WAIT_OBJECT_0)
+ {
+ set_errno (EINTR);
+ res = -1;
+ break;
+ }
+ WSASetLastError (0);
+ }
+
+ debug_printf ("%d = fhandler_socket::close()", res);
+ return res;
+}
+
+int
+fhandler_socket::ioctl (unsigned int cmd, void *p)
+{
+ extern int get_ifconf (struct ifconf *ifc, int what); /* net.cc */
+ int res;
+ struct ifconf ifc, *ifcp;
+ struct ifreq *ifr, *ifrp;
+
+ switch (cmd)
+ {
+ case SIOCGIFCONF:
+ ifcp = (struct ifconf *) p;
+ if (!ifcp)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ res = get_ifconf (ifcp, cmd);
+ if (res)
+ debug_printf ("error in get_ifconf");
+ break;
+ case SIOCGIFFLAGS:
+ ifr = (struct ifreq *) p;
+ if (ifr == 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ ifr->ifr_flags = IFF_NOTRAILERS | IFF_UP | IFF_RUNNING;
+ if (!strncmp(ifr->ifr_name, "lo", 2)
+ || ntohl (((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr)
+ == INADDR_LOOPBACK)
+ ifr->ifr_flags |= IFF_LOOPBACK;
+ else
+ ifr->ifr_flags |= IFF_BROADCAST;
+ res = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCGIFADDR:
+ case SIOCGIFHWADDR:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ {
+ ifc.ifc_len = 2048;
+ ifc.ifc_buf = (char *) alloca (2048);
+
+ ifr = (struct ifreq *) p;
+ if (ifr == 0)
+ {
+ debug_printf ("ifr == NULL");
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ res = get_ifconf (&ifc, cmd);
+ if (res)
+ {
+ debug_printf ("error in get_ifconf");
+ break;
+ }
+
+ debug_printf (" name: %s", ifr->ifr_name);
+ for (ifrp = ifc.ifc_req;
+ (caddr_t) ifrp < ifc.ifc_buf + ifc.ifc_len;
+ ++ifrp)
+ {
+ debug_printf ("testname: %s", ifrp->ifr_name);
+ if (! strcmp (ifrp->ifr_name, ifr->ifr_name))
+ {
+ switch (cmd)
+ {
+ case SIOCGIFADDR:
+ ifr->ifr_addr = ifrp->ifr_addr;
+ break;
+ case SIOCGIFBRDADDR:
+ ifr->ifr_broadaddr = ifrp->ifr_broadaddr;
+ break;
+ case SIOCGIFNETMASK:
+ ifr->ifr_netmask = ifrp->ifr_netmask;
+ break;
+ case SIOCGIFHWADDR:
+ ifr->ifr_hwaddr = ifrp->ifr_hwaddr;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = ifrp->ifr_metric;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = ifrp->ifr_mtu;
+ break;
+ }
+ break;
+ }
+ }
+ if ((caddr_t) ifrp >= ifc.ifc_buf + ifc.ifc_len)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ break;
+ }
+ case FIOASYNC:
+ res = WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO,
+ *(int *) p ? ASYNC_MASK : 0);
+ syscall_printf ("Async I/O on socket %s",
+ *(int *) p ? "started" : "cancelled");
+ async_io (*(int *) p != 0);
+ break;
+ case FIONREAD:
+ res = ioctlsocket (get_socket (), FIONREAD, (unsigned long *) p);
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ break;
+ default:
+ /* We must cancel WSAAsyncSelect (if any) before setting socket to
+ * blocking mode
+ */
+ if (cmd == FIONBIO && *(int *) p == 0)
+ {
+ if (async_io ())
+ WSAAsyncSelect (get_socket (), winmsg, 0, 0);
+ if (WSAEventSelect (get_socket (), NULL, 0) == SOCKET_ERROR)
+ debug_printf ("WSAEventSelect(NULL), %d", WSAGetLastError ());
+ }
+ res = ioctlsocket (get_socket (), cmd, (unsigned long *) p);
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ if (cmd == FIONBIO)
+ {
+ if (!res)
+ {
+ syscall_printf ("socket is now %sblocking",
+ *(int *) p ? "non" : "");
+ set_nonblocking (*(int *) p);
+ }
+ /* Start AsyncSelect if async socket unblocked */
+ if (*(int *) p && async_io ())
+ WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK);
+ }
+ break;
+ }
+ syscall_printf ("%d = ioctl_socket (%x, %x)", res, cmd, p);
+ return res;
+}
+
+int
+fhandler_socket::fcntl (int cmd, void *arg)
+{
+ int res = 0;
+ int request, current;
+
+ switch (cmd)
+ {
+ case F_SETOWN:
+ {
+ /* Urgh! Bad hack! */
+ pid_t pid = (pid_t) arg;
+ owner (pid == getpid ());
+ debug_printf ("owner set to %d", owner ());
+ }
+ break;
+ case F_SETFL:
+ {
+ /* Carefully test for the O_NONBLOCK or deprecated OLD_O_NDELAY flag.
+ Set only the flag that has been passed in. If both are set, just
+ record O_NONBLOCK. */
+ int new_flags = (int) arg & O_NONBLOCK_MASK;
+ if ((new_flags & OLD_O_NDELAY) && (new_flags & O_NONBLOCK))
+ new_flags = O_NONBLOCK;
+ current = get_flags () & O_NONBLOCK_MASK;
+ request = new_flags ? 1 : 0;
+ if (!!current != !!new_flags && (res = ioctl (FIONBIO, &request)))
+ break;
+ set_flags ((get_flags () & ~O_NONBLOCK_MASK) | new_flags);
+ break;
+ }
+ default:
+ res = fhandler_base::fcntl (cmd, arg);
+ break;
+ }
+ return res;
+}
+
+void
+fhandler_socket::set_close_on_exec (bool val)
+{
+ close_on_exec (val);
+ debug_printf ("set close_on_exec for %s to %d", get_name (), val);
+}
+
+void
+fhandler_socket::set_sun_path (const char *path)
+{
+ sun_path = path ? cstrdup (path) : NULL;
+}
+
+int
+fhandler_socket::getpeereid (pid_t *pid, __uid32_t *euid, __gid32_t *egid)
+{
+ if (get_addr_family () != AF_LOCAL || get_socket_type () != SOCK_STREAM)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ if (connect_state () != connected)
+ {
+ set_errno (ENOTCONN);
+ return -1;
+ }
+ if (sec_peer_pid == (pid_t) 0)
+ {
+ set_errno (ENOTCONN); /* Usually when calling getpeereid on
+ accepting (instead of accepted) socket. */
+ return -1;
+ }
+
+ myfault efault;
+ if (efault.faulted (EFAULT))
+ return -1;
+ if (pid)
+ *pid = sec_peer_pid;
+ if (euid)
+ *euid = sec_peer_uid;
+ if (egid)
+ *egid = sec_peer_gid;
+ return 0;
+}