[Spice-devel] [PATCH spice-server 05/33] sys-socket: Introduce some utility to make sockets more portable

Marc-André Lureau marcandre.lureau at gmail.com
Sat Dec 22 10:16:34 UTC 2018


Hi

On Fri, Dec 21, 2018 at 4:03 PM Frediano Ziglio <fziglio at redhat.com> wrote:
>
> Between Unix and Windows socket are quite different:
> - on Windows sockets have a different namespace from C file
>   descriptors so you can't use read/write/close or similar functions;
> - errors are not stored in errno but you must be read/write the
>   errors with specific function;
> - sometimes sockets are put in non-blocking mode automatically
>   calling some functions;
> - SOCKET type is 64 bit on Windows 64 which does not fit technically
>   in an int (although many programs assume it and currently is true).
>

While Windows could use values that don't fit in int, they would break
so many programs that we can safely assume this will never happen:
https://stackoverflow.com/questions/1953639/is-it-safe-to-cast-socket-to-int-under-win64

> So encapsulate the socket APIs in some definition to make easier
> and more safe to deal with them.
> Where the portability to Windows would make to code more offuscated a Unix

obfuscated

> style was preferred. For instance if errors are detected errno is set from
> Windows socket error instead of changing all code handling.
> Fortunately on Windows Qemu core interface accepts socket (but not
> other types like C file descriptors!).
>
> Signed-off-by: Frediano Ziglio <fziglio at redhat.com>
> ---
>  server/Makefile.am  |   2 +
>  server/sys-socket.c | 212 ++++++++++++++++++++++++++++++++++++++++++++
>  server/sys-socket.h | 188 +++++++++++++++++++++++++++++++++++++++
>  3 files changed, 402 insertions(+)
>  create mode 100644 server/sys-socket.c
>  create mode 100644 server/sys-socket.h
>
> diff --git a/server/Makefile.am b/server/Makefile.am
> index 5009d197..3c0ec0c0 100644
> --- a/server/Makefile.am
> +++ b/server/Makefile.am
> @@ -167,6 +167,8 @@ libserver_la_SOURCES =                              \
>         stat.h                                  \
>         stream-channel.c                        \
>         stream-channel.h                        \
> +       sys-socket.h                            \
> +       sys-socket.c                            \
>         red-stream-device.c                     \
>         red-stream-device.h                     \
>         sw-canvas.c                             \
> diff --git a/server/sys-socket.c b/server/sys-socket.c
> new file mode 100644
> index 00000000..7ce5dab1
> --- /dev/null
> +++ b/server/sys-socket.c
> @@ -0,0 +1,212 @@
> +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
> +/*
> +   Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
> +*/
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <sys/types.h>
> +#ifndef _WIN32
> +#include <arpa/inet.h>
> +#include <netinet/in.h>
> +#include <netinet/ip.h>
> +#include <netinet/tcp.h>
> +#include <sys/socket.h>
> +#endif
> +
> +#include <common/log.h>
> +
> +#include "sys-socket.h"
> +
> +#ifdef _WIN32
> +// Map Windows socket errors to standard C ones
> +// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
> +void socket_win32_set_errno(void)
> +{
> +    int err = EPIPE; // default
> +    switch (WSAGetLastError()) {
> +    case WSAEWOULDBLOCK:
> +    case WSAEINPROGRESS:
> +        err = EAGAIN;
> +        break;
> +    case WSAEINTR:
> +        err = EINTR;
> +        break;
> +    case WSAEBADF:
> +        err = EBADF;
> +        break;
> +    case WSA_INVALID_HANDLE:
> +    case WSA_INVALID_PARAMETER:
> +    case WSAEINVAL:
> +        err = EINVAL;
> +        break;
> +    case WSAENOTSOCK:
> +        err = ENOTSOCK;
> +        break;
> +    case WSA_NOT_ENOUGH_MEMORY:
> +        err = ENOMEM;
> +        break;
> +    case WSAEPROTONOSUPPORT:
> +    case WSAESOCKTNOSUPPORT:
> +    case WSAEOPNOTSUPP:
> +    case WSAEPFNOSUPPORT:
> +    case WSAEAFNOSUPPORT:
> +    case WSAVERNOTSUPPORTED:
> +        err = ENOTSUP;
> +        break;
> +    case WSAEFAULT:
> +        err = EFAULT;
> +        break;
> +    case WSAEACCES:
> +        err = EACCES;
> +        break;
> +    case WSAEMFILE:
> +        err = EMFILE;
> +        break;
> +    case WSAENAMETOOLONG:
> +        err = ENAMETOOLONG;
> +        break;
> +    case WSAENOTEMPTY:
> +        err = ENOTEMPTY;
> +        break;
> +    case WSA_OPERATION_ABORTED:
> +    case WSAECANCELLED:
> +    case WSA_E_CANCELLED:
> +        err = ECANCELED;
> +        break;
> +    case WSAEADDRINUSE:
> +        err = EADDRINUSE;
> +        break;
> +    case WSAENETDOWN:
> +        err = ENETDOWN;
> +        break;
> +    case WSAENETUNREACH:
> +        err = ENETUNREACH;
> +        break;
> +    case WSAENETRESET:
> +        err = ENETRESET;
> +        break;
> +    case WSAECONNABORTED:
> +        err = ECONNABORTED;
> +        break;
> +    case WSAECONNRESET:
> +        err = ECONNRESET;
> +        break;
> +    case WSAEISCONN:
> +        err = EISCONN;
> +        break;
> +    case WSAENOTCONN:
> +        err = ENOTCONN;
> +        break;
> +    case WSAETIMEDOUT:
> +        err = ETIMEDOUT;
> +        break;
> +    case WSAECONNREFUSED:
> +        err = ECONNREFUSED;
> +        break;
> +    case WSAEHOSTUNREACH:
> +        err = EHOSTUNREACH;
> +        break;
> +    case WSAEDESTADDRREQ:
> +        err = EDESTADDRREQ;
> +        break;
> +    case WSAEMSGSIZE:
> +        err = EMSGSIZE;
> +        break;
> +    case WSAEPROTOTYPE:
> +        err = EPROTOTYPE;
> +        break;
> +    case WSAENOPROTOOPT:
> +        err = ENOPROTOOPT;
> +        break;
> +    case WSAEADDRNOTAVAIL:
> +        err = EADDRNOTAVAIL;
> +        break;
> +    case WSAENOBUFS:
> +        err = ENOBUFS;
> +        break;
> +    // TODO
> +    case WSAESTALE:
> +    case WSAEDISCON:
> +    case WSA_IO_INCOMPLETE:
> +    case WSA_IO_PENDING:
> +    case WSAEALREADY:
> +    case WSAESHUTDOWN:
> +    case WSAETOOMANYREFS:
> +    case WSAELOOP:
> +    case WSAEHOSTDOWN:
> +    case WSAEPROCLIM:
> +    case WSAEUSERS:
> +    case WSAEDQUOT:
> +    case WSAEREMOTE:
> +    case WSASYSNOTREADY:
> +    case WSANOTINITIALISED:
> +    case WSAENOMORE:
> +    case WSAEINVALIDPROCTABLE:
> +    case WSAEINVALIDPROVIDER:
> +    case WSAEPROVIDERFAILEDINIT:
> +    case WSASYSCALLFAILURE:
> +    case WSASERVICE_NOT_FOUND:
> +    case WSATYPE_NOT_FOUND:
> +    case WSA_E_NO_MORE:
> +    case WSAEREFUSED:
> +    case WSAHOST_NOT_FOUND:
> +    case WSATRY_AGAIN:
> +    case WSANO_RECOVERY:
> +    case WSANO_DATA:
> +    case WSA_QOS_RECEIVERS:
> +    case WSA_QOS_SENDERS:
> +    case WSA_QOS_NO_SENDERS:
> +    case WSA_QOS_NO_RECEIVERS:
> +    case WSA_QOS_REQUEST_CONFIRMED:
> +    case WSA_QOS_ADMISSION_FAILURE:
> +    case WSA_QOS_POLICY_FAILURE:
> +    case WSA_QOS_BAD_STYLE:
> +    case WSA_QOS_BAD_OBJECT:
> +    case WSA_QOS_TRAFFIC_CTRL_ERROR:
> +    case WSA_QOS_GENERIC_ERROR:
> +    case WSA_QOS_ESERVICETYPE:
> +    case WSA_QOS_EFLOWSPEC:
> +    case WSA_QOS_EPROVSPECBUF:
> +    case WSA_QOS_EFILTERSTYLE:
> +    case WSA_QOS_EFILTERTYPE:
> +    case WSA_QOS_EFILTERCOUNT:
> +    case WSA_QOS_EOBJLENGTH:
> +    case WSA_QOS_EFLOWCOUNT:
> +    case WSA_QOS_EUNKOWNPSOBJ:
> +    case WSA_QOS_EPOLICYOBJ:
> +    case WSA_QOS_EFLOWDESC:
> +    case WSA_QOS_EPSFLOWSPEC:
> +    case WSA_QOS_EPSFILTERSPEC:
> +    case WSA_QOS_ESDMODEOBJ:
> +    case WSA_QOS_ESHAPERATEOBJ:
> +    case WSA_QOS_RESERVED_PETYPE:
> +        break;
> +    }
> +    errno = err;
> +}
> +
> +SPICE_CONSTRUCTOR_FUNC(socket_win32_init)
> +{
> +    WSADATA wsaData;
> +    WSAStartup(MAKEWORD(2, 2), &wsaData);
> +}
> +#endif
> diff --git a/server/sys-socket.h b/server/sys-socket.h
> new file mode 100644
> index 00000000..c21e9a69
> --- /dev/null
> +++ b/server/sys-socket.h
> @@ -0,0 +1,188 @@
> +/*
> +   Copyright (C) 2018 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 <http://www.gnu.org/licenses/>.
> +*/
> +
> +/* Small compatibility layer for sockets, mostly to make easier portability
> + * for Windows but without loosing performances under Unix, the most supported
> + * system */
> +#ifndef RED_SYS_SOCKET_H_
> +#define RED_SYS_SOCKET_H_
> +
> +#include <stdbool.h>
> +
> +#ifndef _WIN32
> +#  include <sys/socket.h>
> +
> +#if 0

should be removed

> +typedef struct {
> +    int fd;
> +} socket_t;
> +
> +static inline int socket_get_raw(socket_t sock)
> +{
> +    return sock.fd;
> +}
> +#define SOCKET_INVALID ((socket_t){-1})
> +#define SOCKET_FROM_INT(fd) ((socket_t){fd})
> +#else
> +typedef int socket_t;

Imho, it's not a great idea to introduce socket_t.

Everybody uses int. It's a mistake from Microsoft (actually winsock)
to use a type that isn't compatible with int (berkley/posix api).
They will never break compatibility with int, because most programs
would start breaking. (there are various sources of that claim on the
internet).

Please avoid _t, it may conflict with system types, it is reserved:
(https://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html)

Please reconsider using plain int (btw, that's what qemu and glib use
two to store SOCKET, so Spice is doomed)

> +static inline int socket_get_raw(socket_t sock)
> +{
> +    return sock;
> +}
> +#define SOCKET_INVALID (-1)
> +#define SOCKET_FROM_INT(fd) (fd)
> +#endif
> +
> +static inline bool socket_is_valid(socket_t sock)
> +{
> +    return socket_get_raw(sock) >= 0;
> +}
> +
> +static inline void socket_win32_set_errno(void)
> +{
> +}
> +
> +#define socket_read(sock, buf, len) read(socket_get_raw(sock), buf, len)
> +#define socket_write(sock, buf, len) write(socket_get_raw(sock), buf, len)
> +#define socket_writev(sock, iov, n) writev(socket_get_raw(sock), iov, n)
> +#define socket_close(sock) close(socket_get_raw(sock))
> +#define socket_getopt(sock, lvl, type, value, len) \
> +    getsockopt(socket_get_raw(sock), lvl, type, value, len)
> +#define socket_setopt(sock, lvl, type, value, len) \
> +    setsockopt(socket_get_raw(sock), lvl, type, value, len)
> +#define socket_listen(sock, num) \
> +    listen(socket_get_raw(sock), num)
> +
> +#else
> +#  include <winsock2.h>
> +#  include <windows.h>
> +typedef int socklen_t;
> +
> +#if ENABLE_EXTRA_CHECKS

same

> +typedef struct {
> +    SOCKET fd;
> +} socket_t;
> +
> +static inline int socket_get_raw(socket_t sock)
> +{
> +    return sock.fd;
> +}
> +
> +#define SOCKET_INVALID ((socket_t){INVALID_SOCKET})
> +#define SOCKET_FROM_INT(fd) ((socket_t){fd})
> +#else
> +typedef SOCKET socket_t;
> +
> +static inline int socket_get_raw(socket_t sock)
> +{
> +    return sock;
> +}
> +
> +#define SOCKET_INVALID INVALID_SOCKET
> +#endif
> +
> +// this definition is ABI compatible with WSABUF
> +struct iovec {
> +    u_long iov_len;
> +    void FAR *iov_base;
> +};
> +
> +void socket_win32_set_errno(void);
> +
> +static inline ssize_t socket_read(socket_t sock, void *buf, size_t count)
> +{
> +    ssize_t res = recv(socket_get_raw(sock), buf, count, 0);
> +    if (res < 0) {
> +        socket_win32_set_errno();
> +    }
> +    return res;
> +}
> +
> +static inline ssize_t socket_write(socket_t sock, const void *buf, size_t count)
> +{
> +    ssize_t res = send(socket_get_raw(sock), buf, count, 0);
> +    if (res < 0) {
> +        socket_win32_set_errno();
> +    }
> +    return res;
> +}
> +
> +static inline ssize_t socket_writev(socket_t sock, const struct iovec *iov, int n_iov)
> +{
> +    DWORD sent;
> +    int res = WSASend(socket_get_raw(sock), (LPWSABUF) iov, n_iov, &sent, 0, NULL, NULL);
> +    if (res) {
> +        socket_win32_set_errno();
> +        return -1;
> +    }
> +    return sent;
> +}
> +
> +#define socket_close(sock) closesocket(socket_get_raw(sock))
> +
> +static inline bool socket_is_valid(socket_t sock)
> +{
> +    return socket_get_raw(sock) != INVALID_SOCKET;
> +}
> +
> +#define SHUT_RDWR SD_BOTH
> +
> +static inline int
> +socket_getopt(socket_t sock, int lvl, int type, void *value, socklen_t *len)
> +{
> +    int res = getsockopt(socket_get_raw(sock), lvl, type, value, len);
> +    if (res < 0) {
> +        socket_win32_set_errno();
> +    }
> +    return res;
> +}
> +
> +static inline int
> +socket_setopt(socket_t sock, int lvl, int type, const void *value, socklen_t len)
> +{
> +    int res = setsockopt(socket_get_raw(sock), lvl, type, value, len);
> +    if (res < 0) {
> +        socket_win32_set_errno();
> +    }
> +    return res;
> +}
> +
> +static inline int
> +socket_listen(socket_t sock, int backlog)
> +{
> +    int res = listen(socket_get_raw(sock), backlog);
> +    if (res < 0) {
> +        socket_win32_set_errno();
> +    }
> +    return res;
> +}
> +#endif
> +
> +// common part
> +//
> +
> +#define socket_getpeername(sock, name, len) \
> +    getpeername(socket_get_raw(sock), name, len)
> +#define socket_getsockname(sock, name, len) \
> +    getsockname(socket_get_raw(sock), name, len)
> +#define socket_new(af, type, flags) \
> +    SOCKET_FROM_INT(socket(af, type, flags))
> +// FIXME windows, set error!
> +#define socket_bind(sock, addr, len) \
> +    bind(socket_get_raw(sock), addr, len)
> +
> +#endif // RED_SYS_SOCKET_H_
> --
> 2.17.2
>

Looks ok overall.

It's a bit sad that Spice doesn't use GIO instead, GSocket would do
most of this, and more.
There is also GIOChannel, although it's a bit more archaic,
g_io_channel_win32_new_socket() etc..



--
Marc-André Lureau


More information about the Spice-devel mailing list