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