File binddynport-honor-ip_local_reserved_ports.patch of Package libtirpc.27300
src/binddynport.c | 107 ++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 99 insertions(+), 8 deletions(-)
diff --git a/src/binddynport.c b/src/binddynport.c
index 062629a..90e2748 100644
--- a/src/binddynport.c
+++ b/src/binddynport.c
@@ -37,6 +37,7 @@
#include <unistd.h>
#include <errno.h>
#include <string.h>
+#include <syslog.h>
#include <rpc/rpc.h>
@@ -56,6 +57,84 @@ enum {
NPORTS = ENDPORT - LOWPORT + 1,
};
+/*
+ * This function decodes information about given port from provided array and
+ * return if port is reserved or not.
+ *
+ * @reserved_ports an array of size at least "NPORTS / (8*sizeof(char)) + 1".
+ * @port port number within range LOWPORT and ENDPORT
+ *
+ * Returns 0 if port is not reserved, non-negative if port is reserved.
+ */
+static int is_reserved(char *reserved_ports, int port) {
+ port -= LOWPORT;
+ if (port < 0 || port >= NPORTS)
+ return 0;
+ return reserved_ports[port/(8*sizeof(char))] & 1<<(port%(8*sizeof(char)));
+}
+
+/*
+ * This function encodes information about given *reserved* port into provided
+ * array. Don't call this function for ports which are not reserved.
+ *
+ * @reserved_ports an array of size at least "NPORTS / (8*sizeof(char)) + 1".
+ * @port port number within range LOWPORT and ENDPORT
+ *
+ */
+static void set_reserved(char *reserved_ports, int port) {
+ port -= LOWPORT;
+ if (port < 0 || port >= NPORTS)
+ return;
+ reserved_ports[port/(8*sizeof(char))] |= 1<<(port%(8*sizeof(char)));
+}
+
+/*
+ * Parse local reserved ports obtained from
+ * /proc/sys/net/ipv4/ip_local_reserved_ports into bit array.
+ *
+ * @reserved_ports a zeroed array of size at least
+ * "NPORTS / (8*sizeof(char)) + 1". Will be used for bit-wise encoding of
+ * reserved ports.
+ *
+ * On each call, reserved ports are read from /proc and bit-wise stored into
+ * provided array
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+
+static int parse_reserved_ports(char *reserved_ports) {
+ int from, to;
+ char delimiter = ',';
+ int res;
+ FILE * file_ptr = fopen("/proc/sys/net/ipv4/ip_local_reserved_ports","r");
+ if (file_ptr == NULL) {
+ (void) syslog(LOG_ERR,
+ "Unable to open open /proc/sys/net/ipv4/ip_local_reserved_ports.");
+ return -1;
+ }
+ do {
+ if ((res = fscanf(file_ptr, "%d", &to)) != 1) {
+ if (res == EOF) break;
+ goto err;
+ }
+ if (delimiter != '-') {
+ from = to;
+ }
+ for (int i = from; i <= to; ++i) {
+ set_reserved(reserved_ports, i);
+ }
+ } while ((res = fscanf(file_ptr, "%c", &delimiter)) == 1);
+ if (res != EOF)
+ goto err;
+ fclose(file_ptr);
+ return 0;
+err:
+ (void) syslog(LOG_ERR,
+ "An error occurred while parsing ip_local_reserved_ports.");
+ fclose(file_ptr);
+ return -1;
+}
+
/*
* Bind a socket to a dynamically-assigned IP port.
*
@@ -81,7 +160,8 @@ int __binddynport(int fd)
in_port_t port, *portp;
struct sockaddr *sap;
socklen_t salen;
- int i, res;
+ int i, res, array_size;
+ char *reserved_ports;
if (__rpc_sockisbound(fd))
return 0;
@@ -119,21 +199,32 @@ int __binddynport(int fd)
gettimeofday(&tv, NULL);
seed = tv.tv_usec * getpid();
}
+ array_size = NPORTS / (8*sizeof(char)) + 1;
+ reserved_ports = malloc(array_size);
+ if (!reserved_ports) {
+ goto out;
+ }
+ memset(reserved_ports, 0, array_size);
+ parse_reserved_ports(reserved_ports);
+
port = (rand_r(&seed) % NPORTS) + LOWPORT;
for (i = 0; i < NPORTS; ++i) {
- *portp = htons(port++);
- res = bind(fd, sap, salen);
- if (res >= 0) {
- res = 0;
- break;
+ *portp = htons(port);
+ if (!is_reserved(reserved_ports, port++)) {
+ res = bind(fd, sap, salen);
+ if (res >= 0) {
+ res = 0;
+ break;
+ }
+ if (errno != EADDRINUSE)
+ break;
}
- if (errno != EADDRINUSE)
- break;
if (port > ENDPORT)
port = LOWPORT;
}
out:
+ free(reserved_ports);
mutex_unlock(&port_lock);
return res;
}
--
2.26.2