]> git.decadent.org.uk Git - nfs-utils.git/blob - support/nfs/getport.c
Added support/nfs/getport.c
[nfs-utils.git] / support / nfs / getport.c
1 /*
2  * Provide a variety of APIs that query an rpcbind daemon to
3  * discover RPC service ports and allowed protocol version
4  * numbers.
5  *
6  * Copyright (C) 2008 Oracle Corporation.  All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public
19  * License along with this program; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 021110-1307, USA.
22  *
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <sys/types.h>
30 #include <sys/time.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34
35 #include <sys/socket.h>
36 #include <netinet/in.h>
37 #include <netdb.h>
38 #include <arpa/inet.h>
39
40 #include <rpc/rpc.h>
41 #include <rpc/pmap_prot.h>
42
43 #ifdef HAVE_TIRPC_NETCONFIG_H
44 #include <tirpc/netconfig.h>
45 #include <tirpc/rpc/rpcb_prot.h>
46 #endif
47
48 #include "nfsrpc.h"
49
50 /*
51  * Try a local socket first to access the local rpcbind daemon
52  *
53  * Rpcbind's local socket service does not seem to be working.
54  * Disable this logic for now.
55  */
56 #ifdef HAVE_XDR_RPCB
57 #undef NFS_GP_LOCAL
58 #else   /* HAVE_XDR_RPCB */
59 #undef NFS_GP_LOCAL
60 #endif  /* HAVE_XDR_RPCB */
61
62 #ifdef HAVE_XDR_RPCB
63 const static rpcvers_t default_rpcb_version = RPCBVERS_4;
64 #else
65 const static rpcvers_t default_rpcb_version = PMAPVERS;
66 #endif
67
68 static const char *nfs_gp_rpcb_pgmtbl[] = {
69         "rpcbind",
70         "portmap",
71         "portmapper",
72         "sunrpc",
73         NULL,
74 };
75
76 /*
77  * Discover the port number that should be used to contact an
78  * rpcbind service.  This will detect if the port has a local
79  * value that may have been set in /etc/services.
80  *
81  * NB: s_port is already in network byte order.
82  *
83  * Returns network byte-order port number of rpcbind service
84  * on this system.
85  */
86 static in_port_t nfs_gp_get_rpcb_port(const unsigned short protocol)
87 {
88         struct protoent *proto;
89
90         proto = getprotobynumber((int)protocol);
91         if (proto != NULL) {
92                 struct servent *entry;
93
94                 entry = getservbyname("rpcbind", proto->p_name);
95                 if (entry != NULL)
96                         return (in_port_t)entry->s_port;
97
98                 entry = getservbyname("portmapper", proto->p_name);
99                 if (entry != NULL)
100                         return (in_port_t)entry->s_port;
101
102                 entry = getservbyname("sunrpc", proto->p_name);
103                 if (entry != NULL)
104                         return (in_port_t)entry->s_port;
105         }
106         return htons((uint16_t)PMAPPORT);
107 }
108
109 /*
110  * Plant port number in @sap.  @port is already in network byte order.
111  */
112 static void nfs_gp_set_port(struct sockaddr *sap, const in_port_t port)
113 {
114         struct sockaddr_in *sin = (struct sockaddr_in *)sap;
115         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
116
117         switch (sap->sa_family) {
118         case AF_INET:
119                 sin->sin_port = port;
120                 break;
121         case AF_INET6:
122                 sin6->sin6_port = port;
123                 break;
124         default:
125                 fprintf(stderr, "%s: unrecognized address family\n",
126                         __func__);
127         }
128 }
129
130 /*
131  * Set up an RPC client for communicating with an rpcbind daemon at
132  * @sap over @transport with protocol version @version.
133  *
134  * Returns a pointer to a prepared RPC client if successful, and
135  * @timeout is initialized; caller must destroy a non-NULL returned RPC
136  * client.  Otherwise returns NULL, and rpc_createerr.cf_stat is set to
137  * reflect the error.
138  */
139 static CLIENT *nfs_gp_get_rpcbclient(const struct sockaddr *sap,
140                                      const socklen_t salen,
141                                      const unsigned short transport,
142                                      const rpcvers_t version,
143                                      struct timeval *timeout)
144 {
145         struct sockaddr_storage address;
146         struct sockaddr *saddr = (struct sockaddr *)&address;
147         rpcprog_t rpcb_prog = nfs_getrpcbyname(RPCBPROG, nfs_gp_rpcb_pgmtbl);
148
149         memcpy(saddr, sap, (size_t)salen);
150         nfs_gp_set_port(saddr, nfs_gp_get_rpcb_port(transport));
151
152         return nfs_get_rpcclient(saddr, salen, transport, rpcb_prog,
153                                         version, timeout);
154 }
155
156 /*
157  * One of the arguments passed when querying remote rpcbind services
158  * via rpcbind v3 or v4 is a netid string.  This replaces the pm_prot
159  * field used in legacy PMAP_GETPORT calls.
160  *
161  * RFC 1833 says netids are not standard but rather defined on the local
162  * host.  There are, however, standard definitions for nc_protofmly and
163  * nc_proto that can be used to derive a netid string on the local host,
164  * based on the contents of /etc/netconfig.
165  *
166  * Walk through the local netconfig database and grab the netid of the
167  * first entry that matches @family and @protocol and whose netid string
168  * fits in the provided buffer.
169  *
170  * Returns a '\0'-terminated string if successful; otherwise NULL.
171  * rpc_createerr.cf_stat is set to reflect the error.
172  */
173 #ifdef HAVE_XDR_RPCB
174
175 static char *nfs_gp_get_netid(const sa_family_t family,
176                               const unsigned short protocol)
177 {
178         char *nc_protofmly, *nc_proto, *nc_netid;
179         struct netconfig *nconf;
180         struct protoent *proto;
181         void *handle;
182
183         switch (family) {
184         case AF_LOCAL:
185         case AF_INET:
186                 nc_protofmly = NC_INET;
187                 break;
188         case AF_INET6:
189                 nc_protofmly = NC_INET6;
190                 break;
191         default:
192                 goto out;
193         }
194
195         proto = getprotobynumber(protocol);
196         if (proto == NULL)
197                 goto out;
198         nc_proto = proto->p_name;
199
200         handle = setnetconfig();
201         while ((nconf = getnetconfig(handle)) != NULL) {
202
203                 if (nconf->nc_protofmly != NULL &&
204                     strcmp(nconf->nc_protofmly, nc_protofmly) != 0)
205                         continue;
206                 if (nconf->nc_proto != NULL &&
207                     strcmp(nconf->nc_proto, nc_proto) != 0)
208                         continue;
209
210                 nc_netid = strdup(nconf->nc_netid);
211                 endnetconfig(handle);
212                 return nc_netid;
213         }
214         endnetconfig(handle);
215
216 out:
217         rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
218         return NULL;
219 }
220
221 #endif  /* HAVE_XDR_RPCB */
222
223 /*
224  * Extract a port number from a universal address, and terminate the
225  * string in @addrstr just after the address part.
226  *
227  * Returns -1 if unsuccesful; otherwise a decoded port number (possibly 0)
228  * is returned.
229  */
230 static int nfs_gp_universal_porthelper(char *addrstr)
231 {
232         char *p, *endptr;
233         unsigned long portlo, porthi;
234         int port = -1;
235
236         p = strrchr(addrstr, '.');
237         if (p == NULL)
238                 goto out;
239         portlo = strtoul(p + 1, &endptr, 10);
240         if (*endptr != '\0' || portlo > 255)
241                 goto out;
242         *p = '\0';
243
244         p = strrchr(addrstr, '.');
245         if (p == NULL)
246                 goto out;
247         porthi = strtoul(p + 1, &endptr, 10);
248         if (*endptr != '\0' || porthi > 255)
249                 goto out;
250         *p = '\0';
251         port = (porthi << 8) | portlo;
252
253 out:
254         return port;
255 }
256
257 /**
258  * nfs_universal2port - extract port number from a "universal address"
259  * @uaddr: '\0'-terminated C string containing a universal address
260  *
261  * Universal addresses (defined in RFC 1833) are used when calling an
262  * rpcbind daemon via protocol versions 3 or 4..
263  *
264  * Returns -1 if unsuccesful; otherwise a decoded port number (possibly 0)
265  * is returned.
266  */
267 int nfs_universal2port(const char *uaddr)
268 {
269         char *addrstr;
270         int port = -1;
271
272         addrstr = strdup(uaddr);
273         if (addrstr != NULL) {
274                 port = nfs_gp_universal_porthelper(addrstr);
275                 free(addrstr);
276         }
277         return port;
278 }
279
280 /**
281  * nfs_sockaddr2universal - convert a sockaddr to a "universal address"
282  * @sap: pointer to a socket address
283  * @salen: length of socket address
284  *
285  * Universal addresses (defined in RFC 1833) are used when calling an
286  * rpcbind daemon via protocol versions 3 or 4..
287  *
288  * Returns a '\0'-terminated string if successful; caller must free
289  * the returned string.  Otherwise NULL is returned and
290  * rpc_createerr.cf_stat is set to reflect the error.
291  *
292  */
293 #ifdef HAVE_GETNAMEINFO
294
295 char *nfs_sockaddr2universal(const struct sockaddr *sap,
296                              const socklen_t salen)
297 {
298         struct sockaddr_un *sun = (struct sockaddr_un *)sap;
299         char buf[NI_MAXHOST];
300         uint16_t port;
301
302         switch (sap->sa_family) {
303         case AF_LOCAL:
304                 return strndup(sun->sun_path, sizeof(sun->sun_path));
305         case AF_INET:
306                 if (getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
307                                         NULL, 0, NI_NUMERICHOST) != 0)
308                         goto out_err;
309                 port = ntohs(((struct sockaddr_in *)sap)->sin_port);
310                 break;
311         case AF_INET6:
312                 if (getnameinfo(sap, salen, buf, (socklen_t)sizeof(buf),
313                                         NULL, 0, NI_NUMERICHOST) != 0)
314                         goto out_err;
315                 port = ntohs(((struct sockaddr_in6 *)sap)->sin6_port);
316                 break;
317         default:
318                 goto out_err;
319         }
320
321         (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%u.%u",
322                         (unsigned)(port >> 8), (unsigned)(port & 0xff));
323
324         return strdup(buf);
325
326 out_err:
327         rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
328         return NULL;
329 }
330
331 #else   /* HAVE_GETNAMEINFO */
332
333 char *nfs_sockaddr2universal(const struct sockaddr *sap,
334                              const socklen_t salen)
335 {
336         struct sockaddr_un *sun = (struct sockaddr_un *)sap;
337         char buf[NI_MAXHOST];
338         uint16_t port;
339         char *addr;
340
341         switch (sap->sa_family) {
342         case AF_LOCAL:
343                 return strndup(sun->sun_path, sizeof(sun->sun_path));
344         case AF_INET:
345                 addr = inet_ntoa(((struct sockaddr_in *)sap)->sin_addr);
346                 if (addr != NULL && strlen(addr) > sizeof(buf))
347                         goto out_err;
348                 strcpy(buf, addr);
349                 port = ntohs(((struct sockaddr_in *)sap)->sin_port);
350                 break;
351         default:
352                 goto out_err;
353         }
354
355         (void)snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ".%u.%u",
356                         (unsigned)(port >> 8), (unsigned)(port & 0xff));
357
358         return strdup(buf);
359
360 out_err:
361         rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
362         return NULL;
363 }
364
365 #endif  /* HAVE_GETNAMEINFO */
366
367 /*
368  * Send a NULL request to the indicated RPC service.
369  *
370  * Returns 1 if the service responded; otherwise 0;
371  */
372 static int nfs_gp_ping(CLIENT *client, struct timeval timeout)
373 {
374         enum clnt_stat status;
375
376         status = CLNT_CALL(client, NULLPROC,
377                            (xdrproc_t)xdr_void, NULL,
378                            (xdrproc_t)xdr_void, NULL,
379                            timeout);
380
381         return (int)(status == RPC_SUCCESS);
382 }
383
384 #ifdef HAVE_XDR_RPCB
385
386 /*
387  * Initialize the rpcb argument for a GETADDR request.
388  *
389  * The rpcbind daemon ignores the parms.r_owner field in GETADDR
390  * requests, but we plant an eye-catcher to help distinguish these
391  * requests in network traces.
392  *
393  * Returns 1 if successful, and caller must free strings pointed
394  * to by r_netid and r_addr; otherwise 0.
395  */
396 static int nfs_gp_init_rpcb_parms(const struct sockaddr *sap,
397                                   const socklen_t salen,
398                                   const rpcprog_t program,
399                                   const rpcvers_t version,
400                                   const unsigned short protocol,
401                                   struct rpcb *parms)
402 {
403         char *netid, *addr;
404
405         netid = nfs_gp_get_netid(sap->sa_family, protocol);
406         if (netid == NULL)
407                 return 0;
408
409         addr = nfs_sockaddr2universal(sap, salen);
410         if (addr == NULL) {
411                 free(netid);
412                 return 0;
413         }
414
415         memset(parms, 0, sizeof(*parms));
416         parms->r_prog   = program;
417         parms->r_vers   = version;
418         parms->r_netid  = netid;
419         parms->r_addr   = addr;
420         parms->r_owner  = "nfs-utils";  /* eye-catcher */
421
422         return 1;
423 }
424
425 static void nfs_gp_free_rpcb_parms(struct rpcb *parms)
426 {
427         free(parms->r_netid);
428         free(parms->r_addr);
429 }
430
431 /*
432  * Try rpcbind GETADDR via version 4.  If that fails, try same
433  * request via version 3.
434  *
435  * Returns non-zero port number on success; otherwise returns
436  * zero.  rpccreateerr is set to reflect the nature of the error.
437  */
438 static unsigned short nfs_gp_rpcb_getaddr(CLIENT *client,
439                                           struct rpcb *parms,
440                                           struct timeval timeout)
441 {
442         rpcvers_t rpcb_version;
443         struct rpc_err rpcerr;
444         int port = 0;
445
446         for (rpcb_version = RPCBVERS_4;
447              rpcb_version >= RPCBVERS_3;
448              rpcb_version--) {
449                 enum clnt_stat status;
450                 char *uaddr = NULL;
451
452                 CLNT_CONTROL(client, CLSET_VERS, (void *)&rpcb_version);
453                 status = CLNT_CALL(client, (rpcproc_t)RPCBPROC_GETADDR,
454                                    (xdrproc_t)xdr_rpcb, (void *)parms,
455                                    (xdrproc_t)xdr_wrapstring, (void *)&uaddr,
456                                    timeout);
457
458                 switch (status) {
459                 case RPC_SUCCESS:
460                         if ((uaddr == NULL) || (uaddr[0] == '\0')) {
461                                 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
462                                 continue;
463                         }
464
465                         port = nfs_universal2port(uaddr);
466                         xdr_free((xdrproc_t)xdr_wrapstring, (char *)&uaddr);
467                         if (port == -1) {
468                                 rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
469                                 return 0;
470                         }
471                         return (unsigned short)port;
472                 case RPC_PROGVERSMISMATCH:
473                         clnt_geterr(client, &rpcerr);
474                         if (rpcerr.re_vers.low > RPCBVERS4)
475                                 return 0;
476                         continue;
477                 case RPC_PROCUNAVAIL:
478                 case RPC_PROGUNAVAIL:
479                         continue;
480                 default:
481                         /* Most likely RPC_TIMEDOUT or RPC_CANTRECV */
482                         rpc_createerr.cf_stat = status;
483                         clnt_geterr(client, &rpc_createerr.cf_error);
484                         return 0;
485                 }
486
487         }
488
489         if (port == 0) {
490                 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
491                 clnt_geterr(client, &rpc_createerr.cf_error);
492         }
493         return port;
494 }
495
496 #endif  /* HAVE_XDR_RPCB */
497
498 /*
499  * Try GETPORT request via rpcbind version 2.
500  *
501  * Returns non-zero port number on success; otherwise returns
502  * zero.  rpccreateerr is set to reflect the nature of the error.
503  */
504 static unsigned long nfs_gp_pmap_getport(CLIENT *client,
505                                          struct pmap *parms,
506                                          struct timeval timeout)
507 {
508         enum clnt_stat status;
509         unsigned long port;
510
511         status = CLNT_CALL(client, (rpcproc_t)PMAPPROC_GETPORT,
512                            (xdrproc_t)xdr_pmap, (void *)parms,
513                            (xdrproc_t)xdr_u_long, (void *)&port,
514                            timeout);
515
516         if (status != RPC_SUCCESS) {
517                 rpc_createerr.cf_stat = status;
518                 clnt_geterr(client, &rpc_createerr.cf_error);
519                 port = 0;
520         } else if (port == 0)
521                 rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
522
523         return port;
524 }
525
526 #ifdef HAVE_XDR_RPCB
527
528 static unsigned short nfs_gp_getport_rpcb(CLIENT *client,
529                                           const struct sockaddr *sap,
530                                           const socklen_t salen,
531                                           const rpcprog_t program,
532                                           const rpcvers_t version,
533                                           const unsigned short protocol,
534                                           struct timeval timeout)
535 {
536         unsigned short port = 0;
537         struct rpcb parms;
538
539         if (nfs_gp_init_rpcb_parms(sap, salen, program,
540                                         version, protocol, &parms) != 0) {
541                 port = nfs_gp_rpcb_getaddr(client, &parms, timeout);
542                 nfs_gp_free_rpcb_parms(&parms);
543         }
544
545         return port;
546 }
547
548 #endif  /* HAVE_XDR_RPCB */
549
550 static unsigned long nfs_gp_getport_pmap(CLIENT *client,
551                                          const rpcprog_t program,
552                                          const rpcvers_t version,
553                                          const unsigned short protocol,
554                                          struct timeval timeout)
555 {
556         struct pmap parms = {
557                 .pm_prog        = program,
558                 .pm_vers        = version,
559                 .pm_prot        = protocol,
560         };
561         rpcvers_t pmap_version = PMAPVERS;
562
563         CLNT_CONTROL(client, CLSET_VERS, (void *)&pmap_version);
564         return nfs_gp_pmap_getport(client, &parms, timeout);
565 }
566
567 /*
568  * Try an AF_INET6 request via rpcbind v4/v3; try an AF_INET
569  * request via rpcbind v2.
570  *
571  * Returns non-zero port number on success; otherwise returns
572  * zero.  rpccreateerr is set to reflect the nature of the error.
573  */
574 static unsigned short nfs_gp_getport(CLIENT *client,
575                                      const struct sockaddr *sap,
576                                      const socklen_t salen,
577                                      const rpcprog_t program,
578                                      const rpcvers_t version,
579                                      const unsigned short protocol,
580                                      struct timeval timeout)
581 {
582         switch (sap->sa_family) {
583 #ifdef HAVE_XDR_RPCB
584         case AF_INET6:
585                 return nfs_gp_getport_rpcb(client, sap, salen, program,
586                                                 version, protocol, timeout);
587 #endif  /* HAVE_XDR_RPCB */
588         case AF_INET:
589                 return nfs_gp_getport_pmap(client, program, version,
590                                                         protocol, timeout);
591         }
592
593         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
594         return 0;
595 }
596
597 /**
598  * nfs_rcp_ping - Determine if RPC service is responding to requests
599  * @sap: pointer to address of server to query (port is already filled in)
600  * @salen: length of server address
601  * @program: requested RPC program number
602  * @version: requested RPC version number
603  * @protocol: requested IPPROTO_ value of transport protocol
604  * @timeout: pointer to request timeout (NULL means use default timeout)
605  *
606  * Returns 1 if the remote service responded without an error; otherwise
607  * zero.
608  */
609 int nfs_rpc_ping(const struct sockaddr *sap, const socklen_t salen,
610                  const rpcprog_t program, const rpcvers_t version,
611                  const unsigned short protocol, const struct timeval *timeout)
612 {
613         CLIENT *client;
614         struct timeval tout = { -1, 0 };
615         int result = 0;
616
617         if (timeout != NULL)
618                 tout = *timeout;
619
620         client = nfs_get_rpcclient(sap, salen, protocol, program, version, &tout);
621         if (client != NULL) {
622                 result = nfs_gp_ping(client, tout);
623                 CLNT_DESTROY(client);
624         }
625
626         return result;
627 }
628
629 /**
630  * nfs_getport - query server's rpcbind to get port number for an RPC service
631  * @sap: pointer to address of server to query
632  * @salen: length of server's address
633  * @program: requested RPC program number
634  * @version: requested RPC version number
635  * @protocol: IPPROTO_ value of requested transport protocol
636  *
637  * Uses any acceptable rpcbind version to discover the port number for the
638  * RPC service described by the given [program, version, transport] tuple.
639  * Uses a quick timeout and an ephemeral source port.  Supports AF_INET and
640  * AF_INET6 server addresses.
641  *
642  * Returns a positive integer representing the port number of the RPC
643  * service advertised by the server (in host byte order), or zero if the
644  * service is not advertised or there was some problem querying the server's
645  * rpcbind daemon.  rpccreateerr is set to reflect the underlying cause of
646  * the error.
647  *
648  * There are a variety of ways to choose which transport and rpcbind versions
649  * to use.  We chose to conserve local resources and try to avoid incurring
650  * timeouts.
651  *
652  * Transport
653  * To provide rudimentary support for traversing firewalls, query the remote
654  * using the same transport as the requested service.  This provides some
655  * guarantee that the requested transport is available between this client
656  * and the server, and if the caller specifically requests TCP, for example,
657  * this may be becuase a firewall is in place that blocks UDP traffic.  We
658  * could try both, but that could involve a lengthy timeout in several cases,
659  * and would often consume an extra ephemeral port.
660  *
661  * Rpcbind version
662  * To avoid using up too many ephemeral ports, AF_INET queries use tried-and-
663  * true rpcbindv2, and don't try the newer versions; and AF_INET6 queries use
664  * rpcbindv4, then rpcbindv3 on the same socket.  The newer rpcbind protocol
665  * versions can adequately detect if a remote RPC service does not support
666  * AF_INET6 at all.  The rpcbind socket is re-used in an attempt to keep the
667  * overall number of consumed ephemeral ports low.
668  */
669 unsigned short nfs_getport(const struct sockaddr *sap,
670                            const socklen_t salen,
671                            const rpcprog_t program,
672                            const rpcvers_t version,
673                            const unsigned short protocol)
674 {
675         struct timeval timeout = { -1, 0 };
676         unsigned short port = 0;
677         CLIENT *client;
678
679         client = nfs_gp_get_rpcbclient(sap, salen, protocol,
680                                                 default_rpcb_version, &timeout);
681         if (client != NULL) {
682                 port = nfs_gp_getport(client, sap, salen, program,
683                                         version, protocol, timeout);
684                 CLNT_DESTROY(client);
685         }
686
687         return port;
688 }
689
690 /**
691  * nfs_getport_ping - query server's rpcbind and do RPC ping to verify result
692  * @sap: IN: pointer to address of server to query;
693  *       OUT: pointer to updated address
694  * @salen: length of server's address
695  * @program: requested RPC program number
696  * @version: requested RPC version number
697  * @protocol: IPPROTO_ value of requested transport protocol
698  *
699  * Uses any acceptable rpcbind version to discover the port number for the
700  * RPC service described by the given [program, version, transport] tuple.
701  * Uses a quick timeout and an ephemeral source port.  Supports AF_INET and
702  * AF_INET6 server addresses.
703  *
704  * Returns a 1 and sets the port number in the passed-in server address
705  * if both the query and the ping were successful; otherwise zero.
706  * rpccreateerr is set to reflect the underlying cause of the error.
707  */
708 int nfs_getport_ping(struct sockaddr *sap, const socklen_t salen,
709                      const rpcprog_t program, const rpcvers_t version,
710                      const unsigned short protocol)
711 {
712         struct timeval timeout = { -1, 0 };
713         unsigned short port = 0;
714         CLIENT *client;
715         int result = 0;
716         
717         client = nfs_gp_get_rpcbclient(sap, salen, protocol,
718                                                 default_rpcb_version, &timeout);
719         if (client != NULL) {
720                 port = nfs_gp_getport(client, sap, salen, program,
721                                         version, protocol, timeout);
722                 CLNT_DESTROY(client);
723                 client = NULL;
724         }
725
726         if (port != 0) {
727                 struct sockaddr_storage address;
728                 struct sockaddr *saddr = (struct sockaddr *)&address;
729
730                 memcpy(saddr, sap, (size_t)salen);
731                 nfs_gp_set_port(saddr, htons(port));
732
733                 client = nfs_get_rpcclient(saddr, salen, protocol,
734                                                 program, version, &timeout);
735                 if (client != NULL) {
736                         result = nfs_gp_ping(client, timeout);
737                         CLNT_DESTROY(client);
738                 }
739         }
740
741         if (result)
742                 nfs_gp_set_port(sap, htons(port));
743
744         return result;
745 }
746
747 /**
748  * nfs_getlocalport - query local rpcbind to get port number for an RPC service
749  * @program: requested RPC program number
750  * @version: requested RPC version number
751  * @protocol: IPPROTO_ value of requested transport protocol
752  *
753  * Uses any acceptable rpcbind version to discover the port number for the
754  * RPC service described by the given [program, version, transport] tuple.
755  * Uses a quick timeout and an ephemeral source port.  Supports AF_INET and
756  * AF_INET6 local addresses.
757  *
758  * Returns a positive integer representing the port number of the RPC
759  * service advertised by the server (in host byte order), or zero if the
760  * service is not advertised or there was some problem querying the server's
761  * rpcbind daemon.  rpccreateerr is set to reflect the underlying cause of
762  * the error.
763  *
764  * Try an AF_LOCAL connection first.  The rpcbind daemon implementation should
765  * listen on AF_LOCAL.
766  *
767  * If that doesn't work (for example, if portmapper is running, or rpcbind
768  * isn't listening on /var/run/rpcbind.sock), send a query via UDP to localhost
769  * (UDP doesn't leave a socket in TIME_WAIT, and the timeout is a relatively
770  * short 3 seconds).
771  *
772  * getaddrinfo(3) generates a usable loopback address.  RFC 3484 requires that
773  * the results are sorted so that the first result has the best likelihood of
774  * working, so we try just that first result.  If IPv6 is all that is
775  * available, we are sure to generate an AF_INET6 loopback address and use
776  * rpcbindv4/v3 GETADDR.  AF_INET6 requests go via rpcbind v4/3 in order to
777  * detect if the requested RPC service supports AF_INET6 or not.
778  */
779 unsigned short nfs_getlocalport(const rpcprot_t program,
780                                 const rpcvers_t version,
781                                 const unsigned short protocol)
782 {
783         struct addrinfo *gai_results;
784         struct addrinfo gai_hint = {
785                 .ai_flags       = AI_ADDRCONFIG,
786         };
787         unsigned short port = 0;
788         int error;
789
790 #ifdef NFS_GP_LOCAL
791         const struct sockaddr_un sun = {
792                 .sun_family     = AF_LOCAL,
793                 .sun_path       = _PATH_RPCBINDSOCK,
794         };
795         const struct sockaddr *sap = (struct sockaddr *)&sun;
796         const socklen_t salen = SUN_LEN(&sun);
797         CLIENT *client;
798         struct timeval timeout = { -1, 0 };
799
800         client = nfs_gp_get_rpcbclient(sap, salen, 0, RPCBVERS_4, &timeout);
801         if (client != NULL) {
802                 struct rpcb parms;
803
804                 if (nfs_gp_init_rpcb_parms(sap, salen, program, version,
805                                                 protocol, &parms) != 0) {
806                         port = nfs_gp_rpcb_getaddr(client, &parms, timeout);
807                         nfs_gp_free_rpcb_parms(&parms);
808                 }
809                 CLNT_DESTROY(client);
810         }
811 #endif  /* NFS_GP_LOCAL */
812
813         if (port == 0) {
814                 error = getaddrinfo(NULL, "sunrpc", &gai_hint, &gai_results);
815                 if (error == 0) {
816                         port = nfs_getport(gai_results->ai_addr,
817                                                 gai_results->ai_addrlen,
818                                                 program, version, protocol);
819                         freeaddrinfo(gai_results);
820                 } else
821                         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
822         }
823
824         return port;
825 }
826
827 /**
828  * nfs_rpcb_getaddr - query rpcbind via rpcbind versions 4 and 3
829  * @sap: pointer to address of server to query
830  * @salen: length of server address
831  * @transport: transport protocol to use for the query
832  * @addr: pointer to r_addr address
833  * @addrlen: length of address
834  * @program: requested RPC program number
835  * @version: requested RPC version number
836  * @protocol: requested IPPROTO_ value of transport protocol
837  * @timeout: pointer to request timeout (NULL means use default timeout)
838  *
839  * Returns a positive integer representing the port number of the RPC
840  * service advertised by the server (in host byte order), or zero if the
841  * service is not advertised or there was some problem querying the
842  * server's rpcbind daemon.  rpccreateerr is set to reflect the
843  * underlying cause of the error.
844  *
845  * This function provides similar functionality to nfs_pmap_getport(),
846  * but performs the rpcbind lookup via rpcbind version 4.  If the server
847  * doesn't support rpcbind version 4, it will retry with version 3.
848  * The GETADDR procedure is exactly the same in these two versions of
849  * the rpcbind protocol, so the socket, RPC client, and arguments are
850  * re-used when retrying, saving ephemeral port space.
851  *
852  * These RPC procedures take a universal address as an argument, so the
853  * query will fail if the remote rpcbind daemon doesn't find an entry
854  * with a matching address.  A matching address includes an ANYADDR
855  * address of the same address family.  In this way an RPC server can
856  * advertise via rpcbind that it does not support AF_INET6.
857  */
858 #ifdef HAVE_XDR_RPCB
859
860 unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
861                                 const socklen_t salen,
862                                 const unsigned short transport,
863                                 const struct sockaddr *addr,
864                                 const socklen_t addrlen,
865                                 const rpcprog_t program,
866                                 const rpcvers_t version,
867                                 const unsigned short protocol,
868                                 const struct timeval *timeout)
869 {
870         CLIENT *client;
871         struct rpcb parms;
872         struct timeval tout = { -1, 0 };
873         unsigned short port = 0;
874
875         if (timeout != NULL)
876                 tout = *timeout;
877
878         client = nfs_gp_get_rpcbclient(sap, salen, transport, RPCBVERS_4, &tout);
879         if (client != NULL) {
880                 if (nfs_gp_init_rpcb_parms(addr, addrlen, program, version,
881                                                 protocol, &parms) != 0) {
882                         port = nfs_gp_rpcb_getaddr(client, &parms, tout);
883                         nfs_gp_free_rpcb_parms(&parms);
884                 }
885                 CLNT_DESTROY(client);
886         }
887
888         return port;
889 }
890
891 #else   /* HAVE_XDR_RPCB */
892
893 unsigned short nfs_rpcb_getaddr(const struct sockaddr *sap,
894                                 const socklen_t salen,
895                                 const unsigned short transport,
896                                 const struct sockaddr *addr,
897                                 const socklen_t addrlen,
898                                 const rpcprog_t program,
899                                 const rpcvers_t version,
900                                 const unsigned short protocol,
901                                 const struct timeval *timeout)
902 {
903         rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
904         return 0;
905 }
906
907 #endif  /* HAVE_XDR_RPCB */
908
909 /**
910  * nfs_pmap_getport - query rpcbind via the portmap protocol (rpcbindv2)
911  * @sin: pointer to AF_INET address of server to query
912  * @transport: transport protocol to use for the query
913  * @program: requested RPC program number
914  * @version: requested RPC version number
915  * @protocol: requested IPPROTO_ value of transport protocol
916  * @timeout: pointer to request timeout (NULL means use default timeout)
917  *
918  * Returns a positive integer representing the port number of the RPC service
919  * advertised by the server (in host byte order), or zero if the service is
920  * not advertised or there was some problem querying the server's rpcbind
921  * daemon.  rpccreateerr is set to reflect the underlying cause of the error.
922  *
923  * nfs_pmap_getport() is very similar to pmap_getport(), except that:
924  *
925  *  1.  This version always tries to use an ephemeral port, since reserved
926  *      ports are not needed for GETPORT queries.  This conserves the very
927  *      limited reserved port space, helping reduce failed socket binds
928  *      during mount storms.
929  *
930  *  2.  This version times out quickly by default.  It time-limits the
931  *      connect process as well as the actual RPC call, and even allows the
932  *      caller to specify the timeout.
933  *
934  *  3.  This version shares code with the rpcbindv3 and rpcbindv4 query
935  *      functions.  It can use a TI-RPC generated CLIENT.
936  */
937 unsigned long nfs_pmap_getport(const struct sockaddr_in *sin,
938                                const unsigned short transport,
939                                const unsigned long program,
940                                const unsigned long version,
941                                const unsigned long protocol,
942                                const struct timeval *timeout)
943 {
944         CLIENT *client;
945         struct pmap parms = {
946                 .pm_prog        = program,
947                 .pm_vers        = version,
948                 .pm_prot        = protocol,
949         };
950         struct timeval tout = { -1, 0 };
951         unsigned long port = 0;
952
953         if (timeout != NULL)
954                 tout = *timeout;
955
956         client = nfs_gp_get_rpcbclient((struct sockaddr *)sin,
957                                         (socklen_t)sizeof(*sin),
958                                         transport, PMAPVERS, &tout);
959         if (client != NULL) {
960                 port = nfs_gp_pmap_getport(client, &parms, tout);
961                 CLNT_DESTROY(client);
962         }
963
964         return port;
965 }