366 lines
8.6 KiB
C++
366 lines
8.6 KiB
C++
/* fhandler_socket.cc. See fhandler.h for a description of the fhandler classes.
|
|
|
|
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. */
|
|
|
|
#include "winsup.h"
|
|
#include <sys/socket.h>
|
|
#include <asm/byteorder.h>
|
|
#include <unistd.h>
|
|
#include <sys/param.h>
|
|
#include <sys/statvfs.h>
|
|
#include <cygwin/acl.h>
|
|
#include "cygerrno.h"
|
|
#include "path.h"
|
|
#include "fhandler.h"
|
|
#include "tls_pbuf.h"
|
|
|
|
extern "C" {
|
|
int sscanf (const char *, const char *, ...);
|
|
} /* End of "C" section */
|
|
|
|
/**********************************************************************/
|
|
/* fhandler_socket */
|
|
|
|
fhandler_socket::fhandler_socket () :
|
|
fhandler_base (),
|
|
uid (myself->uid),
|
|
gid (myself->gid),
|
|
mode (S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO),
|
|
_rcvtimeo (INFINITE),
|
|
_sndtimeo (INFINITE)
|
|
{
|
|
}
|
|
|
|
fhandler_socket::~fhandler_socket ()
|
|
{
|
|
}
|
|
|
|
char *
|
|
fhandler_socket::get_proc_fd_name (char *buf)
|
|
{
|
|
__small_sprintf (buf, "socket:[%lu]", get_plain_ino ());
|
|
return buf;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::getpeereid (pid_t *pid, uid_t *euid, gid_t *egid)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
/* Definitions of old ifreq stuff used prior to Cygwin 1.7.0. */
|
|
#define OLD_SIOCGIFFLAGS _IOW('s', 101, struct __old_ifreq)
|
|
#define OLD_SIOCGIFADDR _IOW('s', 102, struct __old_ifreq)
|
|
#define OLD_SIOCGIFBRDADDR _IOW('s', 103, struct __old_ifreq)
|
|
#define OLD_SIOCGIFNETMASK _IOW('s', 104, struct __old_ifreq)
|
|
#define OLD_SIOCGIFHWADDR _IOW('s', 105, struct __old_ifreq)
|
|
#define OLD_SIOCGIFMETRIC _IOW('s', 106, struct __old_ifreq)
|
|
#define OLD_SIOCGIFMTU _IOW('s', 107, struct __old_ifreq)
|
|
#define OLD_SIOCGIFINDEX _IOW('s', 108, struct __old_ifreq)
|
|
|
|
#define CONV_OLD_TO_NEW_SIO(old) (((old)&0xff00ffff)|(((long)sizeof(struct ifreq)&IOCPARM_MASK)<<16))
|
|
|
|
struct __old_ifreq {
|
|
#define __OLD_IFNAMSIZ 16
|
|
union {
|
|
char ifrn_name[__OLD_IFNAMSIZ]; /* if name, e.g. "en0" */
|
|
} ifr_ifrn;
|
|
|
|
union {
|
|
struct sockaddr ifru_addr;
|
|
struct sockaddr ifru_broadaddr;
|
|
struct sockaddr ifru_netmask;
|
|
struct sockaddr ifru_hwaddr;
|
|
short ifru_flags;
|
|
int ifru_metric;
|
|
int ifru_mtu;
|
|
int ifru_ifindex;
|
|
} ifr_ifru;
|
|
};
|
|
|
|
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 *ifrp;
|
|
|
|
switch (cmd)
|
|
{
|
|
case SIOCGIFCONF:
|
|
ifcp = (struct ifconf *) p;
|
|
if (!ifcp)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
if (CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ)
|
|
{
|
|
ifc.ifc_len = ifcp->ifc_len / sizeof (struct __old_ifreq)
|
|
* sizeof (struct ifreq);
|
|
ifc.ifc_buf = (caddr_t) alloca (ifc.ifc_len);
|
|
}
|
|
else
|
|
{
|
|
ifc.ifc_len = ifcp->ifc_len;
|
|
ifc.ifc_buf = ifcp->ifc_buf;
|
|
}
|
|
res = get_ifconf (&ifc, cmd);
|
|
if (res)
|
|
debug_printf ("error in get_ifconf");
|
|
if (CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ)
|
|
{
|
|
struct __old_ifreq *ifr = (struct __old_ifreq *) ifcp->ifc_buf;
|
|
for (ifrp = ifc.ifc_req;
|
|
(caddr_t) ifrp < ifc.ifc_buf + ifc.ifc_len;
|
|
++ifrp, ++ifr)
|
|
{
|
|
memcpy (&ifr->ifr_ifrn, &ifrp->ifr_ifrn, sizeof ifr->ifr_ifrn);
|
|
ifr->ifr_name[__OLD_IFNAMSIZ - 1] = '\0';
|
|
memcpy (&ifr->ifr_ifru, &ifrp->ifr_ifru, sizeof ifr->ifr_ifru);
|
|
}
|
|
ifcp->ifc_len = ifc.ifc_len / sizeof (struct ifreq)
|
|
* sizeof (struct __old_ifreq);
|
|
}
|
|
else
|
|
ifcp->ifc_len = ifc.ifc_len;
|
|
break;
|
|
case OLD_SIOCGIFFLAGS:
|
|
case OLD_SIOCGIFADDR:
|
|
case OLD_SIOCGIFBRDADDR:
|
|
case OLD_SIOCGIFNETMASK:
|
|
case OLD_SIOCGIFHWADDR:
|
|
case OLD_SIOCGIFMETRIC:
|
|
case OLD_SIOCGIFMTU:
|
|
case OLD_SIOCGIFINDEX:
|
|
cmd = CONV_OLD_TO_NEW_SIO (cmd);
|
|
fallthrough;
|
|
case SIOCGIFFLAGS:
|
|
case SIOCGIFBRDADDR:
|
|
case SIOCGIFNETMASK:
|
|
case SIOCGIFADDR:
|
|
case SIOCGIFHWADDR:
|
|
case SIOCGIFMETRIC:
|
|
case SIOCGIFMTU:
|
|
case SIOCGIFINDEX:
|
|
case SIOCGIFFRNDLYNAM:
|
|
case SIOCGIFDSTADDR:
|
|
{
|
|
if (!p)
|
|
{
|
|
debug_printf ("ifr == NULL");
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
|
|
if (cmd > SIOCGIFINDEX && CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ)
|
|
{
|
|
debug_printf ("cmd not supported on this platform");
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
ifc.ifc_len = 64 * sizeof (struct ifreq);
|
|
ifc.ifc_buf = (caddr_t) alloca (ifc.ifc_len);
|
|
if (cmd == SIOCGIFFRNDLYNAM)
|
|
{
|
|
struct ifreq_frndlyname *iff = (struct ifreq_frndlyname *)
|
|
alloca (64 * sizeof (struct ifreq_frndlyname));
|
|
for (int i = 0; i < 64; ++i)
|
|
ifc.ifc_req[i].ifr_frndlyname = &iff[i];
|
|
}
|
|
|
|
res = get_ifconf (&ifc, cmd);
|
|
if (res)
|
|
{
|
|
debug_printf ("error in get_ifconf");
|
|
break;
|
|
}
|
|
|
|
if (CYGWIN_VERSION_CHECK_FOR_OLD_IFREQ)
|
|
{
|
|
struct __old_ifreq *ifr = (struct __old_ifreq *) p;
|
|
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))
|
|
{
|
|
memcpy (&ifr->ifr_ifru, &ifrp->ifr_ifru,
|
|
sizeof ifr->ifr_ifru);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
struct ifreq *ifr = (struct ifreq *) p;
|
|
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))
|
|
{
|
|
if (cmd == SIOCGIFFRNDLYNAM)
|
|
/* The application has to care for the space. */
|
|
memcpy (ifr->ifr_frndlyname, ifrp->ifr_frndlyname,
|
|
sizeof (struct ifreq_frndlyname));
|
|
else
|
|
memcpy (&ifr->ifr_ifru, &ifrp->ifr_ifru,
|
|
sizeof ifr->ifr_ifru);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if ((caddr_t) ifrp >= ifc.ifc_buf + ifc.ifc_len)
|
|
{
|
|
set_errno (EINVAL);
|
|
return -1;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
res = fhandler_base::ioctl (cmd, p);
|
|
break;
|
|
}
|
|
syscall_printf ("%d = ioctl_socket(%x, %p)", res, cmd, p);
|
|
return res;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::fcntl (int cmd, intptr_t arg)
|
|
{
|
|
int res = 0;
|
|
int request, current;
|
|
|
|
switch (cmd)
|
|
{
|
|
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 = 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;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::open (int flags, mode_t mode)
|
|
{
|
|
set_errno (ENXIO);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::fstat (struct stat *buf)
|
|
{
|
|
int res;
|
|
|
|
res = fhandler_base::fstat (buf);
|
|
if (!res)
|
|
{
|
|
buf->st_dev = FHDEV (DEV_SOCK_MAJOR, 0);
|
|
if (!(buf->st_ino = get_plain_ino ()))
|
|
sscanf (get_name (), "/proc/%*d/fd/socket:[%lld]",
|
|
(long long *) &buf->st_ino);
|
|
buf->st_uid = uid;
|
|
buf->st_gid = gid;
|
|
buf->st_mode = mode;
|
|
buf->st_size = 0;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::fstatvfs (struct statvfs *sfs)
|
|
{
|
|
memset (sfs, 0, sizeof (*sfs));
|
|
sfs->f_bsize = sfs->f_frsize = 4096;
|
|
sfs->f_namemax = NAME_MAX;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::fchmod (mode_t newmode)
|
|
{
|
|
mode = (newmode & ~S_IFMT) | S_IFSOCK;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::fchown (uid_t newuid, gid_t newgid)
|
|
{
|
|
bool perms = check_token_membership (&well_known_admins_sid);
|
|
|
|
/* Admin rulez */
|
|
if (!perms)
|
|
{
|
|
/* Otherwise, new uid == old uid or current uid is fine */
|
|
if (newuid == ILLEGAL_UID || newuid == uid || newuid == myself->uid)
|
|
perms = true;
|
|
/* Otherwise, new gid == old gid or current gid is fine */
|
|
else if (newgid == ILLEGAL_GID || newgid == gid || newgid == myself->gid)
|
|
perms = true;
|
|
else
|
|
{
|
|
/* Last but not least, newgid in supplementary group list is fine */
|
|
tmp_pathbuf tp;
|
|
gid_t *gids = (gid_t *) tp.w_get ();
|
|
int num = getgroups (65536 / sizeof (*gids), gids);
|
|
|
|
for (int idx = 0; idx < num; ++idx)
|
|
if (newgid == gids[idx])
|
|
{
|
|
perms = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (perms)
|
|
{
|
|
if (newuid != ILLEGAL_UID)
|
|
uid = newuid;
|
|
if (newgid != ILLEGAL_GID)
|
|
gid = newgid;
|
|
return 0;
|
|
}
|
|
set_errno (EPERM);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::facl (int cmd, int nentries, aclent_t *aclbufp)
|
|
{
|
|
set_errno (EOPNOTSUPP);
|
|
return -1;
|
|
}
|
|
|
|
int
|
|
fhandler_socket::link (const char *newpath)
|
|
{
|
|
return fhandler_base::link (newpath);
|
|
}
|