summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2017-08-31 16:58:07 +0200
committerJan Kara <jack@suse.cz>2017-09-05 16:03:59 +0200
commitbeb573b06bb1d478d4e49740787e5a5bd54479ab (patch)
tree4799b19d98509b5a26b0ca54083890cdafd7a313
parent188a2ad8a1ffe795f8216c43e6d07ff42b902852 (diff)
downloadquota-tools-beb573b06bb1d478d4e49740787e5a5bd54479ab.tar.gz
rpc.rquotad: IPv6 support
Add support for listening on IPv6 addresses as well. [Heavily modified from original code by Anders Blomdell] Signed-off-by: Jan Kara <jack@suse.cz>
-rw-r--r--Makefile.am4
-rw-r--r--configure.ac9
-rw-r--r--rquota_svc.c108
-rw-r--r--svc_socket.c125
4 files changed, 167 insertions, 79 deletions
diff --git a/Makefile.am b/Makefile.am
index 41aadcb..a978a28 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -223,10 +223,12 @@ rpc_rquotad_SOURCES = \
rquota_server.c \
rquota_svc.c \
svc_socket.c
+rpc_rquotad_CFLAGS = $(TIRPC_CFLAGS)
rpc_rquotad_LDADD = \
libquota.a \
$(WRAP_LIBS) \
- $(RPCLIBS)
+ $(RPCLIBS) \
+ $(TIRPC_LIBS)
endif
quota_nld_SOURCES = quota_nld.c
diff --git a/configure.ac b/configure.ac
index 9a90a5b..76d2cdd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -206,6 +206,15 @@ AS_IF([test x"$enable_rpc" != "xno"], [
])
build_rpc="no"
])
+
+ PKG_CHECK_MODULES([TIRPC], [libtirpc], [], [
+ AS_IF([test x"$enable_rpc" = "xyes"], [
+ AC_MSG_ERROR([could not locate required libtirpc])
+ ], [
+ AC_MSG_WARN([libtirpc not found])
+ ])
+ build_rpc="no"
+ ])
AS_IF([test x"$build_rpc" != "xno"], [
AC_DEFINE([RPC], 1, [Support for RPC])
diff --git a/rquota_svc.c b/rquota_svc.c
index 95c62fb..1759a35 100644
--- a/rquota_svc.c
+++ b/rquota_svc.c
@@ -21,10 +21,10 @@
#include "config.h"
#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <rpc/pmap_clnt.h> /* for pmap_unset */
#include <stdio.h>
#include <stdlib.h> /* getenv, exit */
#include <string.h> /* strcmp */
@@ -33,6 +33,7 @@
#include <getopt.h>
#include <signal.h>
#include <errno.h>
+#include <netconfig.h>
#ifdef HOSTS_ACCESS
#include <tcpd.h>
#include <netdb.h>
@@ -44,9 +45,6 @@ int deny_severity, allow_severity; /* Needed by some versions of libwrap */
#define SIG_PF void(*)(int)
#endif
-extern int svctcp_socket (u_long __number, int __port, int __reuse);
-extern int svcudp_socket (u_long __number, int __port, int __reuse);
-
#include "pot.h"
#include "common.h"
#include "rquota.h"
@@ -408,8 +406,8 @@ static void rquotaprog_2(struct svc_req *rqstp, register SVCXPRT * transp)
static void
unregister (int sig)
{
- pmap_unset(RQUOTAPROG, RQUOTAVERS);
- pmap_unset(RQUOTAPROG, EXT_RQUOTAVERS);
+ rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
+ rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL);
exit(0);
}
@@ -446,11 +444,71 @@ static void get_pseudoroot(void)
fclose(f);
}
+extern SVCXPRT *svc_create_nconf(rpcprog_t program, struct netconfig *nconf,
+ int port);
+
+static void rquota_svc_create(int port)
+{
+ int maxrec = RPC_MAXDATASIZE;
+ void *handlep;
+ int visible = 0;
+ struct netconfig *nconf;
+ SVCXPRT *xprt;
+
+ /*
+ * Setting MAXREC also enables non-blocking mode for tcp connections.
+ * This avoids DOS attacks by a client sending many requests but never
+ * reading the reply:
+ * - if a second request already is present for reading in the socket,
+ * after the first request just was read, libtirpc will break the
+ * connection. Thus an attacker can't simply send requests as fast as
+ * he can without waiting for the response.
+ * - if the write buffer of the socket is full, the next write() will
+ * fail with EAGAIN. libtirpc will retry the write in a loop for max.
+ * 2 seconds. If write still fails, the connection will be closed.
+ */
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ handlep = setnetconfig();
+ if (!handlep) {
+ errstr(_("Failed to access local netconfig database: %s\n"),
+ nc_sperror());
+ exit(1);
+ }
+ while ((nconf = getnetconfig(handlep)) != NULL) {
+ if (!(nconf->nc_flag & NC_VISIBLE))
+ continue;
+ xprt = svc_create_nconf(RQUOTAPROG, nconf, port);
+ if (!xprt) {
+ errstr(_("Failed to create %s service.\n"),
+ nconf->nc_netid);
+ exit(1);
+ }
+ if (!svc_reg(xprt, RQUOTAPROG, RQUOTAVERS, rquotaprog_1, nconf)) {
+ errstr(_("Unable to register (RQUOTAPROG, RQUOTAVERS, %s).\n"),
+ nconf->nc_netid);
+ } else {
+ visible++;
+ }
+ if (!svc_reg(xprt, RQUOTAPROG, EXT_RQUOTAVERS, rquotaprog_2, nconf)) {
+ errstr(_("Unable to register (RQUOTAPROG, EXT_RQUOTAVERS, %s).\n"),
+ nconf->nc_netid);
+ } else {
+ visible++;
+ }
+ }
+
+ if (visible == 0) {
+ errstr("Failed to register any service.\n");
+ exit(1);
+ }
+
+ endnetconfig(handlep);
+}
+
int main(int argc, char **argv)
{
- register SVCXPRT *transp;
struct sigaction sa;
- int sock;
gettexton();
progname = basename(argv[0]);
@@ -458,8 +516,8 @@ int main(int argc, char **argv)
init_kernel_interface();
get_pseudoroot();
- pmap_unset(RQUOTAPROG, RQUOTAVERS);
- pmap_unset(RQUOTAPROG, EXT_RQUOTAVERS);
+ rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
+ rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL);
sa.sa_handler = SIG_IGN;
sa.sa_flags = 0;
@@ -471,35 +529,7 @@ int main(int argc, char **argv)
sigaction(SIGINT, &sa, NULL);
sigaction(SIGTERM, &sa, NULL);
- sock = svcudp_socket(RQUOTAPROG, port, 1);
- transp = svcudp_create(sock == -1 ? RPC_ANYSOCK : sock);
- if (transp == NULL) {
- errstr(_("cannot create udp service.\n"));
- exit(1);
- }
- if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquotaprog_1, IPPROTO_UDP)) {
- errstr(_("unable to register (RQUOTAPROG, RQUOTAVERS, UDP).\n"));
- exit(1);
- }
- if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, rquotaprog_2, IPPROTO_UDP)) {
- errstr(_("unable to register (RQUOTAPROG, EXT_RQUOTAVERS, UDP).\n"));
- exit(1);
- }
-
- sock = svctcp_socket(RQUOTAPROG, port, 1);
- transp = svctcp_create(sock == -1 ? RPC_ANYSOCK : sock, 0, 0);
- if (transp == NULL) {
- errstr(_("cannot create TCP service.\n"));
- exit(1);
- }
- if (!svc_register(transp, RQUOTAPROG, RQUOTAVERS, rquotaprog_1, IPPROTO_TCP)) {
- errstr(_("unable to register (RQUOTAPROG, RQUOTAVERS, TCP).\n"));
- exit(1);
- }
- if (!svc_register(transp, RQUOTAPROG, EXT_RQUOTAVERS, rquotaprog_2, IPPROTO_TCP)) {
- errstr(_("unable to register (RQUOTAPROG, EXT_RQUOTAVERS, TCP).\n"));
- exit(1);
- }
+ rquota_svc_create(port);
if (!(flags & FL_NODAEMON)) {
use_syslog();
diff --git a/svc_socket.c b/svc_socket.c
index 9a78757..8a44604 100644
--- a/svc_socket.c
+++ b/svc_socket.c
@@ -58,61 +58,108 @@ static int get_service_port(u_long number, const char *proto)
return 0;
}
-static int svc_socket (u_long number, int type, int protocol, int port, int reuse)
+static struct addrinfo *svc_create_bindaddr(struct netconfig *nconf, int port)
{
- struct sockaddr_in addr;
- int sock;
- const char *proto = protocol == IPPROTO_TCP ? "tcp" : "udp";
-
- if ((sock = socket (AF_INET, type, protocol)) < 0) {
- errstr(_("Cannot create socket: %s\n"), strerror(errno));
- return -1;
+ struct addrinfo *ai = NULL;
+ struct addrinfo hint = {
+ .ai_flags = AI_PASSIVE | AI_NUMERICSERV,
+ };
+ char portbuf[16];
+ int err;
+
+ if (!strcmp(nconf->nc_protofmly, NC_INET))
+ hint.ai_family = AF_INET;
+ else if (!strcmp(nconf->nc_protofmly, NC_INET6))
+ hint.ai_family = AF_INET6;
+ else {
+ errstr(_("Unrecognized bind address family: %s\n"),
+ nconf->nc_protofmly);
+ return NULL;
}
- if (reuse) {
- int optval = 1;
+ if (!strcmp(nconf->nc_proto, NC_UDP))
+ hint.ai_protocol = IPPROTO_UDP;
+ else if (!strcmp(nconf->nc_proto, NC_TCP))
+ hint.ai_protocol = IPPROTO_TCP;
+ else {
+ errstr(_("Unrecognized bind address protocol: %s\n"),
+ nconf->nc_proto);
+ return NULL;
+ }
- if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
- errstr(_("Cannot set socket options: %s\n"), strerror(errno));
- return -1;
- }
+ if (nconf->nc_semantics == NC_TPI_CLTS)
+ hint.ai_socktype = SOCK_DGRAM;
+ else if (nconf->nc_semantics == NC_TPI_COTS_ORD)
+ hint.ai_socktype = SOCK_STREAM;
+ else {
+ errstr(_("Unrecognized address semantics: %lu\n"),
+ nconf->nc_semantics);
+ return NULL;
}
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
+ snprintf(portbuf, sizeof(portbuf), "%u", port);
+ err = getaddrinfo(NULL, portbuf, &hint, &ai);
+ if (err) {
+ errstr(_("Failed to construct bind address: %s\n"),
+ gai_strerror(err));
+ return NULL;
+ }
+ return ai;
+}
- if (!port)
- port = get_service_port(number, proto);
+static int svc_create_sock(struct addrinfo *ai)
+{
+ int fd;
+ int optval = 1;
- if (port) {
- addr.sin_port = htons(port);
- if (bind(sock, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) {
- errstr(_("Cannot bind to given address: %s\n"), strerror(errno));
- close (sock);
- return -1;
- }
+ fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (fd < 0) {
+ errstr(_("Error creating socket: %s\n"), strerror(errno));
+ return -1;
}
- else {
- /* Service not found? */
- close(sock);
+
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
+ errstr(_("Cannot set socket options: %s\n"), strerror(errno));
+ close(fd);
return -1;
}
- return sock;
+ if (bind(fd, ai->ai_addr, ai->ai_addrlen) < 0) {
+ errstr(_("Cannot bind to address: %s\n"), strerror(errno));
+ close(fd);
+ return -1;
+ }
+ return fd;
}
/*
- * Create and bind a TCP socket based on program number
+ * Create service structure based on passed netconfig and port
*/
-int svctcp_socket(u_long number, int port, int reuse)
+SVCXPRT *svc_create_nconf(rpcprog_t program, struct netconfig *nconf, int port)
{
- return svc_socket(number, SOCK_STREAM, IPPROTO_TCP, port, reuse);
-}
+ SVCXPRT *xprt;
-/*
- * Create and bind a UDP socket based on program number
- */
-int svcudp_socket(u_long number, int port, int reuse)
-{
- return svc_socket(number, SOCK_DGRAM, IPPROTO_UDP, port, reuse);
+ if (!port)
+ port = get_service_port(program, nconf->nc_proto);
+
+ if (port) {
+ struct addrinfo *ai = svc_create_bindaddr(nconf, port);
+ int fd;
+
+ if (!ai)
+ return NULL;
+
+ fd = svc_create_sock(ai);
+ freeaddrinfo(ai);
+ if (fd < 0)
+ return NULL;
+ xprt = svc_tli_create(fd, nconf, NULL, 0, 0);
+ if (!xprt) {
+ close(fd);
+ return NULL;
+ }
+ } else {
+ xprt = svc_tli_create(RPC_ANYFD, nconf, NULL, 0, 0);
+ }
+ return xprt;
}