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
inline void dump_core(void);
58
static CLIENT *rpc_clntudp_create(struct sockaddr *addr, struct conn_info *info, int *fd)
60
struct sockaddr_in *in4_raddr;
61
struct sockaddr_in6 *in6_raddr;
62
CLIENT *client = NULL;
64
switch (addr->sa_family) {
66
in4_raddr = (struct sockaddr_in *) addr;
67
in4_raddr->sin_port = htons(info->port);
68
client = clntudp_bufcreate(in4_raddr,
69
info->program, info->version,
71
info->send_sz, info->recv_sz);
76
/* Quiet compile warning */
79
in6_raddr = (struct sockaddr_in6 *) addr;
80
in6_raddr->sin6_port = htons(info->port);
81
client = clntudp6_bufcreate(in6_raddr,
82
info->program, info->version,
84
info->send_sz, info->recv_sz);
95
static CLIENT *rpc_clnttcp_create(struct sockaddr *addr, struct conn_info *info, int *fd)
97
struct sockaddr_in *in4_raddr;
98
struct sockaddr_in6 *in6_raddr;
99
CLIENT *client = NULL;
101
switch (addr->sa_family) {
103
in4_raddr = (struct sockaddr_in *) addr;
104
in4_raddr->sin_port = htons(info->port);
105
client = clnttcp_create(in4_raddr,
106
info->program, info->version, fd,
107
info->send_sz, info->recv_sz);
112
/* Quiet compile warning */
115
in6_raddr = (struct sockaddr_in6 *) addr;
116
in6_raddr->sin6_port = htons(info->port);
117
client = clnttcp6_create(in6_raddr,
118
info->program, info->version, fd,
119
info->send_sz, info->recv_sz);
131
* Perform a non-blocking connect on the socket fd.
133
* The input struct timeval always has tv_nsec set to zero,
134
* we only ever use tv_sec for timeouts.
136
static int connect_nb(int fd, struct sockaddr *addr, socklen_t len, struct timeval *tout)
138
struct pollfd pfd[1];
139
int timeout = tout->tv_sec;
142
flags = fcntl(fd, F_GETFL, 0);
146
ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
151
* From here on subsequent sys calls could change errno so
152
* we set ret = -errno to capture it in case we decide to
155
ret = connect(fd, addr, len);
156
if (ret < 0 && errno != EINPROGRESS) {
165
if (timeout >= (INT_MAX - 1)/1000)
166
timeout = INT_MAX - 1;
168
timeout = timeout * 1000;
172
pfd[0].events = POLLOUT;
174
ret = poll(pfd, 1, timeout);
183
if (pfd[0].revents) {
187
status = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len);
189
char buf[MAX_ERR_BUF + 1];
190
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
193
* We assume getsockopt amounts to a read on the
194
* descriptor and gives us the errno we need for
195
* the POLLERR revent case.
199
/* Unexpected case, log it so we know we got caught */
200
if (pfd[0].revents & POLLNVAL)
201
logerr("unexpected poll(2) error on connect:"
207
/* Oops - something wrong with connect */
213
fcntl(fd, F_SETFL, flags);
217
static CLIENT *rpc_do_create_client(struct sockaddr *addr, struct conn_info *info, int *fd)
219
CLIENT *client = NULL;
220
struct sockaddr *laddr;
221
struct sockaddr_in in4_laddr;
222
struct sockaddr_in6 in6_laddr;
226
proto = info->proto->p_proto;
227
if (proto == IPPROTO_UDP)
233
* bind to any unused port. If we left this up to the rpc
234
* layer, it would bind to a reserved port, which has been shown
235
* to exhaust the reserved port range in some situations.
237
switch (addr->sa_family) {
239
in4_laddr.sin_family = AF_INET;
240
in4_laddr.sin_port = htons(0);
241
in4_laddr.sin_addr.s_addr = htonl(INADDR_ANY);
242
slen = sizeof(struct sockaddr_in);
243
laddr = (struct sockaddr *) &in4_laddr;
249
in6_laddr.sin6_family = AF_INET6;
252
in6_laddr.sin6_family = AF_INET6;
253
in6_laddr.sin6_port = htons(0);
254
in6_laddr.sin6_addr = in6addr_any;
255
slen = sizeof(struct sockaddr_in6);
256
laddr = (struct sockaddr *) &in6_laddr;
263
switch (info->proto->p_proto) {
266
*fd = open_sock(addr->sa_family, type, proto);
270
if (bind(*fd, laddr, slen) < 0) {
275
client = rpc_clntudp_create(addr, info, fd);
280
*fd = open_sock(addr->sa_family, type, proto);
284
if (connect_nb(*fd, laddr, slen, &info->timeout) < 0) {
289
client = rpc_clnttcp_create(addr, info, fd);
300
* Create a UDP RPC client
302
static CLIENT *create_udp_client(struct conn_info *info)
304
CLIENT *client = NULL;
305
struct addrinfo *ai, *haddr;
306
struct addrinfo hints;
309
if (info->proto->p_proto != IPPROTO_UDP)
315
if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) {
317
clnt_destroy(info->client);
320
clnt_control(info->client, CLSET_FD_NCLOSE, NULL);
321
clnt_destroy(info->client);
326
client = rpc_do_create_client(info->addr, info, &fd);
336
memset(&hints, 0, sizeof(hints));
337
hints.ai_flags = AI_ADDRCONFIG;
338
hints.ai_family = AF_UNSPEC;
339
hints.ai_socktype = SOCK_DGRAM;
341
ret = getaddrinfo(info->host, NULL, &hints, &ai);
344
"hostname lookup failed: %s", gai_strerror(ret));
351
client = rpc_do_create_client(haddr->ai_addr, info, &fd);
360
haddr = haddr->ai_next;
370
/* Close socket fd on destroy, as is default for rpcowned fds */
371
if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) {
372
clnt_destroy(client);
385
int rpc_udp_getclient(struct conn_info *info,
386
unsigned int program, unsigned int version)
388
struct protoent *pe_proto;
392
pe_proto = getprotobyname("udp");
396
info->proto = pe_proto;
397
info->send_sz = UDPMSGSIZE;
398
info->recv_sz = UDPMSGSIZE;
401
info->program = program;
402
info->version = version;
404
client = create_udp_client(info);
409
info->client = client;
414
void rpc_destroy_udp_client(struct conn_info *info)
419
clnt_destroy(info->client);
425
* Create a TCP RPC client using non-blocking connect
427
static CLIENT *create_tcp_client(struct conn_info *info)
429
CLIENT *client = NULL;
430
struct addrinfo *ai, *haddr;
431
struct addrinfo hints;
434
if (info->proto->p_proto != IPPROTO_TCP)
440
if (!clnt_control(info->client, CLGET_FD, (char *) &fd)) {
442
clnt_destroy(info->client);
445
clnt_control(info->client, CLSET_FD_NCLOSE, NULL);
446
clnt_destroy(info->client);
451
client = rpc_do_create_client(info->addr, info, &fd);
461
memset(&hints, 0, sizeof(hints));
462
hints.ai_flags = AI_ADDRCONFIG;
463
hints.ai_family = AF_UNSPEC;
464
hints.ai_socktype = SOCK_STREAM;
466
ret = getaddrinfo(info->host, NULL, &hints, &ai);
469
"hostname lookup failed: %s", gai_strerror(ret));
476
client = rpc_do_create_client(haddr->ai_addr, info, &fd);
485
haddr = haddr->ai_next;
495
/* Close socket fd on destroy, as is default for rpcowned fds */
496
if (!clnt_control(client, CLSET_FD_CLOSE, NULL)) {
497
clnt_destroy(client);
510
int rpc_tcp_getclient(struct conn_info *info,
511
unsigned int program, unsigned int version)
513
struct protoent *pe_proto;
517
pe_proto = getprotobyname("tcp");
521
info->proto = pe_proto;
526
info->program = program;
527
info->version = version;
529
client = create_tcp_client(info);
534
info->client = client;
539
void rpc_destroy_tcp_client(struct conn_info *info)
541
struct linger lin = { 1, 0 };
542
socklen_t lin_len = sizeof(struct linger);
548
if (!clnt_control(info->client, CLGET_FD, (char *) &fd))
551
switch (info->close_option) {
552
case RPC_CLOSE_NOLINGER:
554
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len);
558
clnt_destroy(info->client);
564
int rpc_portmap_getclient(struct conn_info *info,
565
const char *host, struct sockaddr *addr, size_t addr_len,
566
const char *proto, unsigned int option)
568
struct protoent *pe_proto;
571
pe_proto = getprotobyname(proto);
577
info->addr_len = addr_len;
578
info->program = PMAPPROG;
579
info->port = PMAPPORT;
580
info->version = PMAPVERS;
581
info->proto = pe_proto;
582
info->send_sz = RPCSMALLMSGSIZE;
583
info->recv_sz = RPCSMALLMSGSIZE;
584
info->timeout.tv_sec = PMAP_TOUT_UDP;
585
info->timeout.tv_usec = 0;
586
info->close_option = option;
589
if (pe_proto->p_proto == IPPROTO_TCP) {
590
info->timeout.tv_sec = PMAP_TOUT_TCP;
591
client = create_tcp_client(info);
593
client = create_udp_client(info);
598
info->client = client;
603
unsigned short rpc_portmap_getport(struct conn_info *info, struct pmap *parms)
605
struct conn_info pmap_info;
606
unsigned short port = 0;
608
enum clnt_stat status;
609
int proto = info->proto->p_proto;
611
memset(&pmap_info, 0, sizeof(struct conn_info));
613
if (proto == IPPROTO_TCP)
614
pmap_info.timeout.tv_sec = PMAP_TOUT_TCP;
616
pmap_info.timeout.tv_sec = PMAP_TOUT_UDP;
619
client = info->client;
621
pmap_info.host = info->host;
622
pmap_info.addr = info->addr;
623
pmap_info.addr_len = info->addr_len;
624
pmap_info.port = PMAPPORT;
625
pmap_info.program = PMAPPROG;
626
pmap_info.version = PMAPVERS;
627
pmap_info.proto = info->proto;
628
pmap_info.send_sz = RPCSMALLMSGSIZE;
629
pmap_info.recv_sz = RPCSMALLMSGSIZE;
631
if (proto == IPPROTO_TCP)
632
client = create_tcp_client(&pmap_info);
634
client = create_udp_client(&pmap_info);
641
* Check to see if server is up otherwise a getport will take
642
* forever to timeout.
644
status = clnt_call(client, PMAPPROC_NULL,
645
(xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0,
648
if (status == RPC_SUCCESS) {
649
status = clnt_call(client, PMAPPROC_GETPORT,
650
(xdrproc_t) xdr_pmap, (caddr_t) parms,
651
(xdrproc_t) xdr_u_short, (caddr_t) &port,
657
* Only play with the close options if we think it
660
if (proto == IPPROTO_TCP && status == RPC_SUCCESS) {
661
struct linger lin = { 1, 0 };
662
socklen_t lin_len = sizeof(struct linger);
665
if (!clnt_control(client, CLGET_FD, (char *) &fd))
668
switch (info->close_option) {
669
case RPC_CLOSE_NOLINGER:
671
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len);
675
clnt_destroy(client);
678
if (status != RPC_SUCCESS)
684
int rpc_ping_proto(struct conn_info *info)
687
enum clnt_stat status;
688
int proto = info->proto->p_proto;
691
client = info->client;
693
if (info->proto->p_proto == IPPROTO_UDP) {
694
info->send_sz = UDPMSGSIZE;
695
info->recv_sz = UDPMSGSIZE;
696
client = create_udp_client(info);
698
client = create_tcp_client(info);
704
clnt_control(client, CLSET_TIMEOUT, (char *) &info->timeout);
705
clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info->timeout);
707
status = clnt_call(client, NFSPROC_NULL,
708
(xdrproc_t) xdr_void, 0, (xdrproc_t) xdr_void, 0,
713
* Only play with the close options if we think it
716
if (proto == IPPROTO_TCP && status == RPC_SUCCESS) {
717
struct linger lin = { 1, 0 };
718
socklen_t lin_len = sizeof(struct linger);
721
if (!clnt_control(client, CLGET_FD, (char *) &fd))
724
switch (info->close_option) {
725
case RPC_CLOSE_NOLINGER:
727
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len);
731
clnt_destroy(client);
734
if (status != RPC_SUCCESS)
740
static unsigned int __rpc_ping(const char *host,
741
unsigned long version,
743
long seconds, long micros,
747
struct conn_info info;
753
info.program = NFS_PROGRAM;
754
info.version = version;
757
info.timeout.tv_sec = seconds;
758
info.timeout.tv_usec = micros;
759
info.close_option = option;
762
status = RPC_PING_FAIL;
764
info.proto = getprotobyname(proto);
768
parms.pm_prog = NFS_PROGRAM;
769
parms.pm_vers = version;
770
parms.pm_prot = info.proto->p_proto;
773
info.port = rpc_portmap_getport(&info, &parms);
777
status = rpc_ping_proto(&info);
782
int rpc_ping(const char *host, long seconds, long micros, unsigned int option)
784
unsigned long vers3 = NFS3_VERSION;
785
unsigned long vers2 = NFS2_VERSION;
788
status = __rpc_ping(host, vers2, "udp", seconds, micros, option);
790
return RPC_PING_V2 | RPC_PING_UDP;
792
status = __rpc_ping(host, vers3, "udp", seconds, micros, option);
794
return RPC_PING_V3 | RPC_PING_UDP;
796
status = __rpc_ping(host, vers2, "tcp", seconds, micros, option);
798
return RPC_PING_V2 | RPC_PING_TCP;
800
status = __rpc_ping(host, vers3, "tcp", seconds, micros, option);
802
return RPC_PING_V3 | RPC_PING_TCP;
807
double elapsed(struct timeval start, struct timeval end)
810
t1 = (double)start.tv_sec + (double)start.tv_usec/(1000*1000);
811
t2 = (double)end.tv_sec + (double)end.tv_usec/(1000*1000);
815
int rpc_time(const char *host,
816
unsigned int ping_vers, unsigned int ping_proto,
817
long seconds, long micros, unsigned int option, double *result)
821
struct timeval start, end;
823
char *proto = (ping_proto & RPC_PING_UDP) ? "udp" : "tcp";
824
unsigned long vers = ping_vers;
826
gettimeofday(&start, &tz);
827
status = __rpc_ping(host, vers, proto, seconds, micros, option);
828
gettimeofday(&end, &tz);
834
taken = elapsed(start, end);
842
static int rpc_get_exports_proto(struct conn_info *info, exports *exp)
845
enum clnt_stat status;
846
int proto = info->proto->p_proto;
847
unsigned int option = info->close_option;
849
if (info->proto->p_proto == IPPROTO_UDP) {
850
info->send_sz = UDPMSGSIZE;
851
info->recv_sz = UDPMSGSIZE;
852
client = create_udp_client(info);
854
client = create_tcp_client(info);
859
clnt_control(client, CLSET_TIMEOUT, (char *) &info->timeout);
860
clnt_control(client, CLSET_RETRY_TIMEOUT, (char *) &info->timeout);
862
client->cl_auth = authunix_create_default();
864
status = clnt_call(client, MOUNTPROC_EXPORT,
865
(xdrproc_t) xdr_void, NULL,
866
(xdrproc_t) xdr_exports, (caddr_t) exp,
869
/* Only play with the close options if we think it completed OK */
870
if (proto == IPPROTO_TCP && status == RPC_SUCCESS) {
871
struct linger lin = { 1, 0 };
872
socklen_t lin_len = sizeof(struct linger);
875
if (!clnt_control(client, CLGET_FD, (char *) &fd))
879
case RPC_CLOSE_NOLINGER:
881
setsockopt(fd, SOL_SOCKET, SO_LINGER, &lin, lin_len);
885
auth_destroy(client->cl_auth);
886
clnt_destroy(client);
888
if (status != RPC_SUCCESS)
894
static void rpc_export_free(exports item)
902
grp = item->ex_groups;
913
void rpc_exports_free(exports list)
919
list = list->ex_next;
920
rpc_export_free(tmp);
925
exports rpc_get_exports(const char *host, long seconds, long micros, unsigned int option)
927
struct conn_info info;
935
info.program = MOUNTPROG;
936
info.version = MOUNTVERS;
939
info.timeout.tv_sec = seconds;
940
info.timeout.tv_usec = micros;
941
info.close_option = option;
944
parms.pm_prog = info.program;
945
parms.pm_vers = info.version;
949
info.proto = getprotobyname("udp");
953
parms.pm_prot = info.proto->p_proto;
955
info.port = rpc_portmap_getport(&info, &parms);
959
memset(&exportlist, '\0', sizeof(exportlist));
961
status = rpc_get_exports_proto(&info, &exportlist);
966
info.proto = getprotobyname("tcp");
970
parms.pm_prot = info.proto->p_proto;
972
info.port = rpc_portmap_getport(&info, &parms);
976
memset(&exportlist, '\0', sizeof(exportlist));
978
status = rpc_get_exports_proto(&info, &exportlist);
988
int main(int argc, char **argv)
992
exports exportlist, tmp;
997
ret = rpc_ping("budgie", 10, 0, RPC_CLOSE_DEFAULT);
998
printf("ret = %d\n", ret);
1001
ret = rpc_time("budgie", NFS2_VERSION, RPC_PING_TCP, 10, 0, RPC_CLOSE_DEFAULT, &res);
1002
printf("v2 tcp ret = %d, res = %f\n", ret, res);
1005
ret = rpc_time("budgie", NFS3_VERSION, RPC_PING_TCP, 10, 0, RPC_CLOSE_DEFAULT, &res);
1006
printf("v3 tcp ret = %d, res = %f\n", ret, res);
1009
ret = rpc_time("budgie", NFS2_VERSION, RPC_PING_UDP, 10, 0, RPC_CLOSE_DEFAULT, &res);
1010
printf("v2 udp ret = %d, res = %f\n", ret, res);
1013
ret = rpc_time("budgie", NFS3_VERSION, RPC_PING_UDP, 10, 0, RPC_CLOSE_DEFAULT, &res);
1014
printf("v3 udp ret = %d, res = %f\n", ret, res);
1016
exportlist = rpc_get_exports("budgie", 10, 0, RPC_CLOSE_NOLINGER);
1017
exportlist = rpc_exports_prune(exportlist);
1020
for (tmp = exportlist; tmp; tmp = tmp->ex_next) {
1021
if ((n = strlen(tmp->ex_dir)) > maxlen)
1026
while (exportlist) {
1027
printf("%-*s ", maxlen, exportlist->ex_dir);
1028
grouplist = exportlist->ex_groups;
1031
printf("%s%s", grouplist->gr_name,
1032
grouplist->gr_next ? "," : "");
1033
grouplist = grouplist->gr_next;
1037
exportlist = exportlist->ex_next;
1040
rpc_exports_free(exportlist);