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