diff options
author | Jan Kara <jack@suse.cz> | 2017-08-31 16:58:07 +0200 |
---|---|---|
committer | Jan Kara <jack@suse.cz> | 2017-09-05 16:03:59 +0200 |
commit | beb573b06bb1d478d4e49740787e5a5bd54479ab (patch) | |
tree | 4799b19d98509b5a26b0ca54083890cdafd7a313 | |
parent | 188a2ad8a1ffe795f8216c43e6d07ff42b902852 (diff) | |
download | quota-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.am | 4 | ||||
-rw-r--r-- | configure.ac | 9 | ||||
-rw-r--r-- | rquota_svc.c | 108 | ||||
-rw-r--r-- | svc_socket.c | 125 |
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; } |