Package: nagios-statd-server Version: 3.12-1 Severity: wishlist Tags: patch ipv6 upstream
I am on a train and got fed up, so I wrote this patch series without the ability to check the Debian BTS. Let's hope I am first. The patch series started out because I needed nagios-statd to listen on AF_INET6 sockets. Given that net.ipv6.bindv6only is usually set and I would not want v6-only listening, I had to change the socket code quite a bit so that it could listen on multiple sockets (which required the addition of threading). And I had to replace the dreadful gethostbyname call with getaddrinfo. To make a long story short: nagios-statd can now listen on multiple IPs (but only a single port). By default, that's 0.0.0.0 and ::, so it listens on all v4 and v6 addresses. You can specify a comma-separated list of addresses with -b to limit the set of IPs. This is compatible with old behaviour, meaning that a single IP just produces a single listening socket. Additionally, the -4 and -6 switches disable IPv6/IPv4 listening respectively. Finally, the first patch is just for convenience and introduces a foreground-run option. -- System Information: Debian Release: squeeze/sid APT prefers unstable APT policy: (500, 'unstable'), (1, 'experimental') Architecture: amd64 (x86_64) Kernel: Linux 2.6.35-trunk-amd64 (SMP w/4 CPU cores) Locale: LANG=en_NZ, LC_CTYPE=en_NZ.UTF-8 (charmap=UTF-8) Shell: /bin/sh linked to /bin/dash Versions of packages nagios-statd-server depends on: ii python 2.6.5-12 interactive high-level object-orie nagios-statd-server recommends no packages. nagios-statd-server suggests no packages. -- no debconf information -- .''`. martin f. krafft <madd...@d.o> Related projects: : :' : proud Debian developer http://debiansystem.info `. `'` http://people.debian.org/~madduck http://vcs-pkg.org `- Debian - when you have better things to do than fixing systems
From 69743887d4d607606c545b5228e8ac8637803df9 Mon Sep 17 00:00:00 2001 From: martin f. krafft <madd...@madduck.net> Date: Tue, 10 Aug 2010 16:56:35 +0200 Subject: [PATCH 1/5] Add -F/--foreground option to facilitate debugging This adds a flag to prevent nagios-statd from daemonising. This makes it easier to debug, and might be useful in other use-cases too. Passing -F simply means that there won't be a fork() call, no session IDs will be set, the file descriptors aren't closed, and stdin/out/err aren't connected to /dev/null. Signed-off-by: martin f. krafft <madd...@madduck.net> --- nagios-statd | 48 +++++++++++++++++++++++++++--------------------- 1 files changed, 27 insertions(+), 21 deletions(-) diff --git a/nagios-statd b/nagios-statd index 688aa86..e350604 100755 --- a/nagios-statd +++ b/nagios-statd @@ -157,6 +157,7 @@ class ReUsingServer (SocketServer.ForkingTCPServer): class Initialization: "Methods for interacting with user - initial code entry point." def __init__(self): + self.foreground = False self.port = 1040 self.ip = '' # Run this through Functions initially, to make sure the platform is supported. @@ -166,7 +167,7 @@ class Initialization: def getoptions(self): "Parses command line" try: - opts, args = getopt.getopt(sys.argv[1:], "a:b:ip:P:Vh", ["allowedhosts=","bindto=","inetd","port=","pid=","version","help"]) + opts, args = getopt.getopt(sys.argv[1:], "a:b:Fip:P:Vh", ["allowedhosts=","bindto=","foreground","inetd","port=","pid=","version","help"]) except getopt.GetoptError, (msg, opt): print sys.argv[0] + ": " + msg print "Try '" + sys.argv[0] + " --help' for more information." @@ -177,6 +178,8 @@ class Initialization: self.allowedhosts = value.split(",") elif option in ("-b","--bindto"): self.ip = value + elif option in ("-F","--foreground"): + self.foreground = 1 elif option in ("-i","--inetd"): self.runfrominetd = 1 elif option in ("-p","--port"): @@ -210,15 +213,33 @@ class Initialization: print "Unable to bind to port %s: %s - exiting." % (self.port, msg) sys.exit(2) - # Detach from terminal + # Detach from terminal unless we are running in foreground + if not self.foreground: self.daemonise() + + # Be polite and chdir to / + os.chdir('/') + + # Set the path + os.environ["PATH"] = "/bin:/usr/bin:/usr/local/bin:/usr/sbin" + + # Reap children automatically + signal.signal(signal.SIGCHLD, signal.SIG_IGN) + + # Save pid if user requested it + if hasattr(self,"pidfile"): + self.savepid(self.pidfile) + + server = ReUsingServer((self.ip,self.port),NagiosStatd) + if hasattr(self,"allowedhosts"): + server.allowedhosts = self.allowedhosts + server.serve_forever() + + def daemonise(self): if os.fork() == 0: # Make this the controlling process os.setsid() - # Be polite and chdir to / - os.chdir('/') - # Try to close all open filehandles for i in range(0,256): try: os.close(i) @@ -229,22 +250,6 @@ class Initialization: sys.stdout = open('/dev/null','w') sys.stderr = open('/dev/null','w') - # Set the path - os.environ["PATH"] = "/bin:/usr/bin:/usr/local/bin:/usr/sbin" - - # Reap children automatically - signal.signal(signal.SIGCHLD, signal.SIG_IGN) - - # Save pid if user requested it - if hasattr(self,"pidfile"): - self.savepid(self.pidfile) - - # Create a forking TCP/IP server and start processing - server = ReUsingServer((self.ip,self.port),NagiosStatd) - if hasattr(self,"allowedhosts"): - server.allowedhosts = self.allowedhosts - server.serve_forever() - # Get rid of the parent else: sys.exit(0) @@ -263,6 +268,7 @@ class Initialization: print "nagios-statd daemon - remote UNIX system monitoring tool for Nagios.\n" print "-a, --allowedhosts=HOSTS Comma delimited list of IPs/hosts allowed to connect." print "-b, --bindto=IP IP address for the daemon to bind to." + print "-F, --foreground Stay in foreground, do not daemonise." print "-i, --inetd Run from inetd." print "-p, --port=PORT Port to listen on." print "-P, --pid=FILE Save pid to FILE." -- 1.7.1
From 77747f4fb131fed83e229fd14f0fce3093230dc6 Mon Sep 17 00:00:00 2001 From: martin f. krafft <madd...@madduck.net> Date: Tue, 10 Aug 2010 16:59:58 +0200 Subject: [PATCH 2/5] Factor out server function for better readability Signed-off-by: martin f. krafft <madd...@madduck.net> --- nagios-statd | 11 +++++++---- 1 files changed, 7 insertions(+), 4 deletions(-) diff --git a/nagios-statd b/nagios-statd index e350604..141f201 100755 --- a/nagios-statd +++ b/nagios-statd @@ -229,10 +229,7 @@ class Initialization: if hasattr(self,"pidfile"): self.savepid(self.pidfile) - server = ReUsingServer((self.ip,self.port),NagiosStatd) - if hasattr(self,"allowedhosts"): - server.allowedhosts = self.allowedhosts - server.serve_forever() + self.serve_forever() def daemonise(self): if os.fork() == 0: @@ -254,6 +251,12 @@ class Initialization: else: sys.exit(0) + def serve_forever(self): + server = ReUsingServer((self.ip,self.port),NagiosStatd) + if hasattr(self,"allowedhosts"): + server.allowedhosts = self.allowedhosts + server.serve_forever() + def savepid(self,file): try: fh = open(file,"w") -- 1.7.1
From 3b706e10ab9d48a93499b315cd3bfa1b143b159d Mon Sep 17 00:00:00 2001 From: martin f. krafft <madd...@madduck.net> Date: Tue, 10 Aug 2010 16:42:20 +0200 Subject: [PATCH 3/5] Replace gethostbyname with getaddrinfo gethostbyname(2) is horrible when it comes to IPv6, but also in other contexts. It is generally accepted that getaddrinfo(2) is the preferred alternative. This patch incorporates getaddrinfo(2) into the logic of checking whether a connecting client matches an entry in the allowedhosts list. It simplifies the logic a bit on the way. Signed-off-by: martin f. krafft <madd...@madduck.net> --- nagios-statd | 13 ++++++++----- 1 files changed, 8 insertions(+), 5 deletions(-) diff --git a/nagios-statd b/nagios-statd index 141f201..500d19f 100755 --- a/nagios-statd +++ b/nagios-statd @@ -96,15 +96,18 @@ class NagiosStatd(SocketServer.StreamRequestHandler): if hasattr(self.server,"allowedhosts") == 0: return 0 for i in self.server.allowedhosts: - if i == self.client_address[0]: # Address is in list - return 0 try: # Do an IP lookup of host in blocked list - i_ip = socket.gethostbyname(i) + gai = socket.getaddrinfo(i, 1040) # port does not matter + i_ips = dict(map(lambda x: (x[4][0], None), gai)).keys() except: self.error = "ERROR DNS lookup of blocked host \"%s\" failed. Denying by default." % i return 1 - if i_ip != i: # If address in list isn't an IP - if socket.getfqdn(i) == socket.getfqdn(self.client_address[0]): + + if self.client_address[0] in i_ips: + return 0 + + if socket.getfqdn(self.client_address[0]) in \ + map(socket.getfqdn, i_ips): return 0 self.error = "ERROR Client is not among hosts allowed to connect." return 1 -- 1.7.1
From fefb7c9559ae87220440fd662b7c2408e4fe52f8 Mon Sep 17 00:00:00 2001 From: martin f. krafft <madd...@madduck.net> Date: Tue, 10 Aug 2010 17:02:40 +0200 Subject: [PATCH 4/5] Enable multiple-sockets and address families Previously, nagios-statd could only bind to 0.0.0.0 or a single IPv4. In order to let it bind to IPv6 as well, assuming that net.ipv6.bindv6only is set, it needs to bind to multiple sockets. This required a bit of re-engineering, as well as the addition of threading at the level of nagios-statd. Signed-off-by: martin f. krafft <madd...@madduck.net> --- nagios-statd | 60 ++++++++++++++++++++++++++++++++++++++++----------------- 1 files changed, 42 insertions(+), 18 deletions(-) diff --git a/nagios-statd b/nagios-statd index 500d19f..d057874 100755 --- a/nagios-statd +++ b/nagios-statd @@ -1,6 +1,6 @@ #!/usr/bin/python -import getopt, os, sys, signal, socket, SocketServer +import getopt, os, sys, signal, socket, SocketServer, threading class Functions: "Contains a set of methods for gathering data from the server." @@ -155,14 +155,18 @@ class GenericHandler: class ReUsingServer (SocketServer.ForkingTCPServer): - allow_reuse_address = True + def __init__(self, af, socket, handler): + self.allow_reuse_address = True + self.address_family = af + SocketServer.ForkingTCPServer.__init__(self, socket, handler) class Initialization: "Methods for interacting with user - initial code entry point." def __init__(self): self.foreground = False self.port = 1040 - self.ip = '' + self.ips = '0.0.0.0,::' + self.addrfam = (socket.AF_INET, socket.AF_INET6) # Run this through Functions initially, to make sure the platform is supported. i = Functions() del(i) @@ -180,7 +184,7 @@ class Initialization: value = value.replace(" ","") self.allowedhosts = value.split(",") elif option in ("-b","--bindto"): - self.ip = value + self.ips = value elif option in ("-F","--foreground"): self.foreground = 1 elif option in ("-i","--inetd"): @@ -195,6 +199,17 @@ class Initialization: elif option in ("-h","--help"): self.usage() + ips = self.ips.split(',') + self.ips = dict(zip(self.addrfam, ([],[]))) + for ip in ips: + af = socket.AF_INET if ip.find(':') < 0 else socket.AF_INET6 + if af in self.ips: + self.ips[af].append(ip) + + if sum(map(len, self.ips.itervalues())) == 0: + print >>sys.stderr, "No sockets found to bind to." + sys.exit(4) + def main(self): # Retrieve command line options self.getoptions() @@ -206,15 +221,17 @@ class Initialization: sys.exit(0) # Check to see if the port is available - try: - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - s.bind((self.ip, self.port)) - s.close() - del(s) - except socket.error, (errno, msg): - print "Unable to bind to port %s: %s - exiting." % (self.port, msg) - sys.exit(2) + for af, ips in self.ips.iteritems(): + for ip in ips: + try: + s = socket.socket(af, socket.SOCK_STREAM) + s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + s.bind((ip, self.port)) + s.close() + del(s) + except socket.error, (errno, msg): + print "Unable to bind to socket %s:%s: %s - exiting." % (ip, self.port, msg) + sys.exit(2) # Detach from terminal unless we are running in foreground if not self.foreground: self.daemonise() @@ -255,10 +272,17 @@ class Initialization: sys.exit(0) def serve_forever(self): - server = ReUsingServer((self.ip,self.port),NagiosStatd) - if hasattr(self,"allowedhosts"): - server.allowedhosts = self.allowedhosts - server.serve_forever() + threads = [] + for af, ips in self.ips.iteritems(): + for ip in ips: + server = ReUsingServer(af,(ip,self.port),NagiosStatd) + if hasattr(self,"allowedhosts"): + server.allowedhosts = self.allowedhosts + thread = threading.Thread(target=server.serve_forever) + thread.start() + threads.append(thread) + + for t in threads: t.join() def savepid(self,file): try: @@ -273,7 +297,7 @@ class Initialization: print "Usage: " + sys.argv[0] + " [OPTION]" print "nagios-statd daemon - remote UNIX system monitoring tool for Nagios.\n" print "-a, --allowedhosts=HOSTS Comma delimited list of IPs/hosts allowed to connect." - print "-b, --bindto=IP IP address for the daemon to bind to." + print "-b, --bindto=IP Comma delimited list of IPs to bind to." print "-F, --foreground Stay in foreground, do not daemonise." print "-i, --inetd Run from inetd." print "-p, --port=PORT Port to listen on." -- 1.7.1
From 56d868ea109b667a64bf4e830040c1be484987e6 Mon Sep 17 00:00:00 2001 From: martin f. krafft <madd...@madduck.net> Date: Tue, 10 Aug 2010 17:02:54 +0200 Subject: [PATCH 5/5] Allow limiting to a single address family For those who might not want to bind to both, IPv4/IPv6 by default, this patch adds -4/-6 (--v4-only/--v6-only) switches in similar spirit to other tools to limit binding to sockets of the respective address family. Signed-off-by: martin f. krafft <madd...@madduck.net> --- nagios-statd | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-) diff --git a/nagios-statd b/nagios-statd index d057874..2683cc3 100755 --- a/nagios-statd +++ b/nagios-statd @@ -174,7 +174,7 @@ class Initialization: def getoptions(self): "Parses command line" try: - opts, args = getopt.getopt(sys.argv[1:], "a:b:Fip:P:Vh", ["allowedhosts=","bindto=","foreground","inetd","port=","pid=","version","help"]) + opts, args = getopt.getopt(sys.argv[1:], "a:b:64Fip:P:Vh", ["allowedhosts=","bindto=","v6only","v4only","foreground","inetd","port=","pid=","version","help"]) except getopt.GetoptError, (msg, opt): print sys.argv[0] + ": " + msg print "Try '" + sys.argv[0] + " --help' for more information." @@ -185,6 +185,10 @@ class Initialization: self.allowedhosts = value.split(",") elif option in ("-b","--bindto"): self.ips = value + elif option in ("-6","--v6oply"): + self.addrfam = (socket.AF_INET6,) + elif option in ("-4","--v4oply"): + self.addrfam = (socket.AF_INET,) elif option in ("-F","--foreground"): self.foreground = 1 elif option in ("-i","--inetd"): @@ -298,6 +302,8 @@ class Initialization: print "nagios-statd daemon - remote UNIX system monitoring tool for Nagios.\n" print "-a, --allowedhosts=HOSTS Comma delimited list of IPs/hosts allowed to connect." print "-b, --bindto=IP Comma delimited list of IPs to bind to." + print "-4, --v4only Only bind to IPv4 sockets." + print "-6, --v6only Only bind to IPv6 sockets." print "-F, --foreground Stay in foreground, do not daemonise." print "-i, --inetd Run from inetd." print "-p, --port=PORT Port to listen on." -- 1.7.1
digital_signature_gpg.asc
Description: Digital signature (see http://martin-krafft.net/gpg/)