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