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 */
56void
57amu_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 */
119int
120bind_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 */
152int
153amu_close(int fd)
154{
155  return close(fd);
156}
157
158
159/*
160 * Create an rpc client attached to the mount daemon.
161 */
162CLIENT *
163get_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 */
225struct sockaddr_in *
226amu_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 */
236int
237amu_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 */
248int
249create_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 */
286int
287create_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 */
375int 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 */
420u_long
421get_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
442try_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 */
504int
505register_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
534int
535unregister_autofs_service(char *autofs_conftype)
536{
537  svc_unregister(AUTOFS_PROG, AUTOFS_VERS);
538  return 0;
539}
540#endif /* HAVE_FS_AUTOFS && AUTOFS_PROG */
541