From 5c6e6747356f5d473c2c62e818bc24432ddef3e2 Mon Sep 17 00:00:00 2001 From: Florian Weimer Date: Tue, 27 Dec 2016 16:44:15 +0100 Subject: sunrpc: Always obtain AF_INET addresses from NSS [BZ #20964] The new __libc_rpc_gethostbyname function calls gethostbyname2_r with an AF_INET argument and is therefore not affected by the RES_USE_INET6 flag. Validated with the following test program, with and without RES_OPTIONS=inet6, against a NFS server. (Link with -lrpcsvc.) #include #include #include #include static void usage (char **argv) { printf ("usage:\n" " %1$s HOST getrpcport\n" " %1$s HOST callrpc\n" " %1$s HOST clnt_create\n", argv[0]); } static void dump_exports (struct exportnode *exports) { while (exports != NULL) { printf ("%s\n", exports->ex_dir); exports = exports->ex_next; } } int main (int argc, char **argv) { if (argc != 3) { usage (argv); return 1; } const char *host = argv[1]; const char *command = argv[2]; if (strcmp (command, "getrpcport") == 0) { int port = getrpcport (host, MOUNTPROG, MOUNTVERS, IPPROTO_UDP); printf ("getrpcport: %d\n", port); } else if (strcmp (command, "callrpc") == 0) { struct exportnode *exports = NULL; int ret = callrpc (host, MOUNTPROG, MOUNTVERS, MOUNTPROC_EXPORT, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_exports, (char *)&exports); if (ret != 0) { clnt_perrno (ret); puts (""); return 1; } dump_exports (exports); } else if (strcmp (command, "clnt_create") == 0) { CLIENT *client = clnt_create (host, MOUNTPROG, MOUNTVERS, "udp"); if (client == NULL) { printf ("error: clnt_create failed\n"); return 1; } struct exportnode *exports = NULL; int ret = CLNT_CALL (client, MOUNTPROC_EXPORT, (xdrproc_t) xdr_void, NULL, (xdrproc_t) xdr_exports, (char *)&exports, ((struct timeval) {15, 0})); if (ret != 0) { clnt_perrno (ret); puts (""); return 1; } dump_exports (exports); } else { usage (argv); return 1; } return 0; } --- sunrpc/Makefile | 3 +- sunrpc/clnt_gen.c | 37 ++--------------------- sunrpc/clnt_simp.c | 23 ++------------- sunrpc/getrpcport.c | 40 ++++++++++++------------- sunrpc/rpc_gethostbyname.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 77 deletions(-) create mode 100644 sunrpc/rpc_gethostbyname.c (limited to 'sunrpc') diff --git a/sunrpc/Makefile b/sunrpc/Makefile index da6dffa..f7c593a 100644 --- a/sunrpc/Makefile +++ b/sunrpc/Makefile @@ -78,7 +78,8 @@ routines := auth_none authuxprot bindrsvprt clnt_raw clnt_simp \ des_crypt des_impl des_soft key_prot openchild rtime svcauth_des \ getrpcent getrpcbyname getrpcbynumber \ getrpcent_r getrpcbyname_r getrpcbynumber_r \ - clnt_unix svc_unix create_xid $(need-export-routines) + clnt_unix svc_unix create_xid $(need-export-routines) \ + rpc_gethostbyname ifneq ($(link-obsolete-rpc),yes) # We only add the RPC for compatibility to libc.so. shared-only-routines = $(routines) diff --git a/sunrpc/clnt_gen.c b/sunrpc/clnt_gen.c index df34672..8dffaa9 100644 --- a/sunrpc/clnt_gen.c +++ b/sunrpc/clnt_gen.c @@ -45,9 +45,6 @@ CLIENT * clnt_create (const char *hostname, u_long prog, u_long vers, const char *proto) { - struct hostent hostbuf, *h; - size_t hstbuflen; - char *hsttmpbuf; struct protoent protobuf, *p; size_t prtbuflen; char *prttmpbuf; @@ -56,7 +53,6 @@ clnt_create (const char *hostname, u_long prog, u_long vers, int sock; struct timeval tv; CLIENT *client; - int herr; if (strcmp (proto, "unix") == 0) { @@ -78,37 +74,8 @@ clnt_create (const char *hostname, u_long prog, u_long vers, return client; } - hstbuflen = 1024; - hsttmpbuf = __alloca (hstbuflen); - while (__gethostbyname_r (hostname, &hostbuf, hsttmpbuf, hstbuflen, - &h, &herr) != 0 - || h == NULL) - if (herr != NETDB_INTERNAL || errno != ERANGE) - { - get_rpc_createerr().cf_stat = RPC_UNKNOWNHOST; - return NULL; - } - else - { - /* Enlarge the buffer. */ - hstbuflen *= 2; - hsttmpbuf = __alloca (hstbuflen); - } - - if (h->h_addrtype != AF_INET) - { - /* - * Only support INET for now - */ - struct rpc_createerr *ce = &get_rpc_createerr (); - ce->cf_stat = RPC_SYSTEMERROR; - ce->cf_error.re_errno = EAFNOSUPPORT; - return NULL; - } - sin.sin_family = h->h_addrtype; - sin.sin_port = 0; - __bzero (sin.sin_zero, sizeof (sin.sin_zero)); - memcpy ((char *) &sin.sin_addr, h->h_addr, h->h_length); + if (__libc_rpc_gethostbyname (hostname, &sin) != 0) + return NULL; prtbuflen = 1024; prttmpbuf = __alloca (prtbuflen); diff --git a/sunrpc/clnt_simp.c b/sunrpc/clnt_simp.c index d612df0..0ecb64c 100644 --- a/sunrpc/clnt_simp.c +++ b/sunrpc/clnt_simp.c @@ -61,7 +61,6 @@ callrpc (const char *host, u_long prognum, u_long versnum, u_long procnum, struct callrpc_private_s *crp = callrpc_private; struct sockaddr_in server_addr; enum clnt_stat clnt_stat; - struct hostent hostbuf, *hp; struct timeval timeout, tottimeout; if (crp == 0) @@ -84,10 +83,6 @@ callrpc (const char *host, u_long prognum, u_long versnum, u_long procnum, } else { - size_t buflen; - char *buffer; - int herr; - crp->valid = 0; if (crp->socket != RPC_ANYSOCK) { @@ -100,25 +95,11 @@ callrpc (const char *host, u_long prognum, u_long versnum, u_long procnum, crp->client = NULL; } - buflen = 1024; - buffer = __alloca (buflen); - while (__gethostbyname_r (host, &hostbuf, buffer, buflen, - &hp, &herr) != 0 - || hp == NULL) - if (herr != NETDB_INTERNAL || errno != ERANGE) - return (int) RPC_UNKNOWNHOST; - else - { - /* Enlarge the buffer. */ - buflen *= 2; - buffer = __alloca (buflen); - } + if (__libc_rpc_gethostbyname (host, &server_addr) != 0) + return (int) get_rpc_createerr().cf_stat; timeout.tv_usec = 0; timeout.tv_sec = 5; - memcpy ((char *) &server_addr.sin_addr, hp->h_addr, hp->h_length); - server_addr.sin_family = AF_INET; - server_addr.sin_port = 0; if ((crp->client = clntudp_create (&server_addr, (u_long) prognum, (u_long) versnum, timeout, &crp->socket)) == NULL) return (int) get_rpc_createerr().cf_stat; diff --git a/sunrpc/getrpcport.c b/sunrpc/getrpcport.c index 07ee8c0..8fde0a9 100644 --- a/sunrpc/getrpcport.c +++ b/sunrpc/getrpcport.c @@ -1,3 +1,21 @@ +/* Obtain the RPC port number for an RPC service on a host. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see . */ + /* * Copyright (c) 2010, Oracle America, Inc. * @@ -43,26 +61,8 @@ int getrpcport (const char *host, u_long prognum, u_long versnum, u_int proto) { struct sockaddr_in addr; - struct hostent hostbuf, *hp; - size_t buflen; - char *buffer; - int herr; - - buflen = 1024; - buffer = __alloca (buflen); - while (__gethostbyname_r (host, &hostbuf, buffer, buflen, &hp, &herr) != 0 - || hp == NULL) - if (herr != NETDB_INTERNAL || errno != ERANGE) - return 0; - else - { - /* Enlarge the buffer. */ - buflen *= 2; - buffer = __alloca (buflen); - } - memcpy ((char *) &addr.sin_addr, hp->h_addr, hp->h_length); - addr.sin_family = AF_INET; - addr.sin_port = 0; + if (__libc_rpc_gethostbyname (host, &addr) != 0) + return 0; return pmap_getport (&addr, prognum, versnum, proto); } diff --git a/sunrpc/rpc_gethostbyname.c b/sunrpc/rpc_gethostbyname.c new file mode 100644 index 0000000..a2ee960 --- /dev/null +++ b/sunrpc/rpc_gethostbyname.c @@ -0,0 +1,73 @@ +/* IPv4-only variant of gethostbyname. + Copyright (C) 2016 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; see the file COPYING.LIB. If + not, see . */ + +#include +#include +#include +#include +#include + +int +__libc_rpc_gethostbyname (const char *host, struct sockaddr_in *addr) +{ + struct hostent hostbuf; + struct hostent *hp = NULL; + int herr; + struct scratch_buffer tmpbuf; + scratch_buffer_init (&tmpbuf); + + while (__gethostbyname2_r (host, AF_INET, + &hostbuf, tmpbuf.data, tmpbuf.length, &hp, + &herr) != 0 + || hp == NULL) + if (herr != NETDB_INTERNAL || errno != ERANGE) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + ce->cf_stat = RPC_UNKNOWNHOST; + scratch_buffer_free (&tmpbuf); + return -1; + } + else + { + if (!scratch_buffer_grow (&tmpbuf)) + { + /* If memory allocation failed, allocating the RPC error + structure might could as well, so this could lead to a + crash. */ + struct rpc_createerr *ce = &get_rpc_createerr (); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = ENOMEM; + return -1; + } + } + + if (hp->h_addrtype != AF_INET || hp->h_length != sizeof (addr->sin_addr)) + { + struct rpc_createerr *ce = &get_rpc_createerr (); + ce->cf_stat = RPC_SYSTEMERROR; + ce->cf_error.re_errno = EAFNOSUPPORT; + scratch_buffer_free (&tmpbuf); + return -1; + } + + addr->sin_family = AF_INET; + addr->sin_port = htons (0); + memcpy (&addr->sin_addr, hp->h_addr, sizeof (addr->sin_addr)); + scratch_buffer_free (&tmpbuf); + return 0; +} -- cgit v1.1