]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/amd/conf/transp/transp_sockets.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / amd / conf / transp / transp_sockets.c
1 /*
2  * Copyright (c) 1997-2006 Erez Zadok
3  * Copyright (c) 1990 Jan-Simon Pendry
4  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5  * Copyright (c) 1990 The Regents of the University of California.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to Berkeley by
9  * Jan-Simon Pendry at Imperial College, London.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  * 3. All advertising materials mentioning features or use of this software
20  *    must display the following acknowledgment:
21  *      This product includes software developed by the University of
22  *      California, Berkeley and its contributors.
23  * 4. Neither the name of the University nor the names of its contributors
24  *    may be used to endorse or promote products derived from this software
25  *    without specific prior written permission.
26  *
27  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37  * SUCH DAMAGE.
38  *
39  *
40  * File: am-utils/conf/transp/transp_sockets.c
41  *
42  * Socket specific utilities.
43  *      -Erez Zadok <ezk@cs.columbia.edu>
44  */
45
46 #ifdef HAVE_CONFIG_H
47 # include <config.h>
48 #endif /* HAVE_CONFIG_H */
49 #include <am_defs.h>
50 #include <amu.h>
51
52
53 /*
54  * find the IP address that can be used to connect to the local host
55  */
56 void
57 amu_get_myaddress(struct in_addr *iap, const char *preferred_localhost)
58 {
59   struct hostent *hp;
60   char dq[20];
61
62 #ifdef DEBUG_off
63 #error this code is old and probably not useful any longer.
64 #error Erez, Jan 21, 2004.
65   struct sockaddr_in sin;
66
67   /*
68    * Most modern systems should use 127.0.0.1 as the localhost address over
69    * which you can do NFS mounts.  In the past we found that some NFS
70    * clients may not allow mounts from localhost.  So we used
71    * get_myaddress() and that seemed to work.  Alas, on some other systems,
72    * get_myaddress() may return one of the interface addresses at random,
73    * and thus use a less efficient IP address than 127.0.0.1.  The solution
74    * is to hard-code 127.0.0.1, but still check if get_myaddress() returns a
75    * different value and warn about it.
76    */
77   memset((char *) &sin, 0, sizeof(sin));
78   get_myaddress(&sin);
79   if (sin.sin_addr.s_addr != htonl(INADDR_LOOPBACK))
80     dlog("amu_get_myaddress: myaddress conflict (0x%x vs. 0x%lx)",
81          sin.sin_addr.s_addr, (u_long) htonl(INADDR_LOOPBACK));
82 #endif /* DEBUG_off */
83
84   if (preferred_localhost == NULL)
85     goto out;
86
87   /* if specified preferred locahost, then try to use it */
88   hp = gethostbyname(preferred_localhost);
89   if (hp == NULL) {
90     /* XXX: if hstrerror()/h_errno aren't portable, then need to port the next statement */
91     plog(XLOG_ERROR, "Unable to resolve localhost_address \"%s\" (%s): using default",
92          preferred_localhost, hstrerror(h_errno));
93     goto out;
94   }
95   if (hp->h_addr_list == NULL) {
96     plog(XLOG_ERROR, "localhost_address \"%s\" has no IP addresses: using default",
97          preferred_localhost);
98     goto out;
99   }
100   if (hp->h_addr_list[1] != NULL) {
101     plog(XLOG_ERROR, "localhost_address \"%s\" has more than one IP addresses: using first",
102          preferred_localhost);
103     goto out;
104   }
105   memmove((voidp) &iap->s_addr, (voidp) hp->h_addr_list[0], sizeof(iap->s_addr));
106   plog(XLOG_INFO, "localhost_address \"%s\" requested, using %s",
107        preferred_localhost, inet_dquad(dq, sizeof(dq), iap->s_addr));
108   return;
109
110  out:
111   iap->s_addr = htonl(INADDR_LOOPBACK);
112 }
113
114
115 /*
116  * How to bind to reserved ports.
117  * Note: if *pp is non-null and is greater than 0, then *pp will not be modified.
118  */
119 int
120 bind_resv_port(int so, u_short *pp)
121 {
122   struct sockaddr_in sin;
123   int rc;
124   u_short port;
125
126   memset((voidp) &sin, 0, sizeof(sin));
127   sin.sin_family = AF_INET;
128
129   if (pp && *pp > 0) {
130     sin.sin_port = htons(*pp);
131     rc = bind(so, (struct sockaddr *) &sin, sizeof(sin));
132   } else {
133     port = IPPORT_RESERVED;
134
135     do {
136       --port;
137       sin.sin_port = htons(port);
138       rc = bind(so, (struct sockaddr *) &sin, sizeof(sin));
139     } while (rc < 0 && (int) port > IPPORT_RESERVED / 2);
140
141     if (pp && rc == 0)
142       *pp = port;
143   }
144
145   return rc;
146 }
147
148
149 /*
150  * close a descriptor, Sockets style
151  */
152 int
153 amu_close(int fd)
154 {
155   return close(fd);
156 }
157
158
159 /*
160  * Create an rpc client attached to the mount daemon.
161  */
162 CLIENT *
163 get_mount_client(char *unused_host, struct sockaddr_in *sin, struct timeval *tv, int *sock, u_long mnt_version)
164 {
165   CLIENT *client;
166
167   /*
168    * First try a TCP socket
169    */
170   if ((*sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) > 0) {
171     /*
172      * Bind to a privileged port
173      */
174     if (bind_resv_port(*sock, (u_short *) 0) < 0)
175       plog(XLOG_ERROR, "can't bind privileged port (socket)");
176
177     /*
178      * Find mountd port to connect to.
179      * Connect to mountd.
180      * Create a tcp client.
181      */
182     if ((sin->sin_port = htons(pmap_getport(sin, MOUNTPROG, mnt_version, IPPROTO_TCP))) != 0) {
183       if (connect(*sock, (struct sockaddr *) sin, sizeof(*sin)) >= 0
184           && ((client = clnttcp_create(sin, MOUNTPROG, mnt_version, sock, 0, 0)) != NULL))
185         return client;
186     }
187     /*
188      * Failed so close socket
189      */
190     (void) close(*sock);
191   }                             /* tcp socket opened */
192   /* TCP failed so try UDP */
193   if ((*sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
194     plog(XLOG_ERROR, "Can't create socket to connect to mountd: %m");
195     *sock = RPC_ANYSOCK;
196     return NULL;
197   }
198   /*
199    * Bind to a privileged port
200    */
201   if (bind_resv_port(*sock, (u_short *) 0) < 0)
202     plog(XLOG_ERROR, "can't bind privileged port");
203
204   /*
205    * Zero out the port - make sure we recompute
206    */
207   sin->sin_port = 0;
208
209   /*
210    * Make a UDP client
211    */
212   if ((client = clntudp_create(sin, MOUNTPROG, mnt_version, *tv, sock)) == NULL) {
213     (void) close(*sock);
214     *sock = RPC_ANYSOCK;
215     return NULL;
216   }
217   dlog("get_mount_client: Using udp, port %d", sin->sin_port);
218   return client;
219 }
220
221
222 /*
223  * find the address of the caller of an RPC procedure.
224  */
225 struct sockaddr_in *
226 amu_svc_getcaller(SVCXPRT *xprt)
227 {
228   /* glibc 2.2 returns a sockaddr_storage ??? */
229   return (struct sockaddr_in *) svc_getcaller(xprt);
230 }
231
232
233 /*
234  * register an RPC server
235  */
236 int
237 amu_svc_register(SVCXPRT *xprt, u_long prognum, u_long versnum,
238                  void (*dispatch)(struct svc_req *rqstp, SVCXPRT *transp),
239                  u_long protocol, struct netconfig *dummy)
240 {
241   return svc_register(xprt, prognum, versnum, dispatch, protocol);
242 }
243
244
245 /*
246  * Create the nfs service for amd
247  */
248 int
249 create_nfs_service(int *soNFSp, u_short *nfs_portp, SVCXPRT **nfs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp))
250 {
251
252   *soNFSp = socket(AF_INET, SOCK_DGRAM, 0);
253
254   if (*soNFSp < 0 || bind_resv_port(*soNFSp, nfs_portp) < 0) {
255     plog(XLOG_FATAL, "Can't create privileged nfs port (socket)");
256     if (*soNFSp >= 0)
257       close(*soNFSp);
258     return 1;
259   }
260   if ((*nfs_xprtp = svcudp_create(*soNFSp)) == NULL) {
261     plog(XLOG_FATAL, "cannot create rpc/udp service");
262     close(*soNFSp);
263     return 2;
264   }
265   if ((*nfs_portp = (*nfs_xprtp)->xp_port) >= IPPORT_RESERVED) {
266     plog(XLOG_FATAL, "Can't create privileged nfs port");
267     svc_destroy(*nfs_xprtp);
268     close(*soNFSp);
269     return 1;
270   }
271   if (!svc_register(*nfs_xprtp, NFS_PROGRAM, NFS_VERSION, dispatch_fxn, 0)) {
272     plog(XLOG_FATAL, "unable to register (%ld, %ld, 0)",
273          (u_long) NFS_PROGRAM, (u_long) NFS_VERSION);
274     svc_destroy(*nfs_xprtp);
275     close(*soNFSp);
276     return 3;
277   }
278
279   return 0;                     /* all is well */
280 }
281
282
283 /*
284  * Create the amq service for amd (both TCP and UDP)
285  */
286 int
287 create_amq_service(int *udp_soAMQp,
288                    SVCXPRT **udp_amqpp,
289                    struct netconfig **dummy1,
290                    int *tcp_soAMQp,
291                    SVCXPRT **tcp_amqpp,
292                    struct netconfig **dummy2,
293                    u_short preferred_amq_port)
294 {
295   /* first create TCP service */
296   if (tcp_soAMQp) {
297     *tcp_soAMQp = socket(AF_INET, SOCK_STREAM, 0);
298     if (*tcp_soAMQp < 0) {
299       plog(XLOG_FATAL, "cannot create tcp socket for amq service: %m");
300       return 1;
301     }
302
303     /* next, bind to a specific (TCP) port if asked for */
304     if (preferred_amq_port > 0) {
305       /*
306        * Note: if &preferred_amq_port is non-null and is greater than 0,
307        * then the pointer will not be modified.  We don't want it to be
308        * modified because it was passed down to create_amq_service as a
309        * non-pointer (a variable on the stack, not to be modified!)
310        */
311       if (bind_resv_port(*tcp_soAMQp, &preferred_amq_port) < 0) {
312         plog(XLOG_FATAL, "can't bind amq service to requested TCP port %d: %m)", preferred_amq_port);
313         return 1;
314       }
315     }
316
317     /* now create RPC service handle for amq */
318     if (tcp_amqpp &&
319         (*tcp_amqpp = svctcp_create(*tcp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) {
320       plog(XLOG_FATAL, "cannot create tcp service for amq: soAMQp=%d", *tcp_soAMQp);
321       return 1;
322     }
323
324 #ifdef SVCSET_CONNMAXREC
325     /*
326      * This is *BSD at its best.
327      * They just had to do things differently than everyone else
328      * so they fixed a library DoS issue by forcing client-side changes...
329      */
330 # ifndef RPC_MAXDATASIZE
331 #  define RPC_MAXDATASIZE 9000
332 # endif /* not RPC_MAXDATASIZE */
333     if (tcp_amqpp) {
334       int maxrec = RPC_MAXDATASIZE;
335       SVC_CONTROL(*tcp_amqpp, SVCSET_CONNMAXREC, &maxrec);
336     }
337 #endif /* not SVCSET_CONNMAXREC */
338   }
339
340   /* next create UDP service */
341   if (udp_soAMQp) {
342     *udp_soAMQp = socket(AF_INET, SOCK_DGRAM, 0);
343     if (*udp_soAMQp < 0) {
344       plog(XLOG_FATAL, "cannot create udp socket for amq service: %m");
345       return 1;
346     }
347
348     /* next, bind to a specific (UDP) port if asked for */
349     if (preferred_amq_port > 0) {
350       /*
351        * Note: see comment about using &preferred_amq_port above in this
352        * function.
353        */
354       if (bind_resv_port(*udp_soAMQp, &preferred_amq_port) < 0) {
355         plog(XLOG_FATAL, "can't bind amq service to requested UDP port %d: %m)", preferred_amq_port);
356         return 1;
357       }
358     }
359
360     /* now create RPC service handle for amq */
361     if (udp_amqpp &&
362         (*udp_amqpp = svcudp_bufcreate(*udp_soAMQp, AMQ_SIZE, AMQ_SIZE)) == NULL) {
363       plog(XLOG_FATAL, "cannot create udp service for amq: soAMQp=%d", *udp_soAMQp);
364       return 1;
365     }
366   }
367
368   return 0;                     /* all is well */
369 }
370
371
372 /*
373  * Check if the portmapper is running and reachable: 0==down, 1==up
374  */
375 int check_pmap_up(char *host, struct sockaddr_in* sin)
376 {
377   CLIENT *client;
378   enum clnt_stat clnt_stat = RPC_TIMEDOUT; /* assume failure */
379   int socket = RPC_ANYSOCK;
380   struct timeval timeout;
381
382   timeout.tv_sec = 2;
383   timeout.tv_usec = 0;
384   sin->sin_port = htons(PMAPPORT);
385   client = clntudp_create(sin, PMAPPROG, PMAPVERS, timeout, &socket);
386
387   if (client == (CLIENT *) NULL) {
388     plog(XLOG_ERROR,
389          "check_pmap_up: cannot create connection to contact portmapper on host \"%s\"%s",
390          host, clnt_spcreateerror(""));
391     return 0;
392   }
393
394   timeout.tv_sec = 6;
395   /* Ping the portmapper on a remote system by calling the nullproc */
396   clnt_stat = clnt_call(client,
397                         PMAPPROC_NULL,
398                         (XDRPROC_T_TYPE) xdr_void,
399                         NULL,
400                         (XDRPROC_T_TYPE) xdr_void,
401                         NULL,
402                         timeout);
403   clnt_destroy(client);
404   close(socket);
405   sin->sin_port = 0;
406
407   if (clnt_stat == RPC_TIMEDOUT) {
408     plog(XLOG_ERROR,
409          "check_pmap_up: failed to contact portmapper on host \"%s\": %s",
410          host, clnt_sperrno(clnt_stat));
411     return 0;
412   }
413   return 1;
414 }
415
416
417 /*
418  * Find the best NFS version for a host and protocol.
419  */
420 u_long
421 get_nfs_version(char *host, struct sockaddr_in *sin, u_long nfs_version, const char *proto)
422 {
423   CLIENT *clnt;
424   int again = 0;
425   enum clnt_stat clnt_stat;
426   struct timeval tv;
427   int sock;
428   char *errstr;
429
430   /*
431    * If not set or set wrong, then try from NFS_VERS_MAX on down. If
432    * set, then try from nfs_version on down.
433    */
434   if (nfs_version <= 0 || nfs_version > NFS_VERS_MAX) {
435     nfs_version = NFS_VERS_MAX;
436     again = 1;
437   }
438   tv.tv_sec = 2;                /* retry every 2 seconds, but also timeout */
439   tv.tv_usec = 0;
440
441 #ifdef HAVE_FS_NFS3
442 try_again:
443 #endif /* HAVE_FS_NFS3 */
444
445   sock = RPC_ANYSOCK;
446   errstr = NULL;
447   if (STREQ(proto, "tcp"))
448     clnt = clnttcp_create(sin, NFS_PROGRAM, nfs_version, &sock, 0, 0);
449   else if (STREQ(proto, "udp"))
450     clnt = clntudp_create(sin, NFS_PROGRAM, nfs_version, tv, &sock);
451   else
452     clnt = NULL;
453
454   if (clnt != NULL) {
455     /* Try three times (6/2=3) to verify the CLIENT handle. */
456     tv.tv_sec = 6;
457     clnt_stat = clnt_call(clnt,
458                           NFSPROC_NULL,
459                           (XDRPROC_T_TYPE) xdr_void,
460                           0,
461                           (XDRPROC_T_TYPE) xdr_void,
462                           0,
463                           tv);
464
465     if (clnt_stat != RPC_SUCCESS)
466       errstr = clnt_sperrno(clnt_stat);
467
468     close(sock);
469     clnt_destroy(clnt);
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, "get_nfs_version NFS(%d,%s) failed for %s%s",
480          (int) nfs_version, proto, host, errstr);
481     if (again) {
482 #ifdef HAVE_FS_NFS3
483       if (nfs_version == NFS_VERSION3) {
484         nfs_version = NFS_VERSION;
485         again = 0;
486         plog(XLOG_INFO, "get_nfs_version trying a lower version: NFS(%d,%s)", (int) nfs_version, proto);
487       }
488       goto try_again;
489 #endif /* HAVE_FS_NFS3 */
490     }
491     return 0;
492   }
493
494   plog(XLOG_INFO, "get_nfs_version: returning NFS(%d,%s) on host %s",
495        (int) nfs_version, proto, host);
496   return nfs_version;
497 }
498
499
500 #if defined(HAVE_FS_AUTOFS) && defined(AUTOFS_PROG)
501 /*
502  * Register the autofs service for amd
503  */
504 int
505 register_autofs_service(char *autofs_conftype, void (*autofs_dispatch)(struct svc_req *rqstp, SVCXPRT *transp))
506 {
507   int autofs_socket;
508   SVCXPRT *autofs_xprt = NULL;
509
510   autofs_socket = socket(AF_INET, SOCK_DGRAM, 0);
511
512   if (autofs_socket < 0 || bind_resv_port(autofs_socket, NULL) < 0) {
513     plog(XLOG_FATAL, "Can't create privileged autofs port (socket)");
514     return 1;
515   }
516   if ((autofs_xprt = svcudp_create(autofs_socket)) == NULL) {
517     plog(XLOG_FATAL, "Can't create autofs rpc/udp service");
518     return 2;
519   }
520   if (autofs_xprt->xp_port >= IPPORT_RESERVED) {
521     plog(XLOG_FATAL, "Can't create privileged autofs port");
522     return 1;
523   }
524   if (!svc_register(autofs_xprt, AUTOFS_PROG, AUTOFS_VERS, autofs_dispatch, 0)) {
525     plog(XLOG_FATAL, "unable to register (%ld, %ld, 0)",
526          (u_long) AUTOFS_PROG, (u_long) AUTOFS_VERS);
527     return 3;
528   }
529
530   return 0;                     /* all is well */
531 }
532
533
534 int
535 unregister_autofs_service(char *autofs_conftype)
536 {
537   svc_unregister(AUTOFS_PROG, AUTOFS_VERS);
538   return 0;
539 }
540 #endif /* HAVE_FS_AUTOFS && AUTOFS_PROG */