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