xref: /netbsd-src/external/bsd/am-utils/dist/conf/transp/transp_sockets.c (revision 4da6d876cd9670a0f55b4857d574006fa142db45)
1 /*	$NetBSD: transp_sockets.c,v 1.2 2015/01/17 17:46:31 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_sockets.c
39  *
40  * Socket 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 
51 /*
52  * find the IP address that can be used to connect to the local host
53  */
54 void
amu_get_myaddress(struct in_addr * iap,const char * preferred_localhost)55 amu_get_myaddress(struct in_addr *iap, const char *preferred_localhost)
56 {
57   struct hostent *hp;
58   char dq[20];
59 
60 #ifdef DEBUG_off
61 #error this code is old and probably not useful any longer.
62 #error Erez, Jan 21, 2004.
63   struct sockaddr_in sin;
64 
65   /*
66    * Most modern systems should use 127.0.0.1 as the localhost address over
67    * which you can do NFS mounts.  In the past we found that some NFS
68    * clients may not allow mounts from localhost.  So we used
69    * get_myaddress() and that seemed to work.  Alas, on some other systems,
70    * get_myaddress() may return one of the interface addresses at random,
71    * and thus use a less efficient IP address than 127.0.0.1.  The solution
72    * is to hard-code 127.0.0.1, but still check if get_myaddress() returns a
73    * different value and warn about it.
74    */
75   memset((char *) &sin, 0, sizeof(sin));
76   get_myaddress(&sin);
77   if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK))
78     dlog("amu_get_myaddress: myaddress conflict (0x%x vs. 0x%lx)",
79 	 sin.sin_addr.s_addr, (u_long) htonl(INADDR_LOOPBACK));
80 #endif /* DEBUG_off */
81 
82   if (preferred_localhost == NULL)
83     goto out;
84 
85   /* if specified preferred locahost, then try to use it */
86   hp = gethostbyname(preferred_localhost);
87   if (hp == NULL) {
88     /* XXX: if hstrerror()/h_errno aren't portable, then need to port the next statement */
89     plog(XLOG_ERROR, "Unable to resolve localhost_address \"%s\" (%s): using default",
90 	 preferred_localhost, hstrerror(h_errno));
91     goto out;
92   }
93   if (hp->h_addr_list == NULL) {
94     plog(XLOG_ERROR, "localhost_address \"%s\" has no IP addresses: using default",
95 	 preferred_localhost);
96     goto out;
97   }
98   if (hp->h_addr_list[1] != NULL) {
99     plog(XLOG_ERROR, "localhost_address \"%s\" has more than one IP addresses: using first",
100 	 preferred_localhost);
101     goto out;
102   }
103   memmove((voidp) &iap->s_addr, (voidp) hp->h_addr_list[0], sizeof(iap->s_addr));
104   plog(XLOG_INFO, "localhost_address \"%s\" requested, using %s",
105        preferred_localhost, inet_dquad(dq, sizeof(dq), iap->s_addr));
106   return;
107 
108  out:
109   iap->s_addr = htonl(INADDR_LOOPBACK);
110 }
111 
112 
113 /*
114  * How to bind to reserved ports.
115  * Note: if *pp is non-null and is greater than 0, then *pp will not be modified.
116  */
117 int
bind_resv_port(int so,u_short * pp)118 bind_resv_port(int so, u_short *pp)
119 {
120   struct sockaddr_in sin;
121   int rc;
122   u_short port;
123 
124   memset((voidp) &sin, 0, sizeof(sin));
125   sin.sin_family = AF_INET;
126 
127   if (pp && *pp > 0) {
128     sin.sin_port = htons(*pp);
129     rc = bind(so, (struct sockaddr *) &sin, sizeof(sin));
130   } else {
131     port = IPPORT_RESERVED;
132 
133     do {
134       --port;
135       sin.sin_port = htons(port);
136       rc = bind(so, (struct sockaddr *) &sin, sizeof(sin));
137     } while (rc < 0 && (int) port > IPPORT_RESERVED / 2);
138 
139     if (pp && rc == 0)
140       *pp = port;
141   }
142 
143   return rc;
144 }
145 
146 
147 /*
148  * close a descriptor, Sockets style
149  */
150 int
amu_close(int fd)151 amu_close(int fd)
152 {
153   return close(fd);
154 }
155 
156 
157 /*
158  * Create an rpc client attached to the mount daemon.
159  */
160 CLIENT *
get_mount_client(char * unused_host,struct sockaddr_in * sin,struct timeval * tv,int * sock,u_long mnt_version)161 get_mount_client(char *unused_host, struct sockaddr_in *sin, struct timeval *tv, int *sock, u_long mnt_version)
162 {
163   CLIENT *client;
164 
165   /*
166    * First try a TCP socket
167    */
168   if ((*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0) {
169     /*
170      * Bind to a privileged port
171      */
172     if (bind_resv_port(*sock, (u_short *) NULL) < 0)
173       plog(XLOG_ERROR, "can't bind privileged port (socket)");
174 
175     /*
176      * Find mountd port to connect to.
177      * Connect to mountd.
178      * Create a tcp client.
179      */
180     if ((sin->sin_port = htons(pmap_getport(sin, MOUNTPROG, mnt_version, IPPROTO_TCP))) != 0) {
181       if (connect(*sock, (struct sockaddr *) sin, sizeof(*sin)) >= 0
182 	  && ((client = clnttcp_create(sin, MOUNTPROG, mnt_version, sock, 0, 0)) != NULL))
183 	return client;
184     }
185     /*
186      * Failed so close socket
187      */
188     (void) close(*sock);
189   }				/* tcp socket opened */
190   /* TCP failed so try UDP */
191   if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
192     plog(XLOG_ERROR, "Can't create socket to connect to mountd: %m");
193     *sock = RPC_ANYSOCK;
194     return NULL;
195   }
196   /*
197    * Bind to a privileged port
198    */
199   if (bind_resv_port(*sock, (u_short *) NULL) < 0)
200     plog(XLOG_ERROR, "can't bind privileged port");
201 
202   /*
203    * Zero out the port - make sure we recompute
204    */
205   sin->sin_port = 0;
206 
207   /*
208    * Make a UDP client
209    */
210   if ((client = clntudp_create(sin, MOUNTPROG, mnt_version, *tv, sock)) == NULL) {
211     (void) close(*sock);
212     *sock = RPC_ANYSOCK;
213     return NULL;
214   }
215   dlog("get_mount_client: Using udp, port %d", sin->sin_port);
216   return client;
217 }
218 
219 
220 /*
221  * find the address of the caller of an RPC procedure.
222  */
223 struct sockaddr_in *
amu_svc_getcaller(SVCXPRT * xprt)224 amu_svc_getcaller(SVCXPRT *xprt)
225 {
226   /* glibc 2.2 returns a sockaddr_storage ??? */
227   return (struct sockaddr_in *) svc_getcaller(xprt);
228 }
229 
230 
231 /*
232  * Register an RPC server:
233  * return 1 on success, 0 otherwise.
234  */
235 int
amu_svc_register(SVCXPRT * xprt,u_long prognum,u_long versnum,void (* dispatch)(struct svc_req * rqstp,SVCXPRT * transp),u_long protocol,struct netconfig * dummy)236 amu_svc_register(SVCXPRT *xprt, u_long prognum, u_long versnum,
237 		 void (*dispatch)(struct svc_req *rqstp, SVCXPRT *transp),
238 		 u_long protocol, struct netconfig *dummy)
239 {
240   /* on Sockets: svc_register returns 1 on success, 0 otherwise */
241   return svc_register(xprt, prognum, versnum, dispatch, protocol);
242 }
243 
244 
245 /*
246  * Create the nfs service for amd
247  */
248 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)249 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)
250 {
251   *soNFSp = socket(AF_INET, SOCK_DGRAM, 0);
252 
253   if (*soNFSp < 0 || bind_resv_port(*soNFSp, nfs_portp) < 0) {
254     plog(XLOG_FATAL, "Can't create privileged nfs port (socket)");
255     if (*soNFSp >= 0)
256       close(*soNFSp);
257     return 1;
258   }
259   if ((*nfs_xprtp = svcudp_create(*soNFSp)) == NULL) {
260     plog(XLOG_FATAL, "cannot create rpc/udp service");
261     close(*soNFSp);
262     return 2;
263   }
264   if ((*nfs_portp = (*nfs_xprtp)->xp_port) >= IPPORT_RESERVED) {
265     plog(XLOG_FATAL, "Can't create privileged nfs port");
266     svc_destroy(*nfs_xprtp);
267     close(*soNFSp);
268     return 1;
269   }
270   if (!svc_register(*nfs_xprtp, NFS_PROGRAM, nfs_version, dispatch_fxn, 0)) {
271     plog(XLOG_FATAL, "unable to register (%lu, %lu, 0)",
272 	 (u_long) NFS_PROGRAM, nfs_version);
273     svc_destroy(*nfs_xprtp);
274     close(*soNFSp);
275     return 3;
276   }
277 
278   return 0;			/* all is well */
279 }
280 
281 
282 /*
283  * Create the amq service for amd (both TCP and UDP)
284  */
285 int
create_amq_service(int * udp_soAMQp,SVCXPRT ** udp_amqpp,struct netconfig ** dummy1,int * tcp_soAMQp,SVCXPRT ** tcp_amqpp,struct netconfig ** dummy2,u_short preferred_amq_port)286 create_amq_service(int *udp_soAMQp,
287 		   SVCXPRT **udp_amqpp,
288 		   struct netconfig **dummy1,
289 		   int *tcp_soAMQp,
290 		   SVCXPRT **tcp_amqpp,
291 		   struct netconfig **dummy2,
292 		   u_short preferred_amq_port)
293 {
294   /* first create TCP service */
295   if (tcp_soAMQp) {
296     *tcp_soAMQp = socket(AF_INET, SOCK_STREAM, 0);
297     if (*tcp_soAMQp < 0) {
298       plog(XLOG_FATAL, "cannot create tcp socket for amq service: %m");
299       return 1;
300     }
301 
302     /* next, bind to a specific (TCP) port if asked for */
303     if (preferred_amq_port > 0) {
304       /*
305        * Note: if &preferred_amq_port is non-null and is greater than 0,
306        * then the pointer will not be modified.  We don't want it to be
307        * modified because it was passed down to create_amq_service as a
308        * non-pointer (a variable on the stack, not to be modified!)
309        */
310       if (bind_resv_port(*tcp_soAMQp, &preferred_amq_port) < 0) {
311 	plog(XLOG_FATAL, "can't bind amq service to requested TCP port %d: %m)", preferred_amq_port);
312 	return 1;
313       }
314     }
315 
316     /* now create RPC service handle for amq */
317     if (tcp_amqpp &&
318 	(*tcp_amqpp = svctcp_create(*tcp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) {
319       plog(XLOG_FATAL, "cannot create tcp service for amq: soAMQp=%d", *tcp_soAMQp);
320       return 1;
321     }
322 
323 #ifdef SVCSET_CONNMAXREC
324     /*
325      * This is *BSD at its best.
326      * They just had to do things differently than everyone else
327      * so they fixed a library DoS issue by forcing client-side changes...
328      */
329 # ifndef RPC_MAXDATASIZE
330 #  define RPC_MAXDATASIZE 9000
331 # endif /* not RPC_MAXDATASIZE */
332     if (tcp_amqpp) {
333       int maxrec = RPC_MAXDATASIZE;
334       SVC_CONTROL(*tcp_amqpp, SVCSET_CONNMAXREC, &maxrec);
335     }
336 #endif /* not SVCSET_CONNMAXREC */
337   }
338 
339   /* next create UDP service */
340   if (udp_soAMQp) {
341     *udp_soAMQp = socket(AF_INET, SOCK_DGRAM, 0);
342     if (*udp_soAMQp < 0) {
343       plog(XLOG_FATAL, "cannot create udp socket for amq service: %m");
344       return 1;
345     }
346 
347     /* next, bind to a specific (UDP) port if asked for */
348     if (preferred_amq_port > 0) {
349       /*
350        * Note: see comment about using &preferred_amq_port above in this
351        * function.
352        */
353       if (bind_resv_port(*udp_soAMQp, &preferred_amq_port) < 0) {
354 	plog(XLOG_FATAL, "can't bind amq service to requested UDP port %d: %m)", preferred_amq_port);
355 	return 1;
356       }
357     }
358 
359     /* now create RPC service handle for amq */
360     if (udp_amqpp &&
361 	(*udp_amqpp = svcudp_bufcreate(*udp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) {
362       plog(XLOG_FATAL, "cannot create udp service for amq: soAMQp=%d", *udp_soAMQp);
363       return 1;
364     }
365   }
366 
367   return 0;			/* all is well */
368 }
369 
370 
371 /*
372  * Check if the portmapper is running and reachable: 0==down, 1==up
373  */
check_pmap_up(char * host,struct sockaddr_in * sin)374 int check_pmap_up(char *host, struct sockaddr_in* sin)
375 {
376   CLIENT *client;
377   enum clnt_stat clnt_stat = RPC_TIMEDOUT; /* assume failure */
378   int socket = RPC_ANYSOCK;
379   struct timeval timeout;
380 
381   timeout.tv_sec = 2;
382   timeout.tv_usec = 0;
383   sin->sin_port = htons(PMAPPORT);
384   client = clntudp_create(sin, PMAPPROG, PMAPVERS, timeout, &socket);
385 
386   if (client == (CLIENT *) NULL) {
387     plog(XLOG_ERROR,
388 	 "%s: cannot create connection to contact portmapper on host \"%s\"%s",
389 	 __func__, host, clnt_spcreateerror(""));
390     return 0;
391   }
392 
393   timeout.tv_sec = 6;
394   /* Ping the portmapper on a remote system by calling the nullproc */
395   clnt_stat = clnt_call(client,
396 			PMAPPROC_NULL,
397 			(XDRPROC_T_TYPE) xdr_void,
398 			NULL,
399 			(XDRPROC_T_TYPE) xdr_void,
400 			NULL,
401 			timeout);
402   clnt_destroy(client);
403   close(socket);
404   sin->sin_port = 0;
405 
406   if (clnt_stat == RPC_TIMEDOUT) {
407     plog(XLOG_ERROR,
408 	 "%s: failed to contact portmapper on host \"%s\": %s",
409 	 __func__, host, clnt_sperrno(clnt_stat));
410     return 0;
411   }
412   return 1;
413 }
414 
415 
416 /*
417  * Find the best NFS version for a host and protocol.
418  */
419 u_long
get_nfs_version(char * host,struct sockaddr_in * sin,u_long nfs_version,const char * proto,u_long def)420 get_nfs_version(char *host, struct sockaddr_in *sin, u_long nfs_version, const char *proto, u_long def)
421 {
422   CLIENT *clnt;
423   enum clnt_stat clnt_stat;
424   struct timeval tv;
425   int sock;
426   char *errstr;
427 
428   /*
429    * If not set or set wrong, then try from NFS_VERS_MAX on down. If
430    * set, then try from nfs_version on down.
431    */
432   if (!nfs_valid_version(nfs_version)) {
433     if (nfs_valid_version(def))
434       nfs_version = def;
435     else
436       nfs_version = NFS_VERS_MAX;
437   }
438   tv.tv_sec = 2;		/* retry every 2 seconds, but also timeout */
439   tv.tv_usec = 0;
440 
441   for (; nfs_version >= NFS_VERS_MIN; nfs_version--) {
442 
443     sock = RPC_ANYSOCK;
444     errstr = NULL;
445     if (STREQ(proto, "tcp"))
446       clnt = clnttcp_create(sin, NFS_PROGRAM, nfs_version, &sock, 0, 0);
447     else if (STREQ(proto, "udp"))
448       clnt = clntudp_create(sin, NFS_PROGRAM, nfs_version, tv, &sock);
449     else
450       clnt = NULL;
451 
452     if (clnt != NULL) {
453       /* Try three times (6/2=3) to verify the CLIENT handle. */
454       tv.tv_sec = 6;
455       clnt_stat = clnt_call(clnt,
456 			    NFSPROC_NULL,
457 			    (XDRPROC_T_TYPE) xdr_void,
458 			    0,
459 			    (XDRPROC_T_TYPE) xdr_void,
460 			    0,
461 			    tv);
462 
463       if (clnt_stat != RPC_SUCCESS)
464 	errstr = clnt_sperrno(clnt_stat);
465 
466       close(sock);
467       clnt_destroy(clnt);
468       if (clnt_stat == RPC_SUCCESS)
469 	break;
470     } else {
471   #ifdef HAVE_CLNT_SPCREATEERROR
472       errstr = clnt_spcreateerror("");
473   #else /* not HAVE_CLNT_SPCREATEERROR */
474       errstr = "";
475   #endif /* not HAVE_CLNT_SPCREATEERROR */
476     }
477 
478     if (errstr) {
479       plog(XLOG_INFO, "%s: NFS(%lu,%s) failed for %s: %s", __func__,
480 	   nfs_version, proto, host, errstr);
481     }
482   }
483 
484   if (nfs_version < NFS_VERS_MIN)
485     nfs_version = 0;
486 
487   plog(XLOG_INFO, "%s: returning NFS(%lu,%s) on host %s", __func__,
488        nfs_version, proto, host);
489   return nfs_version;
490 }
491 
492 
493 #if defined(HAVE_FS_AUTOFS) && defined(AUTOFS_PROG)
494 /*
495  * Register the autofs service for amd
496  */
497 int
register_autofs_service(char * autofs_conftype,void (* autofs_dispatch)(struct svc_req * rqstp,SVCXPRT * transp))498 register_autofs_service(char *autofs_conftype, void (*autofs_dispatch)(struct svc_req *rqstp, SVCXPRT *transp))
499 {
500   int autofs_socket;
501   SVCXPRT *autofs_xprt = NULL;
502 
503   autofs_socket = socket(AF_INET, SOCK_DGRAM, 0);
504 
505   if (autofs_socket < 0 || bind_resv_port(autofs_socket, NULL) < 0) {
506     plog(XLOG_FATAL, "Can't create privileged autofs port (socket)");
507     return 1;
508   }
509   if ((autofs_xprt = svcudp_create(autofs_socket)) == NULL) {
510     plog(XLOG_FATAL, "Can't create autofs rpc/udp service");
511     return 2;
512   }
513   if (autofs_xprt->xp_port >= IPPORT_RESERVED) {
514     plog(XLOG_FATAL, "Can't create privileged autofs port");
515     return 1;
516   }
517   if (!svc_register(autofs_xprt, AUTOFS_PROG, AUTOFS_VERS, autofs_dispatch, 0)) {
518     plog(XLOG_FATAL, "unable to register (%ld, %ld, 0)",
519 	 (u_long) AUTOFS_PROG, (u_long) AUTOFS_VERS);
520     return 3;
521   }
522 
523   return 0;			/* all is well */
524 }
525 
526 
527 int
unregister_autofs_service(char * autofs_conftype)528 unregister_autofs_service(char *autofs_conftype)
529 {
530   svc_unregister(AUTOFS_PROG, AUTOFS_VERS);
531   return 0;
532 }
533 #endif /* HAVE_FS_AUTOFS && AUTOFS_PROG */
534