1 /* $NetBSD: transp_tli.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_tli.c 43 * 44 * TLI 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 struct netconfig *nfsncp; 55 56 57 /* 58 * find the IP address that can be used to connect to the local host 59 */ 60 void 61 amu_get_myaddress(struct in_addr *iap, const char *preferred_localhost) 62 { 63 int ret; 64 voidp handlep; 65 struct netconfig *ncp; 66 struct nd_addrlist *addrs = (struct nd_addrlist *) NULL; 67 struct nd_hostserv service; 68 69 handlep = setnetconfig(); 70 ncp = getnetconfig(handlep); 71 service.h_host = (preferred_localhost ? (char *) preferred_localhost : HOST_SELF_CONNECT); 72 service.h_serv = (char *) NULL; 73 74 ret = netdir_getbyname(ncp, &service, &addrs); 75 76 if (ret || !addrs || addrs->n_cnt < 1) { 77 plog(XLOG_FATAL, "cannot get local host address. using 127.0.0.1"); 78 iap->s_addr = htonl(INADDR_LOOPBACK); 79 } else { 80 /* 81 * XXX: there may be more more than one address for this local 82 * host. Maybe something can be done with those. 83 */ 84 struct sockaddr_in *sinp = (struct sockaddr_in *) addrs->n_addrs[0].buf; 85 char dq[20]; 86 if (preferred_localhost) 87 plog(XLOG_INFO, "localhost_address \"%s\" requested, using %s", 88 preferred_localhost, inet_dquad(dq, sizeof(dq), iap->s_addr)); 89 iap->s_addr = sinp->sin_addr.s_addr; /* XXX: used to be htonl() */ 90 } 91 92 endnetconfig(handlep); /* free's up internal resources too */ 93 netdir_free((voidp) addrs, ND_ADDRLIST); 94 } 95 96 97 /* 98 * How to bind to reserved ports. 99 * TLI handle (socket) and port version. 100 */ 101 int 102 bind_resv_port(int td, u_short *pp) 103 { 104 int rc = -1, port; 105 struct t_bind *treq, *tret; 106 struct sockaddr_in *sin; 107 108 treq = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR); 109 if (!treq) { 110 plog(XLOG_ERROR, "t_alloc req"); 111 return -1; 112 } 113 tret = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR); 114 if (!tret) { 115 t_free((char *) treq, T_BIND); 116 plog(XLOG_ERROR, "t_alloc ret"); 117 return -1; 118 } 119 memset((char *) treq->addr.buf, 0, treq->addr.len); 120 sin = (struct sockaddr_in *) treq->addr.buf; 121 sin->sin_family = AF_INET; 122 treq->qlen = 64; /* 0 is ok for udp, for tcp you need qlen>0 */ 123 treq->addr.len = treq->addr.maxlen; 124 errno = EADDRINUSE; 125 port = IPPORT_RESERVED; 126 127 do { 128 --port; 129 sin->sin_port = htons(port); 130 rc = t_bind(td, treq, tret); 131 if (rc < 0) { 132 plog(XLOG_ERROR, "t_bind"); 133 } else { 134 if (memcmp(treq->addr.buf, tret->addr.buf, tret->addr.len) == 0) 135 break; 136 else 137 t_unbind(td); 138 } 139 } while ((rc < 0 || errno == EADDRINUSE) && (int) port > IPPORT_RESERVED / 2); 140 141 if (pp) { 142 if (rc == 0) 143 *pp = port; 144 else 145 plog(XLOG_ERROR, "could not t_bind to any reserved port"); 146 } 147 t_free((char *) tret, T_BIND); 148 t_free((char *) treq, T_BIND); 149 return rc; 150 } 151 152 153 154 155 /* 156 * close a descriptor, TLI style 157 */ 158 int 159 amu_close(int fd) 160 { 161 return t_close(fd); 162 } 163 164 165 /* 166 * Create an rpc client attached to the mount daemon. 167 */ 168 CLIENT * 169 get_mount_client(char *host, struct sockaddr_in *unused_sin, struct timeval *tv, int *sock, u_long mnt_version) 170 { 171 CLIENT *client; 172 struct netbuf nb; 173 struct netconfig *nc = NULL; 174 struct sockaddr_in sin; 175 176 nb.maxlen = sizeof(sin); 177 nb.buf = (char *) &sin; 178 179 /* 180 * First try a TCP handler 181 */ 182 183 /* 184 * Find mountd address on TCP 185 */ 186 if ((nc = getnetconfigent(NC_TCP)) == NULL) { 187 plog(XLOG_ERROR, "getnetconfig for tcp failed: %s", nc_sperror()); 188 goto tryudp; 189 } 190 if (!rpcb_getaddr(MOUNTPROG, mnt_version, nc, &nb, host)) { 191 /* 192 * don't print error messages here, since mountd might legitimately 193 * serve udp only 194 */ 195 goto tryudp; 196 } 197 /* 198 * Create privileged TCP socket 199 */ 200 *sock = t_open(nc->nc_device, O_RDWR, 0); 201 202 if (*sock < 0) { 203 plog(XLOG_ERROR, "t_open %s: %m", nc->nc_device); 204 goto tryudp; 205 } 206 if (bind_resv_port(*sock, (u_short *) NULL) < 0) 207 plog(XLOG_ERROR, "couldn't bind mountd socket to privileged port"); 208 209 if ((client = clnt_vc_create(*sock, &nb, MOUNTPROG, mnt_version, 0, 0)) 210 == (CLIENT *) NULL) { 211 plog(XLOG_ERROR, "clnt_vc_create failed"); 212 t_close(*sock); 213 goto tryudp; 214 } 215 /* tcp succeeded */ 216 dlog("get_mount_client: using tcp, port %d", sin.sin_port); 217 if (nc) 218 freenetconfigent(nc); 219 return client; 220 221 tryudp: 222 /* first free possibly previously allocated netconfig entry */ 223 if (nc) 224 freenetconfigent(nc); 225 226 /* 227 * TCP failed so try UDP 228 */ 229 230 /* 231 * Find mountd address on UDP 232 */ 233 if ((nc = getnetconfigent(NC_UDP)) == NULL) { 234 plog(XLOG_ERROR, "getnetconfig for udp failed: %s", nc_sperror()); 235 goto badout; 236 } 237 if (!rpcb_getaddr(MOUNTPROG, mnt_version, nc, &nb, host)) { 238 plog(XLOG_ERROR, "%s", 239 clnt_spcreateerror("couldn't get mountd address on udp")); 240 goto badout; 241 } 242 /* 243 * Create privileged UDP socket 244 */ 245 *sock = t_open(nc->nc_device, O_RDWR, 0); 246 247 if (*sock < 0) { 248 plog(XLOG_ERROR, "t_open %s: %m", nc->nc_device); 249 goto badout; /* neither tcp not udp succeeded */ 250 } 251 if (bind_resv_port(*sock, (u_short *) NULL) < 0) 252 plog(XLOG_ERROR, "couldn't bind mountd socket to privileged port"); 253 254 if ((client = clnt_dg_create(*sock, &nb, MOUNTPROG, mnt_version, 0, 0)) 255 == (CLIENT *) NULL) { 256 plog(XLOG_ERROR, "clnt_dg_create failed"); 257 t_close(*sock); 258 goto badout; /* neither tcp not udp succeeded */ 259 } 260 if (clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) tv) == FALSE) { 261 plog(XLOG_ERROR, "clnt_control CLSET_RETRY_TIMEOUT for udp failed"); 262 clnt_destroy(client); 263 goto badout; /* neither tcp not udp succeeded */ 264 } 265 /* udp succeeded */ 266 dlog("get_mount_client: using udp, port %d", sin.sin_port); 267 return client; 268 269 badout: 270 /* failed */ 271 if (nc) 272 freenetconfigent(nc); 273 return NULL; 274 } 275 276 277 #ifdef NOT_NEEDED_ON_TLI_SYSTEMS 278 /* 279 * find the address of the caller of an RPC procedure. 280 */ 281 struct sockaddr_in * 282 amu_svc_getcaller(SVCXPRT *xprt) 283 { 284 /* 285 * On TLI systems we don't use an INET network type, but a "ticlts" (see 286 * /etc/netconfig). This means that packets could only come from the 287 * loopback interface, and we don't need to check them and filter possibly 288 * spoofed packets. Therefore we simply return NULL here, and the caller 289 * will ignore the result. 290 */ 291 return NULL; /* tell called to ignore check */ 292 } 293 #endif /* NOT_NEEDED_ON_TLI_SYSTEMS */ 294 295 296 /* 297 * Register an RPC server: 298 * return 1 on success, 0 otherwise. 299 */ 300 int 301 amu_svc_register(SVCXPRT *xprt, u_long prognum, u_long versnum, 302 void (*dispatch)(struct svc_req *rqstp, SVCXPRT *xprt), 303 u_long protocol, struct netconfig *ncp) 304 { 305 /* on TLI: svc_reg returns 1 on success, 0 otherwise */ 306 return svc_reg(xprt, prognum, versnum, dispatch, ncp); 307 } 308 309 310 /* 311 * Bind to reserved UDP port, for NFS service only. 312 * Port-only version. 313 */ 314 static int 315 bind_resv_port_only_udp(u_short *pp) 316 { 317 int td, rc = -1, port; 318 struct t_bind *treq, *tret; 319 struct sockaddr_in *sin; 320 extern char *t_errlist[]; 321 extern int t_errno; 322 struct netconfig *nc = (struct netconfig *) NULL; 323 voidp nc_handle; 324 325 if ((nc_handle = setnetconfig()) == (voidp) NULL) { 326 plog(XLOG_ERROR, "Cannot rewind netconfig: %s", nc_sperror()); 327 return -1; 328 } 329 /* 330 * Search the netconfig table for INET/UDP. 331 * This loop will terminate if there was an error in the /etc/netconfig 332 * file or if you reached the end of the file without finding the udp 333 * device. Either way your machine has probably far more problems (for 334 * example, you cannot have nfs v2 w/o UDP). 335 */ 336 while (1) { 337 if ((nc = getnetconfig(nc_handle)) == (struct netconfig *) NULL) { 338 plog(XLOG_ERROR, "Error accessing getnetconfig: %s", nc_sperror()); 339 endnetconfig(nc_handle); 340 return -1; 341 } 342 if (STREQ(nc->nc_protofmly, NC_INET) && 343 STREQ(nc->nc_proto, NC_UDP)) 344 break; 345 } 346 347 /* 348 * This is the primary reason for the getnetconfig code above: to get the 349 * correct device name to udp, and t_open a descriptor to be used in 350 * t_bind below. 351 */ 352 td = t_open(nc->nc_device, O_RDWR, (struct t_info *) NULL); 353 endnetconfig(nc_handle); 354 355 if (td < 0) { 356 plog(XLOG_ERROR, "t_open failed: %d: %s", t_errno, t_errlist[t_errno]); 357 return -1; 358 } 359 treq = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR); 360 if (!treq) { 361 plog(XLOG_ERROR, "t_alloc req"); 362 return -1; 363 } 364 tret = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR); 365 if (!tret) { 366 t_free((char *) treq, T_BIND); 367 plog(XLOG_ERROR, "t_alloc ret"); 368 return -1; 369 } 370 memset((char *) treq->addr.buf, 0, treq->addr.len); 371 sin = (struct sockaddr_in *) treq->addr.buf; 372 sin->sin_family = AF_INET; 373 treq->qlen = 64; /* 0 is ok for udp, for tcp you need qlen>0 */ 374 treq->addr.len = treq->addr.maxlen; 375 errno = EADDRINUSE; 376 377 if (pp && *pp > 0) { 378 sin->sin_port = htons(*pp); 379 rc = t_bind(td, treq, tret); 380 } else { 381 port = IPPORT_RESERVED; 382 383 do { 384 --port; 385 sin->sin_port = htons(port); 386 rc = t_bind(td, treq, tret); 387 if (rc < 0) { 388 plog(XLOG_ERROR, "t_bind for port %d: %s", port, t_errlist[t_errno]); 389 } else { 390 if (memcmp(treq->addr.buf, tret->addr.buf, tret->addr.len) == 0) 391 break; 392 else 393 t_unbind(td); 394 } 395 } while ((rc < 0 || errno == EADDRINUSE) && (int) port > IPPORT_RESERVED / 2); 396 397 if (pp && rc == 0) 398 *pp = port; 399 } 400 401 t_free((char *) tret, T_BIND); 402 t_free((char *) treq, T_BIND); 403 return rc; 404 } 405 406 407 /* 408 * Bind NFS to a reserved port. 409 */ 410 static int 411 bind_nfs_port(int unused_so, u_short *nfs_portp) 412 { 413 u_short port = 0; 414 int error = bind_resv_port_only_udp(&port); 415 416 if (error == 0) 417 *nfs_portp = port; 418 return error; 419 } 420 421 422 /* 423 * Create the nfs service for amd 424 * return 0 (TRUE) if OK, 1 (FALSE) if failed. 425 */ 426 int 427 create_nfs_service(int *soNFSp, u_short *nfs_portp, SVCXPRT **nfs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp)) 428 { 429 char *nettype = "ticlts"; 430 431 nfsncp = getnetconfigent(nettype); 432 if (nfsncp == NULL) { 433 plog(XLOG_ERROR, "cannot getnetconfigent for %s", nettype); 434 /* failed with ticlts, try plain udp (hpux11) */ 435 nettype = "udp"; 436 nfsncp = getnetconfigent(nettype); 437 if (nfsncp == NULL) { 438 plog(XLOG_ERROR, "cannot getnetconfigent for %s", nettype); 439 return 1; 440 } 441 } 442 *nfs_xprtp = svc_tli_create(RPC_ANYFD, nfsncp, NULL, 0, 0); 443 if (*nfs_xprtp == NULL) { 444 plog(XLOG_ERROR, "cannot create nfs tli service for amd"); 445 return 1; 446 } 447 448 /* 449 * Get the service file descriptor and check its number to see if 450 * the t_open failed. If it succeeded, then go on to binding to a 451 * reserved nfs port. 452 */ 453 *soNFSp = (*nfs_xprtp)->xp_fd; 454 if (*soNFSp < 0 || bind_nfs_port(*soNFSp, nfs_portp) < 0) { 455 plog(XLOG_ERROR, "Can't create privileged nfs port (TLI)"); 456 svc_destroy(*nfs_xprtp); 457 return 1; 458 } 459 if (svc_reg(*nfs_xprtp, NFS_PROGRAM, NFS_VERSION, dispatch_fxn, NULL) != 1) { 460 plog(XLOG_ERROR, "could not register amd NFS service"); 461 svc_destroy(*nfs_xprtp); 462 return 1; 463 } 464 465 return 0; /* all is well */ 466 } 467 468 469 /* 470 * Bind to preferred AMQ port. 471 */ 472 static int 473 bind_preferred_amq_port(u_short pref_port, 474 const struct netconfig *ncp, 475 struct t_bind **tretpp) 476 { 477 int td = -1, rc = -1; 478 struct t_bind *treq; 479 struct sockaddr_in *sin, *sin2; 480 extern char *t_errlist[]; 481 extern int t_errno; 482 483 if (!ncp) { 484 plog(XLOG_ERROR, "null ncp"); 485 return -1; 486 } 487 488 td = t_open(ncp->nc_device, O_RDWR, (struct t_info *) NULL); 489 if (td < 0) { 490 plog(XLOG_ERROR, "t_open failed: %d: %s", t_errno, t_errlist[t_errno]); 491 return -1; 492 } 493 treq = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR); 494 if (!treq) { 495 plog(XLOG_ERROR, "t_alloc req"); 496 return -1; 497 } 498 *tretpp = (struct t_bind *) t_alloc(td, T_BIND, T_ADDR); 499 if (!*tretpp) { 500 t_free((char *) treq, T_BIND); 501 plog(XLOG_ERROR, "t_alloc tretpp"); 502 return -1; 503 } 504 memset((char *) treq->addr.buf, 0, treq->addr.len); 505 sin = (struct sockaddr_in *) treq->addr.buf; 506 sin->sin_family = AF_INET; 507 treq->qlen = 64; /* must be greater than 0 to work for TCP connections */ 508 treq->addr.len = treq->addr.maxlen; 509 510 if (pref_port > 0) { 511 sin->sin_port = htons(pref_port); 512 sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK); /* XXX: may not be needed */ 513 rc = t_bind(td, treq, *tretpp); 514 if (rc < 0) { 515 plog(XLOG_ERROR, "t_bind return err %d", rc); 516 goto out; 517 } 518 /* check if we got the port we asked for */ 519 sin2 = (struct sockaddr_in *) (*tretpp)->addr.buf; 520 if (sin->sin_port != sin2->sin_port) { 521 plog(XLOG_ERROR, "asked for port %d, got different one (%d)", 522 ntohs(sin->sin_port), ntohs(sin2->sin_port)); 523 t_errno = TNOADDR; /* XXX: is this correct? */ 524 rc = -1; 525 goto out; 526 } 527 if (sin->sin_addr.s_addr != sin2->sin_addr.s_addr) { 528 plog(XLOG_ERROR, "asked for address %x, got different one (%x)", 529 (int) ntohl(sin->sin_addr.s_addr), (int) ntohl(sin2->sin_addr.s_addr)); 530 t_errno = TNOADDR; /* XXX: is this correct? */ 531 rc = -1; 532 goto out; 533 } 534 } 535 out: 536 t_free((char *) treq, T_BIND); 537 return (rc < 0 ? rc : td); 538 } 539 540 541 /* 542 * Create the amq service for amd (both TCP and UDP) 543 */ 544 int 545 create_amq_service(int *udp_soAMQp, 546 SVCXPRT **udp_amqpp, 547 struct netconfig **udp_amqncpp, 548 int *tcp_soAMQp, 549 SVCXPRT **tcp_amqpp, 550 struct netconfig **tcp_amqncpp, 551 u_short preferred_amq_port) 552 { 553 /* 554 * (partially) create the amq service for amd 555 * to be completed further in by caller. 556 * XXX: is this "partially" still true?! See amd/nfs_start.c. -Erez 557 */ 558 559 /* first create the TCP service */ 560 if (tcp_amqncpp) 561 if ((*tcp_amqncpp = getnetconfigent(NC_TCP)) == NULL) { 562 plog(XLOG_ERROR, "cannot getnetconfigent for %s", NC_TCP); 563 return 1; 564 } 565 566 if (tcp_amqpp) { 567 if (preferred_amq_port > 0) { 568 struct t_bind *tbp = NULL; 569 int sock; 570 571 plog(XLOG_INFO, "requesting preferred amq TCP port %d", preferred_amq_port); 572 sock = bind_preferred_amq_port(preferred_amq_port, *tcp_amqncpp, &tbp); 573 if (sock < 0) { 574 plog(XLOG_ERROR, "bind_preferred_amq_port failed for TCP port %d: %s", 575 preferred_amq_port, t_errlist[t_errno]); 576 return 1; 577 } 578 *tcp_amqpp = svc_tli_create(sock, *tcp_amqncpp, tbp, 0, 0); 579 if (*tcp_amqpp != NULL) 580 plog(XLOG_INFO, "amq service bound to TCP port %d", preferred_amq_port); 581 t_free((char *) tbp, T_BIND); 582 } else { 583 /* select any port */ 584 *tcp_amqpp = svc_tli_create(RPC_ANYFD, *tcp_amqncpp, NULL, 0, 0); 585 } 586 if (*tcp_amqpp == NULL) { 587 plog(XLOG_ERROR, "cannot create (tcp) tli service for amq"); 588 return 1; 589 } 590 } 591 if (tcp_soAMQp && tcp_amqpp) 592 *tcp_soAMQp = (*tcp_amqpp)->xp_fd; 593 594 /* next create the UDP service */ 595 if (udp_amqncpp) 596 if ((*udp_amqncpp = getnetconfigent(NC_UDP)) == NULL) { 597 plog(XLOG_ERROR, "cannot getnetconfigent for %s", NC_UDP); 598 return 1; 599 } 600 if (udp_amqpp) { 601 if (preferred_amq_port > 0) { 602 struct t_bind *tbp = NULL; 603 int sock; 604 605 plog(XLOG_INFO, "requesting preferred amq UDP port %d", preferred_amq_port); 606 sock = bind_preferred_amq_port(preferred_amq_port, *udp_amqncpp, &tbp); 607 if (sock < 0) { 608 plog(XLOG_ERROR, "bind_preferred_amq_port failed for UDP port %d: %s", 609 preferred_amq_port, t_errlist[t_errno]); 610 return 1; 611 } 612 *udp_amqpp = svc_tli_create(sock, *udp_amqncpp, tbp, 0, 0); 613 if (*udp_amqpp != NULL) 614 plog(XLOG_INFO, "amq service bound to UDP port %d", preferred_amq_port); 615 t_free((char *) tbp, T_BIND); 616 } else { 617 /* select any port */ 618 *udp_amqpp = svc_tli_create(RPC_ANYFD, *udp_amqncpp, NULL, 0, 0); 619 } 620 if (*udp_amqpp == NULL) { 621 plog(XLOG_ERROR, "cannot create (udp) tli service for amq"); 622 return 1; 623 } 624 } 625 if (udp_soAMQp && udp_amqpp) 626 *udp_soAMQp = (*udp_amqpp)->xp_fd; 627 628 return 0; /* all is well */ 629 } 630 631 632 /* 633 * Find netconfig info for TCP/UDP device, and fill in the knetconfig 634 * structure. If in_ncp is not NULL, use that instead of defaulting 635 * to a TCP/UDP service. If in_ncp is NULL, then use the service type 636 * specified in nc_protoname (which may be either "tcp" or "udp"). If 637 * nc_protoname is NULL, default to UDP. 638 */ 639 int 640 get_knetconfig(struct knetconfig **kncpp, struct netconfig *in_ncp, char *nc_protoname) 641 { 642 struct netconfig *ncp = NULL; 643 struct stat statbuf; 644 645 if (in_ncp) 646 ncp = in_ncp; 647 else { 648 if (nc_protoname) 649 ncp = getnetconfigent(nc_protoname); 650 else 651 ncp = getnetconfigent(NC_UDP); 652 } 653 if (!ncp) 654 return -2; 655 656 *kncpp = (struct knetconfig *) xzalloc(sizeof(struct knetconfig)); 657 if (*kncpp == (struct knetconfig *) NULL) { 658 if (!in_ncp) 659 freenetconfigent(ncp); 660 return -3; 661 } 662 (*kncpp)->knc_semantics = ncp->nc_semantics; 663 (*kncpp)->knc_protofmly = strdup(ncp->nc_protofmly); 664 (*kncpp)->knc_proto = strdup(ncp->nc_proto); 665 666 if (stat(ncp->nc_device, &statbuf) < 0) { 667 plog(XLOG_ERROR, "could not stat() %s: %m", ncp->nc_device); 668 XFREE(*kncpp); 669 *kncpp = NULL; 670 if (!in_ncp) 671 freenetconfigent(ncp); 672 return -3; /* amd will end (free not needed) */ 673 } 674 (*kncpp)->knc_rdev = (dev_t) statbuf.st_rdev; 675 if (!in_ncp) { /* free only if argument not passed */ 676 freenetconfigent(ncp); 677 ncp = NULL; 678 } 679 return 0; 680 } 681 682 683 /* 684 * Free a previously allocated knetconfig structure. 685 */ 686 void 687 free_knetconfig(struct knetconfig *kncp) 688 { 689 if (kncp) { 690 if (kncp->knc_protofmly) 691 XFREE(kncp->knc_protofmly); 692 if (kncp->knc_proto) 693 XFREE(kncp->knc_proto); 694 XFREE(kncp); 695 kncp = (struct knetconfig *) NULL; 696 } 697 } 698 699 700 /* 701 * Check if the portmapper is running and reachable: 0==down, 1==up 702 */ 703 int check_pmap_up(char *host, struct sockaddr_in* sin) 704 { 705 CLIENT *client; 706 enum clnt_stat clnt_stat = RPC_TIMEDOUT; /* assume failure */ 707 int socket = RPC_ANYSOCK; 708 struct timeval timeout; 709 710 timeout.tv_sec = 2; 711 timeout.tv_usec = 0; 712 sin->sin_port = htons(PMAPPORT); 713 client = clntudp_create(sin, PMAPPROG, PMAPVERS, timeout, &socket); 714 if (client == (CLIENT *) NULL) { 715 plog(XLOG_ERROR, 716 "check_pmap_up: cannot create connection to contact portmapper on host \"%s\"%s", 717 host, clnt_spcreateerror("")); 718 return 0; 719 } 720 721 timeout.tv_sec = 6; 722 /* Ping the portmapper on a remote system by calling the nullproc */ 723 clnt_stat = clnt_call(client, 724 PMAPPROC_NULL, 725 (XDRPROC_T_TYPE) xdr_void, 726 NULL, 727 (XDRPROC_T_TYPE) xdr_void, 728 NULL, 729 timeout); 730 clnt_destroy(client); 731 close(socket); 732 sin->sin_port = 0; 733 734 if (clnt_stat == RPC_TIMEDOUT) { 735 plog(XLOG_ERROR, 736 "check_pmap_up: failed to contact portmapper on host \"%s\": %s", 737 host, clnt_sperrno(clnt_stat)); 738 return 0; 739 } 740 return 1; 741 } 742 743 744 /* 745 * Find the best NFS version for a host. 746 */ 747 u_long 748 get_nfs_version(char *host, struct sockaddr_in *sin, u_long nfs_version, const char *proto) 749 { 750 CLIENT *clnt = NULL; 751 rpcvers_t versout; 752 struct timeval tv; 753 754 /* 755 * If not set or set wrong, then try from NFS_VERS_MAX on down. If 756 * set, then try from nfs_version on down. 757 */ 758 if (nfs_version <= 0 || nfs_version > NFS_VERS_MAX) { 759 nfs_version = NFS_VERS_MAX; 760 } 761 762 if (nfs_version == NFS_VERSION) { 763 dlog("get_nfs_version trying NFS(%d,%s) for %s", 764 (int) nfs_version, proto, host); 765 } else { 766 dlog("get_nfs_version trying NFS(%d-%d,%s) for %s", 767 (int) NFS_VERSION, (int) nfs_version, proto, host); 768 } 769 770 /* 3 seconds is more than enough for a LAN */ 771 memset(&tv, 0, sizeof(tv)); 772 tv.tv_sec = 3; 773 tv.tv_usec = 0; 774 775 #ifdef HAVE_CLNT_CREATE_VERS_TIMED 776 clnt = clnt_create_vers_timed(host, NFS_PROGRAM, &versout, NFS_VERSION, nfs_version, proto, &tv); 777 #else /* not HAVE_CLNT_CREATE_VERS_TIMED */ 778 clnt = clnt_create_vers(host, NFS_PROGRAM, &versout, NFS_VERSION, nfs_version, proto); 779 #endif /* not HAVE_CLNT_CREATE_VERS_TIMED */ 780 781 if (clnt == NULL) { 782 if (nfs_version == NFS_VERSION) 783 plog(XLOG_INFO, "get_nfs_version NFS(%d,%s) failed for %s: %s", 784 (int) nfs_version, proto, host, clnt_spcreateerror("")); 785 else 786 plog(XLOG_INFO, "get_nfs_version NFS(%d-%d,%s) failed for %s: %s", 787 (int) NFS_VERSION, (int) nfs_version, proto, host, clnt_spcreateerror("")); 788 return 0; 789 } 790 clnt_destroy(clnt); 791 792 return versout; 793 } 794 795 796 #if defined(HAVE_FS_AUTOFS) && defined(AUTOFS_PROG) 797 /* 798 * find the IP address that can be used to connect autofs service to. 799 */ 800 static int 801 get_autofs_address(struct netconfig *ncp, struct t_bind *tbp) 802 { 803 int ret; 804 struct nd_addrlist *addrs = (struct nd_addrlist *) NULL; 805 struct nd_hostserv service; 806 807 service.h_host = HOST_SELF_CONNECT; 808 service.h_serv = "autofs"; 809 810 ret = netdir_getbyname(ncp, &service, &addrs); 811 812 if (ret) { 813 plog(XLOG_FATAL, "get_autofs_address: cannot get local host address: %s", netdir_sperror()); 814 goto out; 815 } 816 817 /* 818 * XXX: there may be more more than one address for this local 819 * host. Maybe something can be done with those. 820 */ 821 tbp->addr.len = addrs->n_addrs->len; 822 tbp->addr.maxlen = addrs->n_addrs->len; 823 memcpy(tbp->addr.buf, addrs->n_addrs->buf, addrs->n_addrs->len); 824 /* 825 * qlen should not be zero for TCP connections. It's not clear what it 826 * should be for UDP connections, but setting it to something like 64 seems 827 * to be the safe value that works. 828 */ 829 tbp->qlen = 64; 830 831 /* all OK */ 832 netdir_free((voidp) addrs, ND_ADDRLIST); 833 834 out: 835 return ret; 836 } 837 838 839 /* 840 * Register the autofs service for amd 841 */ 842 int 843 register_autofs_service(char *autofs_conftype, 844 void (*autofs_dispatch)(struct svc_req *rqstp, SVCXPRT *xprt)) 845 { 846 struct t_bind *tbp = NULL; 847 struct netconfig *autofs_ncp; 848 SVCXPRT *autofs_xprt = NULL; 849 int fd = -1, err = 1; /* assume failed */ 850 851 plog(XLOG_INFO, "registering autofs service: %s", autofs_conftype); 852 autofs_ncp = getnetconfigent(autofs_conftype); 853 if (autofs_ncp == NULL) { 854 plog(XLOG_ERROR, "register_autofs_service: cannot getnetconfigent for %s", autofs_conftype); 855 goto out; 856 } 857 858 fd = t_open(autofs_ncp->nc_device, O_RDWR, NULL); 859 if (fd < 0) { 860 plog(XLOG_ERROR, "register_autofs_service: t_open failed (%s)", 861 t_errlist[t_errno]); 862 goto out; 863 } 864 865 tbp = (struct t_bind *) t_alloc(fd, T_BIND, T_ADDR); 866 if (!tbp) { 867 plog(XLOG_ERROR, "register_autofs_service: t_alloc failed"); 868 goto out; 869 } 870 871 if (get_autofs_address(autofs_ncp, tbp) != 0) { 872 plog(XLOG_ERROR, "register_autofs_service: get_autofs_address failed"); 873 goto out; 874 } 875 876 autofs_xprt = svc_tli_create(fd, autofs_ncp, tbp, 0, 0); 877 if (autofs_xprt == NULL) { 878 plog(XLOG_ERROR, "cannot create autofs tli service for amd"); 879 goto out; 880 } 881 882 rpcb_unset(AUTOFS_PROG, AUTOFS_VERS, autofs_ncp); 883 if (svc_reg(autofs_xprt, AUTOFS_PROG, AUTOFS_VERS, autofs_dispatch, autofs_ncp) == FALSE) { 884 plog(XLOG_ERROR, "could not register amd AUTOFS service"); 885 goto out; 886 } 887 err = 0; 888 goto really_out; 889 890 out: 891 if (autofs_ncp) 892 freenetconfigent(autofs_ncp); 893 if (autofs_xprt) 894 SVC_DESTROY(autofs_xprt); 895 else { 896 if (fd > 0) 897 t_close(fd); 898 } 899 900 really_out: 901 if (tbp) 902 t_free((char *) tbp, T_BIND); 903 904 dlog("register_autofs_service: returning %d\n", err); 905 return err; 906 } 907 908 909 int 910 unregister_autofs_service(char *autofs_conftype) 911 { 912 struct netconfig *autofs_ncp; 913 int err = 1; 914 915 plog(XLOG_INFO, "unregistering autofs service listener: %s", autofs_conftype); 916 917 autofs_ncp = getnetconfigent(autofs_conftype); 918 if (autofs_ncp == NULL) { 919 plog(XLOG_ERROR, "destroy_autofs_service: cannot getnetconfigent for %s", autofs_conftype); 920 goto out; 921 } 922 923 out: 924 rpcb_unset(AUTOFS_PROG, AUTOFS_VERS, autofs_ncp); 925 return err; 926 } 927 #endif /* HAVE_FS_AUTOFS && AUTOFS_PROG */ 928