1 /* $NetBSD: transp_sockets.c,v 1.2 2015/01/17 17:46:31 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1997-2014 Erez Zadok 5 * Copyright (c) 1990 Jan-Simon Pendry 6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine 7 * Copyright (c) 1990 The Regents of the University of California. 8 * All rights reserved. 9 * 10 * This code is derived from software contributed to Berkeley by 11 * Jan-Simon Pendry at Imperial College, London. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 3. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * 38 * File: am-utils/conf/transp/transp_sockets.c 39 * 40 * Socket specific utilities. 41 * -Erez Zadok <ezk@cs.columbia.edu> 42 */ 43 44 #ifdef HAVE_CONFIG_H 45 # include <config.h> 46 #endif /* HAVE_CONFIG_H */ 47 #include <am_defs.h> 48 #include <amu.h> 49 50 51 /* 52 * find the IP address that can be used to connect to the local host 53 */ 54 void 55 amu_get_myaddress(struct in_addr *iap, const char *preferred_localhost) 56 { 57 struct hostent *hp; 58 char dq[20]; 59 60 #ifdef DEBUG_off 61 #error this code is old and probably not useful any longer. 62 #error Erez, Jan 21, 2004. 63 struct sockaddr_in sin; 64 65 /* 66 * Most modern systems should use 127.0.0.1 as the localhost address over 67 * which you can do NFS mounts. In the past we found that some NFS 68 * clients may not allow mounts from localhost. So we used 69 * get_myaddress() and that seemed to work. Alas, on some other systems, 70 * get_myaddress() may return one of the interface addresses at random, 71 * and thus use a less efficient IP address than 127.0.0.1. The solution 72 * is to hard-code 127.0.0.1, but still check if get_myaddress() returns a 73 * different value and warn about it. 74 */ 75 memset((char *) &sin, 0, sizeof(sin)); 76 get_myaddress(&sin); 77 if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK)) 78 dlog("amu_get_myaddress: myaddress conflict (0x%x vs. 0x%lx)", 79 sin.sin_addr.s_addr, (u_long) htonl(INADDR_LOOPBACK)); 80 #endif /* DEBUG_off */ 81 82 if (preferred_localhost == NULL) 83 goto out; 84 85 /* if specified preferred locahost, then try to use it */ 86 hp = gethostbyname(preferred_localhost); 87 if (hp == NULL) { 88 /* XXX: if hstrerror()/h_errno aren't portable, then need to port the next statement */ 89 plog(XLOG_ERROR, "Unable to resolve localhost_address \"%s\" (%s): using default", 90 preferred_localhost, hstrerror(h_errno)); 91 goto out; 92 } 93 if (hp->h_addr_list == NULL) { 94 plog(XLOG_ERROR, "localhost_address \"%s\" has no IP addresses: using default", 95 preferred_localhost); 96 goto out; 97 } 98 if (hp->h_addr_list[1] != NULL) { 99 plog(XLOG_ERROR, "localhost_address \"%s\" has more than one IP addresses: using first", 100 preferred_localhost); 101 goto out; 102 } 103 memmove((voidp) &iap->s_addr, (voidp) hp->h_addr_list[0], sizeof(iap->s_addr)); 104 plog(XLOG_INFO, "localhost_address \"%s\" requested, using %s", 105 preferred_localhost, inet_dquad(dq, sizeof(dq), iap->s_addr)); 106 return; 107 108 out: 109 iap->s_addr = htonl(INADDR_LOOPBACK); 110 } 111 112 113 /* 114 * How to bind to reserved ports. 115 * Note: if *pp is non-null and is greater than 0, then *pp will not be modified. 116 */ 117 int 118 bind_resv_port(int so, u_short *pp) 119 { 120 struct sockaddr_in sin; 121 int rc; 122 u_short port; 123 124 memset((voidp) &sin, 0, sizeof(sin)); 125 sin.sin_family = AF_INET; 126 127 if (pp && *pp > 0) { 128 sin.sin_port = htons(*pp); 129 rc = bind(so, (struct sockaddr *) &sin, sizeof(sin)); 130 } else { 131 port = IPPORT_RESERVED; 132 133 do { 134 --port; 135 sin.sin_port = htons(port); 136 rc = bind(so, (struct sockaddr *) &sin, sizeof(sin)); 137 } while (rc < 0 && (int) port > IPPORT_RESERVED / 2); 138 139 if (pp && rc == 0) 140 *pp = port; 141 } 142 143 return rc; 144 } 145 146 147 /* 148 * close a descriptor, Sockets style 149 */ 150 int 151 amu_close(int fd) 152 { 153 return close(fd); 154 } 155 156 157 /* 158 * Create an rpc client attached to the mount daemon. 159 */ 160 CLIENT * 161 get_mount_client(char *unused_host, struct sockaddr_in *sin, struct timeval *tv, int *sock, u_long mnt_version) 162 { 163 CLIENT *client; 164 165 /* 166 * First try a TCP socket 167 */ 168 if ((*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0) { 169 /* 170 * Bind to a privileged port 171 */ 172 if (bind_resv_port(*sock, (u_short *) NULL) < 0) 173 plog(XLOG_ERROR, "can't bind privileged port (socket)"); 174 175 /* 176 * Find mountd port to connect to. 177 * Connect to mountd. 178 * Create a tcp client. 179 */ 180 if ((sin->sin_port = htons(pmap_getport(sin, MOUNTPROG, mnt_version, IPPROTO_TCP))) != 0) { 181 if (connect(*sock, (struct sockaddr *) sin, sizeof(*sin)) >= 0 182 && ((client = clnttcp_create(sin, MOUNTPROG, mnt_version, sock, 0, 0)) != NULL)) 183 return client; 184 } 185 /* 186 * Failed so close socket 187 */ 188 (void) close(*sock); 189 } /* tcp socket opened */ 190 /* TCP failed so try UDP */ 191 if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 192 plog(XLOG_ERROR, "Can't create socket to connect to mountd: %m"); 193 *sock = RPC_ANYSOCK; 194 return NULL; 195 } 196 /* 197 * Bind to a privileged port 198 */ 199 if (bind_resv_port(*sock, (u_short *) NULL) < 0) 200 plog(XLOG_ERROR, "can't bind privileged port"); 201 202 /* 203 * Zero out the port - make sure we recompute 204 */ 205 sin->sin_port = 0; 206 207 /* 208 * Make a UDP client 209 */ 210 if ((client = clntudp_create(sin, MOUNTPROG, mnt_version, *tv, sock)) == NULL) { 211 (void) close(*sock); 212 *sock = RPC_ANYSOCK; 213 return NULL; 214 } 215 dlog("get_mount_client: Using udp, port %d", sin->sin_port); 216 return client; 217 } 218 219 220 /* 221 * find the address of the caller of an RPC procedure. 222 */ 223 struct sockaddr_in * 224 amu_svc_getcaller(SVCXPRT *xprt) 225 { 226 /* glibc 2.2 returns a sockaddr_storage ??? */ 227 return (struct sockaddr_in *) svc_getcaller(xprt); 228 } 229 230 231 /* 232 * Register an RPC server: 233 * return 1 on success, 0 otherwise. 234 */ 235 int 236 amu_svc_register(SVCXPRT *xprt, u_long prognum, u_long versnum, 237 void (*dispatch)(struct svc_req *rqstp, SVCXPRT *transp), 238 u_long protocol, struct netconfig *dummy) 239 { 240 /* on Sockets: svc_register returns 1 on success, 0 otherwise */ 241 return svc_register(xprt, prognum, versnum, dispatch, protocol); 242 } 243 244 245 /* 246 * Create the nfs service for amd 247 */ 248 int 249 create_nfs_service(int *soNFSp, u_short *nfs_portp, SVCXPRT **nfs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp), u_long nfs_version) 250 { 251 *soNFSp = socket(AF_INET, SOCK_DGRAM, 0); 252 253 if (*soNFSp < 0 || bind_resv_port(*soNFSp, nfs_portp) < 0) { 254 plog(XLOG_FATAL, "Can't create privileged nfs port (socket)"); 255 if (*soNFSp >= 0) 256 close(*soNFSp); 257 return 1; 258 } 259 if ((*nfs_xprtp = svcudp_create(*soNFSp)) == NULL) { 260 plog(XLOG_FATAL, "cannot create rpc/udp service"); 261 close(*soNFSp); 262 return 2; 263 } 264 if ((*nfs_portp = (*nfs_xprtp)->xp_port) >= IPPORT_RESERVED) { 265 plog(XLOG_FATAL, "Can't create privileged nfs port"); 266 svc_destroy(*nfs_xprtp); 267 close(*soNFSp); 268 return 1; 269 } 270 if (!svc_register(*nfs_xprtp, NFS_PROGRAM, nfs_version, dispatch_fxn, 0)) { 271 plog(XLOG_FATAL, "unable to register (%lu, %lu, 0)", 272 (u_long) NFS_PROGRAM, nfs_version); 273 svc_destroy(*nfs_xprtp); 274 close(*soNFSp); 275 return 3; 276 } 277 278 return 0; /* all is well */ 279 } 280 281 282 /* 283 * Create the amq service for amd (both TCP and UDP) 284 */ 285 int 286 create_amq_service(int *udp_soAMQp, 287 SVCXPRT **udp_amqpp, 288 struct netconfig **dummy1, 289 int *tcp_soAMQp, 290 SVCXPRT **tcp_amqpp, 291 struct netconfig **dummy2, 292 u_short preferred_amq_port) 293 { 294 /* first create TCP service */ 295 if (tcp_soAMQp) { 296 *tcp_soAMQp = socket(AF_INET, SOCK_STREAM, 0); 297 if (*tcp_soAMQp < 0) { 298 plog(XLOG_FATAL, "cannot create tcp socket for amq service: %m"); 299 return 1; 300 } 301 302 /* next, bind to a specific (TCP) port if asked for */ 303 if (preferred_amq_port > 0) { 304 /* 305 * Note: if &preferred_amq_port is non-null and is greater than 0, 306 * then the pointer will not be modified. We don't want it to be 307 * modified because it was passed down to create_amq_service as a 308 * non-pointer (a variable on the stack, not to be modified!) 309 */ 310 if (bind_resv_port(*tcp_soAMQp, &preferred_amq_port) < 0) { 311 plog(XLOG_FATAL, "can't bind amq service to requested TCP port %d: %m)", preferred_amq_port); 312 return 1; 313 } 314 } 315 316 /* now create RPC service handle for amq */ 317 if (tcp_amqpp && 318 (*tcp_amqpp = svctcp_create(*tcp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) { 319 plog(XLOG_FATAL, "cannot create tcp service for amq: soAMQp=%d", *tcp_soAMQp); 320 return 1; 321 } 322 323 #ifdef SVCSET_CONNMAXREC 324 /* 325 * This is *BSD at its best. 326 * They just had to do things differently than everyone else 327 * so they fixed a library DoS issue by forcing client-side changes... 328 */ 329 # ifndef RPC_MAXDATASIZE 330 # define RPC_MAXDATASIZE 9000 331 # endif /* not RPC_MAXDATASIZE */ 332 if (tcp_amqpp) { 333 int maxrec = RPC_MAXDATASIZE; 334 SVC_CONTROL(*tcp_amqpp, SVCSET_CONNMAXREC, &maxrec); 335 } 336 #endif /* not SVCSET_CONNMAXREC */ 337 } 338 339 /* next create UDP service */ 340 if (udp_soAMQp) { 341 *udp_soAMQp = socket(AF_INET, SOCK_DGRAM, 0); 342 if (*udp_soAMQp < 0) { 343 plog(XLOG_FATAL, "cannot create udp socket for amq service: %m"); 344 return 1; 345 } 346 347 /* next, bind to a specific (UDP) port if asked for */ 348 if (preferred_amq_port > 0) { 349 /* 350 * Note: see comment about using &preferred_amq_port above in this 351 * function. 352 */ 353 if (bind_resv_port(*udp_soAMQp, &preferred_amq_port) < 0) { 354 plog(XLOG_FATAL, "can't bind amq service to requested UDP port %d: %m)", preferred_amq_port); 355 return 1; 356 } 357 } 358 359 /* now create RPC service handle for amq */ 360 if (udp_amqpp && 361 (*udp_amqpp = svcudp_bufcreate(*udp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) { 362 plog(XLOG_FATAL, "cannot create udp service for amq: soAMQp=%d", *udp_soAMQp); 363 return 1; 364 } 365 } 366 367 return 0; /* all is well */ 368 } 369 370 371 /* 372 * Check if the portmapper is running and reachable: 0==down, 1==up 373 */ 374 int check_pmap_up(char *host, struct sockaddr_in* sin) 375 { 376 CLIENT *client; 377 enum clnt_stat clnt_stat = RPC_TIMEDOUT; /* assume failure */ 378 int socket = RPC_ANYSOCK; 379 struct timeval timeout; 380 381 timeout.tv_sec = 2; 382 timeout.tv_usec = 0; 383 sin->sin_port = htons(PMAPPORT); 384 client = clntudp_create(sin, PMAPPROG, PMAPVERS, timeout, &socket); 385 386 if (client == (CLIENT *) NULL) { 387 plog(XLOG_ERROR, 388 "%s: cannot create connection to contact portmapper on host \"%s\"%s", 389 __func__, host, clnt_spcreateerror("")); 390 return 0; 391 } 392 393 timeout.tv_sec = 6; 394 /* Ping the portmapper on a remote system by calling the nullproc */ 395 clnt_stat = clnt_call(client, 396 PMAPPROC_NULL, 397 (XDRPROC_T_TYPE) xdr_void, 398 NULL, 399 (XDRPROC_T_TYPE) xdr_void, 400 NULL, 401 timeout); 402 clnt_destroy(client); 403 close(socket); 404 sin->sin_port = 0; 405 406 if (clnt_stat == RPC_TIMEDOUT) { 407 plog(XLOG_ERROR, 408 "%s: failed to contact portmapper on host \"%s\": %s", 409 __func__, host, clnt_sperrno(clnt_stat)); 410 return 0; 411 } 412 return 1; 413 } 414 415 416 /* 417 * Find the best NFS version for a host and protocol. 418 */ 419 u_long 420 get_nfs_version(char *host, struct sockaddr_in *sin, u_long nfs_version, const char *proto, u_long def) 421 { 422 CLIENT *clnt; 423 enum clnt_stat clnt_stat; 424 struct timeval tv; 425 int sock; 426 char *errstr; 427 428 /* 429 * If not set or set wrong, then try from NFS_VERS_MAX on down. If 430 * set, then try from nfs_version on down. 431 */ 432 if (!nfs_valid_version(nfs_version)) { 433 if (nfs_valid_version(def)) 434 nfs_version = def; 435 else 436 nfs_version = NFS_VERS_MAX; 437 } 438 tv.tv_sec = 2; /* retry every 2 seconds, but also timeout */ 439 tv.tv_usec = 0; 440 441 for (; nfs_version >= NFS_VERS_MIN; nfs_version--) { 442 443 sock = RPC_ANYSOCK; 444 errstr = NULL; 445 if (STREQ(proto, "tcp")) 446 clnt = clnttcp_create(sin, NFS_PROGRAM, nfs_version, &sock, 0, 0); 447 else if (STREQ(proto, "udp")) 448 clnt = clntudp_create(sin, NFS_PROGRAM, nfs_version, tv, &sock); 449 else 450 clnt = NULL; 451 452 if (clnt != NULL) { 453 /* Try three times (6/2=3) to verify the CLIENT handle. */ 454 tv.tv_sec = 6; 455 clnt_stat = clnt_call(clnt, 456 NFSPROC_NULL, 457 (XDRPROC_T_TYPE) xdr_void, 458 0, 459 (XDRPROC_T_TYPE) xdr_void, 460 0, 461 tv); 462 463 if (clnt_stat != RPC_SUCCESS) 464 errstr = clnt_sperrno(clnt_stat); 465 466 close(sock); 467 clnt_destroy(clnt); 468 if (clnt_stat == RPC_SUCCESS) 469 break; 470 } else { 471 #ifdef HAVE_CLNT_SPCREATEERROR 472 errstr = clnt_spcreateerror(""); 473 #else /* not HAVE_CLNT_SPCREATEERROR */ 474 errstr = ""; 475 #endif /* not HAVE_CLNT_SPCREATEERROR */ 476 } 477 478 if (errstr) { 479 plog(XLOG_INFO, "%s: NFS(%lu,%s) failed for %s: %s", __func__, 480 nfs_version, proto, host, errstr); 481 } 482 } 483 484 if (nfs_version < NFS_VERS_MIN) 485 nfs_version = 0; 486 487 plog(XLOG_INFO, "%s: returning NFS(%lu,%s) on host %s", __func__, 488 nfs_version, proto, host); 489 return nfs_version; 490 } 491 492 493 #if defined(HAVE_FS_AUTOFS) && defined(AUTOFS_PROG) 494 /* 495 * Register the autofs service for amd 496 */ 497 int 498 register_autofs_service(char *autofs_conftype, void (*autofs_dispatch)(struct svc_req *rqstp, SVCXPRT *transp)) 499 { 500 int autofs_socket; 501 SVCXPRT *autofs_xprt = NULL; 502 503 autofs_socket = socket(AF_INET, SOCK_DGRAM, 0); 504 505 if (autofs_socket < 0 || bind_resv_port(autofs_socket, NULL) < 0) { 506 plog(XLOG_FATAL, "Can't create privileged autofs port (socket)"); 507 return 1; 508 } 509 if ((autofs_xprt = svcudp_create(autofs_socket)) == NULL) { 510 plog(XLOG_FATAL, "Can't create autofs rpc/udp service"); 511 return 2; 512 } 513 if (autofs_xprt->xp_port >= IPPORT_RESERVED) { 514 plog(XLOG_FATAL, "Can't create privileged autofs port"); 515 return 1; 516 } 517 if (!svc_register(autofs_xprt, AUTOFS_PROG, AUTOFS_VERS, autofs_dispatch, 0)) { 518 plog(XLOG_FATAL, "unable to register (%ld, %ld, 0)", 519 (u_long) AUTOFS_PROG, (u_long) AUTOFS_VERS); 520 return 3; 521 } 522 523 return 0; /* all is well */ 524 } 525 526 527 int 528 unregister_autofs_service(char *autofs_conftype) 529 { 530 svc_unregister(AUTOFS_PROG, AUTOFS_VERS); 531 return 0; 532 } 533 #endif /* HAVE_FS_AUTOFS && AUTOFS_PROG */ 534