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 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 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 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 vers, nconf->nc_netid); 148 } 149 } 150 } 151 152 static 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 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 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 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", strerror(errno)); 325 close(sock); 326 return; 327 } 328 329 printf("Oh no, bind2 worked! test FAILED\n"); 330 close(sock); 331 return; 332 }