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>
44
#include <netconfig.h>
45
#include <rpc/rpcb_prot.h>
46
#endif /* HAVE_LIBTIRPC */
49
* If "-1" is specified in the tv_sec field, use these defaults instead.
51
#define NFSRPC_TIMEOUT_UDP (3)
52
#define NFSRPC_TIMEOUT_TCP (10)
55
* Set up an RPC client for communicating via a AF_LOCAL socket.
57
* @timeout is initialized upon return
59
* Returns a pointer to a prepared RPC client if successful; caller
60
* must destroy a non-NULL returned RPC client. Otherwise NULL, and
61
* rpc_createerr.cf_stat is set to reflect the error.
63
static CLIENT *nfs_get_localclient(const struct sockaddr *sap,
64
const socklen_t salen,
65
const rpcprog_t program,
66
const rpcvers_t version,
67
struct timeval *timeout)
70
struct sockaddr_storage address;
71
const struct netbuf nbuf = {
72
.maxlen = sizeof(struct sockaddr_un),
76
#endif /* HAVE_LIBTIRPC */
80
sock = socket(AF_LOCAL, SOCK_STREAM, 0);
82
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
83
rpc_createerr.cf_error.re_errno = errno;
87
if (timeout->tv_sec == -1)
88
timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
91
memcpy(nbuf.buf, sap, (size_t)salen);
92
client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
93
#else /* !HAVE_LIBTIRPC */
94
client = clntunix_create((struct sockaddr_un *)sap,
95
program, version, &sock, 0, 0);
96
#endif /* !HAVE_LIBTIRPC */
98
CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
106
* Bind a socket using an unused ephemeral source port.
108
* Returns zero on success, or returns -1 on error. errno is
109
* set to reflect the nature of the error.
111
static int nfs_bind(const int sock, const sa_family_t family)
113
struct sockaddr_in sin = {
114
.sin_family = AF_INET,
115
.sin_addr.s_addr = htonl(INADDR_ANY),
117
struct sockaddr_in6 sin6 = {
118
.sin6_family = AF_INET6,
119
.sin6_addr = IN6ADDR_ANY_INIT,
124
return bind(sock, (struct sockaddr *)&sin,
125
(socklen_t)sizeof(sin));
127
return bind(sock, (struct sockaddr *)&sin6,
128
(socklen_t)sizeof(sin6));
131
errno = EAFNOSUPPORT;
135
#ifdef IPV6_SUPPORTED
138
* Bind a socket using an unused privileged source port.
140
* Returns zero on success, or returns -1 on error. errno is
141
* set to reflect the nature of the error.
143
static int nfs_bindresvport(const int sock, const sa_family_t family)
145
struct sockaddr_in sin = {
146
.sin_family = AF_INET,
147
.sin_addr.s_addr = htonl(INADDR_ANY),
149
struct sockaddr_in6 sin6 = {
150
.sin6_family = AF_INET6,
151
.sin6_addr = IN6ADDR_ANY_INIT,
156
return bindresvport_sa(sock, (struct sockaddr *)&sin);
158
return bindresvport_sa(sock, (struct sockaddr *)&sin6);
161
errno = EAFNOSUPPORT;
165
#else /* !IPV6_SUPPORTED */
168
* Bind a socket using an unused privileged source port.
170
* Returns zero on success, or returns -1 on error. errno is
171
* set to reflect the nature of the error.
173
static int nfs_bindresvport(const int sock, const sa_family_t family)
175
if (family != AF_INET) {
176
errno = EAFNOSUPPORT;
180
return bindresvport(sock, NULL);
183
#endif /* !IPV6_SUPPORTED */
186
* Perform a non-blocking connect on the socket fd.
188
* @timeout is modified to contain the time remaining (i.e. time provided
189
* minus time elasped).
191
* Returns zero on success, or returns -1 on error. errno is
192
* set to reflect the nature of the error.
194
static int nfs_connect_nb(const int fd, const struct sockaddr *sap,
195
const socklen_t salen, struct timeval *timeout)
200
flags = fcntl(fd, F_GETFL, 0);
204
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
209
* From here on subsequent sys calls could change errno so
210
* we set ret = -errno to capture it in case we decide to
213
ret = connect(fd, sap, salen);
214
if (ret < 0 && errno != EINPROGRESS) {
226
ret = select(fd + 1, NULL, &rset, NULL, timeout);
234
if (FD_ISSET(fd, &rset)) {
236
socklen_t len = (socklen_t)sizeof(ret);
238
status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
244
/* Oops - something wrong with connect */
252
(void)fcntl(fd, F_SETFL, flags);
257
* Set up an RPC client for communicating via a datagram socket.
258
* A connected UDP socket is used to detect a missing remote
259
* listener as quickly as possible.
261
* @timeout is initialized upon return
263
* Returns a pointer to a prepared RPC client if successful; caller
264
* must destroy a non-NULL returned RPC client. Otherwise NULL, and
265
* rpc_createerr.cf_stat is set to reflect the error.
267
static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
268
const socklen_t salen,
269
const rpcprog_t program,
270
const rpcvers_t version,
271
struct timeval *timeout,
277
struct sockaddr_storage address;
278
const struct netbuf nbuf = {
284
#else /* !HAVE_LIBTIRPC */
286
if (sap->sa_family != AF_INET) {
287
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
290
#endif /* !HAVE_LIBTIRPC */
292
sock = socket((int)sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
294
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
295
rpc_createerr.cf_error.re_errno = errno;
300
ret = nfs_bindresvport(sock, sap->sa_family);
302
ret = nfs_bind(sock, sap->sa_family);
304
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
305
rpc_createerr.cf_error.re_errno = errno;
310
if (timeout->tv_sec == -1)
311
timeout->tv_sec = NFSRPC_TIMEOUT_UDP;
313
ret = nfs_connect_nb(sock, sap, salen, timeout);
315
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
316
rpc_createerr.cf_error.re_errno = errno;
322
memcpy(nbuf.buf, sap, (size_t)salen);
323
client = clnt_dg_create(sock, &nbuf, program, version, 0, 0);
324
#else /* !HAVE_LIBTIRPC */
325
client = clntudp_create((struct sockaddr_in *)sap, program,
326
version, *timeout, &sock);
327
#endif /* !HAVE_LIBTIRPC */
328
if (client != NULL) {
329
CLNT_CONTROL(client, CLSET_RETRY_TIMEOUT, (char *)timeout);
330
CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
338
* Set up and connect an RPC client for communicating via a stream socket.
340
* @timeout is initialized upon return
342
* Returns a pointer to a prepared and connected RPC client if
343
* successful; caller must destroy a non-NULL returned RPC client.
344
* Otherwise NULL, and rpc_createerr.cf_stat is set to reflect the
347
static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
348
const socklen_t salen,
349
const rpcprog_t program,
350
const rpcvers_t version,
351
struct timeval *timeout,
357
struct sockaddr_storage address;
358
const struct netbuf nbuf = {
364
#else /* !HAVE_LIBTIRPC */
366
if (sap->sa_family != AF_INET) {
367
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
370
#endif /* !HAVE_LIBTIRPC */
372
sock = socket((int)sap->sa_family, SOCK_STREAM, IPPROTO_TCP);
374
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
375
rpc_createerr.cf_error.re_errno = errno;
380
ret = nfs_bindresvport(sock, sap->sa_family);
382
ret = nfs_bind(sock, sap->sa_family);
384
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
385
rpc_createerr.cf_error.re_errno = errno;
390
if (timeout->tv_sec == -1)
391
timeout->tv_sec = NFSRPC_TIMEOUT_TCP;
393
ret = nfs_connect_nb(sock, sap, salen, timeout);
395
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
396
rpc_createerr.cf_error.re_errno = errno;
402
memcpy(nbuf.buf, sap, (size_t)salen);
403
client = clnt_vc_create(sock, &nbuf, program, version, 0, 0);
404
#else /* !HAVE_LIBTIRPC */
405
client = clnttcp_create((struct sockaddr_in *)sap,
406
program, version, &sock, 0, 0);
407
#endif /* !HAVE_LIBTIRPC */
409
CLNT_CONTROL(client, CLSET_FD_CLOSE, NULL);
417
* nfs_get_rpcclient - acquire an RPC client
418
* @sap: pointer to socket address of RPC server
419
* @salen: length of socket address
420
* @transport: IPPROTO_ value of transport protocol to use
421
* @program: RPC program number
422
* @version: RPC version number
423
* @timeout: pointer to request timeout (must not be NULL)
425
* Set up an RPC client for communicating with an RPC program @program
426
* and @version on the server @sap over @transport. An unprivileged
427
* source port is used.
429
* Returns a pointer to a prepared RPC client if successful, and
430
* @timeout is initialized; caller must destroy a non-NULL returned RPC
431
* client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to
434
CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
435
const socklen_t salen,
436
const unsigned short transport,
437
const rpcprog_t program,
438
const rpcvers_t version,
439
struct timeval *timeout)
441
struct sockaddr_in *sin = (struct sockaddr_in *)sap;
442
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
444
switch (sap->sa_family) {
446
return nfs_get_localclient(sap, salen, program,
449
if (sin->sin_port == 0) {
450
rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
455
if (sin6->sin6_port == 0) {
456
rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
461
rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
467
return nfs_get_tcpclient(sap, salen, program, version,
471
return nfs_get_udpclient(sap, salen, program, version,
475
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
480
* nfs_get_priv_rpcclient - acquire an RPC client
481
* @sap: pointer to socket address of RPC server
482
* @salen: length of socket address
483
* @transport: IPPROTO_ value of transport protocol to use
484
* @program: RPC program number
485
* @version: RPC version number
486
* @timeout: pointer to request timeout (must not be NULL)
488
* Set up an RPC client for communicating with an RPC program @program
489
* and @version on the server @sap over @transport. A privileged
490
* source port is used.
492
* Returns a pointer to a prepared RPC client if successful, and
493
* @timeout is initialized; caller must destroy a non-NULL returned RPC
494
* client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to
497
CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap,
498
const socklen_t salen,
499
const unsigned short transport,
500
const rpcprog_t program,
501
const rpcvers_t version,
502
struct timeval *timeout)
504
struct sockaddr_in *sin = (struct sockaddr_in *)sap;
505
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
507
switch (sap->sa_family) {
509
return nfs_get_localclient(sap, salen, program,
512
if (sin->sin_port == 0) {
513
rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
518
if (sin6->sin6_port == 0) {
519
rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
524
rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
530
return nfs_get_tcpclient(sap, salen, program, version,
534
return nfs_get_udpclient(sap, salen, program, version,
538
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
543
* nfs_getrpcbyname - convert an RPC program name to a rpcprog_t
544
* @program: default program number to use if names not found in db
545
* @table: pointer to table of 'char *' names to try to find
547
* Returns program number of first name to be successfully looked
548
* up, or the default program number if all lookups fail.
550
rpcprog_t nfs_getrpcbyname(const rpcprog_t program, const char *table[])
552
#ifdef HAVE_GETRPCBYNAME
553
struct rpcent *entry;
557
for (i = 0; table[i] != NULL; i++) {
558
entry = getrpcbyname(table[i]);
560
return (rpcprog_t)entry->r_number;
562
#endif /* HAVE_GETRPCBYNAME */