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