summaryrefslogtreecommitdiffstats
path: root/support/nfs/rpc_socket.c
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2009-04-18 09:43:58 -0400
committerSteve Dickson <steved@redhat.com>2009-04-18 09:43:58 -0400
commit8c94296bc84f3a204f2061c0391a1d2350e4f37e (patch)
tree0ec8b0644473604b8c13ab71f4aebfe36791666f /support/nfs/rpc_socket.c
parent41eb279c2f46ca020bc3b8d17811555f74b99d2e (diff)
downloadnfs-utils-8c94296bc84f3a204f2061c0391a1d2350e4f37e.tar.gz
nfs-utils-8c94296bc84f3a204f2061c0391a1d2350e4f37e.tar.xz
nfs-utils-8c94296bc84f3a204f2061c0391a1d2350e4f37e.zip
support: Provide an API for creating a privileged RPC client
We needed to guarantee that some RPC programs, such as PMAP, got an unprivileged port, to prevent exhausting the local privileged port space sending RPC requests that don't need such privileges. nfs_get_rpcclient() provides that feature. However, some RPC programs, such as MNT and UMNT, require a privileged port. So, let's provide an additional API for this that also supports IPv6 and setting a destination port. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Steve Dickson <steved@redhat.com>
Diffstat (limited to 'support/nfs/rpc_socket.c')
-rw-r--r--support/nfs/rpc_socket.c140
1 files changed, 133 insertions, 7 deletions
diff --git a/support/nfs/rpc_socket.c b/support/nfs/rpc_socket.c
index 2b11e35..85e6064 100644
--- a/support/nfs/rpc_socket.c
+++ b/support/nfs/rpc_socket.c
@@ -132,6 +132,58 @@ static int nfs_bind(const int sock, const sa_family_t family)
return -1;
}
+#ifdef IPV6_SUPPORT
+
+/*
+ * Bind a socket using an unused privileged source port.
+ *
+ * Returns zero on success, or returns -1 on error. errno is
+ * set to reflect the nature of the error.
+ */
+static int nfs_bindresvport(const int sock, const sa_family_t family)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_ANY),
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_ANY_INIT,
+ };
+
+ switch (family) {
+ case AF_INET:
+ return bindresvport_sa(sock, (struct sockaddr *)&sin,
+ (socklen_t)sizeof(sin));
+ case AF_INET6:
+ return bindresvport_sa(sock, (struct sockaddr *)&sin6,
+ (socklen_t)sizeof(sin6));
+ }
+
+ errno = EAFNOSUPPORT;
+ return -1;
+}
+
+#else /* !IPV6_SUPPORT */
+
+/*
+ * Bind a socket using an unused privileged source port.
+ *
+ * Returns zero on success, or returns -1 on error. errno is
+ * set to reflect the nature of the error.
+ */
+static int nfs_bindresvport(const int sock, const sa_family_t family)
+{
+ if (family != AF_INET) {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ return bindresvport(sock, NULL);
+}
+
+#endif /* !IPV6_SUPPORT */
+
/*
* Perform a non-blocking connect on the socket fd.
*
@@ -218,7 +270,8 @@ static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
const socklen_t salen,
const rpcprog_t program,
const rpcvers_t version,
- struct timeval *timeout)
+ struct timeval *timeout,
+ const int resvport)
{
CLIENT *client;
int ret, sock;
@@ -245,7 +298,10 @@ static CLIENT *nfs_get_udpclient(const struct sockaddr *sap,
return NULL;
}
- ret = nfs_bind(sock, sap->sa_family);
+ if (resvport)
+ ret = nfs_bindresvport(sock, sap->sa_family);
+ else
+ ret = nfs_bind(sock, sap->sa_family);
if (ret < 0) {
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
rpc_createerr.cf_error.re_errno = errno;
@@ -294,7 +350,8 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
const socklen_t salen,
const rpcprog_t program,
const rpcvers_t version,
- struct timeval *timeout)
+ struct timeval *timeout,
+ const int resvport)
{
CLIENT *client;
int ret, sock;
@@ -321,7 +378,10 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
return NULL;
}
- ret = nfs_bind(sock, sap->sa_family);
+ if (resvport)
+ ret = nfs_bindresvport(sock, sap->sa_family);
+ else
+ ret = nfs_bind(sock, sap->sa_family);
if (ret < 0) {
rpc_createerr.cf_stat = RPC_SYSTEMERROR;
rpc_createerr.cf_error.re_errno = errno;
@@ -365,7 +425,8 @@ static CLIENT *nfs_get_tcpclient(const struct sockaddr *sap,
* @timeout: pointer to request timeout (must not be NULL)
*
* Set up an RPC client for communicating with an RPC program @program
- * and @version on the server @sap over @transport.
+ * and @version on the server @sap over @transport. An unprivileged
+ * source port is used.
*
* Returns a pointer to a prepared RPC client if successful, and
* @timeout is initialized; caller must destroy a non-NULL returned RPC
@@ -405,10 +466,75 @@ CLIENT *nfs_get_rpcclient(const struct sockaddr *sap,
switch (transport) {
case IPPROTO_TCP:
- return nfs_get_tcpclient(sap, salen, program, version, timeout);
+ return nfs_get_tcpclient(sap, salen, program, version,
+ timeout, 0);
+ case 0:
+ case IPPROTO_UDP:
+ return nfs_get_udpclient(sap, salen, program, version,
+ timeout, 0);
+ }
+
+ rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
+ return NULL;
+}
+
+/**
+ * nfs_get_priv_rpcclient - acquire an RPC client
+ * @sap: pointer to socket address of RPC server
+ * @salen: length of socket address
+ * @transport: IPPROTO_ value of transport protocol to use
+ * @program: RPC program number
+ * @version: RPC version number
+ * @timeout: pointer to request timeout (must not be NULL)
+ *
+ * Set up an RPC client for communicating with an RPC program @program
+ * and @version on the server @sap over @transport. A privileged
+ * source port is used.
+ *
+ * Returns a pointer to a prepared RPC client if successful, and
+ * @timeout is initialized; caller must destroy a non-NULL returned RPC
+ * client. Otherwise returns NULL, and rpc_createerr.cf_stat is set to
+ * reflect the error.
+ */
+CLIENT *nfs_get_priv_rpcclient(const struct sockaddr *sap,
+ const socklen_t salen,
+ const unsigned short transport,
+ const rpcprog_t program,
+ const rpcvers_t version,
+ struct timeval *timeout)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sap;
+
+ switch (sap->sa_family) {
+ case AF_LOCAL:
+ return nfs_get_localclient(sap, salen, program,
+ version, timeout);
+ case AF_INET:
+ if (sin->sin_port == 0) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ return NULL;
+ }
+ break;
+ case AF_INET6:
+ if (sin6->sin6_port == 0) {
+ rpc_createerr.cf_stat = RPC_UNKNOWNADDR;
+ return NULL;
+ }
+ break;
+ default:
+ rpc_createerr.cf_stat = RPC_UNKNOWNHOST;
+ return NULL;
+ }
+
+ switch (transport) {
+ case IPPROTO_TCP:
+ return nfs_get_tcpclient(sap, salen, program, version,
+ timeout, 1);
case 0:
case IPPROTO_UDP:
- return nfs_get_udpclient(sap, salen, program, version, timeout);
+ return nfs_get_udpclient(sap, salen, program, version,
+ timeout, 1);
}
rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;