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 */
52void
53amu_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 */
115int
116bind_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 */
148int
149amu_close(int fd)
150{
151  return close(fd);
152}
153
154
155/*
156 * Create an rpc client attached to the mount daemon.
157 */
158CLIENT *
159get_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 */
221struct sockaddr_in *
222amu_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 */
233int
234amu_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 */
246int
247create_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 */
283int
284create_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 */
372int 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 */
417u_long
418get_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 */
497int
498register_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
527int
528unregister_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