xref: /illumos-gate/usr/src/cmd/dtrace/test/tst/common/ip/msnc.c (revision 1b1703a43cdfe482965d40a4baae758d05844ac2)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2025 Oxide Computer Company
14  */
15 
16 /*
17  * This is a mini/silent version of netcat. It's used by a few of the networking
18  * tests in this folder to make sure we exercise a full TCP state machine by
19  * calling shutdown(SHUT_RD) specifically to make sure that we can hit the
20  * TCPS_LAST_ACK state.
21  *
22  * It basically sets up an event port and tracks attempting to:
23  *
24  *  - Connect to a target source
25  *  - Read some data from the target source (but doesn't care how much)
26  *  - Call shutdown for read
27  *  - Close the socket
28  *
29  * We put a 5 second timeout on all of these as an arbitrary threshold.
30  */
31 
32 #include <err.h>
33 #include <stdlib.h>
34 #include <port.h>
35 #include <sys/socket.h>
36 #include <netdb.h>
37 #include <port.h>
38 #include <sys/debug.h>
39 #include <errno.h>
40 
41 uint32_t timeout_sec = 5;
42 const char *hello = "Hello World";
43 
44 static void
45 msnc_wait(int port, int sock, int event)
46 {
47 	port_event_t pe;
48 	timespec_t ts;
49 
50 	if (port_associate(port, PORT_SOURCE_FD, sock, event, NULL) != 0) {
51 		err(EXIT_FAILURE, "failed to associate for event 0x%x", event);
52 	}
53 
54 	ts.tv_sec = (time_t)timeout_sec;
55 	ts.tv_nsec = 0;
56 	if (port_get(port, &pe, &ts) != 0) {
57 		err(EXIT_FAILURE, "failed to wait for event 0x%x", event);
58 	}
59 
60 	VERIFY3U(pe.portev_source, ==, PORT_SOURCE_FD);
61 	VERIFY3U(pe.portev_object, ==, sock);
62 	if ((pe.portev_events & event) == 0) {
63 		err(EXIT_FAILURE, "got events we weren't going to handle: "
64 		    "0x%x, wanted 0x%x", event, event);
65 	}
66 }
67 
68 int
69 main(int argc, char *argv[])
70 {
71 	int ret, cerr, sock, eport;
72 	struct addrinfo *res, hints;
73 	socklen_t sz = sizeof (cerr);
74 	uint8_t buf[16];
75 
76 	if (argc != 3) {
77 		(void) fprintf(stderr, "missing required arguments\n");
78 		errx(EXIT_FAILURE, "msnc: Usage <ip> <port>");
79 	}
80 
81 	(void) memset(&hints, 0, sizeof (struct addrinfo));
82 	hints.ai_flags = AI_NUMERICHOST | AI_ADDRCONFIG;
83 	hints.ai_family = PF_UNSPEC;
84 	hints.ai_socktype = SOCK_STREAM;
85 
86 	if ((ret = getaddrinfo(argv[1], argv[2], &hints, &res)) != 0) {
87 		errx(EXIT_FAILURE, "failed to resolve %s: %s", argv[1],
88 		    gai_strerror(ret));
89 	}
90 
91 	sock = socket(res->ai_family, res->ai_socktype | SOCK_NONBLOCK,
92 	    res->ai_protocol);
93 	if (sock < 0) {
94 		err(EXIT_FAILURE, "failed to create socket");
95 	}
96 
97 	eport = port_create();
98 	if (eport < 0) {
99 		err(EXIT_FAILURE, "failed to create event port");
100 	}
101 
102 	ret = connect(sock, res->ai_addr, res->ai_addrlen);
103 	if (ret != 0 && errno != EINPROGRESS && errno != EINTR) {
104 		err(EXIT_FAILURE, "failed to connect to %s:%s\n", argv[1],
105 		    argv[2]);
106 	}
107 
108 	msnc_wait(eport, sock, POLLOUT);
109 	if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &cerr, &sz) != 0) {
110 		err(EXIT_FAILURE, "failed to get connection status sockopt");
111 	}
112 
113 	if (write(sock, hello, strlen(hello)) < 0) {
114 		err(EXIT_FAILURE, "failed to write any data to the socket");
115 	}
116 
117 	/*
118 	 * Read some bytes, the exact quantity doesn't matter. But before that
119 	 * perform our shutdown() to help get the state machine in motion.
120 	 */
121 	msnc_wait(eport, sock, POLLIN);
122 	if (shutdown(sock, SHUT_WR) != 0) {
123 		err(EXIT_FAILURE, "failed to shutdown write side socket");
124 	}
125 
126 	if (read(sock, buf, sizeof (buf)) < 0) {
127 		err(EXIT_FAILURE, "failed to read any data from remote side");
128 	}
129 
130 	VERIFY0(close(sock));
131 	VERIFY0(close(eport));
132 	return (EXIT_SUCCESS);
133 }
134