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