newlib/winsup/cygwin/fhandler_socket.cc

1702 lines
40 KiB
C++

/* 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 "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>
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;
static void
secret_event_name (char *buf, short port, int *secret_ptr)
{
__small_sprintf (buf, "%scygwin.local_socket.secret.%d.%08x-%08x-%08x-%08x",
wincap.has_terminal_services () ? "Global\\" : "",
port,
secret_ptr [0], secret_ptr [1],
secret_ptr [2], secret_ptr [3]);
}
char *
fhandler_socket::eid_pipe_name (char *buf)
{
__small_sprintf (buf, "\\\\.\\pipe\\cygwin-unix-%s", get_sun_path ());
debug_printf ("%s", buf);
return buf;
}
void
fhandler_socket::set_socketpair_eids (void)
{
sec_pid = sec_peer_pid = getpid ();
sec_uid = sec_peer_uid = geteuid32 ();
sec_gid = sec_peer_gid = getegid32 ();
}
/* 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, int* secret = 0)
{
int secret_buf [4];
int* secret_ptr = (secret ? : secret_buf);
if (in->sa_family == AF_INET)
{
*out = * (sockaddr_in *)in;
*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))
{
sockaddr_in sin;
sin.sin_family = AF_INET;
sscanf (buf + strlen (SOCKET_COOKIE), "%hu %08x-%08x-%08x-%08x",
&sin.sin_port,
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);
*out = sin;
*outlen = sizeof sin;
ret = 1;
}
CloseHandle (fh);
return ret;
}
else
{
set_errno (EAFNOSUPPORT);
return 0;
}
}
/**********************************************************************/
/* fhandler_socket */
fhandler_socket::fhandler_socket () :
fhandler_base (),
sec_pipe (INVALID_HANDLE_VALUE),
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);
/* Close eid credentials pipe handle. */
if (sec_pipe != INVALID_HANDLE_VALUE)
CloseHandle (sec_pipe);
}
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::set_connect_secret ()
{
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));
}
}
void
fhandler_socket::get_connect_secret (char* buf)
{
__small_sprintf (buf, "%08x-%08x-%08x-%08x",
connect_secret [0], connect_secret [1],
connect_secret [2], connect_secret [3]);
}
HANDLE
fhandler_socket::create_secret_event (int* secret)
{
struct sockaddr_in sin;
int sin_len = sizeof (sin);
if (secret_event)
return secret_event;
if (::getsockname (get_socket (), (struct sockaddr*) &sin, &sin_len))
{
debug_printf ("error getting local socket name (%d)", WSAGetLastError ());
return NULL;
}
char event_name[CYG_MAX_PATH];
secret_event_name (event_name, sin.sin_port, secret ?: connect_secret);
secret_event = CreateEvent (&sec_all, FALSE, FALSE, event_name);
if (!secret_event)
debug_printf("create event %E");
else if (close_on_exec ())
/* Event allows inheritance, but handle will not be inherited */
set_no_inheritance (secret_event, 1);
return secret_event;
}
void
fhandler_socket::signal_secret_event ()
{
if (!secret_event)
debug_printf ("no secret event?");
else
{
SetEvent (secret_event);
debug_printf ("signaled secret_event");
}
}
void
fhandler_socket::close_secret_event ()
{
if (secret_event)
CloseHandle (secret_event);
secret_event = NULL;
}
int
fhandler_socket::check_peer_secret_event (struct sockaddr_in* peer, int* secret)
{
char event_name[CYG_MAX_PATH];
secret_event_name (event_name, peer->sin_port, secret ?: connect_secret);
HANDLE ev = CreateEvent (&sec_all_nih, FALSE, FALSE, event_name);
if (!ev)
debug_printf("create event %E");
signal_secret_event ();
if (ev)
{
DWORD rc = WaitForSingleObject (ev, 10000);
debug_printf ("WFSO rc=%d", rc);
CloseHandle (ev);
return (rc == WAIT_OBJECT_0 ? 1 : 0);
}
else
return 0;
}
void
fhandler_socket::fixup_before_fork_exec (DWORD win_proc_id)
{
if (!winsock2_active)
{
fhandler_base::fixup_before_fork_exec (win_proc_id);
debug_printf ("Without Winsock 2.0");
}
else 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 ();
}
}
extern "C" void __stdcall load_wsock32 ();
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 if (!new_sock && !winsock2_active)
{
load_wsock32 ();
fhandler_base::fixup_after_fork (parent);
debug_printf ("Without Winsock 2.0");
}
else
{
debug_printf ("WSASocket went fine new_sock %p, old_sock %p", new_sock, get_io_handle ());
set_io_handle ((HANDLE) new_sock);
}
if (secret_event)
fork_fixup (parent, secret_event, "secret_event");
}
void
fhandler_socket::fixup_after_exec ()
{
debug_printf ("here");
if (!close_on_exec ())
fixup_after_fork (NULL);
#if 0
else if (!winsock2_active)
closesocket (get_socket ());
#endif
}
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;
if (sec_pipe != INVALID_HANDLE_VALUE)
{
if (!DuplicateHandle (hMainProc, sec_pipe, hMainProc, &nh, 0,
TRUE, DUPLICATE_SAME_ACCESS))
{
system_printf ("!DuplicateHandle(%x) failed, %E", sec_pipe);
__seterrno ();
return -1;
}
else
fhs->sec_pipe = nh;
}
}
}
fhs->connect_state (connect_state ());
if (winsock2_active)
{
/* 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,
!winsock2_active, DUPLICATE_SAME_ACCESS))
{
system_printf ("!DuplicateHandle(%x) failed, %E", get_io_handle ());
__seterrno ();
if (fhs->sec_pipe != INVALID_HANDLE_VALUE)
CloseHandle (fhs->sec_pipe);
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);
}
int
fhandler_socket::utimes (const struct timeval *tvp)
{
if (get_device () == FH_UNIX)
{
fhandler_disk_file fh (pc);
return fh.utimes (tvp);
}
return fhandler_base::utimes (tvp);
}
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 ();
}
set_connect_secret ();
char buf[sizeof (SOCKET_COOKIE) + 80];
__small_sprintf (buf, "%s%u ", SOCKET_COOKIE, sin.sin_port);
get_connect_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 (::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 secret_check_failed = false;
bool in_progress = false;
sockaddr_in sin;
int secret [4];
DWORD err;
if (!get_inet_addr (name, namelen, &sin, &namelen, secret))
return -1;
res = ::connect (get_socket (), (sockaddr *) &sin, namelen);
if (res)
{
/* Special handling for connect to return the correct error code
when called on a non-blocking socket. */
if (is_nonblocking () || connect_state () == connect_pending)
{
err = WSAGetLastError ();
if (err == WSAEWOULDBLOCK || err == WSAEALREADY)
in_progress = true;
if (err == WSAEWOULDBLOCK)
WSASetLastError (WSAEINPROGRESS);
else if (err == WSAEINVAL)
WSASetLastError (WSAEISCONN);
}
set_winsock_errno ();
}
if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
{
if (!res || in_progress)
{
if (!create_secret_event (secret))
{
secret_check_failed = true;
}
else if (in_progress)
signal_secret_event ();
}
if (!secret_check_failed && !res)
{
if (!check_peer_secret_event (&sin, secret))
{
debug_printf ("accept from unauthorized server");
secret_check_failed = true;
}
}
if (secret_check_failed)
{
close_secret_event ();
if (res)
closesocket (res);
set_errno (ECONNREFUSED);
res = -1;
}
if (!res || in_progress)
{
/* eid credential transaction. */
set_sun_path (name->sa_data);
if (wincap.has_named_pipes ())
{
struct ucred in = { getpid (), geteuid32 (), getegid32 () };
struct ucred out = { (pid_t) 0, (__uid32_t) -1, (__gid32_t) -1 };
DWORD bytes = 0;
if (CallNamedPipe(eid_pipe_name ((char *) alloca (CYG_MAX_PATH + 1)),
&in, sizeof in, &out, sizeof out, &bytes, 1000))
{
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: %E");
}
else /* 9x */
{
/* Incorrect but wrong pid at least doesn't break getpeereid. */
sec_peer_pid = getpid ();
sec_peer_uid = geteuid32 ();
sec_peer_gid = getegid32 ();
}
}
}
err = WSAGetLastError ();
if (err == WSAEINPROGRESS || err == WSAEALREADY)
connect_state (connect_pending);
else
connect_state (connected);
return res;
}
int
fhandler_socket::listen (int backlog)
{
int res = ::listen (get_socket (), backlog);
if (res)
set_winsock_errno ();
else
{
if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
{
/* Prepare eid credential transaction. */
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;
/* A listening socket can call listen again, but that shouldn't
result in trying to create another pipe. */
if (wincap.has_named_pipes () && sec_pipe == INVALID_HANDLE_VALUE)
sec_pipe =
CreateNamedPipe (eid_pipe_name ((char *) alloca (CYG_MAX_PATH + 1)),
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
PIPE_UNLIMITED_INSTANCES, sizeof (struct ucred),
sizeof (struct ucred), 1000, &sec_all);
debug_printf ("sec_pipe: %x", sec_pipe);
}
connect_state (connected);
}
return res;
}
int
fhandler_socket::accept (struct sockaddr *peer, int *len)
{
int res = -1;
bool secret_check_failed = false;
bool in_progress = false;
struct ucred in = { sec_pid, sec_uid, sec_gid };
struct ucred out = { (pid_t) 0, (__uid32_t) -1, (__gid32_t) -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 ((SOCKET) res == INVALID_SOCKET && WSAGetLastError () == WSAEWOULDBLOCK)
in_progress = true;
if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
{
if ((SOCKET) res != INVALID_SOCKET || in_progress)
{
if (!create_secret_event ())
secret_check_failed = true;
else if (in_progress)
signal_secret_event ();
}
if (!secret_check_failed &&
(SOCKET) res != INVALID_SOCKET)
{
if (!check_peer_secret_event ((struct sockaddr_in*) peer))
{
debug_printf ("connect from unauthorized client");
secret_check_failed = true;
}
}
if (secret_check_failed)
{
close_secret_event ();
if ((SOCKET) res != INVALID_SOCKET)
closesocket (res);
set_errno (ECONNABORTED);
return -1;
}
if ((SOCKET) res != INVALID_SOCKET || in_progress)
{
/* eid credential transaction. */
if (wincap.has_named_pipes ())
{
DWORD bytes = 0;
bool ret = ConnectNamedPipe (sec_pipe, NULL);
if (ret || GetLastError () == ERROR_PIPE_CONNECTED)
{
if (!ReadFile (sec_pipe, &out, sizeof out, &bytes, NULL))
debug_printf ("Receiving eid credentials failed: %E");
else
debug_printf ("Received eid credentials: pid: %d, uid: %d"
", gid: %d", out.pid, out.uid, out.gid);
if (!WriteFile (sec_pipe, &in, sizeof in, &bytes, NULL))
debug_printf ("Sending eid credentials failed: %E");
DisconnectNamedPipe (sec_pipe);
}
else
debug_printf ("Connecting the eid credential pipe failed: %E");
}
else /* 9x */
{
/* Incorrect but wrong pid at least doesn't break getpeereid. */
out.pid = sec_pid;
out.uid = sec_uid;
out.gid = sec_gid;
}
}
}
if ((SOCKET) res == INVALID_SOCKET)
set_winsock_errno ();
else
{
cygheap_fdnew res_fd;
if (res_fd >= 0 && fdsock (res_fd, &dev (), res))
{
((fhandler_socket *) res_fd)->set_addr_family (get_addr_family ());
((fhandler_socket *) res_fd)->set_socket_type (get_socket_type ());
if (get_addr_family () == AF_LOCAL)
{
((fhandler_socket *) res_fd)->set_sun_path (get_sun_path ());
if (get_socket_type () == SOCK_STREAM)
{
((fhandler_socket *) res_fd)->sec_peer_pid = out.pid;
((fhandler_socket *) res_fd)->sec_peer_uid = out.uid;
((fhandler_socket *) res_fd)->sec_peer_gid = out.gid;
}
}
((fhandler_socket *) res_fd)->connect_state (connected);
res = res_fd;
}
else
{
closesocket (res);
res = -1;
}
}
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, %E");
return false;
}
return true;
}
int
fhandler_socket::wait (HANDLE event, int flags)
{
int ret = SOCKET_ERROR;
int wsa_err = 0;
WSAEVENT ev[2] = { event, signal_arrived };
WSANETWORKEVENTS evts;
switch (WSAWaitForMultipleEvents (2, ev, FALSE, 10, 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_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:
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. */
WSAEventSelect (get_socket (), NULL, 0);
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_accrights: NULL,
msg_accrightslen: 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;
flags &= MSG_WINMASK;
if (!winsock2_active)
ret = res = ::recvfrom (get_socket (),
(char *) ptr, len, flags,
from, fromlen);
else
{
WSABUF wsabuf = { len, (char *) ptr };
if (is_nonblocking () || closed () || async_io ())
res = WSARecvFrom (get_socket (), &wsabuf, 1, (ret = 0, &ret),
(DWORD *) &flags, from, fromlen, NULL, NULL);
else
{
HANDLE evt;
if (prepare (evt, FD_CLOSE | FD_READ | (owner () ? FD_OOB : 0)))
{
do
{
DWORD lflags = (DWORD) flags;
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;
set_winsock_errno ();
}
else
res = ret;
return res;
}
int
fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot)
{
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;
if (!winsock2_active)
{
if (iovcnt == 1)
res = recvfrom (iov->iov_base, iov->iov_len, flags, from, fromlen);
else
{
if (tot == -1) // i.e. if not pre-calculated by the caller.
{
tot = 0;
const struct iovec *iovptr = iov + iovcnt;
do
{
iovptr -= 1;
tot += iovptr->iov_len;
}
while (iovptr != iov);
}
char *buf = (char *) alloca (tot);
if (!buf)
{
set_errno (ENOMEM);
res = SOCKET_ERROR;
}
else
{
res = recvfrom (buf, tot, flags, from, fromlen);
const struct iovec *iovptr = iov;
int nbytes = res;
while (nbytes > 0)
{
const int frag = min (nbytes, (ssize_t) iovptr->iov_len);
memcpy (iovptr->iov_base, buf, frag);
buf += frag;
iovptr += 1;
nbytes -= frag;
}
}
}
}
else
{
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;
if (is_nonblocking () || closed () || async_io ())
res = WSARecvFrom (get_socket (), wsabuf, iovcnt, (ret = 0, &ret),
(DWORD *) &flags, from, fromlen, NULL, NULL);
else
{
HANDLE evt;
if (prepare (evt, FD_CLOSE | FD_READ | (owner () ? FD_OOB : 0)))
{
do
{
DWORD lflags = (DWORD) flags;
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;
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_accrights: NULL,
msg_accrightslen: 0
};
return sendmsg (&msg, 0, tot);
}
int
fhandler_socket::sendto (const void *ptr, size_t len, int flags,
const struct sockaddr *to, int tolen)
{
sockaddr_in sin;
if (to && !get_inet_addr (to, tolen, &sin, &tolen))
return SOCKET_ERROR;
int res = SOCKET_ERROR;
DWORD ret;
if (!winsock2_active)
ret = res = ::sendto (get_socket (), (const char *) ptr, len,
flags & MSG_WINMASK,
(to ? (const struct sockaddr *) &sin : NULL), tolen);
else
{
WSABUF wsabuf = { len, (char *) ptr };
if (is_nonblocking () || closed () || async_io ())
res = WSASendTo (get_socket (), &wsabuf, 1, (ret = 0, &ret),
flags & MSG_WINMASK,
(to ? (const struct sockaddr *) &sin : 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 *) &sin : 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;
if (!winsock2_active)
{
if (iovcnt == 1)
res = sendto (iov->iov_base, iov->iov_len, flags,
(struct sockaddr *) msg->msg_name,
msg->msg_namelen);
else
{
if (tot == -1) // i.e. if not pre-calculated by the caller.
{
tot = 0;
const struct iovec *iovptr = iov + iovcnt;
do
{
iovptr -= 1;
tot += iovptr->iov_len;
}
while (iovptr != iov);
}
char *const buf = (char *) alloca (tot);
if (!buf)
{
set_errno (ENOMEM);
res = SOCKET_ERROR;
}
else
{
char *bufptr = buf;
const struct iovec *iovptr = iov;
int nbytes = tot;
while (nbytes != 0)
{
const int frag = min (nbytes, (ssize_t) iovptr->iov_len);
memcpy (bufptr, iovptr->iov_base, frag);
bufptr += frag;
iovptr += 1;
nbytes -= frag;
}
res = sendto (buf, tot, flags,
(struct sockaddr *) msg->msg_name,
msg->msg_namelen);
}
}
}
else
{
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;
if (is_nonblocking () || closed () || async_io ())
res = WSASendTo (get_socket (), wsabuf, iovcnt, (ret = 0, &ret),
flags, (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,
(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);
}
close_secret_event ();
debug_printf ("%d = fhandler_socket::close()", res);
return res;
}
#define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT)
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 && async_io () && *(int *) p == 0)
WSAAsyncSelect (get_socket (), winmsg, 0, 0);
res = ioctlsocket (get_socket (), cmd, (unsigned long *) p);
if (res == SOCKET_ERROR)
set_winsock_errno ();
if (cmd == FIONBIO)
{
syscall_printf ("socket is now %sblocking",
*(int *) p ? "non" : "");
/* Start AsyncSelect if async socket unblocked */
if (*(int *) p && async_io ())
WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK);
set_nonblocking (*(int *) p);
}
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)
{
if (secret_event)
set_no_inheritance (secret_event, val);
if (!winsock2_active) /* < Winsock 2.0 */
set_no_inheritance (get_handle (), 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)
{
if (connect_state () == connected && sec_peer_pid != (pid_t) -1)
{
if (!check_null_invalid_struct (pid))
*pid = sec_peer_pid;
if (!check_null_invalid_struct (euid))
*euid = sec_peer_uid;
if (!check_null_invalid_struct (egid))
*egid = sec_peer_gid;
return 0;
}
set_errno (ENOTCONN);
}
else
set_errno (EINVAL);
return -1;
}