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