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