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