2 * Generic RPC client socket-level APIs for nfs-utils
4 * Copyright (C) 2008 Oracle Corporation. All rights reserved.
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 * Boston, MA 021110-1307, USA.
27 #include <sys/types.h>
33 #include <sys/socket.h>
34 #include <netinet/in.h>
36 #include <arpa/inet.h>
39 #include <rpc/pmap_prot.h>
43 #ifdef HAVE_TIRPC_NETCONFIG_H
46 * Most of the headers under /usr/include/tirpc are currently
47 * unusable for various reasons. We statically define the bits
48 * we need here until the official headers are fixed.
50 * The commonly used RPC calls such as CLNT_CALL and CLNT_DESTROY
51 * are actually virtual functions in both the legacy and TI-RPC
52 * implementations. The proper _CALL or _DESTROY will be invoked
53 * no matter if we used a legacy clnt_create() or clnt_tli_create()
57 #include <tirpc/netconfig.h>
58 #include <tirpc/rpc/rpcb_prot.h>
60 /* definitions from tirpc/rpc/types.h */
63 * The netbuf structure is used for transport-independent address storage.
71 /* definitions from tirpc/rpc/clnt.h */
74 * Low level clnt create routine for connectionless transports, e.g. udp.
76 extern CLIENT *clnt_dg_create(const int, const struct netbuf *,
77 const rpcprog_t, const rpcvers_t,
78 const u_int, const u_int);
81 * Low level clnt create routine for connectionful transports, e.g. tcp.
83 extern CLIENT *clnt_vc_create(const int, const struct netbuf *,
84 const rpcprog_t, const rpcvers_t,
87 #endif /* HAVE_TIRPC_NETCONFIG_H */
90 * If "-1" is specified in the tv_sec field, use these defaults instead.
92 #define NFSRPC_TIMEOUT_UDP (3)
93 #define NFSRPC_TIMEOUT_TCP (10)
96 * Set up an RPC client for communicating via a AF_LOCAL socket.
98 * @timeout is initialized upon return
100 * Returns a pointer to a prepared RPC client if successful; caller
101 * must destroy a non-NULL returned RPC client. Otherwise NULL, and
102 * rpc_createerr.cf_stat is set to reflect the error.
104 static CLIENT *nfs_get_localclient(const struct sockaddr *sap,
105 const socklen_t salen,
106 const rpcprog_t program,
107 const rpcvers_t version,
108 struct timeval *timeout)
110 #ifdef HAVE_CLNT_VC_CREATE
111 struct sockaddr_storage address;
112 const struct netbuf nbuf = {
113 .maxlen = sizeof(struct sockaddr_un),
114 .len = (size_t)salen,
117 #endif /* HAVE_CLNT_VC_CREATE */
121 sock = socket(AF_LOCAL, SOCK_STREAM, 0);
123 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
124 rpc_createerr.cf_error.re_errno = errno;
128 if (timeout->tv_sec == -1)
129 timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
131 #ifdef HAVE_CLNT_VC_CREATE
132 memcpy(nbuf.buf, sap, (size_t)salen);
133 client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
134 #else /* HAVE_CLNT_VC_CREATE */
135 client = clntunix_create((struct sockaddr_un *)sap,
136 program, version, &sock, 0, 0);
137 #endif /* HAVE_CLNT_VC_CREATE */
139 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
147 * Bind a socket using an unused ephemeral source port.
149 * Returns zero on success, or returns -1 on error. errno is
150 * set to reflect the nature of the error.
152 static int nfs_bind(const int sock, const sa_family_t family)
154 struct sockaddr_in sin = {
155 .sin_family = AF_INET,
156 .sin_addr.s_addr = htonl(INADDR_ANY),
158 struct sockaddr_in6 sin6 = {
159 .sin6_family = AF_INET6,
160 .sin6_addr = IN6ADDR_ANY_INIT,
165 return bind(sock, (struct sockaddr *)&sin,
166 (socklen_t)sizeof(sin));
168 return bind(sock, (struct sockaddr *)&sin6,
169 (socklen_t)sizeof(sin6));
172 errno = EAFNOSUPPORT;
177 * Perform a non-blocking connect on the socket fd.
179 * @timeout is modified to contain the time remaining (i.e. time provided
180 * minus time elasped).
182 * Returns zero on success, or returns -1 on error. errno is
183 * set to reflect the nature of the error.
185 static int nfs_connect_nb(const int fd, const struct sockaddr *sap,
186 const socklen_t salen, struct timeval *timeout)
191 flags = fcntl(fd, F_GETFL, 0);
195 ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
200 * From here on subsequent sys calls could change errno so
201 * we set ret = -errno to capture it in case we decide to
204 ret = connect(fd, sap, salen);
205 if (ret < 0 && errno != EINPROGRESS) {
217 ret = select(fd + 1, NULL, &rset, NULL, timeout);
225 if (FD_ISSET(fd, &rset)) {
227 socklen_t len = (socklen_t)sizeof(ret);
229 status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
235 /* Oops - something wrong with connect */
243 (void)fcntl(fd, F_SETFL, flags);
248 * Set up an RPC client for communicating via a datagram socket.
249 * A connected UDP socket is used to detect a missing remote
250 * listener as quickly as possible.
252 * @timeout is initialized upon return
254 * Returns a pointer to a prepared RPC client if successful; caller
255 * must destroy a non-NULL returned RPC client. Otherwise NULL, and
256 * rpc_createerr.cf_stat is set to reflect the error.
258 static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
259 const socklen_t salen,
260 const rpcprog_t program,
261 const rpcvers_t version,
262 struct timeval *timeout)
264 #ifdef HAVE_CLNT_DG_CREATE
265 struct sockaddr_storage address;
266 const struct netbuf nbuf = {
271 #endif /* HAVE_CLNT_DG_CREATE */
275 #ifndef HAVE_CLNT_DG_CREATE
276 if (sap->sa_family != AF_INET) {
277 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
280 #endif /* !HAVE_CLNT_DG_CREATE */
282 sock = socket((int)sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
284 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
285 rpc_createerr.cf_error.re_errno = errno;
289 ret = nfs_bind(sock, sap->sa_family);
291 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
292 rpc_createerr.cf_error.re_errno = errno;
297 if (timeout->tv_sec == -1)
298 timeout->tv_sec = NFSRPC_TIMEOUT_UDP;
300 ret = nfs_connect_nb(sock, sap, salen, timeout);
302 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
303 rpc_createerr.cf_error.re_errno = errno;
308 #ifdef HAVE_CLNT_DG_CREATE
309 memcpy(nbuf.buf, sap, (size_t)salen);
310 client = clnt_dg_create(sock, &nbuf, program, version, 0, 0);
311 #else /* HAVE_CLNT_DG_CREATE */
312 client = clntudp_create((struct sockaddr_in *)sap, program,
313 version, *timeout, &sock);
314 #endif /* HAVE_CLNT_DG_CREATE */
315 if (client != NULL) {
316 CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)timeout);
317 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
325 * Set up and connect an RPC client for communicating via a stream socket.
327 * @timeout is initialized upon return
329 * Returns a pointer to a prepared and connected RPC client if
330 * successful; caller must destroy a non-NULL returned RPC client.
331 * Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
334 static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
335 const socklen_t salen,
336 const rpcprog_t program,
337 const rpcvers_t version,
338 struct timeval *timeout)
340 #ifdef HAVE_CLNT_VC_CREATE
341 struct sockaddr_storage address;
342 const struct netbuf nbuf = {
347 #endif /* HAVE_CLNT_VC_CREATE */
351 #ifndef HAVE_CLNT_VC_CREATE
352 if (sap->sa_family != AF_INET) {
353 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
356 #endif /* !HAVE_CLNT_VC_CREATE */
358 sock = socket((int)sap->sa_family, SOCK_STREAM, IPPROTO_TCP);
360 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
361 rpc_createerr.cf_error.re_errno = errno;
365 ret = nfs_bind(sock, sap->sa_family);
367 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
368 rpc_createerr.cf_error.re_errno = errno;
373 if (timeout->tv_sec == -1)
374 timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
376 ret = nfs_connect_nb(sock, sap, salen, timeout);
378 rpc_createerr.cf_stat = RPC_SYSTEMERROR;
379 rpc_createerr.cf_error.re_errno = errno;
384 #ifdef HAVE_CLNT_VC_CREATE
385 memcpy(nbuf.buf, sap, (size_t)salen);
386 client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
387 #else /* HAVE_CLNT_VC_CREATE */
388 client = clnttcp_create((struct sockaddr_in *)sap,
389 program, version, &sock, 0, 0);
390 #endif /* HAVE_CLNT_VC_CREATE */
392 CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
400 * nfs_get_rpcclient - acquire an RPC client
401 * @sap: pointer to socket address of RPC server
402 * @salen: length of socket address
403 * @transport: IPPROTO_ value of transport protocol to use
404 * @program: RPC program number
405 * @version: RPC version number
406 * @timeout: pointer to request timeout (must not be NULL)
408 * Set up an RPC client for communicating with an RPC program @program
409 * and @version on the server @sap over @transport.
411 * Returns a pointer to a prepared RPC client if successful, and
412 * @timeout is initialized; caller must destroy a non-NULL returned RPC
413 * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to
416 CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
417 const socklen_t salen,
418 const unsigned short transport,
419 const rpcprog_t program,
420 const rpcvers_t version,
421 struct timeval *timeout)
423 struct sockaddr_in *sin = (struct sockaddr_in *)sap;
424 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
426 switch (sap->sa_family) {
428 return nfs_get_localclient(sap, salen, program,
431 if (sin->sin_port == 0) {
432 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
437 if (sin6->sin6_port == 0) {
438 rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
443 rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
449 return nfs_get_tcpclient(sap, salen, program, version, timeout);
452 return nfs_get_udpclient(sap, salen, program, version, timeout);
455 rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
460 * nfs_getrpcbyname - convert an RPC program name to a rpcprog_t
461 * @program: default program number to use if names not found in db
462 * @table: pointer to table of 'char *' names to try to find
464 * Returns program number of first name to be successfully looked
465 * up, or the default program number if all lookups fail.
467 rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[])
469 #ifdef HAVE_GETRPCBYNAME
470 struct rpcent *entry;
474 for (i = 0; table[i] != NULL; i++) {
475 entry = getrpcbyname(table[i]);
477 return (rpcprog_t)entry->r_number;
479 #endif /* HAVE_GETRPCBYNAME */