Blame usbredirserver/usbredirserver.c

Packit 9795e1
/* usbredirserver.c simple usb network redirection tcp/ip server (host).
Packit 9795e1
Packit 9795e1
   Copyright 2010-2011 Red Hat, Inc.
Packit 9795e1
Packit 9795e1
   Red Hat Authors:
Packit 9795e1
   Hans de Goede <hdegoede@redhat.com>
Packit 9795e1
Packit 9795e1
   This program is free software; you can redistribute it and/or
Packit 9795e1
   modify it under the terms of the GNU General Public
Packit 9795e1
   License as published by the Free Software Foundation; either
Packit 9795e1
   version 2 of the License, or (at your option) any later version.
Packit 9795e1
Packit 9795e1
   This program is distributed in the hope that it will be useful,
Packit 9795e1
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 9795e1
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 9795e1
   General Public License for more details.
Packit 9795e1
Packit 9795e1
   You should have received a copy of the GNU General Public License
Packit 9795e1
   along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit 9795e1
*/
Packit 9795e1
Packit 9795e1
#include "config.h"
Packit 9795e1
Packit 9795e1
#include <stdio.h>
Packit 9795e1
#include <stdlib.h>
Packit 9795e1
#include <stdarg.h>
Packit 9795e1
#include <string.h>
Packit 9795e1
#include <getopt.h>
Packit 9795e1
#include <unistd.h>
Packit 9795e1
#include <errno.h>
Packit 9795e1
#include <poll.h>
Packit 9795e1
#include <fcntl.h>
Packit 9795e1
#include <signal.h>
Packit 9795e1
#include <sys/socket.h>
Packit 9795e1
#include <sys/types.h>
Packit 9795e1
#include <sys/time.h>
Packit 9795e1
#include <netdb.h>
Packit 9795e1
#include <netinet/in.h>
Packit 9795e1
#include <arpa/inet.h>
Packit 9795e1
#include <netinet/tcp.h>
Packit 9795e1
#include "usbredirhost.h"
Packit 9795e1
Packit 9795e1
Packit 9795e1
#define SERVER_VERSION "usbredirserver " PACKAGE_VERSION
Packit 9795e1
Packit 9795e1
static int verbose = usbredirparser_info;
Packit 9795e1
static int client_fd, running = 1;
Packit 9795e1
static libusb_context *ctx;
Packit 9795e1
static struct usbredirhost *host;
Packit 9795e1
Packit 9795e1
static const struct option longopts[] = {
Packit 9795e1
    { "port", required_argument, NULL, 'p' },
Packit 9795e1
    { "verbose", required_argument, NULL, 'v' },
Packit 9795e1
    { "ipv4", required_argument, NULL, '4' },
Packit 9795e1
    { "ipv6", required_argument, NULL, '6' },
Packit 9795e1
    { "keepalive", required_argument, NULL, 'k' },
Packit 9795e1
    { "help", no_argument, NULL, 'h' },
Packit 9795e1
    { NULL, 0, NULL, 0 }
Packit 9795e1
};
Packit 9795e1
Packit 9795e1
static void usbredirserver_log(void *priv, int level, const char *msg)
Packit 9795e1
{
Packit 9795e1
    if (level <= verbose)
Packit 9795e1
        fprintf(stderr, "%s\n", msg);
Packit 9795e1
}
Packit 9795e1
Packit 9795e1
static int usbredirserver_read(void *priv, uint8_t *data, int count)
Packit 9795e1
{
Packit 9795e1
    int r = read(client_fd, data, count);
Packit 9795e1
    if (r < 0) {
Packit 9795e1
        if (errno == EAGAIN)
Packit 9795e1
            return 0;
Packit 9795e1
        return -1;
Packit 9795e1
    }
Packit 9795e1
    if (r == 0) { /* Client disconnected */
Packit 9795e1
        close(client_fd);
Packit 9795e1
        client_fd = -1;
Packit 9795e1
    }
Packit 9795e1
    return r;
Packit 9795e1
}
Packit 9795e1
Packit 9795e1
static int usbredirserver_write(void *priv, uint8_t *data, int count)
Packit 9795e1
{
Packit 9795e1
    int r = write(client_fd, data, count);
Packit 9795e1
    if (r < 0) {
Packit 9795e1
        if (errno == EAGAIN)
Packit 9795e1
            return 0;
Packit 9795e1
        if (errno == EPIPE) { /* Client disconnected */
Packit 9795e1
            close(client_fd);
Packit 9795e1
            client_fd = -1;
Packit 9795e1
            return 0;
Packit 9795e1
        }
Packit 9795e1
        return -1;
Packit 9795e1
    }
Packit 9795e1
    return r;
Packit 9795e1
}
Packit 9795e1
Packit 9795e1
static void usage(int exit_code, char *argv0)
Packit 9795e1
{
Packit 9795e1
    fprintf(exit_code? stderr:stdout,
Packit 9795e1
        "Usage: %s [-p|--port <port>] [-v|--verbose <0-5>] "
Packit 9795e1
        "[[-4|--ipv4 ipaddr]|[-6|--ipv6 ipaddr]] "
Packit 9795e1
        "[-k|--keepalive seconds] "
Packit 9795e1
        "<busnum-devnum|vendorid:prodid>\n",
Packit 9795e1
        argv0);
Packit 9795e1
    exit(exit_code);
Packit 9795e1
}
Packit 9795e1
Packit 9795e1
static void invalid_usb_device_id(char *usb_device_id, char *argv0)
Packit 9795e1
{
Packit 9795e1
    fprintf(stderr, "Invalid usb device identifier: %s\n", usb_device_id);
Packit 9795e1
    usage(1, argv0);
Packit 9795e1
}
Packit 9795e1
Packit 9795e1
static void run_main_loop(void)
Packit 9795e1
{
Packit 9795e1
    const struct libusb_pollfd **pollfds = NULL;
Packit 9795e1
    fd_set readfds, writefds;
Packit 9795e1
    int i, n, nfds;
Packit 9795e1
    struct timeval timeout, *timeout_p;
Packit 9795e1
Packit 9795e1
    while (running && client_fd != -1) {
Packit 9795e1
        FD_ZERO(&readfds);
Packit 9795e1
        FD_ZERO(&writefds);
Packit 9795e1
Packit 9795e1
        FD_SET(client_fd, &readfds);
Packit 9795e1
        if (usbredirhost_has_data_to_write(host)) {
Packit 9795e1
            FD_SET(client_fd, &writefds);
Packit 9795e1
        }
Packit 9795e1
        nfds = client_fd + 1;
Packit 9795e1
Packit 9795e1
        free(pollfds);
Packit 9795e1
        pollfds = libusb_get_pollfds(ctx);
Packit 9795e1
        for (i = 0; pollfds && pollfds[i]; i++) {
Packit 9795e1
            if (pollfds[i]->events & POLLIN) {
Packit 9795e1
                FD_SET(pollfds[i]->fd, &readfds);
Packit 9795e1
            }
Packit 9795e1
            if (pollfds[i]->events & POLLOUT) {
Packit 9795e1
                FD_SET(pollfds[i]->fd, &writefds);
Packit 9795e1
            }
Packit 9795e1
            if (pollfds[i]->fd >= nfds)
Packit 9795e1
                nfds = pollfds[i]->fd + 1;
Packit 9795e1
        }
Packit 9795e1
Packit 9795e1
        if (libusb_get_next_timeout(ctx, &timeout) == 1) {
Packit 9795e1
            timeout_p = &timeout;
Packit 9795e1
        } else {
Packit 9795e1
            timeout_p = NULL;
Packit 9795e1
        }
Packit 9795e1
        n = select(nfds, &readfds, &writefds, NULL, timeout_p);
Packit 9795e1
        if (n == -1) {
Packit 9795e1
            if (errno == EINTR) {
Packit 9795e1
                continue;
Packit 9795e1
            }
Packit 9795e1
            perror("select");
Packit 9795e1
            break;
Packit 9795e1
        }
Packit 9795e1
        memset(&timeout, 0, sizeof(timeout));
Packit 9795e1
        if (n == 0) {
Packit 9795e1
            libusb_handle_events_timeout(ctx, &timeout);
Packit 9795e1
            continue;
Packit 9795e1
        }
Packit 9795e1
Packit 9795e1
        if (FD_ISSET(client_fd, &readfds)) {
Packit 9795e1
            if (usbredirhost_read_guest_data(host)) {
Packit 9795e1
                break;
Packit 9795e1
            }
Packit 9795e1
        }
Packit 9795e1
        /* usbredirhost_read_guest_data may have detected client disconnect */
Packit 9795e1
        if (client_fd == -1)
Packit 9795e1
            break;
Packit 9795e1
Packit 9795e1
        if (FD_ISSET(client_fd, &writefds)) {
Packit 9795e1
            if (usbredirhost_write_guest_data(host)) {
Packit 9795e1
                break;
Packit 9795e1
            }
Packit 9795e1
        }
Packit 9795e1
Packit 9795e1
        for (i = 0; pollfds && pollfds[i]; i++) {
Packit 9795e1
            if (FD_ISSET(pollfds[i]->fd, &readfds) ||
Packit 9795e1
                FD_ISSET(pollfds[i]->fd, &writefds)) {
Packit 9795e1
                libusb_handle_events_timeout(ctx, &timeout);
Packit 9795e1
                break;
Packit 9795e1
            }
Packit 9795e1
        }
Packit 9795e1
    }
Packit 9795e1
    if (client_fd != -1) { /* Broken out of the loop because of an error ? */
Packit 9795e1
        close(client_fd);
Packit 9795e1
        client_fd = -1;
Packit 9795e1
    }
Packit 9795e1
    free(pollfds);
Packit 9795e1
}
Packit 9795e1
Packit 9795e1
static void quit_handler(int sig)
Packit 9795e1
{
Packit 9795e1
    running = 0;
Packit 9795e1
}
Packit 9795e1
Packit 9795e1
int main(int argc, char *argv[])
Packit 9795e1
{
Packit 9795e1
    int o, flags, server_fd = -1;
Packit 9795e1
    char *endptr, *delim;
Packit 9795e1
    int port       = 4000;
Packit 9795e1
    int usbbus     = -1;
Packit 9795e1
    int usbaddr    = -1;
Packit 9795e1
    int usbvendor  = -1;
Packit 9795e1
    int usbproduct = -1;
Packit 9795e1
    int on = 1;
Packit 9795e1
    int keepalive  = -1;
Packit 9795e1
    char *ipv4_addr = NULL, *ipv6_addr = NULL;
Packit 9795e1
    union {
Packit 9795e1
        struct sockaddr_in v4;
Packit 9795e1
        struct sockaddr_in6 v6;
Packit 9795e1
    } serveraddr;
Packit 9795e1
    struct sigaction act;
Packit 9795e1
    libusb_device_handle *handle = NULL;
Packit 9795e1
Packit 9795e1
    while ((o = getopt_long(argc, argv, "hp:v:4:6:k:", longopts, NULL)) != -1) {
Packit 9795e1
        switch (o) {
Packit 9795e1
        case 'p':
Packit 9795e1
            port = strtol(optarg, &endptr, 10);
Packit 9795e1
            if (*endptr != '\0') {
Packit 9795e1
                fprintf(stderr, "Invalid value for --port: '%s'\n", optarg);
Packit 9795e1
                usage(1, argv[0]);
Packit 9795e1
            }
Packit 9795e1
            break;
Packit 9795e1
        case 'v':
Packit 9795e1
            verbose = strtol(optarg, &endptr, 10);
Packit 9795e1
            if (*endptr != '\0') {
Packit 9795e1
                fprintf(stderr, "Invalid value for --verbose: '%s'\n", optarg);
Packit 9795e1
                usage(1, argv[0]);
Packit 9795e1
            }
Packit 9795e1
            break;
Packit 9795e1
        case '4':
Packit 9795e1
            ipv4_addr = optarg;
Packit 9795e1
            break;
Packit 9795e1
        case '6':
Packit 9795e1
            ipv6_addr = optarg;
Packit 9795e1
            break;
Packit 9795e1
        case 'k':
Packit 9795e1
            keepalive = strtol(optarg, &endptr, 10);
Packit 9795e1
            if (*endptr != '\0') {
Packit 9795e1
                fprintf(stderr, "Invalid value for -k: '%s'\n", optarg);
Packit 9795e1
                usage(1, argv[0]);
Packit 9795e1
            }
Packit 9795e1
            break;
Packit 9795e1
        case '?':
Packit 9795e1
        case 'h':
Packit 9795e1
            usage(o == '?', argv[0]);
Packit 9795e1
            break;
Packit 9795e1
        }
Packit 9795e1
    }
Packit 9795e1
    if (optind == argc) {
Packit 9795e1
        fprintf(stderr, "Missing usb device identifier argument\n");
Packit 9795e1
        usage(1, argv[0]);
Packit 9795e1
    }
Packit 9795e1
    delim = strchr(argv[optind], '-');
Packit 9795e1
    if (delim && delim[1]) {
Packit 9795e1
        usbbus = strtol(argv[optind], &endptr, 10);
Packit 9795e1
        if (*endptr != '-') {
Packit 9795e1
            invalid_usb_device_id(argv[optind], argv[0]);
Packit 9795e1
        }
Packit 9795e1
        usbaddr = strtol(delim + 1, &endptr, 10);
Packit 9795e1
        if (*endptr != '\0') {
Packit 9795e1
            invalid_usb_device_id(argv[optind], argv[0]);
Packit 9795e1
        }
Packit 9795e1
    } else {
Packit 9795e1
        delim = strchr(argv[optind], ':');
Packit 9795e1
        if (!delim || !delim[1]) {
Packit 9795e1
            invalid_usb_device_id(argv[optind], argv[0]);
Packit 9795e1
        }
Packit 9795e1
        usbvendor = strtol(argv[optind], &endptr, 16);
Packit 9795e1
        if (*endptr != ':' || usbvendor <= 0 || usbvendor > 0xffff) {
Packit 9795e1
            invalid_usb_device_id(argv[optind], argv[0]);
Packit 9795e1
        }
Packit 9795e1
        usbproduct = strtol(delim + 1, &endptr, 16);
Packit 9795e1
        /* Product ID 0000 is valid */
Packit 9795e1
        if (*endptr != '\0' || usbproduct < 0 || usbproduct > 0xffff) {
Packit 9795e1
            invalid_usb_device_id(argv[optind], argv[0]);
Packit 9795e1
        }
Packit 9795e1
    }
Packit 9795e1
    optind++;
Packit 9795e1
    if (optind != argc) {
Packit 9795e1
        fprintf(stderr, "Excess non option arguments\n");
Packit 9795e1
        usage(1, argv[0]);
Packit 9795e1
    }
Packit 9795e1
Packit 9795e1
    memset(&act, 0, sizeof(act));
Packit 9795e1
    act.sa_handler = quit_handler;
Packit 9795e1
    sigaction(SIGINT, &act, NULL);
Packit 9795e1
    sigaction(SIGHUP, &act, NULL);
Packit 9795e1
    sigaction(SIGTERM, &act, NULL);
Packit 9795e1
    sigaction(SIGQUIT, &act, NULL);
Packit 9795e1
Packit 9795e1
    if (libusb_init(&ctx)) {
Packit 9795e1
        fprintf(stderr, "Could not init libusb\n");
Packit 9795e1
        exit(1);
Packit 9795e1
    }
Packit 9795e1
Packit 9795e1
#if LIBUSB_API_VERSION >= 0x01000106
Packit 9795e1
    libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, verbose);
Packit 9795e1
#else
Packit 9795e1
    libusb_set_debug(ctx, verbose);
Packit 9795e1
#endif
Packit 9795e1
Packit 9795e1
    if (ipv4_addr) {
Packit 9795e1
        server_fd = socket(AF_INET, SOCK_STREAM, 0);
Packit 9795e1
    } else {
Packit 9795e1
        server_fd = socket(AF_INET6, SOCK_STREAM, 0);
Packit 9795e1
    }
Packit 9795e1
    if (server_fd == -1) {
Packit 9795e1
        perror("Error creating ip socket");
Packit 9795e1
        exit(1);
Packit 9795e1
    }
Packit 9795e1
Packit 9795e1
    if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))) {
Packit 9795e1
        perror("Error setsockopt(SO_REUSEADDR) failed");
Packit 9795e1
        exit(1);
Packit 9795e1
    }
Packit 9795e1
Packit 9795e1
    memset(&serveraddr, 0, sizeof(serveraddr));
Packit 9795e1
Packit 9795e1
    if (ipv4_addr) {
Packit 9795e1
        serveraddr.v4.sin_family = AF_INET;
Packit 9795e1
        serveraddr.v4.sin_port   = htons(port);
Packit 9795e1
        if ((inet_pton(AF_INET, ipv4_addr,
Packit 9795e1
                       &serveraddr.v4.sin_addr)) != 1) {
Packit 9795e1
            perror("Error convert ipv4 address");
Packit 9795e1
            exit(1);
Packit 9795e1
        }
Packit 9795e1
    } else {
Packit 9795e1
        serveraddr.v6.sin6_family = AF_INET6;
Packit 9795e1
        serveraddr.v6.sin6_port   = htons(port);
Packit 9795e1
        if (ipv6_addr) {
Packit 9795e1
            if ((inet_pton(AF_INET6, ipv6_addr,
Packit 9795e1
                           &serveraddr.v6.sin6_addr)) != 1) {
Packit 9795e1
                perror("Error convert ipv6 address");
Packit 9795e1
                exit(1);
Packit 9795e1
            }
Packit 9795e1
        } else {
Packit 9795e1
            serveraddr.v6.sin6_addr   = in6addr_any;
Packit 9795e1
        }
Packit 9795e1
    }
Packit 9795e1
Packit 9795e1
    if (bind(server_fd, (struct sockaddr *)&serveraddr,
Packit 9795e1
             sizeof(serveraddr))) {
Packit 9795e1
        perror("Error bind");
Packit 9795e1
        exit(1);
Packit 9795e1
    }
Packit 9795e1
Packit 9795e1
    if (listen(server_fd, 1)) {
Packit 9795e1
        perror("Error listening");
Packit 9795e1
        exit(1);
Packit 9795e1
    }
Packit 9795e1
Packit 9795e1
    while (running) {
Packit 9795e1
        client_fd = accept(server_fd, NULL, 0);
Packit 9795e1
        if (client_fd == -1) {
Packit 9795e1
            if (errno == EINTR) {
Packit 9795e1
                continue;
Packit 9795e1
            }
Packit 9795e1
            perror("accept");
Packit 9795e1
            break;
Packit 9795e1
        }
Packit 9795e1
Packit 9795e1
        if (keepalive > 0) {
Packit 9795e1
            int optval = 1;
Packit 9795e1
            socklen_t optlen = sizeof(optval);
Packit 9795e1
            if (setsockopt(client_fd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) == -1) {
Packit 9795e1
                if (errno != ENOTSUP) {
Packit 9795e1
                    perror("setsockopt SO_KEEPALIVE error.");
Packit 9795e1
                    break;
Packit 9795e1
                }
Packit 9795e1
            }
Packit 9795e1
            optval = keepalive;	/* set default TCP_KEEPIDLE time from cmdline */
Packit 9795e1
            if (setsockopt(client_fd, SOL_TCP, TCP_KEEPIDLE, &optval, optlen) == -1) {
Packit 9795e1
                if (errno != ENOTSUP) {
Packit 9795e1
                    perror("setsockopt TCP_KEEPIDLE error.");
Packit 9795e1
                    break;
Packit 9795e1
                }
Packit 9795e1
            }
Packit 9795e1
            optval = 10;	/* set default TCP_KEEPINTVL time as 10s */
Packit 9795e1
            if (setsockopt(client_fd, SOL_TCP, TCP_KEEPINTVL, &optval, optlen) == -1) {
Packit 9795e1
                if (errno != ENOTSUP) {
Packit 9795e1
                    perror("setsockopt TCP_KEEPINTVL error.");
Packit 9795e1
                    break;
Packit 9795e1
                }
Packit 9795e1
            }
Packit 9795e1
            optval = 3;	/* set default TCP_KEEPCNT as 3 */
Packit 9795e1
            if (setsockopt(client_fd, SOL_TCP, TCP_KEEPCNT, &optval, optlen) == -1) {
Packit 9795e1
                if (errno != ENOTSUP) {
Packit 9795e1
                    perror("setsockopt TCP_KEEPCNT error.");
Packit 9795e1
                    break;
Packit 9795e1
                }
Packit 9795e1
            }
Packit 9795e1
        }
Packit 9795e1
Packit 9795e1
        flags = fcntl(client_fd, F_GETFL);
Packit 9795e1
        if (flags == -1) {
Packit 9795e1
            perror("fcntl F_GETFL");
Packit 9795e1
            break;
Packit 9795e1
        }
Packit 9795e1
        flags = fcntl(client_fd, F_SETFL, flags | O_NONBLOCK);
Packit 9795e1
        if (flags == -1) {
Packit 9795e1
            perror("fcntl F_SETFL O_NONBLOCK");
Packit 9795e1
            break;
Packit 9795e1
        }
Packit 9795e1
Packit 9795e1
        /* Try to find the specified usb device */
Packit 9795e1
        if (usbvendor != -1) {
Packit 9795e1
            handle = libusb_open_device_with_vid_pid(ctx, usbvendor,
Packit 9795e1
                                                     usbproduct);
Packit 9795e1
            if (!handle) {
Packit 9795e1
                fprintf(stderr,
Packit 9795e1
                    "Could not open an usb-device with vid:pid %04x:%04x\n",
Packit 9795e1
                    usbvendor, usbproduct);
Packit 9795e1
            } else if (verbose >= usbredirparser_info) {
Packit 9795e1
                libusb_device *dev;
Packit 9795e1
                dev = libusb_get_device(handle);
Packit 9795e1
                fprintf(stderr, "Open a usb-device with vid:pid %04x:%04x on "
Packit 9795e1
                        "bus %03x device %03x\n",
Packit 9795e1
                        usbvendor, usbproduct,
Packit 9795e1
                        libusb_get_bus_number(dev),
Packit 9795e1
                        libusb_get_device_address(dev));
Packit 9795e1
            }
Packit 9795e1
        } else {
Packit 9795e1
            libusb_device **list = NULL;
Packit 9795e1
            ssize_t i, n;
Packit 9795e1
Packit 9795e1
            n = libusb_get_device_list(ctx, &list);
Packit 9795e1
            for (i = 0; i < n; i++) {
Packit 9795e1
                if (libusb_get_bus_number(list[i]) == usbbus &&
Packit 9795e1
                        libusb_get_device_address(list[i]) == usbaddr)
Packit 9795e1
                    break;
Packit 9795e1
            }
Packit 9795e1
            if (i < n) {
Packit 9795e1
                if (libusb_open(list[i], &handle) != 0) {
Packit 9795e1
                    fprintf(stderr,
Packit 9795e1
                        "Could not open usb-device at busnum-devnum %d-%d\n",
Packit 9795e1
                        usbbus, usbaddr);
Packit 9795e1
                }
Packit 9795e1
            } else {
Packit 9795e1
                fprintf(stderr,
Packit 9795e1
                    "Could not find an usb-device at busnum-devnum %d-%d\n",
Packit 9795e1
                    usbbus, usbaddr);
Packit 9795e1
            }
Packit 9795e1
            libusb_free_device_list(list, 1);
Packit 9795e1
        }
Packit 9795e1
        if (!handle) {
Packit 9795e1
            close(client_fd);
Packit 9795e1
            continue;
Packit 9795e1
        }
Packit 9795e1
Packit 9795e1
        host = usbredirhost_open(ctx, handle, usbredirserver_log,
Packit 9795e1
                                 usbredirserver_read, usbredirserver_write,
Packit 9795e1
                                 NULL, SERVER_VERSION, verbose, 0);
Packit 9795e1
        if (!host)
Packit 9795e1
            exit(1);
Packit 9795e1
        run_main_loop();
Packit 9795e1
        usbredirhost_close(host);
Packit 9795e1
        handle = NULL;
Packit 9795e1
    }
Packit 9795e1
Packit 9795e1
    close(server_fd);
Packit 9795e1
    libusb_exit(ctx);
Packit 9795e1
    exit(0);
Packit 9795e1
}