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