xref: /netbsd-src/external/bsd/am-utils/dist/conf/transp/transp_tli.c (revision 413d532bcc3f62d122e56d92e13ac64825a40baf)
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