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