1
/* ----------------------------------------------------------------------- *
3
* rpc_subs.c - routines for rpc discovery
5
* Copyright 2004 Ian Kent <raven@themaw.net> - All Rights Reserved
6
* Copyright 2004 Jeff Moyer <jmoyer@redaht.com> - All Rights Reserved
8
* This program is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
11
* USA; either version 2 of the License, or (at your option) any later
12
* version; incorporated herein by reference.
14
* ----------------------------------------------------------------------- */
22
#include <rpc/types.h>
24
#include <rpc/pmap_prot.h>
25
#include <sys/socket.h>
28
#include <netinet/in.h>
29
#include <arpa/inet.h>
30
#include <rpcsvc/ypclnt.h>
32
#include <sys/ioctl.h>
39
#include "automount.h"
41
/* #define STANDALONE */
43
#define error(logopt, msg, args...) fprintf(stderr, msg "\n", ##args)
48
#define MAX_IFC_BUF 1024
49
#define MAX_ERR_BUF 128
51
#define MAX_NETWORK_LEN 255
53
/* Get numeric value of the n bits starting at position p */
54
#define getbits(x, p, n) ((x >> (p + 1 - n)) & ~(~0 << n))
56
static int connect_nb(int, struct sockaddr *, socklen_t, struct timeval *);
57
inline void dump_core(void);
59
static CLIENT *rpc_clntudp_create(struct sockaddr *addr, struct conn_info *info, int *fd)
61
struct sockaddr_in *in4_raddr;
62
struct sockaddr_in6 *in6_raddr;
63
CLIENT *client = NULL;
65
switch (addr->sa_family) {
67
in4_raddr = (struct sockaddr_in *) addr;
68
in4_raddr->sin_port = htons(info->port);
69
client = clntudp_bufcreate(in4_raddr,
70
info->program, info->version,
72
info->send_sz, info->recv_sz);
77
/* Quiet compile warning */
80
in6_raddr = (struct sockaddr_in6 *) addr;
81
in6_raddr->sin6_port = htons(info->port);
82
client = clntudp6_bufcreate(in6_raddr,
83
info->program, info->version,
85
info->send_sz, info->recv_sz);
96
static CLIENT *rpc_clnttcp_create(struct sockaddr *addr, struct conn_info *info, int *fd)
98
struct sockaddr_in *in4_raddr;
99
struct sockaddr_in6 *in6_raddr;
100
CLIENT *client = NULL;
103
switch (addr->sa_family) {
105
in4_raddr = (struct sockaddr_in *) addr;
106
in4_raddr->sin_port = htons(info->port);
107
slen = sizeof(struct sockaddr_in);
109
if (connect_nb(*fd, addr, slen, &info->timeout) < 0)
112
client = clnttcp_create(in4_raddr,
113
info->program, info->version, fd,
114
info->send_sz, info->recv_sz);
119
/* Quiet compile warning */
122
in6_raddr = (struct sockaddr_in6 *) addr;
123
in6_raddr->sin6_port = htons(info->port);
124
slen = sizeof(struct sockaddr_in6);
126
if (connect_nb(*fd, addr, slen, &info->timeout) < 0)
129
client = clnttcp6_create(in6_raddr,
130
info->program, info->version, fd,
131
info->send_sz, info->recv_sz);
143
* Perform a non-blocking connect on the socket fd.
145
* The input struct timeval always has tv_nsec set to zero,
146
* we only ever use tv_sec for timeouts.
148
static int connect_nb(int fd, struct sockaddr *addr, socklen_t len, struct timeval *tout)
150
struct pollfd pfd[1];
151
int timeout = tout->tv_sec;
154
flags = fcntl(fd, F_GETFL, 0);
158
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
163
* From here on subsequent sys calls could change errno so
164
* we set ret = -errno to capture it in case we decide to
167
ret = connect(fd, addr, len);
168
if (ret < 0 && errno != EINPROGRESS) {
177
if (timeout >= (INT_MAX - 1)/1000)
178
timeout = INT_MAX - 1;
180
timeout = timeout * 1000;
184
pfd[0].events = POLLOUT;
186
ret = poll(pfd, 1, timeout);
195
if (pfd[0].revents) {
199
status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
201
char buf[MAX_ERR_BUF + 1];
202
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
205
* We assume getsockopt amounts to a read on the
206
* descriptor and gives us the errno we need for
207
* the POLLERR revent case.
211
/* Unexpected case, log it so we know we got caught */
212
if (pfd[0].revents & POLLNVAL)
213
logerr("unexpected poll(2) error on connect:"
219
/* Oops - something wrong with connect */
225
fcntl(fd, F_SETFL, flags);
229
static CLIENT *rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, int *fd)
231
CLIENT *client = NULL;
232
struct sockaddr *laddr;
233
struct sockaddr_in in4_laddr;
234
struct sockaddr_in6 in6_laddr;
238
proto = info->proto->p_proto;
239
if (proto == IPPROTO_UDP)
245
* bind to any unused port. If we left this up to the rpc
246
* layer, it would bind to a reserved port, which has been shown
247
* to exhaust the reserved port range in some situations.
249
switch (addr->sa_family) {
251
in4_laddr.sin_family = AF_INET;
252
in4_laddr.sin_port = htons(0);
253
in4_laddr.sin_addr.s_addr = htonl(INADDR_ANY);
254
slen = sizeof(struct sockaddr_in);
255
laddr = (struct sockaddr *) &in4_laddr;
261
in6_laddr.sin6_family = AF_INET6;
264
in6_laddr.sin6_family = AF_INET6;
265
in6_laddr.sin6_port = htons(0);
266
in6_laddr.sin6_addr = in6addr_any;
267
slen = sizeof(struct sockaddr_in6);
268
laddr = (struct sockaddr *) &in6_laddr;
276
*fd = open_sock(addr->sa_family, type, proto);
280
if (bind(*fd, laddr, slen) < 0)
284
switch (info->proto->p_proto) {
286
client = rpc_clntudp_create(addr, info, fd);
290
client = rpc_clnttcp_create(addr, info, fd);
301
* Create a UDP RPC client
303
static CLIENT *create_udp_client(struct conn_info *info)
305
CLIENT *client = NULL;
306
struct addrinfo *ai, *haddr;
307
struct addrinfo hints;
310
if (info->proto->p_proto != IPPROTO_UDP)
316
if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) {
318
clnt_destroy(info->client);
321
clnt_control(info->client, CLSET_FD_NCLOSE, NULL);
322
clnt_destroy(info->client);
327
client = rpc_do_create_client(info->addr, info, &fd);
331
if (!info->client && fd != RPC_ANYSOCK) {
337
memset(&hints, 0, sizeof(hints));
338
hints.ai_flags = AI_ADDRCONFIG;
339
hints.ai_family = AF_UNSPEC;
340
hints.ai_socktype = SOCK_DGRAM;
342
ret = getaddrinfo(info->host, NULL, &hints, &ai);
345
"hostname lookup failed: %s", gai_strerror(ret));
352
client = rpc_do_create_client(haddr->ai_addr, info, &fd);
356
if (!info->client && fd != RPC_ANYSOCK) {
361
haddr = haddr->ai_next;
371
/* Close socket fd on destroy, as is default for rpcowned fds */
372
if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) {
373
clnt_destroy(client);
386
int rpc_udp_getclient(struct conn_info *info,
387
unsigned int program, unsigned int version)
389
struct protoent *pe_proto;
393
pe_proto = getprotobyname("udp");
397
info->proto = pe_proto;
398
info->send_sz = UDPMSGSIZE;
399
info->recv_sz = UDPMSGSIZE;
402
info->program = program;
403
info->version = version;
405
client = create_udp_client(info);
410
info->client = client;
415
void rpc_destroy_udp_client(struct conn_info *info)
420
clnt_destroy(info->client);
426
* Create a TCP RPC client using non-blocking connect
428
static CLIENT *create_tcp_client(struct conn_info *info)
430
CLIENT *client = NULL;
431
struct addrinfo *ai, *haddr;
432
struct addrinfo hints;
435
if (info->proto->p_proto != IPPROTO_TCP)
441
if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) {
443
clnt_destroy(info->client);
446
clnt_control(info->client, CLSET_FD_NCLOSE, NULL);
447
clnt_destroy(info->client);
452
client = rpc_do_create_client(info->addr, info, &fd);
462
memset(&hints, 0, sizeof(hints));
463
hints.ai_flags = AI_ADDRCONFIG;
464
hints.ai_family = AF_UNSPEC;
465
hints.ai_socktype = SOCK_STREAM;
467
ret = getaddrinfo(info->host, NULL, &hints, &ai);
470
"hostname lookup failed: %s", gai_strerror(ret));
477
client = rpc_do_create_client(haddr->ai_addr, info, &fd);
481
if (!info->client && fd != RPC_ANYSOCK) {
486
haddr = haddr->ai_next;
496
/* Close socket fd on destroy, as is default for rpcowned fds */
497
if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) {
498
clnt_destroy(client);
511
int rpc_tcp_getclient(struct conn_info *info,
512
unsigned int program, unsigned int version)
514
struct protoent *pe_proto;
518
pe_proto = getprotobyname("tcp");
522
info->proto = pe_proto;
527
info->program = program;
528
info->version = version;
530
client = create_tcp_client(info);
535
info->client = client;
540
void rpc_destroy_tcp_client(struct conn_info *info)
542
struct linger lin = { 1, 0 };
543
socklen_t lin_len = sizeof(struct linger);
549
if (!clnt_control(info->client, CLGET_FD, (char *) &fd))
552
switch (info->close_option) {
553
case RPC_CLOSE_NOLINGER:
555
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len);
559
clnt_destroy(info->client);
565
int rpc_portmap_getclient(struct conn_info *info,
566
const char *host, struct sockaddr *addr, size_t addr_len,
567
const char *proto, unsigned int option)
569
struct protoent *pe_proto;
572
pe_proto = getprotobyname(proto);
578
info->addr_len = addr_len;
579
info->program = PMAPPROG;
580
info->port = PMAPPORT;
581
info->version = PMAPVERS;
582
info->proto = pe_proto;
583
info->send_sz = RPCSMALLMSGSIZE;
584
info->recv_sz = RPCSMALLMSGSIZE;
585
info->timeout.tv_sec = PMAP_TOUT_UDP;
586
info->timeout.tv_usec = 0;
587
info->close_option = option;
590
if (pe_proto->p_proto == IPPROTO_TCP) {
591
info->timeout.tv_sec = PMAP_TOUT_TCP;
592
client = create_tcp_client(info);
594
client = create_udp_client(info);
599
info->client = client;
604
unsigned short rpc_portmap_getport(struct conn_info *info, struct pmap *parms)
606
struct conn_info pmap_info;
607
unsigned short port = 0;
609
enum clnt_stat status;
610
int proto = info->proto->p_proto;
612
memset(&pmap_info, 0, sizeof(struct conn_info));
614
if (proto == IPPROTO_TCP)
615
pmap_info.timeout.tv_sec = PMAP_TOUT_TCP;
617
pmap_info.timeout.tv_sec = PMAP_TOUT_UDP;
620
client = info->client;
622
pmap_info.host = info->host;
623
pmap_info.addr = info->addr;
624
pmap_info.addr_len = info->addr_len;
625
pmap_info.port = PMAPPORT;
626
pmap_info.program = PMAPPROG;
627
pmap_info.version = PMAPVERS;
628
pmap_info.proto = info->proto;
629
pmap_info.send_sz = RPCSMALLMSGSIZE;
630
pmap_info.recv_sz = RPCSMALLMSGSIZE;
632
if (proto == IPPROTO_TCP)
633
client = create_tcp_client(&pmap_info);
635
client = create_udp_client(&pmap_info);
642
* Check to see if server is up otherwise a getport will take
643
* forever to timeout.
645
status = clnt_call(client, PMAPPROC_NULL,
646
(xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0,
649
if (status == RPC_SUCCESS) {
650
status = clnt_call(client, PMAPPROC_GETPORT,
651
(xdrproc_t) xdr_pmap, (caddr_t) parms,
652
(xdrproc_t) xdr_u_short, (caddr_t) &port,
658
* Only play with the close options if we think it
661
if (proto == IPPROTO_TCP && status == RPC_SUCCESS) {
662
struct linger lin = { 1, 0 };
663
socklen_t lin_len = sizeof(struct linger);
666
if (!clnt_control(client, CLGET_FD, (char *) &fd))
669
switch (info->close_option) {
670
case RPC_CLOSE_NOLINGER:
672
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len);
676
clnt_destroy(client);
679
if (status != RPC_SUCCESS)
685
int rpc_ping_proto(struct conn_info *info)
688
enum clnt_stat status;
689
int proto = info->proto->p_proto;
692
client = info->client;
694
if (info->proto->p_proto == IPPROTO_UDP) {
695
info->send_sz = UDPMSGSIZE;
696
info->recv_sz = UDPMSGSIZE;
697
client = create_udp_client(info);
699
client = create_tcp_client(info);
705
clnt_control(client, CLSET_TIMEOUT, (char *) &info->timeout);
706
clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info->timeout);
708
status = clnt_call(client, NFSPROC_NULL,
709
(xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0,
714
* Only play with the close options if we think it
717
if (proto == IPPROTO_TCP && status == RPC_SUCCESS) {
718
struct linger lin = { 1, 0 };
719
socklen_t lin_len = sizeof(struct linger);
722
if (!clnt_control(client, CLGET_FD, (char *) &fd))
725
switch (info->close_option) {
726
case RPC_CLOSE_NOLINGER:
728
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len);
732
clnt_destroy(client);
735
if (status != RPC_SUCCESS)
741
static unsigned int __rpc_ping(const char *host,
742
unsigned long version,
744
long seconds, long micros,
748
struct conn_info info;
754
info.program = NFS_PROGRAM;
755
info.version = version;
758
info.timeout.tv_sec = seconds;
759
info.timeout.tv_usec = micros;
760
info.close_option = option;
763
status = RPC_PING_FAIL;
765
info.proto = getprotobyname(proto);
769
parms.pm_prog = NFS_PROGRAM;
770
parms.pm_vers = version;
771
parms.pm_prot = info.proto->p_proto;
774
info.port = rpc_portmap_getport(&info, &parms);
778
status = rpc_ping_proto(&info);
783
int rpc_ping(const char *host, long seconds, long micros, unsigned int option)
785
unsigned long vers3 = NFS3_VERSION;
786
unsigned long vers2 = NFS2_VERSION;
789
status = __rpc_ping(host, vers2, "udp", seconds, micros, option);
791
return RPC_PING_V2 | RPC_PING_UDP;
793
status = __rpc_ping(host, vers3, "udp", seconds, micros, option);
795
return RPC_PING_V3 | RPC_PING_UDP;
797
status = __rpc_ping(host, vers2, "tcp", seconds, micros, option);
799
return RPC_PING_V2 | RPC_PING_TCP;
801
status = __rpc_ping(host, vers3, "tcp", seconds, micros, option);
803
return RPC_PING_V3 | RPC_PING_TCP;
808
double elapsed(struct timeval start, struct timeval end)
811
t1 = (double)start.tv_sec + (double)start.tv_usec/(1000*1000);
812
t2 = (double)end.tv_sec + (double)end.tv_usec/(1000*1000);
816
int rpc_time(const char *host,
817
unsigned int ping_vers, unsigned int ping_proto,
818
long seconds, long micros, unsigned int option, double *result)
822
struct timeval start, end;
824
char *proto = (ping_proto & RPC_PING_UDP) ? "udp" : "tcp";
825
unsigned long vers = ping_vers;
827
gettimeofday(&start, &tz);
828
status = __rpc_ping(host, vers, proto, seconds, micros, option);
829
gettimeofday(&end, &tz);
835
taken = elapsed(start, end);
843
static int rpc_get_exports_proto(struct conn_info *info, exports *exp)
846
enum clnt_stat status;
847
int proto = info->proto->p_proto;
848
unsigned int option = info->close_option;
850
if (info->proto->p_proto == IPPROTO_UDP) {
851
info->send_sz = UDPMSGSIZE;
852
info->recv_sz = UDPMSGSIZE;
853
client = create_udp_client(info);
855
client = create_tcp_client(info);
860
clnt_control(client, CLSET_TIMEOUT, (char *) &info->timeout);
861
clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info->timeout);
863
client->cl_auth = authunix_create_default();
865
status = clnt_call(client, MOUNTPROC_EXPORT,
866
(xdrproc_t) xdr_void, NULL,
867
(xdrproc_t) xdr_exports, (caddr_t) exp,
870
/* Only play with the close options if we think it completed OK */
871
if (proto == IPPROTO_TCP && status == RPC_SUCCESS) {
872
struct linger lin = { 1, 0 };
873
socklen_t lin_len = sizeof(struct linger);
876
if (!clnt_control(client, CLGET_FD, (char *) &fd))
880
case RPC_CLOSE_NOLINGER:
882
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len);
886
auth_destroy(client->cl_auth);
887
clnt_destroy(client);
889
if (status != RPC_SUCCESS)
895
static void rpc_export_free(exports item)
903
grp = item->ex_groups;
914
void rpc_exports_free(exports list)
920
list = list->ex_next;
921
rpc_export_free(tmp);
926
exports rpc_get_exports(const char *host, long seconds, long micros, unsigned int option)
928
struct conn_info info;
936
info.program = MOUNTPROG;
937
info.version = MOUNTVERS;
940
info.timeout.tv_sec = seconds;
941
info.timeout.tv_usec = micros;
942
info.close_option = option;
945
parms.pm_prog = info.program;
946
parms.pm_vers = info.version;
950
info.proto = getprotobyname("udp");
954
parms.pm_prot = info.proto->p_proto;
956
info.port = rpc_portmap_getport(&info, &parms);
960
memset(&exportlist, '\0', sizeof(exportlist));
962
status = rpc_get_exports_proto(&info, &exportlist);
967
info.proto = getprotobyname("tcp");
971
parms.pm_prot = info.proto->p_proto;
973
info.port = rpc_portmap_getport(&info, &parms);
977
memset(&exportlist, '\0', sizeof(exportlist));
979
status = rpc_get_exports_proto(&info, &exportlist);
989
int main(int argc, char **argv)
993
exports exportlist, tmp;
998
ret = rpc_ping("budgie", 10, 0, RPC_CLOSE_DEFAULT);
999
printf("ret = %d\n", ret);
1002
ret = rpc_time("budgie", NFS2_VERSION, RPC_PING_TCP, 10, 0, RPC_CLOSE_DEFAULT, &res);
1003
printf("v2 tcp ret = %d, res = %f\n", ret, res);
1006
ret = rpc_time("budgie", NFS3_VERSION, RPC_PING_TCP, 10, 0, RPC_CLOSE_DEFAULT, &res);
1007
printf("v3 tcp ret = %d, res = %f\n", ret, res);
1010
ret = rpc_time("budgie", NFS2_VERSION, RPC_PING_UDP, 10, 0, RPC_CLOSE_DEFAULT, &res);
1011
printf("v2 udp ret = %d, res = %f\n", ret, res);
1014
ret = rpc_time("budgie", NFS3_VERSION, RPC_PING_UDP, 10, 0, RPC_CLOSE_DEFAULT, &res);
1015
printf("v3 udp ret = %d, res = %f\n", ret, res);
1017
exportlist = rpc_get_exports("budgie", 10, 0, RPC_CLOSE_NOLINGER);
1018
exportlist = rpc_exports_prune(exportlist);
1021
for (tmp = exportlist; tmp; tmp = tmp->ex_next) {
1022
if ((n = strlen(tmp->ex_dir)) > maxlen)
1027
while (exportlist) {
1028
printf("%-*s ", maxlen, exportlist->ex_dir);
1029
grouplist = exportlist->ex_groups;
1032
printf("%s%s", grouplist->gr_name,
1033
grouplist->gr_next ? "," : "");
1034
grouplist = grouplist->gr_next;
1038
exportlist = exportlist->ex_next;
1041
rpc_exports_free(exportlist);