diff --git a/src/xrt/drivers/remote/r_hub.c b/src/xrt/drivers/remote/r_hub.c index 0f985beab..69f824c7f 100644 --- a/src/xrt/drivers/remote/r_hub.c +++ b/src/xrt/drivers/remote/r_hub.c @@ -1,4 +1,4 @@ -// Copyright 2020-2022, Collabora, Ltd. +// Copyright 2020-2023, Collabora, Ltd. // SPDX-License-Identifier: BSL-1.0 /*! * @file @@ -17,22 +17,34 @@ #include #include #include -#include +#if defined(XRT_OS_WINDOWS) +#include +#include +#include + +#pragma comment(lib, "ws2_32.lib") +#else +#include #include #include #include #include #include +#include + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE // same, but for musl // NOLINT +#endif +#endif + +#ifndef SOCKET +#define SOCKET int +#endif #ifndef __USE_MISC #define __USE_MISC // SOL_TCP on C11 #endif -#ifndef _BSD_SOURCE -#define _BSD_SOURCE // same, but for musl // NOLINT -#endif - -#include /* @@ -62,27 +74,100 @@ DEBUG_GET_ONCE_LOG_OPTION(remote_log, "REMOTE_LOG", U_LOGGING_INFO) * */ + + +#if defined(XRT_OS_WINDOWS) +static void +socket_close(SOCKET id) +{ + closesocket(id); +} + +static SOCKET +socket_create(void) +{ + return socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); +} + +static int +socket_set_opt(SOCKET id, int flag) +{ + return setsockopt(id, SOL_SOCKET, SO_REUSEADDR, (const char *)&flag, sizeof(flag)); +} + +static ssize_t +socket_read(SOCKET id, void *ptr, size_t size, size_t current) +{ + return recv(id, (char *)ptr, size - current, 0); +} + +static size_t +socket_write(SOCKET id, void *ptr, size_t size, size_t current) +{ + return send(id, (const char *)ptr, size - current, 0); +} + +#else +static void +socket_close(SOCKET id) +{ + close(id); +} + +static SOCKET +socket_create(void) +{ + return socket(AF_INET, SOCK_STREAM, 0); +} + +static int +socket_set_opt(SOCKET id, int flag) +{ + return setsockopt(id, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); +} + +static ssize_t +socket_read(SOCKET id, void *ptr, size_t size, size_t current) +{ + return read(id, ptr, size - current); +} + +static ssize_t +socket_write(SOCKET id, void *ptr, size_t size, size_t current) +{ + return write(id, ptr, size - current); +} +#endif + static int setup_accept_fd(struct r_hub *r) { struct sockaddr_in server_address = {0}; - int ret; +#if defined(XRT_OS_WINDOWS) + // Initialize Winsock. + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + int error = WSAGetLastError(); + R_ERROR(r, "Failed to do WSAStartup %ld", error); + return error; + } +#endif + SOCKET ret = socket_create(); - ret = socket(AF_INET, SOCK_STREAM, 0); if (ret < 0) { R_ERROR(r, "socket: %i", ret); - return ret; + goto cleanup; } r->accept_fd = ret; int flag = 1; - ret = setsockopt(r->accept_fd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)); + ret = socket_set_opt(r->accept_fd, flag); if (ret < 0) { R_ERROR(r, "setsockopt: %i", ret); - close(r->accept_fd); + socket_close(r->accept_fd); r->accept_fd = -1; - return ret; + goto cleanup; } server_address.sin_family = AF_INET; @@ -92,18 +177,25 @@ setup_accept_fd(struct r_hub *r) ret = bind(r->accept_fd, (struct sockaddr *)&server_address, sizeof(server_address)); if (ret < 0) { R_ERROR(r, "bind: %i", ret); - close(r->accept_fd); + socket_close(r->accept_fd); r->accept_fd = -1; - return ret; + goto cleanup; } + R_INFO(r, "Listen address %s on port %d", inet_ntoa(server_address.sin_addr), r->port); + listen(r->accept_fd, 5); return 0; +cleanup: +#if defined(XRT_OS_WINDOWS) + WSACleanup(); +#endif + return ret; } static bool -wait_for_read_and_to_continue(struct r_hub *r, int socket) +wait_for_read_and_to_continue(struct r_hub *r, SOCKET socket) { fd_set set; int ret = 0; @@ -139,9 +231,8 @@ do_accept(struct r_hub *r) { struct sockaddr_in addr = {0}; int ret = 0; - int conn_fd; - if (!wait_for_read_and_to_continue(r, r->accept_fd)) { + R_ERROR(r, "Failed to wait for id %d", r->accept_fd); return -1; } @@ -152,13 +243,13 @@ do_accept(struct r_hub *r) return ret; } - conn_fd = ret; + SOCKET conn_fd = ret; int flags = 1; - ret = setsockopt(conn_fd, SOL_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); + ret = socket_set_opt(r->accept_fd, flags); if (ret < 0) { R_ERROR(r, "setsockopt: %i", ret); - close(conn_fd); + socket_close(conn_fd); return ret; } @@ -184,9 +275,13 @@ read_one(struct r_hub *r, struct r_remote_data *data) return -1; } - ssize_t ret = read(rc->fd, ptr, size - current); + ssize_t ret = socket_read(rc->fd, ptr, size, current); if (ret < 0) { +#if defined(XRT_OS_WINDOWS) + RC_ERROR(rc, "recv: %zi", WSAGetLastError()); +#else RC_ERROR(rc, "read: %zi", ret); +#endif return ret; } else if (ret > 0) { current += (size_t)ret; @@ -257,7 +352,7 @@ r_hub_system_devices_destroy(struct xrt_system_devices *xsysd) // Should be safe to destroy the sockets now. if (r->accept_fd >= 0) { - close(r->accept_fd); + socket_close(r->accept_fd); r->accept_fd = -1; } @@ -267,6 +362,11 @@ r_hub_system_devices_destroy(struct xrt_system_devices *xsysd) } free(r); + +#if defined(XRT_OS_WINDOWS) + // Clean up Winsock. + WSACleanup(); +#endif } @@ -359,7 +459,6 @@ r_create_devices(uint16_t port, struct xrt_system_devices **out_xsysd) u_var_add_bool(r, &r->latest.right.active, "right.active"); u_var_add_pose(r, &r->latest.right.pose, "right.pose"); - /* * Done now. */ @@ -386,42 +485,75 @@ r_remote_connection_init(struct r_remote_connection *rc, const char *ip_addr, ui // Set log level. rc->log_level = debug_get_log_option_remote_log(); +#if defined(XRT_OS_WINDOWS) + // Initialize Winsock. + WSADATA wsaData; + ret = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (ret != 0) { + RC_ERROR(rc, "Failed to do WSAStartup %ld", WSAGetLastError()); + return ret; + } +#endif + // Address addr.sin_family = AF_INET; addr.sin_port = htons(port); - ret = inet_pton(AF_INET, ip_addr, &addr.sin_addr); + // inet_pton/InetPton resolves "localhost" as 0.0.0.0 or 255.255.255.255, and it causes connection error. To + // avoid this issue, the following logic converts "localhost" to "127.0.0.1" first. + if (strcmp("localhost", ip_addr) == 0) { + ret = inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr); + } else { + ret = inet_pton(AF_INET, ip_addr, &addr.sin_addr); + } if (ret < 0) { - RC_ERROR(rc, "inet_pton: %i", ret); - return ret; + RC_ERROR(rc, "Failed to do inet pton for %s: %i", ip_addr, ret); + goto cleanup; } - ret = socket(AF_INET, SOCK_STREAM, 0); + ret = socket_create(); if (ret < 0) { - RC_ERROR(rc, "socket: %i", ret); - return ret; +#if defined(XRT_OS_WINDOWS) + RC_ERROR(rc, "Failed to create socket %ld", WSAGetLastError()); +#else + RC_ERROR(rc, "Failed to create socket: %i", ret); +#endif + goto cleanup; } conn_fd = ret; ret = connect(conn_fd, (struct sockaddr *)&addr, sizeof(addr)); - if (ret < 0) { - RC_ERROR(rc, "connect: %i", ret); - close(conn_fd); - return ret; + // If connect operation succeed, both Windows and POSIX returns 0. + if (ret != 0) { +#if defined(XRT_OS_WINDOWS) + RC_ERROR(rc, "Failed to connect id %d and addr %s with failure %d", conn_fd, inet_ntoa(addr.sin_addr), + WSAGetLastError()); +#else + RC_ERROR(rc, "Failed to connect id %d and addr %s with failure %d", conn_fd, inet_ntoa(addr.sin_addr), + ret); +#endif + socket_close(conn_fd); + goto cleanup; } int flags = 1; - ret = setsockopt(conn_fd, SOL_TCP, TCP_NODELAY, (void *)&flags, sizeof(flags)); + ret = socket_set_opt(conn_fd, flags); if (ret < 0) { - RC_ERROR(rc, "setsockopt: %i", ret); - close(conn_fd); - return ret; + RC_ERROR(rc, "Failed to setsockopt: %i", ret); + socket_close(conn_fd); + goto cleanup; } rc->fd = conn_fd; return 0; + +cleanup: +#if defined(XRT_OS_WINDOWS) + WSACleanup(); +#endif + return ret; } int @@ -432,8 +564,7 @@ r_remote_connection_read_one(struct r_remote_connection *rc, struct r_remote_dat while (current < size) { void *ptr = (uint8_t *)data + current; - - ssize_t ret = read(rc->fd, ptr, size - current); + ssize_t ret = socket_read(rc->fd, ptr, size, current); if (ret < 0) { RC_ERROR(rc, "read: %zi", ret); return ret; @@ -456,9 +587,9 @@ r_remote_connection_write_one(struct r_remote_connection *rc, const struct r_rem size_t current = 0; while (current < size) { - const void *ptr = (const uint8_t *)data + current; + void *ptr = (uint8_t *)data + current; - ssize_t ret = write(rc->fd, ptr, size - current); + ssize_t ret = socket_write(rc->fd, ptr, size, current); if (ret < 0) { RC_ERROR(rc, "write: %zi", ret); return ret;