xref: /netbsd-src/external/bsd/am-utils/dist/conf/transp/transp_tli.c (revision 8bae5d409deb915cf7c8f0539fae22ff2cb8a313)
1 /*	$NetBSD: transp_tli.c,v 1.1.1.3 2015/01/17 16:34:16 christos Exp $	*/
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 
50 struct netconfig *nfsncp;
51 
52 
53 /*
54  * find the IP address that can be used to connect to the local host
55  */
56 void
amu_get_myaddress(struct in_addr * iap,const char * preferred_localhost)57 amu_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  */
97 int
bind_resv_port(int td,u_short * pp)98 bind_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  */
154 int
amu_close(int fd)155 amu_close(int fd)
156 {
157   return t_close(fd);
158 }
159 
160 
161 /*
162  * Create an rpc client attached to the mount daemon.
163  */
164 CLIENT *
get_mount_client(char * host,struct sockaddr_in * unused_sin,struct timeval * tv,int * sock,u_long mnt_version)165 get_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 
217 tryudp:
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 
265 badout:
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  */
277 struct sockaddr_in *
amu_svc_getcaller(SVCXPRT * xprt)278 amu_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  */
296 int
amu_svc_register(SVCXPRT * xprt,u_long prognum,u_long versnum,void (* dispatch)(struct svc_req * rqstp,SVCXPRT * xprt),u_long protocol,struct netconfig * ncp)297 amu_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  */
310 static int
bind_resv_port_only_udp(u_short * pp)311 bind_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  */
406 static int
bind_nfs_port(int unused_so,u_short * nfs_portp)407 bind_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  */
422 int
create_nfs_service(int * soNFSp,u_short * nfs_portp,SVCXPRT ** nfs_xprtp,void (* dispatch_fxn)(struct svc_req * rqstp,SVCXPRT * transp),u_long nfs_version)423 create_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  */
468 static int
bind_preferred_amq_port(u_short pref_port,const struct netconfig * ncp,struct t_bind ** tretpp)469 bind_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   }
531 out:
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  */
540 int
create_amq_service(int * udp_soAMQp,SVCXPRT ** udp_amqpp,struct netconfig ** udp_amqncpp,int * tcp_soAMQp,SVCXPRT ** tcp_amqpp,struct netconfig ** tcp_amqncpp,u_short preferred_amq_port)541 create_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  */
635 int
get_knetconfig(struct knetconfig ** kncpp,struct netconfig * in_ncp,char * nc_protoname)636 get_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  */
682 void
free_knetconfig(struct knetconfig * kncp)683 free_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  */
check_pmap_up(char * host,struct sockaddr_in * sin)699 int 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  */
743 u_long
get_nfs_version(char * host,struct sockaddr_in * sin,u_long nfs_version,const char * proto,u_long def)744 get_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  */
799 static int
800 get_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 
833 out:
834   return ret;
835 }
836 
837 
838 /*
839  * Register the autofs service for amd
840  */
841 int
842 register_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 
889 out:
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 
899 really_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 
908 int
909 unregister_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 
922 out:
923   rpcb_unset(AUTOFS_PROG, AUTOFS_VERS, autofs_ncp);
924   return err;
925 }
926 #endif /* HAVE_FS_AUTOFS && AUTOFS_PROG */
927