1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2012, 2016 by Delphix. All rights reserved.
25  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
29 /*	  All Rights Reserved  	*/
30 
31 /*
32  * Portions of this source code were derived from Berkeley 4.3 BSD
33  * under license from the Regents of the University of California.
34  */
35 
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <ctype.h>
39 #include <sys/types.h>
40 #include <string.h>
41 #include <syslog.h>
42 #include <sys/param.h>
43 #include <rpc/rpc.h>
44 #include <sys/stat.h>
45 #include <netconfig.h>
46 #include <netdir.h>
47 
48 #include <sys/file.h>
49 #include <sys/time.h>
50 #include <sys/errno.h>
51 #include <rpcsvc/mount.h>
52 
53 #include <signal.h>
54 #include <locale.h>
55 #include <unistd.h>
56 #include <errno.h>
57 #include <sys/socket.h>
58 #include <netinet/in.h>
59 #include <arpa/inet.h>
60 #include <netdb.h>
61 
62 #include <thread.h>
63 #include <assert.h>
64 
65 #include <limits.h>
66 
67 #define	TESTPROG	987654
68 
69 uint32_t test_vers_max = 2;
70 uint32_t test_vers_min = 1;
71 
72 int debug;
73 int verbose;
74 int testd_port;
75 
76 static void mysvc(struct svc_req *, SVCXPRT *);
77 static void bind2(void);
78 
79 /*
80  * This function is called for each configured network type to
81  * bind and register our RPC service programs.
82  *
83  * On TCP or UDP, we want to bind TESTPROG on a specific port
84  * (when testd_port is specified) in which case we'll use the
85  * variant of svc_tp_create() that lets us pass a bind address.
86  */
87 static void
test_svc_tp_create(struct netconfig * nconf)88 test_svc_tp_create(struct netconfig *nconf)
89 {
90 	char port_str[8];
91 	struct nd_hostserv hs;
92 	struct nd_addrlist *al = NULL;
93 	SVCXPRT *xprt = NULL;
94 	rpcvers_t vers;
95 
96 	vers = test_vers_max;
97 
98 	/*
99 	 * If testd_port is set and this is an inet transport,
100 	 * bind this service on the specified port.
101 	 */
102 	if (testd_port != 0 &&
103 	    (strcmp(nconf->nc_protofmly, NC_INET) == 0 ||
104 	    strcmp(nconf->nc_protofmly, NC_INET6) == 0)) {
105 		int err;
106 
107 		snprintf(port_str, sizeof (port_str), "%u",
108 		    (unsigned short)testd_port);
109 
110 		hs.h_host = HOST_SELF_BIND;
111 		hs.h_serv = port_str;
112 		err = netdir_getbyname((struct netconfig *)nconf, &hs, &al);
113 		if (err == 0 && al != NULL) {
114 			xprt = svc_tp_create_addr(mysvc, TESTPROG, vers,
115 			    nconf, al->n_addrs);
116 			netdir_free(al, ND_ADDRLIST);
117 		}
118 		if (xprt == NULL) {
119 			printf("testd: unable to create "
120 			    "(TESTD,%d) on transport %s (port %d)\n",
121 			    (int)vers, nconf->nc_netid, testd_port);
122 		}
123 		/* fall-back to default bind */
124 	}
125 	if (xprt == NULL) {
126 		/*
127 		 * Had testd_port=0, or non-inet transport,
128 		 * or the bind to a specific port failed.
129 		 * Do a default bind.
130 		 */
131 		xprt = svc_tp_create(mysvc, TESTPROG, vers, nconf);
132 	}
133 	if (xprt == NULL) {
134 		printf("testd: unable to create "
135 		    "(TESTD,%d) on transport %s\n",
136 		    (int)vers, nconf->nc_netid);
137 		return;
138 	}
139 
140 	/*
141 	 * Register additional versions on this transport.
142 	 */
143 	while (--vers >= test_vers_min) {
144 		if (!svc_reg(xprt, TESTPROG, vers, mysvc, nconf)) {
145 			printf("testd: "
146 			    "failed to register vers %d on %s\n",
147 			    (int)vers, nconf->nc_netid);
148 		}
149 	}
150 }
151 
152 static void
test_svc_unreg(void)153 test_svc_unreg(void)
154 {
155 	rpcvers_t vers;
156 
157 	for (vers = test_vers_min; vers <= test_vers_max; vers++)
158 		svc_unreg(TESTPROG, vers);
159 }
160 
161 int
main(int argc,char * argv[])162 main(int argc, char *argv[])
163 {
164 	int	c;
165 	bool_t	exclbind = TRUE;
166 	int tmp;
167 	struct netconfig *nconf;
168 	NCONF_HANDLE *nc;
169 
170 	while ((c = getopt(argc, argv, "dvp:")) != EOF) {
171 		switch (c) {
172 		case 'd':
173 			debug++;
174 			break;
175 		case 'v':
176 			verbose++;
177 			break;
178 		case 'p':
179 			(void) sscanf(optarg, "%d", &tmp);
180 			if (tmp < 1 || tmp > UINT16_MAX) {
181 				(void) fprintf(stderr,
182 				    "testd: -P port invalid.\n");
183 				return (1);
184 			}
185 			testd_port = tmp;
186 			break;
187 		default:
188 			fprintf(stderr, "usage: testd [-v] [-r]\n");
189 			exit(1);
190 		}
191 	}
192 
193 	(void) setlocale(LC_ALL, "");
194 
195 #if !defined(TEXT_DOMAIN)
196 #define	TEXT_DOMAIN "SYS_TEST"
197 #endif
198 	(void) textdomain(TEXT_DOMAIN);
199 
200 	/*
201 	 * Prevent our non-priv udp and tcp ports bound w/wildcard addr
202 	 * from being hijacked by a bind to a more specific addr.
203 	 */
204 	if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) {
205 		fprintf(stderr, "warning: unable to set udp/tcp EXCLBIND\n");
206 	}
207 
208 	if (testd_port < 0 || testd_port > UINT16_MAX) {
209 		fprintf(stderr, "unable to use specified port\n");
210 		exit(1);
211 	}
212 
213 	/*
214 	 * Make sure to unregister any previous versions in case the
215 	 * user is reconfiguring the server in interesting ways.
216 	 */
217 	test_svc_unreg();
218 
219 	/*
220 	 * Enumerate network transports and create service listeners
221 	 * as appropriate for each.
222 	 */
223 	if ((nc = setnetconfig()) == NULL) {
224 		perror("setnetconfig failed");
225 		return (-1);
226 	}
227 	while ((nconf = getnetconfig(nc)) != NULL) {
228 		/*
229 		 * Skip things like tpi_raw, invisible...
230 		 */
231 		if ((nconf->nc_flag & NC_VISIBLE) == 0)
232 			continue;
233 		if (nconf->nc_semantics != NC_TPI_CLTS &&
234 		    nconf->nc_semantics != NC_TPI_COTS &&
235 		    nconf->nc_semantics != NC_TPI_COTS_ORD)
236 			continue;
237 
238 		test_svc_tp_create(nconf);
239 	}
240 	(void) endnetconfig(nc);
241 
242 	/*
243 	 * XXX: Normally would call svc_run() here, but
244 	 * we just want to check our IP bindings.
245 	 */
246 	if (testd_port != 0)
247 		bind2();
248 
249 	if (debug) {
250 		char sysbuf[100];
251 
252 		snprintf(sysbuf, sizeof (sysbuf),
253 		    "rpcinfo -p |grep %u", TESTPROG);
254 		printf("x %s\n", sysbuf);
255 		fflush(stdout);
256 		system(sysbuf);
257 
258 		if (testd_port) {
259 			snprintf(sysbuf, sizeof (sysbuf),
260 			    "netstat -a -f inet -P udp |grep %u", testd_port);
261 			printf("x %s\n", sysbuf);
262 			fflush(stdout);
263 			system(sysbuf);
264 
265 			snprintf(sysbuf, sizeof (sysbuf),
266 			    "netstat -a -f inet -P tcp |grep %u", testd_port);
267 			printf("x %s\n", sysbuf);
268 			fflush(stdout);
269 			system(sysbuf);
270 		}
271 	}
272 
273 	/* cleanup */
274 	test_svc_unreg();
275 
276 	printf("%s complete\n", argv[0]);
277 	return (0);
278 }
279 
280 /*
281  * Server procedure switch routine
282  */
283 static void
mysvc(struct svc_req * rq,SVCXPRT * xprt)284 mysvc(struct svc_req *rq, SVCXPRT *xprt)
285 {
286 
287 	switch (rq->rq_proc) {
288 	case NULLPROC:
289 		errno = 0;
290 		(void) svc_sendreply(xprt, xdr_void, (char *)0);
291 		return;
292 
293 	default:
294 		svcerr_noproc(xprt);
295 		return;
296 	}
297 }
298 
299 struct sockaddr_in addr;
300 
301 /*
302  * The actual test: Try doing a 2nd bind with a specific IP.
303  * The exclusive wildcard bind should prvent this.
304  */
305 static void
bind2(void)306 bind2(void)
307 {
308 	int ret;
309 	int sock;
310 
311 	addr.sin_family = AF_INET;
312 	addr.sin_port = htons(testd_port);
313 	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
314 
315 	sock = socket(AF_INET, SOCK_STREAM, 0);
316 	if (sock == -1) {
317 		fprintf(stderr, "bind2 socket fail %s\n",
318 		    strerror(errno));
319 		exit(1);
320 	}
321 
322 	ret = bind(sock, (struct sockaddr *)&addr, sizeof (addr));
323 	if (ret == -1) {
324 		fprintf(stderr, "bind2 bind fail %s (expected) PASS\n",
325 		    strerror(errno));
326 		close(sock);
327 		return;
328 	}
329 
330 	printf("Oh no, bind2 worked! test FAILED\n");
331 	close(sock);
332 }
333