qemu/qemu-sockets.c
<<
>>
Prefs
   1/*
   2 *  inet and unix socket functions for qemu
   3 *
   4 *  (c) 2008 Gerd Hoffmann <kraxel@redhat.com>
   5 *
   6 *  This program is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License as published by
   8 *  the Free Software Foundation; under version 2 of the License.
   9 *
  10 *  This program is distributed in the hope that it will be useful,
  11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13 *  GNU General Public License for more details.
  14 */
  15#include <stdio.h>
  16#include <stdlib.h>
  17#include <string.h>
  18#include <ctype.h>
  19#include <errno.h>
  20#include <unistd.h>
  21
  22#include "qemu_socket.h"
  23#include "qemu-common.h" /* for qemu_isdigit */
  24
  25#ifndef AI_ADDRCONFIG
  26# define AI_ADDRCONFIG 0
  27#endif
  28
  29static int sockets_debug = 0;
  30static const int on=1, off=0;
  31
  32static int inet_getport(struct addrinfo *e)
  33{
  34    struct sockaddr_in *i4;
  35    struct sockaddr_in6 *i6;
  36
  37    switch (e->ai_family) {
  38    case PF_INET6:
  39        i6 = (void*)e->ai_addr;
  40        return ntohs(i6->sin6_port);
  41    case PF_INET:
  42        i4 = (void*)e->ai_addr;
  43        return ntohs(i4->sin_port);
  44    default:
  45        return 0;
  46    }
  47}
  48
  49static void inet_setport(struct addrinfo *e, int port)
  50{
  51    struct sockaddr_in *i4;
  52    struct sockaddr_in6 *i6;
  53
  54    switch (e->ai_family) {
  55    case PF_INET6:
  56        i6 = (void*)e->ai_addr;
  57        i6->sin6_port = htons(port);
  58        break;
  59    case PF_INET:
  60        i4 = (void*)e->ai_addr;
  61        i4->sin_port = htons(port);
  62        break;
  63    }
  64}
  65
  66static const char *inet_strfamily(int family)
  67{
  68    switch (family) {
  69    case PF_INET6: return "ipv6";
  70    case PF_INET:  return "ipv4";
  71    case PF_UNIX:  return "unix";
  72    }
  73    return "????";
  74}
  75
  76static void inet_print_addrinfo(const char *tag, struct addrinfo *res)
  77{
  78    struct addrinfo *e;
  79    char uaddr[INET6_ADDRSTRLEN+1];
  80    char uport[33];
  81
  82    for (e = res; e != NULL; e = e->ai_next) {
  83        getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
  84                    uaddr,INET6_ADDRSTRLEN,uport,32,
  85                    NI_NUMERICHOST | NI_NUMERICSERV);
  86        fprintf(stderr,"%s: getaddrinfo: family %s, host %s, port %s\n",
  87                tag, inet_strfamily(e->ai_family), uaddr, uport);
  88    }
  89}
  90
  91int inet_listen(const char *str, char *ostr, int olen,
  92                int socktype, int port_offset)
  93{
  94    struct addrinfo ai,*res,*e;
  95    char addr[64];
  96    char port[33];
  97    char uaddr[INET6_ADDRSTRLEN+1];
  98    char uport[33];
  99    const char *opts, *h;
 100    int slisten,rc,pos,to,try_next;
 101
 102    memset(&ai,0, sizeof(ai));
 103    ai.ai_flags = AI_PASSIVE | AI_ADDRCONFIG;
 104    ai.ai_family = PF_UNSPEC;
 105    ai.ai_socktype = socktype;
 106
 107    /* parse address */
 108    if (str[0] == ':') {
 109        /* no host given */
 110        addr[0] = '\0';
 111        if (1 != sscanf(str,":%32[^,]%n",port,&pos)) {
 112            fprintf(stderr, "%s: portonly parse error (%s)\n",
 113                    __FUNCTION__, str);
 114            return -1;
 115        }
 116    } else if (str[0] == '[') {
 117        /* IPv6 addr */
 118        if (2 != sscanf(str,"[%64[^]]]:%32[^,]%n",addr,port,&pos)) {
 119            fprintf(stderr, "%s: ipv6 parse error (%s)\n",
 120                    __FUNCTION__, str);
 121            return -1;
 122        }
 123        ai.ai_family = PF_INET6;
 124    } else if (qemu_isdigit(str[0])) {
 125        /* IPv4 addr */
 126        if (2 != sscanf(str,"%64[0-9.]:%32[^,]%n",addr,port,&pos)) {
 127            fprintf(stderr, "%s: ipv4 parse error (%s)\n",
 128                    __FUNCTION__, str);
 129            return -1;
 130        }
 131        ai.ai_family = PF_INET;
 132    } else {
 133        /* hostname */
 134        if (2 != sscanf(str,"%64[^:]:%32[^,]%n",addr,port,&pos)) {
 135            fprintf(stderr, "%s: hostname parse error (%s)\n",
 136                    __FUNCTION__, str);
 137            return -1;
 138        }
 139    }
 140
 141    /* parse options */
 142    opts = str + pos;
 143    h = strstr(opts, ",to=");
 144    to = h ? atoi(h+4) : 0;
 145    if (strstr(opts, ",ipv4"))
 146        ai.ai_family = PF_INET;
 147    if (strstr(opts, ",ipv6"))
 148        ai.ai_family = PF_INET6;
 149
 150    /* lookup */
 151    if (port_offset)
 152        snprintf(port, sizeof(port), "%d", atoi(port) + port_offset);
 153    rc = getaddrinfo(strlen(addr) ? addr : NULL, port, &ai, &res);
 154    if (rc != 0) {
 155        fprintf(stderr,"%s: getaddrinfo(%s,%s): %s\n", __FUNCTION__,
 156                addr, port, gai_strerror(rc));
 157        return -1;
 158    }
 159    if (sockets_debug)
 160        inet_print_addrinfo(__FUNCTION__, res);
 161
 162    /* create socket + bind */
 163    for (e = res; e != NULL; e = e->ai_next) {
 164        getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
 165                    uaddr,INET6_ADDRSTRLEN,uport,32,
 166                    NI_NUMERICHOST | NI_NUMERICSERV);
 167        slisten = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
 168        if (slisten < 0) {
 169            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
 170                    inet_strfamily(e->ai_family), strerror(errno));
 171            continue;
 172        }
 173
 174        setsockopt(slisten,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
 175#ifdef IPV6_V6ONLY
 176        if (e->ai_family == PF_INET6) {
 177            /* listen on both ipv4 and ipv6 */
 178            setsockopt(slisten,IPPROTO_IPV6,IPV6_V6ONLY,(void*)&off,sizeof(off));
 179        }
 180#endif
 181
 182        for (;;) {
 183            if (bind(slisten, e->ai_addr, e->ai_addrlen) == 0) {
 184                if (sockets_debug)
 185                    fprintf(stderr,"%s: bind(%s,%s,%d): OK\n", __FUNCTION__,
 186                            inet_strfamily(e->ai_family), uaddr, inet_getport(e));
 187                goto listen;
 188            }
 189            try_next = to && (inet_getport(e) <= to + port_offset);
 190            if (!try_next || sockets_debug)
 191                fprintf(stderr,"%s: bind(%s,%s,%d): %s\n", __FUNCTION__,
 192                        inet_strfamily(e->ai_family), uaddr, inet_getport(e),
 193                        strerror(errno));
 194            if (try_next) {
 195                inet_setport(e, inet_getport(e) + 1);
 196                continue;
 197            }
 198            break;
 199        }
 200        closesocket(slisten);
 201    }
 202    fprintf(stderr, "%s: FAILED\n", __FUNCTION__);
 203    freeaddrinfo(res);
 204    return -1;
 205
 206listen:
 207    if (listen(slisten,1) != 0) {
 208        perror("listen");
 209        closesocket(slisten);
 210        return -1;
 211    }
 212    if (ostr) {
 213        if (e->ai_family == PF_INET6) {
 214            snprintf(ostr, olen, "[%s]:%d%s", uaddr,
 215                     inet_getport(e) - port_offset, opts);
 216        } else {
 217            snprintf(ostr, olen, "%s:%d%s", uaddr,
 218                     inet_getport(e) - port_offset, opts);
 219        }
 220    }
 221    freeaddrinfo(res);
 222    return slisten;
 223}
 224
 225int inet_connect(const char *str, int socktype)
 226{
 227    struct addrinfo ai,*res,*e;
 228    char addr[64];
 229    char port[33];
 230    char uaddr[INET6_ADDRSTRLEN+1];
 231    char uport[33];
 232    int sock,rc;
 233
 234    memset(&ai,0, sizeof(ai));
 235    ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
 236    ai.ai_family = PF_UNSPEC;
 237    ai.ai_socktype = socktype;
 238
 239    /* parse address */
 240    if (str[0] == '[') {
 241        /* IPv6 addr */
 242        if (2 != sscanf(str,"[%64[^]]]:%32[^,]",addr,port)) {
 243            fprintf(stderr, "%s: ipv6 parse error (%s)\n",
 244                    __FUNCTION__, str);
 245            return -1;
 246        }
 247        ai.ai_family = PF_INET6;
 248    } else if (qemu_isdigit(str[0])) {
 249        /* IPv4 addr */
 250        if (2 != sscanf(str,"%64[0-9.]:%32[^,]",addr,port)) {
 251            fprintf(stderr, "%s: ipv4 parse error (%s)\n",
 252                    __FUNCTION__, str);
 253            return -1;
 254        }
 255        ai.ai_family = PF_INET;
 256    } else {
 257        /* hostname */
 258        if (2 != sscanf(str,"%64[^:]:%32[^,]",addr,port)) {
 259            fprintf(stderr, "%s: hostname parse error (%s)\n",
 260                    __FUNCTION__, str);
 261            return -1;
 262        }
 263    }
 264
 265    /* parse options */
 266    if (strstr(str, ",ipv4"))
 267        ai.ai_family = PF_INET;
 268    if (strstr(str, ",ipv6"))
 269        ai.ai_family = PF_INET6;
 270
 271    /* lookup */
 272    if (0 != (rc = getaddrinfo(addr, port, &ai, &res))) {
 273        fprintf(stderr,"getaddrinfo(%s,%s): %s\n", gai_strerror(rc),
 274                addr, port);
 275        return -1;
 276    }
 277    if (sockets_debug)
 278        inet_print_addrinfo(__FUNCTION__, res);
 279
 280    for (e = res; e != NULL; e = e->ai_next) {
 281        if (getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen,
 282                        uaddr,INET6_ADDRSTRLEN,uport,32,
 283                        NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
 284            fprintf(stderr,"%s: getnameinfo: oops\n", __FUNCTION__);
 285            continue;
 286        }
 287        sock = socket(e->ai_family, e->ai_socktype, e->ai_protocol);
 288        if (sock < 0) {
 289            fprintf(stderr,"%s: socket(%s): %s\n", __FUNCTION__,
 290                    inet_strfamily(e->ai_family), strerror(errno));
 291            continue;
 292        }
 293        setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(void*)&on,sizeof(on));
 294
 295        /* connect to peer */
 296        if (connect(sock,e->ai_addr,e->ai_addrlen) < 0) {
 297            if (sockets_debug || NULL == e->ai_next)
 298                fprintf(stderr, "%s: connect(%s,%s,%s,%s): %s\n", __FUNCTION__,
 299                        inet_strfamily(e->ai_family),
 300                        e->ai_canonname, uaddr, uport, strerror(errno));
 301            closesocket(sock);
 302            continue;
 303        }
 304        if (sockets_debug)
 305            fprintf(stderr, "%s: connect(%s,%s,%s,%s): OK\n", __FUNCTION__,
 306                    inet_strfamily(e->ai_family),
 307                    e->ai_canonname, uaddr, uport);
 308        freeaddrinfo(res);
 309        return sock;
 310    }
 311    freeaddrinfo(res);
 312    return -1;
 313}
 314
 315#ifndef _WIN32
 316
 317int unix_listen(const char *str, char *ostr, int olen)
 318{
 319    struct sockaddr_un un;
 320    char *path, *opts;
 321    int sock, fd, len;
 322
 323    sock = socket(PF_UNIX, SOCK_STREAM, 0);
 324    if (sock < 0) {
 325        perror("socket(unix)");
 326        return -1;
 327    }
 328
 329    opts = strchr(str, ',');
 330    if (opts) {
 331        len = opts - str;
 332        path = malloc(len+1);
 333        snprintf(path, len+1, "%.*s", len, str);
 334    } else
 335        path = strdup(str);
 336
 337    memset(&un, 0, sizeof(un));
 338    un.sun_family = AF_UNIX;
 339    if (path && strlen(path)) {
 340        snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
 341    } else {
 342        char *tmpdir = getenv("TMPDIR");
 343        snprintf(un.sun_path, sizeof(un.sun_path), "%s/qemu-socket-XXXXXX",
 344                 tmpdir ? tmpdir : "/tmp");
 345        /*
 346         * This dummy fd usage silences the mktemp() unsecure warning.
 347         * Using mkstemp() doesn't make things more secure here
 348         * though.  bind() complains about existing files, so we have
 349         * to unlink first and thus re-open the race window.  The
 350         * worst case possible is bind() failing, i.e. a DoS attack.
 351         */
 352        fd = mkstemp(un.sun_path); close(fd);
 353    }
 354    snprintf(ostr, olen, "%s%s", un.sun_path, opts ? opts : "");
 355
 356    unlink(un.sun_path);
 357    if (bind(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
 358        fprintf(stderr, "bind(unix:%s): %s\n", un.sun_path, strerror(errno));
 359        goto err;
 360    }
 361    if (listen(sock, 1) < 0) {
 362        fprintf(stderr, "listen(unix:%s): %s\n", un.sun_path, strerror(errno));
 363        goto err;
 364    }
 365
 366    if (sockets_debug)
 367        fprintf(stderr, "bind(unix:%s): OK\n", un.sun_path);
 368    free(path);
 369    return sock;
 370
 371err:
 372    free(path);
 373    closesocket(sock);
 374    return -1;
 375}
 376
 377int unix_connect(const char *path)
 378{
 379    struct sockaddr_un un;
 380    int sock;
 381
 382    sock = socket(PF_UNIX, SOCK_STREAM, 0);
 383    if (sock < 0) {
 384        perror("socket(unix)");
 385        return -1;
 386    }
 387
 388    memset(&un, 0, sizeof(un));
 389    un.sun_family = AF_UNIX;
 390    snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
 391    if (connect(sock, (struct sockaddr*) &un, sizeof(un)) < 0) {
 392        fprintf(stderr, "connect(unix:%s): %s\n", path, strerror(errno));
 393        return -1;
 394    }
 395
 396    if (sockets_debug)
 397        fprintf(stderr, "connect(unix:%s): OK\n", path);
 398    return sock;
 399}
 400
 401#else
 402
 403int unix_listen(const char *path, char *ostr, int olen)
 404{
 405    fprintf(stderr, "unix sockets are not available on windows\n");
 406    return -1;
 407}
 408
 409int unix_connect(const char *path)
 410{
 411    fprintf(stderr, "unix sockets are not available on windows\n");
 412    return -1;
 413}
 414
 415#endif
 416