From e06a042c1c0eccefeccc63e419b9b09bef11e28f Mon Sep 17 00:00:00 2001 From: Victor Toso Date: Wed, 30 Nov 2022 21:02:20 +0100 Subject: [PATCH] Revert "remove usbredirserver" Content-type: text/plain This reverts commit f4ffdce329305da2803684776f7659083a530819. --- README.md | 4 + meson.build | 1 + usbredirserver/meson.build | 12 + usbredirserver/usbredirserver.1 | 41 +++ usbredirserver/usbredirserver.c | 474 ++++++++++++++++++++++++++++++++ 5 files changed, 532 insertions(+) create mode 100644 usbredirserver/meson.build create mode 100644 usbredirserver/usbredirserver.1 create mode 100644 usbredirserver/usbredirserver.c diff --git a/README.md b/README.md index ed04cca..2acb7b0 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ The usbredirect binary is an usbredir client for exporting an USB device either as TCP client or server, for use from another (virtual) machine through the usbredir protocol. +## usbredirserver + +A simple tcp server usb-host, using usbredirhost + ## usbredirtestclient A small testclient for the usbredir protocol over tcp, using usbredirparser diff --git a/meson.build b/meson.build index aac9909..e740778 100644 --- a/meson.build +++ b/meson.build @@ -103,6 +103,7 @@ if get_option('tools').enabled() subdir('tools') endif if host_machine.system() != 'windows' + subdir('usbredirserver') subdir('usbredirtestclient') if get_option('fuzzing').enabled() diff --git a/usbredirserver/meson.build b/usbredirserver/meson.build new file mode 100644 index 0000000..de40681 --- /dev/null +++ b/usbredirserver/meson.build @@ -0,0 +1,12 @@ +usbredirserver_sources = [ + 'usbredirserver.c', +] + +executable('usbredirserver', + sources : usbredirserver_sources, + c_args : '-Wno-deprecated-declarations', + install : true, + install_dir: get_option('sbindir'), + dependencies : usbredir_host_lib_dep) + +install_man('usbredirserver.1') diff --git a/usbredirserver/usbredirserver.1 b/usbredirserver/usbredirserver.1 new file mode 100644 index 0000000..d5a6793 --- /dev/null +++ b/usbredirserver/usbredirserver.1 @@ -0,0 +1,41 @@ +.TH USBREDIRSERVER "1" "April 2012" "usbredirserver" "User Commands" +.SH NAME +usbredirserver \- exporting an USB device for use from another (virtual) machine +.SH SYNOPSIS +.B usbredirserver +[\fI-p|--port \fR] [\fI-v|--verbose <0-5>\fR] [\fI-4 ] +\fI\fR +.SH DESCRIPTION +usbredirserver is a small standalone server for exporting an USB device for +use from another (virtual) machine through the usbredir protocol. +.PP +You can specify the USB device to export either by USB id in the form of +\fI:\fR, or by USB bus number and device address in the form +of \fI-\fR. +.PP +Notice that an instance of usbredirserver can only be used to export a +single USB device. If you want to export multiple devices you can start +multiple instances listening on different TCP ports. +.SH OPTIONS +.TP +\fB\-p\fR, \fB\-\-port\fR=\fIPORT\fR +Set the TCP port to listen on to \fIPORT\fR +.TP +\fB\-v\fR, \fB\-\-verbose\fR=\fIVERBOSE\fR +Set usbredirserver's verbosity level to \fIVERBOSE\fR, this mostly affects USB +redirection related messages. Valid values are 0-5: +.br +0:Silent 1:Errors 2:Warnings 3:Info 4:Debug 5:Debug++ +.SH AUTHOR +Written by Hans de Goede +.SH REPORTING BUGS +You can report bugs to the spice-devel mailinglist: +http://lists.freedesktop.org/mailman/listinfo/spice-devel +or filing an issue at: +https://gitlab.freedesktop.org/spice/usbredir/issues/new +.SH COPYRIGHT +Copyright 2010-2012 Red Hat, Inc. +License GPLv2+: GNU GPL version 2 or later . +.br +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. diff --git a/usbredirserver/usbredirserver.c b/usbredirserver/usbredirserver.c new file mode 100644 index 0000000..badb7bc --- /dev/null +++ b/usbredirserver/usbredirserver.c @@ -0,0 +1,474 @@ +/* usbredirserver.c simple usb network redirection tcp/ip server (host). + + Copyright 2010-2011 Red Hat, Inc. + + Red Hat Authors: + Hans de Goede + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program 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 + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this library; if not, see . +*/ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbredirhost.h" + + +#define SERVER_VERSION "usbredirserver " PACKAGE_VERSION + +#if !defined(SOL_TCP) && defined(IPPROTO_TCP) +#define SOL_TCP IPPROTO_TCP +#endif +#if !defined(TCP_KEEPIDLE) && defined(TCP_KEEPALIVE) && defined(__APPLE__) +#define TCP_KEEPIDLE TCP_KEEPALIVE +#endif + +static int verbose = usbredirparser_info; +static int client_fd, running = 1; +static libusb_context *ctx; +static struct usbredirhost *host; + +static const struct option longopts[] = { + { "port", required_argument, NULL, 'p' }, + { "verbose", required_argument, NULL, 'v' }, + { "ipv4", required_argument, NULL, '4' }, + { "ipv6", required_argument, NULL, '6' }, + { "keepalive", required_argument, NULL, 'k' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } +}; + +static void usbredirserver_log(void *priv, int level, const char *msg) +{ + if (level <= verbose) + fprintf(stderr, "%s\n", msg); +} + +static int usbredirserver_read(void *priv, uint8_t *data, int count) +{ + int r = read(client_fd, data, count); + if (r < 0) { + if (errno == EAGAIN) + return 0; + return -1; + } + if (r == 0) { /* Client disconnected */ + close(client_fd); + client_fd = -1; + } + return r; +} + +static int usbredirserver_write(void *priv, uint8_t *data, int count) +{ + int r = write(client_fd, data, count); + if (r < 0) { + if (errno == EAGAIN) + return 0; + if (errno == EPIPE) { /* Client disconnected */ + close(client_fd); + client_fd = -1; + return 0; + } + return -1; + } + return r; +} + +static void usage(int exit_code, char *argv0) +{ + fprintf(exit_code? stderr:stdout, + "Usage: %s [-p|--port ] [-v|--verbose <0-5>] " + "[[-4|--ipv4 ipaddr]|[-6|--ipv6 ipaddr]] " + "[-k|--keepalive seconds] " + "\n", + argv0); + exit(exit_code); +} + +static void invalid_usb_device_id(char *usb_device_id, char *argv0) +{ + fprintf(stderr, "Invalid usb device identifier: %s\n", usb_device_id); + usage(1, argv0); +} + +static void run_main_loop(void) +{ + const struct libusb_pollfd **pollfds = NULL; + fd_set readfds, writefds; + int i, n, nfds; + struct timeval timeout, *timeout_p; + + while (running && client_fd != -1) { + FD_ZERO(&readfds); + FD_ZERO(&writefds); + + FD_SET(client_fd, &readfds); + if (usbredirhost_has_data_to_write(host)) { + FD_SET(client_fd, &writefds); + } + nfds = client_fd + 1; + + free(pollfds); + pollfds = libusb_get_pollfds(ctx); + for (i = 0; pollfds && pollfds[i]; i++) { + if (pollfds[i]->events & POLLIN) { + FD_SET(pollfds[i]->fd, &readfds); + } + if (pollfds[i]->events & POLLOUT) { + FD_SET(pollfds[i]->fd, &writefds); + } + if (pollfds[i]->fd >= nfds) + nfds = pollfds[i]->fd + 1; + } + + if (libusb_get_next_timeout(ctx, &timeout) == 1) { + timeout_p = &timeout; + } else { + timeout_p = NULL; + } + n = select(nfds, &readfds, &writefds, NULL, timeout_p); + if (n == -1) { + if (errno == EINTR) { + continue; + } + perror("select"); + break; + } + memset(&timeout, 0, sizeof(timeout)); + if (n == 0) { + libusb_handle_events_timeout(ctx, &timeout); + continue; + } + + if (FD_ISSET(client_fd, &readfds)) { + if (usbredirhost_read_guest_data(host)) { + break; + } + } + /* usbredirhost_read_guest_data may have detected client disconnect */ + if (client_fd == -1) + break; + + if (FD_ISSET(client_fd, &writefds)) { + if (usbredirhost_write_guest_data(host)) { + break; + } + } + + for (i = 0; pollfds && pollfds[i]; i++) { + if (FD_ISSET(pollfds[i]->fd, &readfds) || + FD_ISSET(pollfds[i]->fd, &writefds)) { + libusb_handle_events_timeout(ctx, &timeout); + break; + } + } + } + if (client_fd != -1) { /* Broken out of the loop because of an error ? */ + close(client_fd); + client_fd = -1; + } + free(pollfds); +} + +static void quit_handler(int sig) +{ + running = 0; +} + +int main(int argc, char *argv[]) +{ + int o, flags, server_fd = -1; + char *endptr, *delim; + int port = 4000; + int usbbus = -1; + int usbaddr = -1; + int usbvendor = -1; + int usbproduct = -1; + int on = 1; + int keepalive = -1; + char *ipv4_addr = NULL, *ipv6_addr = NULL; + union { + struct sockaddr_in v4; + struct sockaddr_in6 v6; + } serveraddr; + struct sigaction act; + libusb_device_handle *handle = NULL; + + while ((o = getopt_long(argc, argv, "hp:v:4:6:k:", longopts, NULL)) != -1) { + switch (o) { + case 'p': + port = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + fprintf(stderr, "Invalid value for --port: '%s'\n", optarg); + usage(1, argv[0]); + } + break; + case 'v': + verbose = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + fprintf(stderr, "Invalid value for --verbose: '%s'\n", optarg); + usage(1, argv[0]); + } + break; + case '4': + ipv4_addr = optarg; + break; + case '6': + ipv6_addr = optarg; + break; + case 'k': + keepalive = strtol(optarg, &endptr, 10); + if (*endptr != '\0') { + fprintf(stderr, "Invalid value for -k: '%s'\n", optarg); + usage(1, argv[0]); + } + break; + case '?': + case 'h': + usage(o == '?', argv[0]); + break; + } + } + if (optind == argc) { + fprintf(stderr, "Missing usb device identifier argument\n"); + usage(1, argv[0]); + } + delim = strchr(argv[optind], '-'); + if (delim && delim[1]) { + usbbus = strtol(argv[optind], &endptr, 10); + if (*endptr != '-') { + invalid_usb_device_id(argv[optind], argv[0]); + } + usbaddr = strtol(delim + 1, &endptr, 10); + if (*endptr != '\0') { + invalid_usb_device_id(argv[optind], argv[0]); + } + } else { + delim = strchr(argv[optind], ':'); + if (!delim || !delim[1]) { + invalid_usb_device_id(argv[optind], argv[0]); + } + usbvendor = strtol(argv[optind], &endptr, 16); + if (*endptr != ':' || usbvendor <= 0 || usbvendor > 0xffff) { + invalid_usb_device_id(argv[optind], argv[0]); + } + usbproduct = strtol(delim + 1, &endptr, 16); + /* Product ID 0000 is valid */ + if (*endptr != '\0' || usbproduct < 0 || usbproduct > 0xffff) { + invalid_usb_device_id(argv[optind], argv[0]); + } + } + optind++; + if (optind != argc) { + fprintf(stderr, "Excess non option arguments\n"); + usage(1, argv[0]); + } + + memset(&act, 0, sizeof(act)); + act.sa_handler = quit_handler; + sigaction(SIGINT, &act, NULL); + sigaction(SIGHUP, &act, NULL); + sigaction(SIGTERM, &act, NULL); + sigaction(SIGQUIT, &act, NULL); + + if (libusb_init(&ctx)) { + fprintf(stderr, "Could not init libusb\n"); + exit(1); + } + +#if LIBUSB_API_VERSION >= 0x01000106 + libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, verbose); +#else + libusb_set_debug(ctx, verbose); +#endif + + if (ipv4_addr) { + server_fd = socket(AF_INET, SOCK_STREAM, 0); + } else { + server_fd = socket(AF_INET6, SOCK_STREAM, 0); + } + if (server_fd == -1) { + perror("Error creating ip socket"); + exit(1); + } + + if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) { + perror("Error setsockopt(SO_REUSEADDR) failed"); + exit(1); + } + + memset(&serveraddr, 0, sizeof(serveraddr)); + + if (ipv4_addr) { + serveraddr.v4.sin_family = AF_INET; + serveraddr.v4.sin_port = htons(port); + if ((inet_pton(AF_INET, ipv4_addr, + &serveraddr.v4.sin_addr)) != 1) { + perror("Error convert ipv4 address"); + exit(1); + } + } else { + serveraddr.v6.sin6_family = AF_INET6; + serveraddr.v6.sin6_port = htons(port); + if (ipv6_addr) { + if ((inet_pton(AF_INET6, ipv6_addr, + &serveraddr.v6.sin6_addr)) != 1) { + perror("Error convert ipv6 address"); + exit(1); + } + } else { + serveraddr.v6.sin6_addr = in6addr_any; + } + } + + if (bind(server_fd, (struct sockaddr *)&serveraddr, + sizeof(serveraddr))) { + perror("Error bind"); + exit(1); + } + + if (listen(server_fd, 1)) { + perror("Error listening"); + exit(1); + } + + while (running) { + client_fd = accept(server_fd, NULL, 0); + if (client_fd == -1) { + if (errno == EINTR) { + continue; + } + perror("accept"); + break; + } + + if (keepalive > 0) { + int optval = 1; + socklen_t optlen = sizeof(optval); + if (setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) == -1) { + if (errno != ENOTSUP) { + perror("setsockopt SO_KEEPALIVE error."); + break; + } + } + optval = keepalive; /* set default TCP_KEEPIDLE time from cmdline */ + if (setsockopt(client_fd, SOL_TCP, TCP_KEEPIDLE, &optval, optlen) == -1) { + if (errno != ENOTSUP) { + perror("setsockopt TCP_KEEPIDLE error."); + break; + } + } + optval = 10; /* set default TCP_KEEPINTVL time as 10s */ + if (setsockopt(client_fd, SOL_TCP, TCP_KEEPINTVL, &optval, optlen) == -1) { + if (errno != ENOTSUP) { + perror("setsockopt TCP_KEEPINTVL error."); + break; + } + } + optval = 3; /* set default TCP_KEEPCNT as 3 */ + if (setsockopt(client_fd, SOL_TCP, TCP_KEEPCNT, &optval, optlen) == -1) { + if (errno != ENOTSUP) { + perror("setsockopt TCP_KEEPCNT error."); + break; + } + } + } + + flags = fcntl(client_fd, F_GETFL); + if (flags == -1) { + perror("fcntl F_GETFL"); + break; + } + flags = fcntl(client_fd, F_SETFL, flags | O_NONBLOCK); + if (flags == -1) { + perror("fcntl F_SETFL O_NONBLOCK"); + break; + } + + /* Try to find the specified usb device */ + if (usbvendor != -1) { + handle = libusb_open_device_with_vid_pid(ctx, usbvendor, + usbproduct); + if (!handle) { + fprintf(stderr, + "Could not open an usb-device with vid:pid %04x:%04x\n", + usbvendor, usbproduct); + } else if (verbose >= usbredirparser_info) { + libusb_device *dev; + dev = libusb_get_device(handle); + fprintf(stderr, "Open a usb-device with vid:pid %04x:%04x on " + "bus %03x device %03x\n", + usbvendor, usbproduct, + libusb_get_bus_number(dev), + libusb_get_device_address(dev)); + } + } else { + libusb_device **list = NULL; + ssize_t i, n; + + n = libusb_get_device_list(ctx, &list); + for (i = 0; i < n; i++) { + if (libusb_get_bus_number(list[i]) == usbbus && + libusb_get_device_address(list[i]) == usbaddr) + break; + } + if (i < n) { + if (libusb_open(list[i], &handle) != 0) { + fprintf(stderr, + "Could not open usb-device at busnum-devnum %d-%d\n", + usbbus, usbaddr); + } + } else { + fprintf(stderr, + "Could not find an usb-device at busnum-devnum %d-%d\n", + usbbus, usbaddr); + } + libusb_free_device_list(list, 1); + } + if (!handle) { + close(client_fd); + continue; + } + + host = usbredirhost_open(ctx, handle, usbredirserver_log, + usbredirserver_read, usbredirserver_write, + NULL, SERVER_VERSION, verbose, 0); + if (!host) + exit(1); + run_main_loop(); + usbredirhost_close(host); + handle = NULL; + } + + close(server_fd); + libusb_exit(ctx); + exit(0); +} -- 2.38.1