virnetsockettest.c 16.0 KB
Newer Older
1
/*
E
Eric Blake 已提交
2
 * Copyright (C) 2011, 2014 Red Hat, Inc.
3 4 5 6 7 8 9 10 11 12 13 14
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
15
 * License along with this library.  If not, see
O
Osier Yang 已提交
16
 * <http://www.gnu.org/licenses/>.
17 18 19 20 21 22 23 24 25 26 27
 *
 * Author: Daniel P. Berrange <berrange@redhat.com>
 */

#include <config.h>

#include <stdlib.h>
#include <signal.h>
#ifdef HAVE_IFADDRS_H
# include <ifaddrs.h>
#endif
28
#include <netdb.h>
29 30

#include "testutils.h"
31
#include "virutil.h"
32
#include "virerror.h"
33
#include "viralloc.h"
34
#include "virlog.h"
E
Eric Blake 已提交
35
#include "virfile.h"
36
#include "virstring.h"
37 38 39 40 41

#include "rpc/virnetsocket.h"

#define VIR_FROM_THIS VIR_FROM_RPC

42 43
VIR_LOG_INIT("tests.netsockettest");

44 45 46 47 48 49 50 51 52 53
#if HAVE_IFADDRS_H
# define BASE_PORT 5672

static int
checkProtocols(bool *hasIPv4, bool *hasIPv6,
               int *freePort)
{
    struct sockaddr_in in4;
    struct sockaddr_in6 in6;
    int s4 = -1, s6 = -1;
54
    size_t i;
55 56 57
    int ret = -1;

    *freePort = 0;
58 59
    if (virNetSocketCheckProtocols(hasIPv4, hasIPv6) < 0)
        return -1;
60

61
    for (i = 0; i < 50; i++) {
62 63 64 65
        int only = 1;
        if ((s4 = socket(AF_INET, SOCK_STREAM, 0)) < 0)
            goto cleanup;

66 67 68
        if (*hasIPv6) {
            if ((s6 = socket(AF_INET6, SOCK_STREAM, 0)) < 0)
                goto cleanup;
69

70 71 72
            if (setsockopt(s6, IPPROTO_IPV6, IPV6_V6ONLY, &only, sizeof(only)) < 0)
                goto cleanup;
        }
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91

        memset(&in4, 0, sizeof(in4));
        memset(&in6, 0, sizeof(in6));

        in4.sin_family = AF_INET;
        in4.sin_port = htons(BASE_PORT + i);
        in4.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
        in6.sin6_family = AF_INET6;
        in6.sin6_port = htons(BASE_PORT + i);
        in6.sin6_addr = in6addr_loopback;

        if (bind(s4, (struct sockaddr *)&in4, sizeof(in4)) < 0) {
            if (errno == EADDRINUSE) {
                VIR_FORCE_CLOSE(s4);
                VIR_FORCE_CLOSE(s6);
                continue;
            }
            goto cleanup;
        }
92 93 94 95 96 97 98 99 100

        if (*hasIPv6) {
            if (bind(s6, (struct sockaddr *)&in6, sizeof(in6)) < 0) {
                if (errno == EADDRINUSE) {
                    VIR_FORCE_CLOSE(s4);
                    VIR_FORCE_CLOSE(s6);
                    continue;
                }
                goto cleanup;
101 102 103 104 105 106 107
            }
        }

        *freePort = BASE_PORT + i;
        break;
    }

J
Jiri Denemark 已提交
108
    VIR_DEBUG("Choose port %d", *freePort);
109 110 111

    ret = 0;

112
 cleanup:
113 114 115 116 117 118
    VIR_FORCE_CLOSE(s4);
    VIR_FORCE_CLOSE(s6);
    return ret;
}


119
struct testSocketData {
120 121 122 123 124
    const char *lnode;
    int port;
    const char *cnode;
};

125
static int testSocketAccept(const void *opaque)
126 127 128 129 130
{
    virNetSocketPtr *lsock = NULL; /* Listen socket */
    size_t nlsock = 0, i;
    virNetSocketPtr ssock = NULL; /* Server socket */
    virNetSocketPtr csock = NULL; /* Client socket */
131
    const struct testSocketData *data = opaque;
132 133
    int ret = -1;
    char portstr[100];
134 135 136
    char *tmpdir = NULL;
    char *path = NULL;
    char template[] = "/tmp/libvirt_XXXXXX";
137

138 139 140 141 142 143 144 145 146
    if (!data) {
        virNetSocketPtr usock;
        tmpdir = mkdtemp(template);
        if (tmpdir == NULL) {
            VIR_WARN("Failed to create temporary directory");
            goto cleanup;
        }
        if (virAsprintf(&path, "%s/test.sock", tmpdir) < 0)
            goto cleanup;
147

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
        if (virNetSocketNewListenUNIX(path, 0700, -1, getegid(), &usock) < 0)
            goto cleanup;

        if (VIR_ALLOC_N(lsock, 1) < 0) {
            virObjectUnref(usock);
            goto cleanup;
        }

        lsock[0] = usock;
        nlsock = 1;
    } else {
        snprintf(portstr, sizeof(portstr), "%d", data->port);
        if (virNetSocketNewListenTCP(data->lnode, portstr,
                                     AF_UNSPEC,
                                     &lsock, &nlsock) < 0)
            goto cleanup;
    }
165

166
    for (i = 0; i < nlsock; i++) {
167
        if (virNetSocketListen(lsock[i], 0) < 0)
168 169 170
            goto cleanup;
    }

171 172 173 174 175 176 177 178 179
    if (!data) {
        if (virNetSocketNewConnectUNIX(path, false, NULL, &csock) < 0)
            goto cleanup;
    } else {
        if (virNetSocketNewConnectTCP(data->cnode, portstr,
                                      AF_UNSPEC,
                                      &csock) < 0)
            goto cleanup;
    }
180

181
    virObjectUnref(csock);
182

183
    for (i = 0; i < nlsock; i++) {
184 185 186 187 188 189 190 191
        if (virNetSocketAccept(lsock[i], &ssock) != -1 && ssock) {
            char c = 'a';
            if (virNetSocketWrite(ssock, &c, 1) != -1 &&
                virNetSocketRead(ssock, &c, 1) != -1) {
                VIR_DEBUG("Unexpected client socket present");
                goto cleanup;
            }
        }
192
        virObjectUnref(ssock);
193 194 195 196 197
        ssock = NULL;
    }

    ret = 0;

198
 cleanup:
199
    virObjectUnref(ssock);
200
    for (i = 0; i < nlsock; i++)
201
        virObjectUnref(lsock[i]);
202 203
    VIR_FREE(lsock);
    VIR_FREE(path);
204 205
    if (tmpdir)
        rmdir(tmpdir);
206 207
    return ret;
}
208
#endif
209 210


211
#ifndef WIN32
212 213 214 215 216 217 218
static int testSocketUNIXAddrs(const void *data ATTRIBUTE_UNUSED)
{
    virNetSocketPtr lsock = NULL; /* Listen socket */
    virNetSocketPtr ssock = NULL; /* Server socket */
    virNetSocketPtr csock = NULL; /* Client socket */
    int ret = -1;

219
    char *path = NULL;
220 221 222 223 224
    char *tmpdir;
    char template[] = "/tmp/libvirt_XXXXXX";

    tmpdir = mkdtemp(template);
    if (tmpdir == NULL) {
225
        VIR_WARN("Failed to create temporary directory");
226 227
        goto cleanup;
    }
228
    if (virAsprintf(&path, "%s/test.sock", tmpdir) < 0)
229
        goto cleanup;
230

231
    if (virNetSocketNewListenUNIX(path, 0700, -1, getegid(), &lsock) < 0)
232 233
        goto cleanup;

234
    if (STRNEQ(virNetSocketLocalAddrStringSASL(lsock), "127.0.0.1;0")) {
235 236 237 238
        VIR_DEBUG("Unexpected local address");
        goto cleanup;
    }

239
    if (virNetSocketRemoteAddrStringSASL(lsock) != NULL) {
240 241 242 243
        VIR_DEBUG("Unexpected remote address");
        goto cleanup;
    }

244
    if (virNetSocketListen(lsock, 0) < 0)
245 246 247 248 249
        goto cleanup;

    if (virNetSocketNewConnectUNIX(path, false, NULL, &csock) < 0)
        goto cleanup;

250
    if (STRNEQ(virNetSocketLocalAddrStringSASL(csock), "127.0.0.1;0")) {
251 252 253 254
        VIR_DEBUG("Unexpected local address");
        goto cleanup;
    }

255
    if (STRNEQ(virNetSocketRemoteAddrStringSASL(csock), "127.0.0.1;0")) {
256
        VIR_DEBUG("Unexpected remote address");
257 258 259
        goto cleanup;
    }

260 261 262 263 264
    if (STRNEQ(virNetSocketRemoteAddrStringURI(csock), "127.0.0.1:0")) {
        VIR_DEBUG("Unexpected remote address");
        goto cleanup;
    }

265 266 267 268 269 270 271

    if (virNetSocketAccept(lsock, &ssock) < 0) {
        VIR_DEBUG("Unexpected client socket missing");
        goto cleanup;
    }


272
    if (STRNEQ(virNetSocketLocalAddrStringSASL(ssock), "127.0.0.1;0")) {
273 274 275 276
        VIR_DEBUG("Unexpected local address");
        goto cleanup;
    }

277
    if (STRNEQ(virNetSocketRemoteAddrStringSASL(ssock), "127.0.0.1;0")) {
278
        VIR_DEBUG("Unexpected remote address");
279 280 281
        goto cleanup;
    }

282 283 284 285 286
    if (STRNEQ(virNetSocketRemoteAddrStringURI(ssock), "127.0.0.1:0")) {
        VIR_DEBUG("Unexpected remote address");
        goto cleanup;
    }

287 288 289

    ret = 0;

290
 cleanup:
291
    VIR_FREE(path);
292 293 294
    virObjectUnref(lsock);
    virObjectUnref(ssock);
    virObjectUnref(csock);
295 296
    if (tmpdir)
        rmdir(tmpdir);
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
    return ret;
}

static int testSocketCommandNormal(const void *data ATTRIBUTE_UNUSED)
{
    virNetSocketPtr csock = NULL; /* Client socket */
    char buf[100];
    size_t i;
    int ret = -1;
    virCommandPtr cmd = virCommandNewArgList("/bin/cat", "/dev/zero", NULL);
    virCommandAddEnvPassCommon(cmd);

    if (virNetSocketNewConnectCommand(cmd, &csock) < 0)
        goto cleanup;

    virNetSocketSetBlocking(csock, true);

    if (virNetSocketRead(csock, buf, sizeof(buf)) < 0)
        goto cleanup;

317
    for (i = 0; i < sizeof(buf); i++)
318 319 320 321 322
        if (buf[i] != '\0')
            goto cleanup;

    ret = 0;

323
 cleanup:
324
    virObjectUnref(csock);
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
    return ret;
}

static int testSocketCommandFail(const void *data ATTRIBUTE_UNUSED)
{
    virNetSocketPtr csock = NULL; /* Client socket */
    char buf[100];
    int ret = -1;
    virCommandPtr cmd = virCommandNewArgList("/bin/cat", "/dev/does-not-exist", NULL);
    virCommandAddEnvPassCommon(cmd);

    if (virNetSocketNewConnectCommand(cmd, &csock) < 0)
        goto cleanup;

    virNetSocketSetBlocking(csock, true);

    if (virNetSocketRead(csock, buf, sizeof(buf)) == 0)
        goto cleanup;

    ret = 0;

346
 cleanup:
347
    virObjectUnref(csock);
348 349 350 351 352 353 354 355 356
    return ret;
}

struct testSSHData {
    const char *nodename;
    const char *service;
    const char *binary;
    const char *username;
    bool noTTY;
357
    bool noVerify;
358
    const char *netcat;
359
    const char *keyfile;
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
    const char *path;

    const char *expectOut;
    bool failConnect;
    bool dieEarly;
};

static int testSocketSSH(const void *opaque)
{
    const struct testSSHData *data = opaque;
    virNetSocketPtr csock = NULL; /* Client socket */
    int ret = -1;
    char buf[1024];

    if (virNetSocketNewConnectSSH(data->nodename,
                                  data->service,
                                  data->binary,
                                  data->username,
                                  data->noTTY,
379
                                  data->noVerify,
380
                                  data->netcat,
381
                                  data->keyfile,
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400
                                  data->path,
                                  &csock) < 0)
        goto cleanup;

    virNetSocketSetBlocking(csock, true);

    if (data->failConnect) {
        if (virNetSocketRead(csock, buf, sizeof(buf)-1) >= 0) {
            VIR_DEBUG("Expected connect failure, but got some socket data");
            goto cleanup;
        }
    } else {
        ssize_t rv;
        if ((rv = virNetSocketRead(csock, buf, sizeof(buf)-1)) < 0) {
            VIR_DEBUG("Didn't get any socket data");
            goto cleanup;
        }
        buf[rv] = '\0';

401
        if (STRNEQ(buf, data->expectOut)) {
402
            virTestDifference(stderr, data->expectOut, buf);
403 404 405 406 407 408 409 410 411 412 413 414
            goto cleanup;
        }

        if (data->dieEarly &&
            virNetSocketRead(csock, buf, sizeof(buf)-1) >= 0) {
            VIR_DEBUG("Got too much socket data");
            goto cleanup;
        }
    }

    ret = 0;

415
 cleanup:
416
    virObjectUnref(csock);
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    return ret;
}

#endif


static int
mymain(void)
{
    int ret = 0;
#ifdef HAVE_IFADDRS_H
    bool hasIPv4, hasIPv6;
    int freePort;
#endif

    signal(SIGPIPE, SIG_IGN);

#ifdef HAVE_IFADDRS_H
    if (checkProtocols(&hasIPv4, &hasIPv6, &freePort) < 0) {
        fprintf(stderr, "Cannot identify IPv4/6 availability\n");
437
        return EXIT_FAILURE;
438 439 440
    }

    if (hasIPv4) {
441 442
        struct testSocketData tcpData = { "127.0.0.1", freePort, "127.0.0.1" };
        if (virTestRun("Socket TCP/IPv4 Accept", testSocketAccept, &tcpData) < 0)
443 444 445
            ret = -1;
    }
    if (hasIPv6) {
446 447
        struct testSocketData tcpData = { "::1", freePort, "::1" };
        if (virTestRun("Socket TCP/IPv6 Accept", testSocketAccept, &tcpData) < 0)
448 449 450
            ret = -1;
    }
    if (hasIPv6 && hasIPv4) {
451 452
        struct testSocketData tcpData = { NULL, freePort, "127.0.0.1" };
        if (virTestRun("Socket TCP/IPv4+IPv6 Accept", testSocketAccept, &tcpData) < 0)
453 454 455
            ret = -1;

        tcpData.cnode = "::1";
456
        if (virTestRun("Socket TCP/IPv4+IPv6 Accept", testSocketAccept, &tcpData) < 0)
457 458 459 460 461
            ret = -1;
    }
#endif

#ifndef WIN32
462
    if (virTestRun("Socket UNIX Accept", testSocketAccept, NULL) < 0)
463 464
        ret = -1;

465
    if (virTestRun("Socket UNIX Addrs", testSocketUNIXAddrs, NULL) < 0)
466 467
        ret = -1;

468
    if (virTestRun("Socket External Command /dev/zero", testSocketCommandNormal, NULL) < 0)
469
        ret = -1;
470
    if (virTestRun("Socket External Command /dev/does-not-exist", testSocketCommandFail, NULL) < 0)
471 472 473 474 475
        ret = -1;

    struct testSSHData sshData1 = {
        .nodename = "somehost",
        .path = "/tmp/socket",
476
        .expectOut = "-- somehost sh -c 'if 'nc' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
477 478 479 480
                                         "ARG=-q0;"
                                     "else "
                                         "ARG=;"
                                     "fi;"
481
                                     "'nc' $ARG -U /tmp/socket'\n",
482
    };
483
    if (virTestRun("SSH test 1", testSocketSSH, &sshData1) < 0)
484 485 486 487 488 489 490 491
        ret = -1;

    struct testSSHData sshData2 = {
        .nodename = "somehost",
        .service = "9000",
        .username = "fred",
        .netcat = "netcat",
        .noTTY = true,
492
        .noVerify = false,
493
        .path = "/tmp/socket",
494
        .expectOut = "-p 9000 -l fred -T -o BatchMode=yes -e none -- somehost sh -c '"
495
                     "if 'netcat' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
496 497 498 499
                         "ARG=-q0;"
                     "else "
                         "ARG=;"
                     "fi;"
500
                     "'netcat' $ARG -U /tmp/socket'\n",
501
    };
502
    if (virTestRun("SSH test 2", testSocketSSH, &sshData2) < 0)
503 504 505
        ret = -1;

    struct testSSHData sshData3 = {
506 507 508 509 510 511
        .nodename = "somehost",
        .service = "9000",
        .username = "fred",
        .netcat = "netcat",
        .noTTY = false,
        .noVerify = true,
512
        .path = "/tmp/socket",
513
        .expectOut = "-p 9000 -l fred -o StrictHostKeyChecking=no -- somehost sh -c '"
514
                     "if 'netcat' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
515 516 517 518
                         "ARG=-q0;"
                     "else "
                         "ARG=;"
                     "fi;"
519
                     "'netcat' $ARG -U /tmp/socket'\n",
520
    };
521
    if (virTestRun("SSH test 3", testSocketSSH, &sshData3) < 0)
522 523 524
        ret = -1;

    struct testSSHData sshData4 = {
525 526 527 528
        .nodename = "nosuchhost",
        .path = "/tmp/socket",
        .failConnect = true,
    };
529
    if (virTestRun("SSH test 4", testSocketSSH, &sshData4) < 0)
530 531 532
        ret = -1;

    struct testSSHData sshData5 = {
533 534
        .nodename = "crashyhost",
        .path = "/tmp/socket",
535
        .expectOut = "-- crashyhost sh -c "
536
                     "'if 'nc' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
537 538 539 540
                         "ARG=-q0;"
                     "else "
                         "ARG=;"
                     "fi;"
541
                     "'nc' $ARG -U /tmp/socket'\n",
542 543
        .dieEarly = true,
    };
544
    if (virTestRun("SSH test 5", testSocketSSH, &sshData5) < 0)
545 546
        ret = -1;

547 548 549 550 551
    struct testSSHData sshData6 = {
        .nodename = "example.com",
        .path = "/tmp/socket",
        .keyfile = "/root/.ssh/example_key",
        .noVerify = true,
552
        .expectOut = "-i /root/.ssh/example_key -o StrictHostKeyChecking=no -- example.com sh -c '"
553
                     "if 'nc' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
554 555 556 557
                         "ARG=-q0;"
                     "else "
                         "ARG=;"
                     "fi;"
558
                     "'nc' $ARG -U /tmp/socket'\n",
559
    };
560
    if (virTestRun("SSH test 6", testSocketSSH, &sshData6) < 0)
561 562
        ret = -1;

563 564 565 566
    struct testSSHData sshData7 = {
        .nodename = "somehost",
        .netcat = "nc -4",
        .path = "/tmp/socket",
567
        .expectOut = "-- somehost sh -c 'if ''nc -4'' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
568 569 570 571 572 573
                                         "ARG=-q0;"
                                     "else "
                                         "ARG=;"
                                     "fi;"
                                     "''nc -4'' $ARG -U /tmp/socket'\n",
    };
574
    if (virTestRun("SSH test 7", testSocketSSH, &sshData7) < 0)
575 576
        ret = -1;

577 578
#endif

579
    return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
580 581
}

582
VIR_TEST_MAIN(mymain)