2 * Copyright (C) 2011, 2014 Red Hat, Inc.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see
16 * <http://www.gnu.org/licenses/>.
27 #include "testutils.h"
33 #include "virstring.h"
35 #include "rpc/virnetsocket.h"
37 #define VIR_FROM_THIS VIR_FROM_RPC
39 VIR_LOG_INIT("tests.netsockettest");
42 # define BASE_PORT 5672
45 checkProtocols(bool *hasIPv4
, bool *hasIPv6
,
48 struct sockaddr_in in4
;
49 struct sockaddr_in6 in6
;
55 if (virNetSocketCheckProtocols(hasIPv4
, hasIPv6
) < 0)
58 for (i
= 0; i
< 50; i
++) {
60 if ((s4
= socket(AF_INET
, SOCK_STREAM
, 0)) < 0)
64 if ((s6
= socket(AF_INET6
, SOCK_STREAM
, 0)) < 0)
67 if (setsockopt(s6
, IPPROTO_IPV6
, IPV6_V6ONLY
, &only
, sizeof(only
)) < 0)
71 memset(&in4
, 0, sizeof(in4
));
72 memset(&in6
, 0, sizeof(in6
));
74 in4
.sin_family
= AF_INET
;
75 in4
.sin_port
= htons(BASE_PORT
+ i
);
76 in4
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
77 in6
.sin6_family
= AF_INET6
;
78 in6
.sin6_port
= htons(BASE_PORT
+ i
);
79 in6
.sin6_addr
= in6addr_loopback
;
81 if (bind(s4
, (struct sockaddr
*)&in4
, sizeof(in4
)) < 0) {
82 if (errno
== EADDRINUSE
) {
91 if (bind(s6
, (struct sockaddr
*)&in6
, sizeof(in6
)) < 0) {
92 if (errno
== EADDRINUSE
) {
101 *freePort
= BASE_PORT
+ i
;
105 VIR_DEBUG("Choose port %d", *freePort
);
115 struct testClientData
{
122 testSocketClient(void *opaque
)
124 struct testClientData
*data
= opaque
;
126 virNetSocketPtr csock
= NULL
;
129 if (virNetSocketNewConnectUNIX(data
->path
, false,
133 if (virNetSocketNewConnectTCP(data
->cnode
, data
->portstr
,
139 virNetSocketSetBlocking(csock
, true);
141 if (virNetSocketRead(csock
, &c
, 1) != 1) {
142 VIR_DEBUG("Cannot read from server");
145 if (virNetSocketWrite(csock
, &c
, 1) != 1) {
146 VIR_DEBUG("Cannot write to server");
151 virObjectUnref(csock
);
156 testSocketIncoming(virNetSocketPtr sock
,
157 int events ATTRIBUTE_UNUSED
,
160 virNetSocketPtr
*retsock
= opaque
;
161 VIR_DEBUG("Incoming sock=%p events=%d", sock
, events
);
166 struct testSocketData
{
174 testSocketAccept(const void *opaque
)
176 virNetSocketPtr
*lsock
= NULL
; /* Listen socket */
177 size_t nlsock
= 0, i
;
178 virNetSocketPtr ssock
= NULL
; /* Server socket */
179 virNetSocketPtr rsock
= NULL
; /* Incoming client socket */
180 const struct testSocketData
*data
= opaque
;
185 char template[] = "/tmp/libvirt_XXXXXX";
187 struct testClientData cdata
= { 0 };
188 bool goodsock
= false;
193 virNetSocketPtr usock
;
194 tmpdir
= mkdtemp(template);
195 if (tmpdir
== NULL
) {
196 VIR_WARN("Failed to create temporary directory");
199 if (virAsprintf(&path
, "%s/test.sock", tmpdir
) < 0)
202 if (virNetSocketNewListenUNIX(path
, 0700, -1, getegid(), &usock
) < 0)
205 if (VIR_ALLOC_N(lsock
, 1) < 0) {
206 virObjectUnref(usock
);
215 snprintf(portstr
, sizeof(portstr
), "%d", data
->port
);
216 if (virNetSocketNewListenTCP(data
->lnode
, portstr
,
218 &lsock
, &nlsock
) < 0)
221 cdata
.cnode
= data
->cnode
;
222 cdata
.portstr
= portstr
;
225 for (i
= 0; i
< nlsock
; i
++) {
226 if (virNetSocketListen(lsock
[i
], 0) < 0)
229 if (virNetSocketAddIOCallback(lsock
[i
],
230 VIR_EVENT_HANDLE_READABLE
,
238 if (virThreadCreate(&th
, true,
243 while (rsock
== NULL
) {
244 if (virEventRunDefaultImpl() < 0)
248 for (i
= 0; i
< nlsock
; i
++) {
249 if (lsock
[i
] == rsock
) {
256 virReportError(VIR_ERR_INTERNAL_ERROR
, "%s",
257 "Unexpected server socket seen");
261 if (virNetSocketAccept(rsock
, &ssock
) < 0)
265 virReportError(VIR_ERR_INTERNAL_ERROR
, "%s",
266 "Client went away unexpectedly");
270 virNetSocketSetBlocking(ssock
, true);
272 if (virNetSocketWrite(ssock
, &a
, 1) < 0 ||
273 virNetSocketRead(ssock
, &b
, 1) < 0) {
278 virReportError(VIR_ERR_INTERNAL_ERROR
,
279 "Bad data received '%x' != '%x'", a
, b
);
283 virObjectUnref(ssock
);
292 virObjectUnref(ssock
);
293 for (i
= 0; i
< nlsock
; i
++) {
294 virNetSocketRemoveIOCallback(lsock
[i
]);
295 virNetSocketClose(lsock
[i
]);
296 virObjectUnref(lsock
[i
]);
308 static int testSocketUNIXAddrs(const void *data ATTRIBUTE_UNUSED
)
310 virNetSocketPtr lsock
= NULL
; /* Listen socket */
311 virNetSocketPtr ssock
= NULL
; /* Server socket */
312 virNetSocketPtr csock
= NULL
; /* Client socket */
317 char template[] = "/tmp/libvirt_XXXXXX";
319 tmpdir
= mkdtemp(template);
320 if (tmpdir
== NULL
) {
321 VIR_WARN("Failed to create temporary directory");
324 if (virAsprintf(&path
, "%s/test.sock", tmpdir
) < 0)
327 if (virNetSocketNewListenUNIX(path
, 0700, -1, getegid(), &lsock
) < 0)
330 if (STRNEQ(virNetSocketLocalAddrStringSASL(lsock
), "127.0.0.1;0")) {
331 VIR_DEBUG("Unexpected local address");
335 if (virNetSocketRemoteAddrStringSASL(lsock
) != NULL
) {
336 VIR_DEBUG("Unexpected remote address");
340 if (virNetSocketListen(lsock
, 0) < 0)
343 if (virNetSocketNewConnectUNIX(path
, false, NULL
, &csock
) < 0)
346 if (STRNEQ(virNetSocketLocalAddrStringSASL(csock
), "127.0.0.1;0")) {
347 VIR_DEBUG("Unexpected local address");
351 if (STRNEQ(virNetSocketRemoteAddrStringSASL(csock
), "127.0.0.1;0")) {
352 VIR_DEBUG("Unexpected remote address");
356 if (STRNEQ(virNetSocketRemoteAddrStringURI(csock
), "127.0.0.1:0")) {
357 VIR_DEBUG("Unexpected remote address");
362 if (virNetSocketAccept(lsock
, &ssock
) < 0) {
363 VIR_DEBUG("Unexpected client socket missing");
368 if (STRNEQ(virNetSocketLocalAddrStringSASL(ssock
), "127.0.0.1;0")) {
369 VIR_DEBUG("Unexpected local address");
373 if (STRNEQ(virNetSocketRemoteAddrStringSASL(ssock
), "127.0.0.1;0")) {
374 VIR_DEBUG("Unexpected remote address");
378 if (STRNEQ(virNetSocketRemoteAddrStringURI(ssock
), "127.0.0.1:0")) {
379 VIR_DEBUG("Unexpected remote address");
388 virObjectUnref(lsock
);
389 virObjectUnref(ssock
);
390 virObjectUnref(csock
);
396 static int testSocketCommandNormal(const void *data ATTRIBUTE_UNUSED
)
398 virNetSocketPtr csock
= NULL
; /* Client socket */
402 virCommandPtr cmd
= virCommandNewArgList("/bin/cat", "/dev/zero", NULL
);
403 virCommandAddEnvPassCommon(cmd
);
405 if (virNetSocketNewConnectCommand(cmd
, &csock
) < 0)
408 virNetSocketSetBlocking(csock
, true);
410 if (virNetSocketRead(csock
, buf
, sizeof(buf
)) < 0)
413 for (i
= 0; i
< sizeof(buf
); i
++)
420 virObjectUnref(csock
);
424 static int testSocketCommandFail(const void *data ATTRIBUTE_UNUSED
)
426 virNetSocketPtr csock
= NULL
; /* Client socket */
429 virCommandPtr cmd
= virCommandNewArgList("/bin/cat", "/dev/does-not-exist", NULL
);
430 virCommandAddEnvPassCommon(cmd
);
432 if (virNetSocketNewConnectCommand(cmd
, &csock
) < 0)
435 virNetSocketSetBlocking(csock
, true);
437 if (virNetSocketRead(csock
, buf
, sizeof(buf
)) == 0)
443 virObjectUnref(csock
);
448 const char *nodename
;
451 const char *username
;
458 const char *expectOut
;
463 static int testSocketSSH(const void *opaque
)
465 const struct testSSHData
*data
= opaque
;
466 virNetSocketPtr csock
= NULL
; /* Client socket */
470 if (virNetSocketNewConnectSSH(data
->nodename
,
482 virNetSocketSetBlocking(csock
, true);
484 if (data
->failConnect
) {
485 if (virNetSocketRead(csock
, buf
, sizeof(buf
)-1) >= 0) {
486 VIR_DEBUG("Expected connect failure, but got some socket data");
491 if ((rv
= virNetSocketRead(csock
, buf
, sizeof(buf
)-1)) < 0) {
492 VIR_DEBUG("Didn't get any socket data");
497 if (STRNEQ(buf
, data
->expectOut
)) {
498 virTestDifference(stderr
, data
->expectOut
, buf
);
502 if (data
->dieEarly
&&
503 virNetSocketRead(csock
, buf
, sizeof(buf
)-1) >= 0) {
504 VIR_DEBUG("Got too much socket data");
512 virObjectUnref(csock
);
523 #ifdef HAVE_IFADDRS_H
524 bool hasIPv4
, hasIPv6
;
528 signal(SIGPIPE
, SIG_IGN
);
530 virEventRegisterDefaultImpl();
532 #ifdef HAVE_IFADDRS_H
533 if (checkProtocols(&hasIPv4
, &hasIPv6
, &freePort
) < 0) {
534 fprintf(stderr
, "Cannot identify IPv4/6 availability\n");
539 struct testSocketData tcpData
= { "127.0.0.1", freePort
, "127.0.0.1" };
540 if (virTestRun("Socket TCP/IPv4 Accept", testSocketAccept
, &tcpData
) < 0)
544 struct testSocketData tcpData
= { "::1", freePort
, "::1" };
545 if (virTestRun("Socket TCP/IPv6 Accept", testSocketAccept
, &tcpData
) < 0)
548 if (hasIPv6
&& hasIPv4
) {
549 struct testSocketData tcpData
= { NULL
, freePort
, "127.0.0.1" };
550 if (virTestRun("Socket TCP/IPv4+IPv6 Accept", testSocketAccept
, &tcpData
) < 0)
553 tcpData
.cnode
= "::1";
554 if (virTestRun("Socket TCP/IPv4+IPv6 Accept", testSocketAccept
, &tcpData
) < 0)
560 if (virTestRun("Socket UNIX Accept", testSocketAccept
, NULL
) < 0)
563 if (virTestRun("Socket UNIX Addrs", testSocketUNIXAddrs
, NULL
) < 0)
566 if (virTestRun("Socket External Command /dev/zero", testSocketCommandNormal
, NULL
) < 0)
568 if (virTestRun("Socket External Command /dev/does-not-exist", testSocketCommandFail
, NULL
) < 0)
571 struct testSSHData sshData1
= {
572 .nodename
= "somehost",
573 .path
= "/tmp/socket",
574 .expectOut
= "-T -e none -- somehost sh -c '"
575 "if 'nc' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
580 "'nc' $ARG -U /tmp/socket'\n",
582 if (virTestRun("SSH test 1", testSocketSSH
, &sshData1
) < 0)
585 struct testSSHData sshData2
= {
586 .nodename
= "somehost",
592 .path
= "/tmp/socket",
593 .expectOut
= "-p 9000 -l fred -T -e none -o BatchMode=yes -- somehost sh -c '"
594 "if 'netcat' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
599 "'netcat' $ARG -U /tmp/socket'\n",
601 if (virTestRun("SSH test 2", testSocketSSH
, &sshData2
) < 0)
604 struct testSSHData sshData3
= {
605 .nodename
= "somehost",
611 .path
= "/tmp/socket",
612 .expectOut
= "-p 9000 -l fred -T -e none -o StrictHostKeyChecking=no -- somehost sh -c '"
613 "if 'netcat' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
618 "'netcat' $ARG -U /tmp/socket'\n",
620 if (virTestRun("SSH test 3", testSocketSSH
, &sshData3
) < 0)
623 struct testSSHData sshData4
= {
624 .nodename
= "nosuchhost",
625 .path
= "/tmp/socket",
628 if (virTestRun("SSH test 4", testSocketSSH
, &sshData4
) < 0)
631 struct testSSHData sshData5
= {
632 .nodename
= "crashyhost",
633 .path
= "/tmp/socket",
634 .expectOut
= "-T -e none -- crashyhost sh -c "
635 "'if 'nc' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
640 "'nc' $ARG -U /tmp/socket'\n",
643 if (virTestRun("SSH test 5", testSocketSSH
, &sshData5
) < 0)
646 struct testSSHData sshData6
= {
647 .nodename
= "example.com",
648 .path
= "/tmp/socket",
649 .keyfile
= "/root/.ssh/example_key",
651 .expectOut
= "-i /root/.ssh/example_key -T -e none -o StrictHostKeyChecking=no -- example.com sh -c '"
652 "if 'nc' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
657 "'nc' $ARG -U /tmp/socket'\n",
659 if (virTestRun("SSH test 6", testSocketSSH
, &sshData6
) < 0)
662 struct testSSHData sshData7
= {
663 .nodename
= "somehost",
665 .path
= "/tmp/socket",
666 .expectOut
= "-T -e none -- somehost sh -c '"
667 "if ''nc -4'' -q 2>&1 | grep \"requires an argument\" >/dev/null 2>&1; then "
672 "''nc -4'' $ARG -U /tmp/socket'\n",
674 if (virTestRun("SSH test 7", testSocketSSH
, &sshData7
) < 0)
679 return ret
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;
682 VIR_TEST_MAIN(mymain
)