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