diff options
Diffstat (limited to 'util/oslib-win32.c')
| -rw-r--r-- | util/oslib-win32.c | 350 |
1 files changed, 313 insertions, 37 deletions
diff --git a/util/oslib-win32.c b/util/oslib-win32.c index 07ade41800..16f8a67f7e 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -180,7 +180,7 @@ static int socket_error(void) void qemu_socket_set_block(int fd) { unsigned long opt = 0; - WSAEventSelect(fd, NULL, 0); + qemu_socket_unselect(fd, NULL); ioctlsocket(fd, FIONBIO, &opt); } @@ -283,21 +283,155 @@ char *qemu_get_pid_name(pid_t pid) } -pid_t qemu_fork(Error **errp) +bool qemu_socket_select(int sockfd, WSAEVENT hEventObject, + long lNetworkEvents, Error **errp) { - errno = ENOSYS; - error_setg_errno(errp, errno, - "cannot fork child process"); - return -1; + SOCKET s = _get_osfhandle(sockfd); + + if (errp == NULL) { + errp = &error_warn; + } + + if (s == INVALID_SOCKET) { + error_setg(errp, "invalid socket fd=%d", sockfd); + return false; + } + + if (WSAEventSelect(s, hEventObject, lNetworkEvents) != 0) { + error_setg_win32(errp, WSAGetLastError(), "failed to WSAEventSelect()"); + return false; + } + + return true; } +bool qemu_socket_unselect(int sockfd, Error **errp) +{ + return qemu_socket_select(sockfd, NULL, 0, errp); +} + +int qemu_socketpair(int domain, int type, int protocol, int sv[2]) +{ + struct sockaddr_un addr = { + 0, + }; + socklen_t socklen; + int listener = -1; + int client = -1; + int server = -1; + g_autofree char *path = NULL; + int tmpfd; + u_long arg; + int ret = -1; + + g_return_val_if_fail(sv != NULL, -1); + + addr.sun_family = AF_UNIX; + socklen = sizeof(addr); + + tmpfd = g_file_open_tmp(NULL, &path, NULL); + if (tmpfd == -1 || !path) { + errno = EACCES; + goto out; + } + + close(tmpfd); + + if (strlen(path) >= sizeof(addr.sun_path)) { + errno = EINVAL; + goto out; + } + + strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1); + + listener = socket(domain, type, protocol); + if (listener == -1) { + goto out; + } + + if (DeleteFile(path) == 0 && GetLastError() != ERROR_FILE_NOT_FOUND) { + errno = EACCES; + goto out; + } + g_clear_pointer(&path, g_free); + + if (bind(listener, (struct sockaddr *)&addr, socklen) == -1) { + goto out; + } + + if (listen(listener, 1) == -1) { + goto out; + } + + client = socket(domain, type, protocol); + if (client == -1) { + goto out; + } + + arg = 1; + if (ioctlsocket(client, FIONBIO, &arg) != NO_ERROR) { + goto out; + } + + if (connect(client, (struct sockaddr *)&addr, socklen) == -1 && + WSAGetLastError() != WSAEWOULDBLOCK) { + goto out; + } + + server = accept(listener, NULL, NULL); + if (server == -1) { + goto out; + } + + arg = 0; + if (ioctlsocket(client, FIONBIO, &arg) != NO_ERROR) { + goto out; + } + + arg = 0; + if (ioctlsocket(client, SIO_AF_UNIX_GETPEERPID, &arg) != NO_ERROR) { + goto out; + } + + if (arg != GetCurrentProcessId()) { + errno = EPERM; + goto out; + } + + sv[0] = server; + server = -1; + sv[1] = client; + client = -1; + ret = 0; + +out: + if (listener != -1) { + close(listener); + } + if (client != -1) { + close(client); + } + if (server != -1) { + close(server); + } + if (path) { + DeleteFile(path); + } + return ret; +} #undef connect int qemu_connect_wrap(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int ret; - ret = connect(sockfd, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = connect(s, addr, addrlen); if (ret < 0) { if (WSAGetLastError() == WSAEWOULDBLOCK) { errno = EINPROGRESS; @@ -313,7 +447,13 @@ int qemu_connect_wrap(int sockfd, const struct sockaddr *addr, int qemu_listen_wrap(int sockfd, int backlog) { int ret; - ret = listen(sockfd, backlog); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = listen(s, backlog); if (ret < 0) { errno = socket_error(); } @@ -326,7 +466,13 @@ int qemu_bind_wrap(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int ret; - ret = bind(sockfd, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = bind(s, addr, addrlen); if (ret < 0) { errno = socket_error(); } @@ -334,15 +480,82 @@ int qemu_bind_wrap(int sockfd, const struct sockaddr *addr, } +#undef close +int qemu_close_wrap(int fd) +{ + int ret; + DWORD flags = 0; + SOCKET s = INVALID_SOCKET; + + if (fd_is_socket(fd)) { + s = _get_osfhandle(fd); + + /* + * If we were to just call _close on the descriptor, it would close the + * HANDLE, but it wouldn't free any of the resources associated to the + * SOCKET, and we can't call _close after calling closesocket, because + * closesocket has already closed the HANDLE, and _close would attempt to + * close the HANDLE again, resulting in a double free. We can however + * protect the HANDLE from actually being closed long enough to close the + * file descriptor, then close the socket itself. + */ + if (!GetHandleInformation((HANDLE)s, &flags)) { + errno = EACCES; + return -1; + } + + if (!SetHandleInformation((HANDLE)s, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE)) { + errno = EACCES; + return -1; + } + } + + ret = close(fd); + + if (s != INVALID_SOCKET && !SetHandleInformation((HANDLE)s, flags, flags)) { + errno = EACCES; + return -1; + } + + /* + * close() returns EBADF since we PROTECT_FROM_CLOSE the underlying handle, + * but the FD is actually freed + */ + if (ret < 0 && (s == INVALID_SOCKET || errno != EBADF)) { + return ret; + } + + if (s != INVALID_SOCKET) { + ret = closesocket(s); + if (ret < 0) { + errno = socket_error(); + } + } + + return ret; +} + + #undef socket int qemu_socket_wrap(int domain, int type, int protocol) { - int ret; - ret = socket(domain, type, protocol); - if (ret < 0) { + SOCKET s; + int fd; + + s = socket(domain, type, protocol); + if (s == -1) { errno = socket_error(); + return -1; } - return ret; + + fd = _open_osfhandle(s, _O_BINARY); + if (fd < 0) { + closesocket(s); + /* _open_osfhandle may not set errno, and closesocket() may override it */ + errno = ENOMEM; + } + + return fd; } @@ -350,12 +563,27 @@ int qemu_socket_wrap(int domain, int type, int protocol) int qemu_accept_wrap(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { - int ret; - ret = accept(sockfd, addr, addrlen); - if (ret < 0) { + int fd; + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + s = accept(s, addr, addrlen); + if (s == -1) { errno = socket_error(); + return -1; } - return ret; + + fd = _open_osfhandle(s, _O_BINARY); + if (fd < 0) { + closesocket(s); + /* _open_osfhandle may not set errno, and closesocket() may override it */ + errno = ENOMEM; + } + + return fd; } @@ -363,7 +591,13 @@ int qemu_accept_wrap(int sockfd, struct sockaddr *addr, int qemu_shutdown_wrap(int sockfd, int how) { int ret; - ret = shutdown(sockfd, how); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = shutdown(s, how); if (ret < 0) { errno = socket_error(); } @@ -375,19 +609,13 @@ int qemu_shutdown_wrap(int sockfd, int how) int qemu_ioctlsocket_wrap(int fd, int req, void *val) { int ret; - ret = ioctlsocket(fd, req, val); - if (ret < 0) { - errno = socket_error(); - } - return ret; -} + SOCKET s = _get_osfhandle(fd); + if (s == INVALID_SOCKET) { + return -1; + } -#undef closesocket -int qemu_closesocket_wrap(int fd) -{ - int ret; - ret = closesocket(fd); + ret = ioctlsocket(s, req, val); if (ret < 0) { errno = socket_error(); } @@ -400,7 +628,13 @@ int qemu_getsockopt_wrap(int sockfd, int level, int optname, void *optval, socklen_t *optlen) { int ret; - ret = getsockopt(sockfd, level, optname, optval, optlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = getsockopt(s, level, optname, optval, optlen); if (ret < 0) { errno = socket_error(); } @@ -413,7 +647,13 @@ int qemu_setsockopt_wrap(int sockfd, int level, int optname, const void *optval, socklen_t optlen) { int ret; - ret = setsockopt(sockfd, level, optname, optval, optlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = setsockopt(s, level, optname, optval, optlen); if (ret < 0) { errno = socket_error(); } @@ -426,7 +666,13 @@ int qemu_getpeername_wrap(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int ret; - ret = getpeername(sockfd, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = getpeername(s, addr, addrlen); if (ret < 0) { errno = socket_error(); } @@ -439,7 +685,13 @@ int qemu_getsockname_wrap(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { int ret; - ret = getsockname(sockfd, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = getsockname(s, addr, addrlen); if (ret < 0) { errno = socket_error(); } @@ -451,7 +703,13 @@ int qemu_getsockname_wrap(int sockfd, struct sockaddr *addr, ssize_t qemu_send_wrap(int sockfd, const void *buf, size_t len, int flags) { int ret; - ret = send(sockfd, buf, len, flags); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = send(s, buf, len, flags); if (ret < 0) { errno = socket_error(); } @@ -464,7 +722,13 @@ ssize_t qemu_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *addr, socklen_t addrlen) { int ret; - ret = sendto(sockfd, buf, len, flags, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = sendto(s, buf, len, flags, addr, addrlen); if (ret < 0) { errno = socket_error(); } @@ -476,7 +740,13 @@ ssize_t qemu_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, ssize_t qemu_recv_wrap(int sockfd, void *buf, size_t len, int flags) { int ret; - ret = recv(sockfd, buf, len, flags); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = recv(s, buf, len, flags); if (ret < 0) { errno = socket_error(); } @@ -489,7 +759,13 @@ ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, struct sockaddr *addr, socklen_t *addrlen) { int ret; - ret = recvfrom(sockfd, buf, len, flags, addr, addrlen); + SOCKET s = _get_osfhandle(sockfd); + + if (s == INVALID_SOCKET) { + return -1; + } + + ret = recvfrom(s, buf, len, flags, addr, addrlen); if (ret < 0) { errno = socket_error(); } |