diff --git a/src/util/Makefile.inc.am b/src/util/Makefile.inc.am index 23de4a6375..528c9f6cfe 100644 --- a/src/util/Makefile.inc.am +++ b/src/util/Makefile.inc.am @@ -188,6 +188,8 @@ UTIL_SOURCES = \ util/virseclabel.h \ util/virsecret.c \ util/virsecret.h \ + util/virsocket.c \ + util/virsocket.h \ util/virsocketaddr.c \ util/virsocketaddr.h \ util/virstorageencryption.c \ diff --git a/src/util/virsocket.c b/src/util/virsocket.c new file mode 100644 index 0000000000..96b9ece2b7 --- /dev/null +++ b/src/util/virsocket.c @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include "virsocket.h" + +#ifdef WIN32 + +# include + +# define FD2SK(fd) _get_osfhandle(fd) +# define SK2FD(sk) (_open_osfhandle((intptr_t) (sk), O_RDWR | O_BINARY)) + +# define GET_HANDLE(fd) \ + +# define RETURN_ERROR(call) \ + if ((call) < 0) { \ + set_errno(); \ + return -1; \ + } + +# undef accept +# undef bind +# undef closesocket +# undef connect +# undef getpeername +# undef getsockname +# undef getsockopt +# undef ioctlsocket +# undef listen +# undef setsockopt +# undef socket + +static void +set_errno(void) +{ + int err = WSAGetLastError(); + + /* Map some WSAE* errors to the runtime library's error codes. */ + switch (err) { + case WSA_INVALID_HANDLE: + errno = EBADF; + break; + case WSA_NOT_ENOUGH_MEMORY: + errno = ENOMEM; + break; + case WSA_INVALID_PARAMETER: + errno = EINVAL; + break; + case WSAENAMETOOLONG: + errno = ENAMETOOLONG; + break; + case WSAENOTEMPTY: + errno = ENOTEMPTY; + break; + case WSAEWOULDBLOCK: + errno = EWOULDBLOCK; + break; + case WSAEINPROGRESS: + errno = EINPROGRESS; + break; + case WSAEALREADY: + errno = EALREADY; + break; + case WSAENOTSOCK: + errno = ENOTSOCK; + break; + case WSAEDESTADDRREQ: + errno = EDESTADDRREQ; + break; + case WSAEMSGSIZE: + errno = EMSGSIZE; + break; + case WSAEPROTOTYPE: + errno = EPROTOTYPE; + break; + case WSAENOPROTOOPT: + errno = ENOPROTOOPT; + break; + case WSAEPROTONOSUPPORT: + errno = EPROTONOSUPPORT; + break; + case WSAEOPNOTSUPP: + errno = EOPNOTSUPP; + break; + case WSAEAFNOSUPPORT: + errno = EAFNOSUPPORT; + break; + case WSAEADDRINUSE: + errno = EADDRINUSE; + break; + case WSAEADDRNOTAVAIL: + errno = EADDRNOTAVAIL; + break; + case WSAENETDOWN: + errno = ENETDOWN; + break; + case WSAENETUNREACH: + errno = ENETUNREACH; + break; + case WSAENETRESET: + errno = ENETRESET; + break; + case WSAECONNABORTED: + errno = ECONNABORTED; + break; + case WSAECONNRESET: + errno = ECONNRESET; + break; + case WSAENOBUFS: + errno = ENOBUFS; + break; + case WSAEISCONN: + errno = EISCONN; + break; + case WSAENOTCONN: + errno = ENOTCONN; + break; + case WSAETIMEDOUT: + errno = ETIMEDOUT; + break; + case WSAECONNREFUSED: + errno = ECONNREFUSED; + break; + case WSAELOOP: + errno = ELOOP; + break; + case WSAEHOSTUNREACH: + errno = EHOSTUNREACH; + break; + default: + errno = (err > 10000 && err < 10025) ? err - 10000 : err; + break; + } +} + + +int +vir_accept(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + SOCKET sk = FD2SK(fd); + SOCKET csk; + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + csk = accept(sk, addr, addrlen); + + if (csk == INVALID_SOCKET) { + set_errno(); + return -1; + } + + return SK2FD(csk); +} + + +int +vir_bind(int fd, const struct sockaddr *addr, socklen_t addrlen) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (bind(sk, addr, addrlen) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_closesocket(int fd) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (closesocket(sk) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_connect(int fd, const struct sockaddr *addr, socklen_t addrlen) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (connect(sk, addr, addrlen) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (getpeername(sk, addr, addrlen) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (getsockname(sk, addr, addrlen) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_listen(int fd, int backlog) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (listen(sk, backlog) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_ioctlsocket(int fd, int cmd, void *arg) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (ioctlsocket(sk, cmd, arg) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_getsockopt(int fd, int level, int optname, + void *optval, socklen_t *optlen) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (getsockopt(sk, level, optname, optval, optlen) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_setsockopt(int fd, int level, int optname, + const void *optval, socklen_t optlen) +{ + SOCKET sk = FD2SK(fd); + + if (sk == INVALID_SOCKET) { + errno = EBADF; + return -1; + } + + if (setsockopt(sk, level, optname, optval, optlen) < 0) { + set_errno(); + return -1; + } + + return 0; +} + + +int +vir_socket(int domain, int type, int protocol) +{ + SOCKET sk; + + /* We have to use WSASocket() instead of socket(), to create + * non-overlapped IO sockets. Overlapped IO sockets cannot + * be used with read/write. + */ + sk = WSASocket(domain, type, protocol, NULL, 0, 0); + if (sk == INVALID_SOCKET) { + set_errno(); + return -1; + } + + return SK2FD(sk); +} + +#endif /* WIN32 */ diff --git a/src/util/virsocket.h b/src/util/virsocket.h new file mode 100644 index 0000000000..33f237886f --- /dev/null +++ b/src/util/virsocket.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 Red Hat, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#pragma once + +#include "internal.h" + +#ifdef WIN32 + +# define WIN32_LEAN_AND_MEAN +# include +# include +# include +# include + +int vir_accept(int fd, struct sockaddr *addr, socklen_t *addrlen); +int vir_bind(int fd, const struct sockaddr *addr, socklen_t addrlen); +int vir_closesocket(int fd); +int vir_connect(int fd, const struct sockaddr *addr, socklen_t addrlen); +int vir_getpeername(int fd, struct sockaddr *addr, socklen_t *addrlen); +int vir_getsockname(int fd, struct sockaddr *addr, socklen_t *addrlen); +int vir_listen(int fd, int backlog); +int vir_ioctlsocket(int fd, int cmd, void *arg); +int vir_getsockopt(int fd, int level, int optname, + void *optval, socklen_t *optlen); +int vir_setsockopt(int fd, int level, int optname, + const void *optval, socklen_t optlen); +int vir_socket(int domain, int type, int protocol); + + +/* Get rid of GNULIB's replacements */ +# undef accept +# undef bind +# undef closesocket +# undef connect +# undef dup +# undef dup2 +# undef getpeername +# undef getsockname +# undef getsockopt +# undef ioctlsocket +# undef listen +# undef setsockopt +# undef socket + +/* Provide our own replacements */ +# define accept vir_accept +# define bind vir_bind +# define closesocket vir_closesocket +# define connect vir_connect +# define dup _dup +# define dup2 _dup2 +# define ioctlsocket vir_ioctlsocket +# define getpeername vir_getpeername +# define getsockname vir_getsockname +# define getsockopt vir_getsockopt +# define listen vir_listen +# define setsockopt vir_setsockopt +# define socket vir_socket + +#else + +# include +# include +# include +# include +# include +# include +# include +# include + +# define closesocket close +# define ioctlsocket ioctl + +#endif diff --git a/src/util/virutil.c b/src/util/virutil.c index a0fd7618ee..9ea9e2946d 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -71,10 +71,10 @@ #include "verify.h" #include "virfile.h" #include "vircommand.h" -#include "nonblocking.h" #include "virprocess.h" #include "virstring.h" #include "virutil.h" +#include "virsocket.h" verify(sizeof(gid_t) <= sizeof(unsigned int) && sizeof(uid_t) <= sizeof(unsigned int)); @@ -99,6 +99,21 @@ int virSetInherit(int fd, bool inherit) return 0; } + +int virSetBlocking(int fd, bool block) +{ + int fflags; + if ((fflags = fcntl(fd, F_GETFL)) < 0) + return -1; + if (block) + fflags &= ~O_NONBLOCK; + else + fflags |= O_NONBLOCK; + if ((fcntl(fd, F_SETFL, fflags)) < 0) + return -1; + return 0; +} + #else /* WIN32 */ int virSetInherit(int fd G_GNUC_UNUSED, bool inherit G_GNUC_UNUSED) @@ -110,13 +125,19 @@ int virSetInherit(int fd G_GNUC_UNUSED, bool inherit G_GNUC_UNUSED) return 0; } -#endif /* WIN32 */ - int virSetBlocking(int fd, bool blocking) { - return set_nonblocking_flag(fd, !blocking); + unsigned long arg = blocking ? 0 : 1; + + if (ioctlsocket(fd, FIONBIO, &arg) < 0) + return -1; + + return 0; } +#endif /* WIN32 */ + + int virSetNonBlock(int fd) { return virSetBlocking(fd, false);