[PATCH] ipv6 patch for nrpe

Leo Baltus Leo.Baltus at omroep.nl
Mon May 2 17:32:55 CEST 2011


Hi,

We have prepared attached patch that allows nrpe & check_nrpe to support
ipv6 and it also add a '-b' commandline argument to select the source address.

It has been running here for a few weeks now so it seems it's pretty stable.

Most of the code is 'inspired' by openssh, so you may find similarities.

It has successfully been compiled on a number of Linux distros, as well as a
recent version of FreeBSD.

I have seen the post from Kristian Lyngstøl in March last only today, so
we may be doing the same thing. As Kristian seems to be more ambitious
than I am to clean up the code, maybe this patch can help to get the server-
part in place en to get it upstream some day.

In the mean time this patch adds ipv6 capabilities to nrpe & check_nrpe
for anyone who has an interest in it.

-- 
Leo Baltus, internetbeheerder                         /\
NPO ICT Internet Services                            /NPO/\
Sumatralaan 45, 1217 GP Hilversum, Filmcentrum, west \  /\/
beheer at omroep.nl, 035-6773555                         \/
-------------- next part --------------
diff -ruN nrpe-2.12.org/include/utils.h nrpe-2.12/include/utils.h
--- nrpe-2.12.org/include/utils.h	2006-12-12 03:04:00.000000000 +0100
+++ nrpe-2.12/include/utils.h	2011-04-13 12:09:35.879689827 +0200
@@ -41,9 +41,9 @@
 void randomize_buffer(char *,int);
 
 int my_tcp_connect(char *,int,int *);
-int my_connect(char *,int,int *,char *);
+int my_connect(const char *host, struct sockaddr_storage * hostaddr, u_short port, int address_family, const char *bind_address);
 
-int my_inet_aton(register const char *,struct in_addr *);
+void add_listen_addr(struct addrinfo **listen_addrs, int address_family, char *addr, int port);
 
 void strip(char *);
 
diff -ruN nrpe-2.12.org/src/check_nrpe.c nrpe-2.12/src/check_nrpe.c
--- nrpe-2.12.org/src/check_nrpe.c	2008-03-10 22:04:43.000000000 +0100
+++ nrpe-2.12/src/check_nrpe.c	2011-04-13 10:40:08.646591612 +0200
@@ -24,8 +24,11 @@
 
 #define DEFAULT_NRPE_COMMAND	"_NRPE_CHECK"  /* check version of NRPE daemon */
 
-int server_port=DEFAULT_SERVER_PORT;
+u_short server_port=DEFAULT_SERVER_PORT;
 char *server_name=NULL;
+char *bind_address=NULL;
+struct sockaddr_storage hostaddr;
+int address_family=AF_UNSPEC;
 char *command_name=NULL;
 int socket_timeout=DEFAULT_SOCKET_TIMEOUT;
 int timeout_return_code=STATE_CRITICAL;
@@ -38,7 +41,6 @@
 int show_version=FALSE;
 
 #ifdef HAVE_SSL
-SSL_METHOD *meth;
 SSL_CTX *ctx;
 SSL *ssl;
 int use_ssl=TRUE;
@@ -84,12 +86,15 @@
 
 	if(result!=OK || show_help==TRUE){
 
-		printf("Usage: check_nrpe -H <host> [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
+		printf("Usage: check_nrpe -H <host> [ -b <bindaddr> ] [-4] [-6] [-n] [-u] [-p <port>] [-t <timeout>] [-c <command>] [-a <arglist...>]\n");
 		printf("\n");
 		printf("Options:\n");
 		printf(" -n         = Do no use SSL\n");
 		printf(" -u         = Make socket timeouts return an UNKNOWN state instead of CRITICAL\n");
-		printf(" <host>     = The address of the host running the NRPE daemon\n");
+		printf(" <host>     = The address of the NRPE daemon\n");
+		printf(" <bindaddr> = bind to local address\n");
+		printf(" -4         = user ipv4 only\n");
+		printf(" -6         = user ipv6 only\n");
 		printf(" [port]     = The port on which the daemon is running (default=%d)\n",DEFAULT_SERVER_PORT);
 		printf(" [timeout]  = Number of seconds before connection times out (default=%d)\n",DEFAULT_SOCKET_TIMEOUT);
 		printf(" [command]  = The name of the command that the remote daemon should run\n");
@@ -123,9 +128,8 @@
 	if(use_ssl==TRUE){
 		SSL_library_init();
 		SSLeay_add_ssl_algorithms();
-		meth=SSLv23_client_method();
 		SSL_load_error_strings();
-		if((ctx=SSL_CTX_new(meth))==NULL){
+		if((ctx=SSL_CTX_new(SSLv23_client_method()))==NULL){
 			printf("CHECK_NRPE: Error - could not create SSL context.\n");
 			exit(STATE_CRITICAL);
 		        }
@@ -143,7 +147,10 @@
 	alarm(socket_timeout);
 
 	/* try to connect to the host at the given port number */
-	result=my_tcp_connect(server_name,server_port,&sd);
+
+	if ((sd=my_connect(server_name, &hostaddr, server_port, address_family, bind_address)) < 0 ) 
+		exit (255);
+	else result=STATE_OK;
 
 #ifdef HAVE_SSL
 	/* do SSL handshake */
@@ -320,10 +327,13 @@
 	int option_index=0;
 	static struct option long_options[]={
 		{"host", required_argument, 0, 'H'},
+		{"bind", required_argument, 0, 'b'},
 		{"command", required_argument, 0, 'c'},
 		{"args", required_argument, 0, 'a'},
 		{"no-ssl", no_argument, 0, 'n'},
 		{"unknown-timeout", no_argument, 0, 'u'},
+		{"ipv4", no_argument, 0, '4'},
+		{"ipv6", no_argument, 0, '6'},
 		{"timeout", required_argument, 0, 't'},
 		{"port", required_argument, 0, 'p'},
 		{"help", no_argument, 0, 'h'},
@@ -336,7 +346,7 @@
 	if(argc<2)
 		return ERROR;
 
-	snprintf(optchars,MAX_INPUT_BUFFER,"H:c:a:t:p:nuhl");
+	snprintf(optchars,MAX_INPUT_BUFFER,"H:b:c:a:t:p:nu46hl");
 
 	while(1){
 #ifdef HAVE_GETOPT_LONG
@@ -354,6 +364,9 @@
 		case 'h':
 			show_help=TRUE;
 			break;
+		case 'b':
+			bind_address=strdup(optarg);
+			break;
 		case 'V':
 			show_version=TRUE;
 			break;
@@ -385,6 +398,12 @@
 		case 'u':
 			timeout_return_code=STATE_UNKNOWN;
 			break;
+		case '4':
+			address_family=AF_INET;
+			break;
+		case '6':
+			address_family=AF_INET6;
+			break;
 		default:
 			return ERROR;
 			break;
diff -ruN nrpe-2.12.org/src/nrpe.c nrpe-2.12/src/nrpe.c
--- nrpe-2.12.org/src/nrpe.c	2008-03-10 22:04:43.000000000 +0100
+++ nrpe-2.12/src/nrpe.c	2011-04-15 09:37:04.150999139 +0200
@@ -18,11 +18,15 @@
  * 
  ******************************************************************************/
 
+#include <stdlib.h>
 #include "../include/common.h"
 #include "../include/config.h"
 #include "../include/nrpe.h"
 #include "../include/utils.h"
 
+# define howmany(x,y)	(((x)+((y)-1))/(y))
+
+
 #ifdef HAVE_SSL
 #include "../include/dh.h"
 #endif
@@ -33,7 +37,6 @@
 #endif
 
 #ifdef HAVE_SSL
-SSL_METHOD *meth;
 SSL_CTX *ctx;
 int use_ssl=TRUE;
 #else
@@ -50,7 +53,15 @@
 char    config_file[MAX_INPUT_BUFFER]="nrpe.cfg";
 int     log_facility=LOG_DAEMON;
 int     server_port=DEFAULT_SERVER_PORT;
-char    server_address[16]="0.0.0.0";
+char    server_address[NI_MAXHOST]="";
+struct addrinfo *listen_addrs=NULL;
+
+#define MAX_LISTEN_SOCKS        16
+int listen_socks[MAX_LISTEN_SOCKS];
+int num_listen_socks = 0;
+
+
+int	address_family=AF_UNSPEC;
 int     socket_timeout=DEFAULT_SOCKET_TIMEOUT;
 int     command_timeout=DEFAULT_COMMAND_TIMEOUT;
 int     connection_timeout=DEFAULT_CONNECTION_TIMEOUT;
@@ -138,11 +149,13 @@
 
 	else if(result!=OK || show_help==TRUE){
 
-		printf("Usage: nrpe [-n] -c <config_file> <mode>\n");
+		printf("Usage: nrpe [-n] -c <config_file> [-4|-6] <mode>\n");
 		printf("\n");
 		printf("Options:\n");
 		printf(" -n            = Do not use SSL\n");
 		printf(" <config_file> = Name of config file to use\n");
+		printf(" -4            = use ipv4 only\n");
+		printf(" -6            = use ipv6 only\n");
 		printf(" <mode>        = One of the following two operating modes:\n");  
 		printf("   -i          =    Run as a service under inetd or xinetd\n");
 		printf("   -d          =    Run as a standalone daemon\n");
@@ -208,7 +221,6 @@
 	if(use_ssl==TRUE){
 		SSL_library_init();
 		SSLeay_add_ssl_algorithms();
-		meth=SSLv23_server_method();
 		SSL_load_error_strings();
 
 		/* use week random seed if necessary */
@@ -230,7 +242,7 @@
 				}
 			}
 
-		if((ctx=SSL_CTX_new(meth))==NULL){
+		if((ctx=SSL_CTX_new(SSLv23_server_method()))==NULL){
 			syslog(LOG_ERR,"Error: could not create SSL context.\n");
 			exit(STATE_CRITICAL);
 		        }
@@ -691,12 +703,38 @@
         }
 
 
+/*
+ * Close all listening sockets
+ */
+static void
+close_listen_socks(void)
+{
+        int i;
+
+        for (i = 0; i <= num_listen_socks; i++) {
+                close(listen_socks[i]);
+        	num_listen_socks--;
+	}
+}
+
 
 /* wait for incoming connection requests */
 void wait_for_connections(void){
+        struct addrinfo *ai;
+        char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+	fd_set *fdset=NULL;
+	int maxfd=0;
+        struct sockaddr_storage from;
+        socklen_t fromlen;
+	char ipstr[INET6_ADDRSTRLEN];
+	int ret;
+	int listen_sock;
+	int i;
+	int r;
+
 	struct sockaddr_in myname;
 	struct sockaddr_in *nptr;
-	struct sockaddr addr;
+	struct sockaddr_storage addr;
 	int rc;
 	int sock, new_sd;
 	socklen_t addrlen;
@@ -708,50 +746,77 @@
 #ifdef HAVE_LIBWRAP
 	struct request_info req;
 #endif
+	
+	add_listen_addr(&listen_addrs, address_family, 
+		(strcmp(server_address,"")==0)?NULL:server_address, server_port);
 
-	/* create a socket for listening */
-	sock=socket(AF_INET,SOCK_STREAM,0);
-
-	/* exit if we couldn't create the socket */
-	if(sock<0){
-	        syslog(LOG_ERR,"Network server socket failure (%d: %s)",errno,strerror(errno));
-	        exit(STATE_CRITICAL);
+	for (ai = listen_addrs; ai; ai = ai->ai_next) {
+		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+			continue;
+                if (num_listen_socks >= MAX_LISTEN_SOCKS) {
+                        syslog(LOG_ERR, "Too many listen sockets. "
+                            "Enlarge MAX_LISTEN_SOCKS");
+			exit(1);
+		}
+                if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen,
+                    ntop, sizeof(ntop), strport, sizeof(strport),
+                    NI_NUMERICHOST|NI_NUMERICSERV)) != 0) {
+                        syslog(LOG_ERR, "getnameinfo failed: %.100s",
+                            gai_strerror(ret));
+                        continue;
+                }
+		/* Create socket for listening. */
+		listen_sock = socket(ai->ai_family, ai->ai_socktype,
+			ai->ai_protocol);
+		if (listen_sock < 0) {
+			/* kernel may not support ipv6 */
+			syslog(LOG_ERR, "socket: %.100s", strerror(errno));
+                        continue;
+                }
+	        /* socket should be non-blocking */
+		fcntl(listen_sock,F_SETFL,O_NONBLOCK);
+		/* set the reuse address flag so we don't get errors when 
+		   restarting */
+        	if(setsockopt(listen_sock, SOL_SOCKET,SO_REUSEADDR,
+			&flag,sizeof(flag))<0){
+			syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %s", 
+				strerror(errno));
+                        continue;
 		}
 
-	/* socket should be non-blocking */
-	fcntl(sock,F_SETFL,O_NONBLOCK);
-
-        /* set the reuse address flag so we don't get errors when restarting */
-        flag=1;
-        if(setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,(char *)&flag,sizeof(flag))<0){
-		syslog(LOG_ERR,"Could not set reuse address option on socket!\n");
-		exit(STATE_UNKNOWN);
-	        }
-
-	myname.sin_family=AF_INET;
-	myname.sin_port=htons(server_port);
- 	bzero(&myname.sin_zero,8);
-
-	/* what address should we bind to? */
-        if(!strlen(server_address))
-		myname.sin_addr.s_addr=INADDR_ANY;
-
-	else if(!my_inet_aton(server_address,&myname.sin_addr)){
-		syslog(LOG_ERR,"Server address is not a valid IP address\n");
-		exit(STATE_CRITICAL);
+#ifdef IPV6_V6ONLY
+                /* Only communicate in IPv6 over AF_INET6 sockets. */
+                if (ai->ai_family == AF_INET6) {
+                        if (setsockopt(listen_sock, IPPROTO_IPV6, IPV6_V6ONLY,
+                            &flag, sizeof(flag)) == -1)
+                                error("setsockopt IPV6_V6ONLY: %s",
+                                    strerror(errno));
                 }
+#endif
 
-	/* bind the address to the Internet socket */
-	if(bind(sock,(struct sockaddr *)&myname,sizeof(myname))<0){
-		syslog(LOG_ERR,"Network server bind failure (%d: %s)\n",errno,strerror(errno));
-	        exit(STATE_CRITICAL);
-	        }
+                /* Bind the socket to the desired port. */
+                if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+                        syslog(LOG_ERR, "Bind to port %s on %s failed: %.200s.",
+                            strport, ntop, strerror(errno));
+                        close(listen_sock);
+                        continue;
+                }
+                listen_socks[num_listen_socks] = listen_sock;
+                num_listen_socks++;
 
-	/* open the socket for listening */
-	if(listen(sock,5)<0){
-	    	syslog(LOG_ERR,"Network server listen failure (%d: %s)\n",errno,strerror(errno));
-	        exit(STATE_CRITICAL);
+                /* Start listening on the port. */
+                if (listen(listen_sock, 5) < 0) {
+                        syslog(LOG_ERR, "listen on [%s]:%s: %.100s",
+                            ntop, strport, strerror(errno));
+			exit(1);
 		}
+		syslog(LOG_INFO, "Server listening on %s port %s.", ntop, strport);
+        }
+
+        if (!num_listen_socks) {
+                syslog(LOG_ERR, "Cannot bind to any address.");
+		exit(1);
+	}
 
 	/* log warning about command arguments */
 #ifdef ENABLE_COMMAND_ARGUMENTS
@@ -759,142 +824,122 @@
 		syslog(LOG_NOTICE,"Warning: Daemon is configured to accept command arguments from clients!");
 #endif
 
-	syslog(LOG_INFO,"Listening for connections on port %d\n",htons(myname.sin_port));
-
 	if(allowed_hosts)
 		syslog(LOG_INFO,"Allowing connections from: %s\n",allowed_hosts);
 
 	/* listen for connection requests - fork() if we get one */
 	while(1){
+		/* bail out if necessary */
+		if(sigrestart==TRUE || sigshutdown==TRUE)
+			break;
+
+			for (i = 0; i < num_listen_socks; i++)
+				if (listen_socks[i] > maxfd)
+					maxfd = listen_socks[i];
 
-		/* wait for a connection request */
-	        while(1){
+			fdset = (fd_set *)calloc(howmany(maxfd + 1, NFDBITS),
+				sizeof(fd_mask));
 
-			/* wait until there's something to do */
-			FD_ZERO(&fdread);
-			FD_SET(sock,&fdread);
-			timeout.tv_sec=0;
-			timeout.tv_usec=500000;
-			retval=select(sock+1,&fdread,NULL,&fdread,&timeout);
+			for (i = 0; i < num_listen_socks; i++)
+				FD_SET(listen_socks[i], fdset);
+
+			/* Wait in select until there is a connection. */
+			retval = select(maxfd+1, fdset, NULL, NULL, NULL);
 
 			/* bail out if necessary */
 			if(sigrestart==TRUE || sigshutdown==TRUE)
 				break;
-
 			/* error */
 			if(retval<0)
 				continue;
 
+                for (i = 0; i < num_listen_socks; i++) {
+                        if (!FD_ISSET(listen_socks[i], fdset))
+                                continue;
+                        fromlen = sizeof(from);
 			/* accept a new connection request */
-			new_sd=accept(sock,0,0);
+                        new_sd = accept(listen_socks[i],
+                            (struct sockaddr *)&from, &fromlen);
 
+			// {{{
 			/* some kind of error occurred... */
 			if(new_sd<0){
-
 				/* bail out if necessary */
 				if(sigrestart==TRUE || sigshutdown==TRUE)
 					break;
-
 				/* retry */
 				if(errno==EWOULDBLOCK || errno==EINTR)
 					continue;
-
 				/* socket is nonblocking and we don't have a connection yet */
 				if(errno==EAGAIN)
 					continue;
-
 				/* fix for HP-UX 11.0 - just retry */
 				if(errno==ENOBUFS)
 					continue;
-
 				/* else handle the error later */
 				break;
-			        }
-
-			/* connection was good */
-			break;
-		        }
-
-		/* bail out if necessary */
-		if(sigrestart==TRUE || sigshutdown==TRUE)
-			break;
-
-		/* child process should handle the connection */
-    		pid=fork();
-    		if(pid==0){
-
-			/* fork again so we don't create zombies */
-			pid=fork();
-			if(pid==0){
-
-				/* hey, there was an error... */
-				if(new_sd<0){
+			}
+			// }}}
+// {{{
+			/* child process should handle the connection */
+			if(pid=fork() == 0) {
 
-					/* log error to syslog facility */
-					syslog(LOG_ERR,"Network server accept failure (%d: %s)",errno,strerror(errno));
-
-					/* close socket prioer to exiting */
-					close(sock);
-			
-					return;
-				        }
+				/* fork again so we don't create zombies */
+				if(pid=fork() == 0) {
 
+// {{{
 				/* handle signals */
 				signal(SIGQUIT,child_sighandler);
 				signal(SIGTERM,child_sighandler);
 				signal(SIGHUP,child_sighandler);
 
-				/* grandchild does not need to listen for connections, so close the socket */
-				close(sock);  
+				/* grandchild does not need to listen for connections */
+				close_listen_socks();
 
 				/* find out who just connected... */
 				addrlen=sizeof(addr);
-				rc=getpeername(new_sd,&addr,&addrlen);
+				rc=getpeername(new_sd, (struct sockaddr *)&addr, &addrlen);
 
 				if(rc<0){
-
 				        /* log error to syslog facility */
 					syslog(LOG_ERR,"Error: Network server getpeername() failure (%d: %s)",errno,strerror(errno));
-
 				        /* close socket prior to exiting */
 					close(new_sd);
-
 					return;
-		                        }
+		                }
 
-				nptr=(struct sockaddr_in *)&addr;
+				if ((r = getnameinfo((struct sockaddr *)&addr, addrlen, ipstr,
+					sizeof(ipstr), NULL, 0, NI_NUMERICHOST)) != 0) {
+						syslog(LOG_ERR,"get_socket_address: getnameinfo %d failed: %s", NI_NUMERICHOST,
+                    					gai_strerror(r));
+						close(new_sd);
+                				return;
+				}
 
-				/* log info to syslog facility */
+// }}}
+// {{{
 				if(debug==TRUE)
-					syslog(LOG_DEBUG,"Connection from %s port %d",inet_ntoa(nptr->sin_addr),nptr->sin_port);
+					syslog(LOG_DEBUG,"Connection from %s",ipstr);
 
                                 /* is this is a blessed machine? */
 				if(allowed_hosts){
-
-					if(!is_an_allowed_host(inet_ntoa(nptr->sin_addr))){
-
+					if(!is_an_allowed_host(ipstr)){
                                                /* log error to syslog facility */
-                                               syslog(LOG_ERR,"Host %s is not allowed to talk to us!",inet_ntoa(nptr->sin_addr));
-
+                                               syslog(LOG_ERR,"Host %s is not allowed to talk to us!",ipstr);
                                                /* log info to syslog facility */
 					       if(debug==TRUE)
-						       syslog(LOG_DEBUG,"Connection from %s closed.",inet_ntoa(nptr->sin_addr));
-
+						       syslog(LOG_DEBUG,"Connection from %s closed.",ipstr);
 					       /* close socket prior to exiting */
                                                close(new_sd);
-
 					       exit(STATE_OK);
-				               }
-                                       else{
-
-                                               /* log info to syslog facility */
+				       } else {
                                                if(debug==TRUE)
                                                        syslog(LOG_DEBUG,"Host address is in allowed_hosts");
-				               }
-				        }
-
+				       }
+				}
+// }}}
+// {{{
 #ifdef HAVE_LIBWRAP
-
 				/* Check whether or not connections are allowed from this host */
 				request_init(&req,RQ_DAEMON,"nrpe",RQ_FILE,new_sd,0);
 				fromhost(&req);
@@ -912,41 +957,40 @@
 					exit(STATE_CRITICAL);
 					}
 #endif
+// }}}
 
-				/* handle the client connection */
-				handle_connection(new_sd);
-
-				/* log info to syslog facility */
-				if(debug==TRUE)
-					syslog(LOG_DEBUG,"Connection from %s closed.",inet_ntoa(nptr->sin_addr));
-
-				/* close socket prior to exiting */
+					/* handle the client connection */
+					handle_connection(new_sd);
+				
+					/* log info to syslog facility */
+					if(debug==TRUE)
+						syslog(LOG_DEBUG,"Connection from %s closed.",ipstr);
+				
+					/* close socket prior to exiting */
+					close(new_sd);
+				
+					exit(STATE_OK);
+    			 	} else {
+					/* first child returns immediately, grandchild is inherited by INIT process -> no zombies... */
+					exit(STATE_OK);
+				}
+		        } else {
+				/* parent doesn't need the new connection */
 				close(new_sd);
+				/* parent waits for first child to exit */
+				waitpid(pid,NULL,0);
+			}
+// }}}
+		}
+	}
 
-				exit(STATE_OK);
-    			        }
-
-			/* first child returns immediately, grandchild is inherited by INIT process -> no zombies... */
-			else
-				exit(STATE_OK);
-		        }
-		
-		/* parent ... */
-		else{
-			/* parent doesn't need the new connection */
-			close(new_sd);
-
-			/* parent waits for first child to exit */
-			waitpid(pid,NULL,0);
-		        }
-  		}
-
-	/* close the socket we're listening on */
-	close(sock);
+	/* close the sockets we're listening on */
+	close_listen_socks();
+        freeaddrinfo(listen_addrs);
+	listen_addrs=NULL;
 
 	return;
-	}
-
+}
 
 
 /* checks to see if a given host is allowed to talk to us */
@@ -958,6 +1002,9 @@
 	char **pptr=NULL;
 	char *save_connecting_host=NULL;
 	struct in_addr addr;
+        int gaierr;
+        struct addrinfo hints, *res, *ai;
+
 	
 	/* make sure we have something */
 	if(connecting_host==NULL)
@@ -987,18 +1034,25 @@
 		save_connecting_host=strdup(connecting_host);
 		for(temp_ptr=strtok(temp_buffer,",");temp_ptr!=NULL;temp_ptr=strtok(NULL,",")){
 
-			myhost=gethostbyname(temp_ptr);
-			if(myhost!=NULL){
+			memset(&hints, 0, sizeof(hints));
+			hints.ai_family = AF_UNSPEC;
+			hints.ai_flags = AI_PASSIVE;
+			if ( ( gaierr = getaddrinfo(temp_ptr, NULL, &hints, &ai) ) != 0) {
+				break;
+			}
 
-				/* check all addresses for the host... */
-				for(pptr=myhost->h_addr_list;*pptr!=NULL;pptr++){
-					memcpy(&addr, *pptr, sizeof(addr));
-					if(!strcmp(save_connecting_host, inet_ntoa(addr))){
-						result=1;
-						break;
-					        }
-				        }
-			        }
+			/* loop over all returned results and do inverse lookup */
+			for (res = ai; res != NULL; res = res->ai_next) {
+				char hostname[NI_MAXHOST] = "";
+				if ( (gaierr = getnameinfo(res->ai_addr, res->ai_addrlen,
+					hostname, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) != 0 ) {
+					continue;
+				}
+				if (*hostname != '\0' && !strcmp(save_connecting_host, hostname)){
+							result=1;
+							break;
+				}
+			}
 
 			if(result==1)
 				break;
@@ -1006,6 +1060,7 @@
 
 		strcpy(connecting_host, save_connecting_host);
 		free(save_connecting_host);
+		freeaddrinfo(ai);
 	        }
 
 	free(temp_buffer);
@@ -1902,6 +1957,8 @@
 	static struct option long_options[]={
 		{"config", required_argument, 0, 'c'},
 		{"inetd", no_argument, 0, 'i'},
+		{"4", no_argument, 0, '4'},
+		{"6", no_argument, 0, '4'},
 		{"daemon", no_argument, 0, 'd'},
 		{"no-ssl", no_argument, 0, 'n'},
 		{"help", no_argument, 0, 'h'},
@@ -1914,7 +1971,7 @@
 	if(argc<2)
 		return ERROR;
 
-	snprintf(optchars,MAX_INPUT_BUFFER,"c:hVldin");
+	snprintf(optchars,MAX_INPUT_BUFFER,"c:hVldi46n");
 
 	while(1){
 #ifdef HAVE_GETOPT_LONG
@@ -1950,6 +2007,12 @@
 			use_inetd=TRUE;
 			have_mode=TRUE;
 			break;
+		case '4':
+			address_family=AF_INET;
+			break;
+		case '6':
+			address_family=AF_INET6;
+			break;
 		case 'n':
 			use_ssl=FALSE;
 			break;
diff -ruN nrpe-2.12.org/src/utils.c nrpe-2.12/src/utils.c
--- nrpe-2.12.org/src/utils.c	2006-12-12 03:04:01.000000000 +0100
+++ nrpe-2.12/src/utils.c	2011-04-13 16:38:23.846594079 +0200
@@ -29,6 +29,10 @@
  *
  ****************************************************************************/
 
+#include "sys/types.h"
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
 #include "../include/common.h"
 #include "../include/utils.h"
 
@@ -109,177 +113,134 @@
         }
 
 
-/* opens a connection to a remote host/tcp port */
-int my_tcp_connect(char *host_name,int port,int *sd){
-	int result;
-
-	result=my_connect(host_name,port,sd,"tcp");
-
-	return result;
-        }
-
-
-/* opens a tcp or udp connection to a remote host */
-int my_connect(char *host_name,int port,int *sd,char *proto){
-	struct sockaddr_in servaddr;
-	struct hostent *hp;
-	struct protoent *ptrp;
-	int result;
-
-	bzero((char *)&servaddr,sizeof(servaddr));
-	servaddr.sin_family=AF_INET;
-	servaddr.sin_port=htons(port);
-
-	/* try to bypass using a DNS lookup if this is just an IP address */
-	if(!my_inet_aton(host_name,&servaddr.sin_addr)){
-
-		/* else do a DNS lookup */
-		hp=gethostbyname((const char *)host_name);
-		if(hp==NULL){
-			printf("Invalid host name '%s'\n",host_name);
-			return STATE_UNKNOWN;
-		        }
-
-		memcpy(&servaddr.sin_addr,hp->h_addr,hp->h_length);
-	        }
+#ifndef NI_MAXSERV
+# define NI_MAXSERV 32
+#endif
+
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif
+
+/* opens a connection to a remote host */
+int my_connect(const char *host, struct sockaddr_storage * hostaddr, u_short port, int address_family, const char *bind_address){
+        int gaierr;
+        int sock = -1;
+        char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+        struct addrinfo hints, *ai, *aitop;
+
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_family = address_family;
+        hints.ai_socktype = SOCK_STREAM;
+        snprintf(strport, sizeof strport, "%u", port);
+        if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
+                fprintf(stderr,"Could not resolve hostname %.100s: %s\n", 
+                    host, gai_strerror(gaierr));
+		exit(1);
+	}
 
-	/* map transport protocol name to protocol number */
-	if(((ptrp=getprotobyname(proto)))==NULL){
-		printf("Cannot map \"%s\" to protocol number\n",proto);
-		return STATE_UNKNOWN;
-	        }
+	/*
+	* Loop through addresses for this host, and try each one in
+	* sequence until the connection succeeds.
+	*/
+	for (ai = aitop; ai; ai = ai->ai_next) {
+		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+			continue;
+		if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
+			ntop, sizeof(ntop), strport, sizeof(strport),
+			NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
+				fprintf(stderr, "my_connect: getnameinfo failed\n");
+				continue;
+		}
 
-	/* create a socket */
-	*sd=socket(PF_INET,(!strcmp(proto,"udp"))?SOCK_DGRAM:SOCK_STREAM,ptrp->p_proto);
-	if(*sd<0){
-		printf("Socket creation failed\n");
-		return STATE_UNKNOWN;
-	        }
+		/* Create a socket for connecting. */
+		sock = my_create_socket(ai, bind_address);
+		if (sock < 0)
+			/* Any error is already output */
+			continue;
 
-	/* open a connection */
-	result=connect(*sd,(struct sockaddr *)&servaddr,sizeof(servaddr));
-	if(result<0){
-		switch(errno){  
-		case ECONNREFUSED:
-			printf("Connection refused by host\n");
-			break;
-		case ETIMEDOUT:
-			printf("Timeout while attempting connection\n");
-			break;
-		case ENETUNREACH:
-			printf("Network is unreachable\n");
+		if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
+			/* Successful connection. */
+			memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
 			break;
-		default:
-			printf("Connection refused or timed out\n");
-		        }
-
-		return STATE_CRITICAL;
-	        }
-
-	return STATE_OK;
-        }
-
-
-
-/* This code was taken from Fyodor's nmap utility, which was originally taken from
-   the GLIBC 2.0.6 libraries because Solaris doesn't contain the inet_aton() funtion. */
-int my_inet_aton(register const char *cp, struct in_addr *addr){
-	register unsigned int val;	/* changed from u_long --david */
-	register int base, n;
-	register char c;
-	u_int parts[4];
-	register u_int *pp = parts;
-
-	c=*cp;
-
-	for(;;){
-
-		/*
-		 * Collect number up to ``.''.
-		 * Values are specified as for C:
-		 * 0x=hex, 0=octal, isdigit=decimal.
-		 */
-		if (!isdigit((int)c))
-			return (0);
-		val=0;
-		base=10;
-
-		if(c=='0'){
-			c=*++cp;
-			if(c=='x'||c=='X')
-				base=16,c=*++cp;
-			else
-				base=8;
-		        }
-
-		for(;;){
-			if(isascii((int)c) && isdigit((int)c)){
-				val=(val*base)+(c -'0');
-				c=*++cp;
-			        } 
-			else if(base==16 && isascii((int)c) && isxdigit((int)c)){
-				val=(val<<4) | (c+10-(islower((int)c)?'a':'A'));
-				c = *++cp;
-			        } 
-			else
-				break;
-		        }
-
-		if(c=='.'){
+		} else {
+			fprintf(stderr,"connect to address %s port %s: %s\n",
+				ntop, strport, strerror(errno));
+			close(sock);
+			sock = -1;
+		}
+	}
 
-			/*
-			 * Internet format:
-			 *	a.b.c.d
-			 *	a.b.c	(with c treated as 16 bits)
-			 *	a.b	(with b treated as 24 bits)
-			 */
-			if(pp>=parts+3)
-				return (0);
-			*pp++=val;
-			c=*++cp;
-		        } 
-		else
-			break;
-	        }
+	freeaddrinfo(aitop);
 
-	/* Check for trailing characters */
-	if(c!='\0' && (!isascii((int)c) || !isspace((int)c)))
-		return (0);
-
-	/* Concoct the address according to the number of parts specified */
-	n=pp-parts+1;
-	switch(n){
-
-	case 0:
-		return (0);		/* initial nondigit */
-
-	case 1:				/* a -- 32 bits */
-		break;
-
-	case 2:				/* a.b -- 8.24 bits */
-		if(val>0xffffff)
-			return (0);
-		val|=parts[0]<<24;
-		break;
-
-	case 3:				/* a.b.c -- 8.8.16 bits */
-		if(val>0xffff)
-			return (0);
-		val|=(parts[0]<< 24) | (parts[1]<<16);
-		break;
-
-	case 4:				/* a.b.c.d -- 8.8.8.8 bits */
-		if(val>0xff)
-			return (0);
-		val|=(parts[0]<<24) | (parts[1]<<16) | (parts[2]<<8);
-		break;
-	        }
+        /* Return failure if we didn't get a successful connection. */
+        if (sock == -1) {
+		error("connect to host %s port %s: %s",
+			host, strport, strerror(errno));
+		return -1;
+	}
 
-	if(addr)
-		addr->s_addr=htonl(val);
+	return sock;
+}
 
-	return (1);
+/* Creates a socket for the connection. */
+int my_create_socket(struct addrinfo *ai, const char *bind_address)
+{
+        int sock, gaierr;
+        struct addrinfo hints, *res;
+
+        sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+        if (sock < 0)
+                fprintf(stderr,"socket: %.100s\n", strerror(errno));
+
+        /* Bind the socket to an alternative local IP address */
+        if (bind_address == NULL)
+                return sock;
+
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_family = ai->ai_family;
+        hints.ai_socktype = ai->ai_socktype;
+        hints.ai_protocol = ai->ai_protocol;
+        hints.ai_flags = AI_PASSIVE;
+        gaierr = getaddrinfo(bind_address, NULL, &hints, &res);
+        if (gaierr) {
+                fprintf(stderr, "getaddrinfo: %s: %s\n", bind_address,
+                    gai_strerror(gaierr));
+                close(sock);
+                return -1;
+        }
+        if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
+                fprintf(stderr, "bind: %s: %s\n", bind_address, strerror(errno));
+                close(sock);
+                freeaddrinfo(res);
+                return -1;
         }
+        freeaddrinfo(res);
+        return sock;
+}
+
+void add_listen_addr(struct addrinfo **listen_addrs, int address_family, char *addr, int port)
+{
+        struct addrinfo hints, *ai, *aitop;
+        char strport[NI_MAXSERV];
+        int gaierr;
+
+        memset(&hints, 0, sizeof(hints));
+        hints.ai_family = address_family;
+        hints.ai_socktype = SOCK_STREAM;
+        hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0;
+        snprintf(strport, sizeof strport, "%d", port);
+        if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) {
+		syslog(LOG_ERR,"bad addr or host: %s (%s)\n",
+                    addr ? addr : "<NULL>",
+                    gai_strerror(gaierr));
+		exit(1);
+		}
+        for (ai = aitop; ai->ai_next; ai = ai->ai_next)
+                ;
+        ai->ai_next = *listen_addrs;
+        *listen_addrs = aitop;
+}
+
 
 
 void strip(char *buffer){
-------------- next part --------------
------------------------------------------------------------------------------
WhatsUp Gold - Download Free Network Management Software
The most intuitive, comprehensive, and cost-effective network 
management toolset available today.  Delivers lowest initial 
acquisition cost and overall TCO of any competing solution.
http://p.sf.net/sfu/whatsupgold-sd
-------------- next part --------------
_______________________________________________
Nagios-devel mailing list
Nagios-devel at lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/nagios-devel


More information about the Developers mailing list