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