Eric Stanley 13 лет назад
Родитель
Сommit
3a19834c51
7 измененных файлов с 615 добавлено и 362 удалено
  1. 3 0
      Changelog
  2. 9 4
      include/acl.h
  3. 3 2
      include/utils.h
  4. 170 26
      src/acl.c
  5. 29 5
      src/check_nrpe.c
  6. 287 160
      src/nrpe.c
  7. 114 165
      src/utils.c

+ 3 - 0
Changelog

@@ -2,6 +2,9 @@
 NRPE Changelog
 **************
 
+2.15 - xx/xx/xxxx
+-----------------
+- Added support for IPv6 (Leo Baltus, Eric Stanley)
 
 2.14 - 12/21/2012
 -----------------

+ 9 - 4
include/acl.h

@@ -40,9 +40,12 @@
 #define CHAR_TO_NUMBER(c)   ((c) - '0')
 
 struct ip_acl {
-        struct in_addr  addr;
-        struct in_addr  mask;
-        struct ip_acl   *next;
+	int				family;
+	struct in_addr	addr;
+	struct in_addr	mask;
+	struct in6_addr	addr6;
+	struct in6_addr	mask6;
+	struct ip_acl   *next;
 };
 
 struct dns_acl {
@@ -57,8 +60,10 @@ static struct dns_acl *dns_acl_head, *dns_acl_prev;
 /* Functions */
 void parse_allowed_hosts(char *allowed_hosts);
 int add_ipv4_to_acl(char *ipv4);
+int add_ipv6_to_acl(char *ipv6);
 int add_domain_to_acl(char *domain);
-int is_an_allowed_host(struct in_addr);
+//int is_an_allowed_host(struct in_addr);
+int is_an_allowed_host(int, void *);
 unsigned int prefix_from_mask(struct in_addr mask);
 void show_acl_lists(void);
 

+ 3 - 2
include/utils.h

@@ -41,9 +41,10 @@ unsigned long calculate_crc32(char *, int);
 void randomize_buffer(char *,int);
 
 int my_tcp_connect(char *,int,int *);
-int my_connect(char *,int,int *,char *);
+int my_connect(const char *, struct sockaddr_storage *, u_short, int, 
+		const char *);
 
-int my_inet_aton(register const char *,struct in_addr *);
+void add_listen_addr(struct addrinfo **, int, char *, int);
 
 void strip(char *);
 

+ 170 - 26
src/acl.c

@@ -234,6 +234,7 @@ int add_ipv4_to_acl(char *ipv4) {
         }
 
         /* Save result in ACL ip list */
+        ip_acl_curr->family = AF_INET;
         ip_acl_curr->addr.s_addr = ip;
         ip_acl_curr->mask.s_addr = mask;
         ip_acl_curr->next = NULL;
@@ -247,6 +248,107 @@ int add_ipv4_to_acl(char *ipv4) {
         return 1;
 }
 
+/*
+ * Add IPv6 host or network to IP ACL. Host will be added to ACL only if 
+ * it has passed IPv6 format check.
+ *
+ */
+
+int add_ipv6_to_acl(char *ipv6) {
+	char	*ipv6tmp;
+	char 	*addrtok;
+	char	*addrsave;
+	struct in6_addr addr;
+	struct in6_addr mask;
+	int		maskval;
+	int		byte, bit;
+	int		nbytes = sizeof(mask.s6_addr) / sizeof(mask.s6_addr[0]);
+	int		x;
+	struct ip_acl	*ip_acl_curr;
+
+	/* Save temporary copy of ipv6 so we can use the original in error 
+		messages if needed */
+	ipv6tmp = strdup(ipv6);
+	if(NULL == ipv6tmp) {
+		syslog(LOG_ERR, "Memory allocation failed for copy of address: %s\n", 
+				ipv6);
+		return 0;
+		}
+
+	/* Parse the address itself */
+	addrtok = strtok_r(ipv6tmp, "/", &addrsave);
+	if(inet_pton(AF_INET6, addrtok, &addr) <= 0) {
+		syslog(LOG_ERR, "Invalid IPv6 address in ACL: %s\n", ipv6);
+		free(ipv6tmp);
+		return 0;
+		}
+
+	/* Check whether there is a netmask */
+	addrtok = strtok_r(NULL, "/", &addrsave);
+	if(NULL != addrtok) {
+		/* If so, build a netmask */
+
+		/* Get the number of bits in the mask */
+		maskval = atoi(addrtok);
+		if(maskval < 0 || maskval > 128) {
+			syslog(LOG_ERR, "Invalid IPv6 netmask in ACL: %s\n", ipv6);
+			free(ipv6tmp);
+			return 0;
+			}
+
+		/* Initialize to zero */
+		for(x = 0; x < nbytes; x++) {
+			mask.s6_addr[x] = 0;
+			}
+
+		/* Set mask based on mask bits */
+		byte = 0;
+		bit = 7;
+		while(maskval > 0) {
+			mask.s6_addr[byte] |= 1 << bit;
+			bit -= 1;
+			if(bit < 0) {
+				bit = 7;
+				byte++;
+				}
+			maskval--;
+			}
+		}
+	else {
+		/* Otherwise, this is a single address */
+		for(x = 0; x < nbytes; x++) {
+			mask.s6_addr[x] = 0xFF;
+			}
+		}
+
+	/* Add address to ip_acl list */
+	ip_acl_curr = malloc(sizeof(*ip_acl_curr));
+	if(NULL == ip_acl_curr) {
+		syslog(LOG_ERR, "Memory allocation failed for ACL: %s\n", ipv6);
+		return 0;
+		}
+
+	/* Save result in ACL ip list */
+	ip_acl_curr->family = AF_INET6;
+	for(x = 0; x < nbytes; x++) {
+		ip_acl_curr->addr6.s6_addr[x] = 
+				addr.s6_addr[x] & mask.s6_addr[x];
+		ip_acl_curr->mask6.s6_addr[x] = mask.s6_addr[x];
+		}
+	ip_acl_curr->next = NULL;
+
+	if(NULL == ip_acl_head) {
+		ip_acl_head = ip_acl_curr;
+		}
+	else {
+		ip_acl_prev->next = ip_acl_curr;
+		}
+	ip_acl_prev = ip_acl_curr;
+
+	free(ipv6tmp);
+	return 1;
+	}
+
 /*
  * Add domain to DNS ACL list
  * Domain will be added only if it has passed domain name check.
@@ -356,33 +458,74 @@ int add_domain_to_acl(char *domain) {
  * 0 - on failure
  */
 
-int is_an_allowed_host(struct in_addr host) {
-        struct ip_acl *ip_acl_curr = ip_acl_head;
-        struct dns_acl *dns_acl_curr = dns_acl_head;
-        struct in_addr addr;
-        struct hostent *he;
-
-        while (ip_acl_curr != NULL) {
-                if ( (host.s_addr & ip_acl_curr->mask.s_addr) == ip_acl_curr->addr.s_addr)
-                        return 1;
-
-                ip_acl_curr = ip_acl_curr->next;
+int is_an_allowed_host(int family, void *host) {
+	struct ip_acl *ip_acl_curr = ip_acl_head;
+	int		nbytes;
+	int		x;
+	struct dns_acl *dns_acl_curr = dns_acl_head;
+	struct in_addr addr;
+	struct in6_addr addr6;
+	struct hostent *he;
+
+	while (ip_acl_curr != NULL) {
+		if(ip_acl_curr->family == family) {
+			switch(ip_acl_curr->family) {
+			case AF_INET:
+				if((((struct in_addr *)host)->s_addr & 
+						ip_acl_curr->mask.s_addr) == 
+						ip_acl_curr->addr.s_addr) {
+					return 1;
+					}
+				break;
+			case AF_INET6:
+				nbytes = sizeof(ip_acl_curr->mask6.s6_addr) / 
+						sizeof(ip_acl_curr->mask6.s6_addr[0]);
+				for(x = 0; x < nbytes; x++) {
+					if((((struct in6_addr *)host)->s6_addr[x] & 
+							ip_acl_curr->mask6.s6_addr[x]) != 
+							ip_acl_curr->addr6.s6_addr[x]) {
+						break;
+						}
+					}
+				if(x == nbytes) { 
+					/* All bytes in host's address pass the netmask mask */
+					return 1;
+					}
+				break;
+				}
+			}
+		ip_acl_curr = ip_acl_curr->next;
         }
 
-        while(dns_acl_curr != NULL) {
-        he = gethostbyname(dns_acl_curr->domain);
-        if (he == NULL)
-                        return 0;
-
-                while (*he->h_addr_list) {
-                        memmove((char *)&addr,*he->h_addr_list++, sizeof(addr));
-                        if (addr.s_addr == host.s_addr)
-                                return 1;
-                }
-                dns_acl_curr = dns_acl_curr->next;
-        }
-        return 0;
-}
+	while(dns_acl_curr != NULL) {
+   		he = gethostbyname(dns_acl_curr->domain);
+		if (he == NULL) return 0;
+
+		while (*he->h_addr_list) {
+			switch(he->h_addrtype) {
+			case AF_INET:
+				memmove((char *)&addr,*he->h_addr_list++, sizeof(addr));
+				if (addr.s_addr == ((struct in_addr *)host)->s_addr) return 1;
+				break;
+			case AF_INET6:
+				memcpy((char *)&addr6, *he->h_addr_list++, sizeof(addr6));
+				for(x = 0; x < nbytes; x++) {
+					if(addr6.s6_addr[x] != 
+							((struct in6_addr *)host)->s6_addr[x]) {
+						break;
+						}
+					}
+				if(x == nbytes) { 
+					/* All bytes in host's address match the ACL */
+					return 1;
+					}
+				break;
+				}
+			}
+		dns_acl_curr = dns_acl_curr->next;
+		}
+	return 0;
+	}
 
 /* The trim() function takes a source string and copies it to the destination string,
  * stripped of leading and training whitespace. The destination string must be 
@@ -420,7 +563,8 @@ void parse_allowed_hosts(char *allowed_hosts) {
 		trimmed_tok = malloc( sizeof( char) * ( strlen( tok) + 1));
 		trim( tok, trimmed_tok);
 		if( strlen( trimmed_tok) > 0) {
-			if (!add_ipv4_to_acl(trimmed_tok) && !add_domain_to_acl(trimmed_tok)) {
+			if (!add_ipv4_to_acl(trimmed_tok) && !add_ipv6_to_acl(trimmed_tok) 
+					&& !add_domain_to_acl(trimmed_tok)) {
 				syslog(LOG_ERR,"Can't add to ACL this record (%s). Check allowed_hosts option!\n",trimmed_tok);
 			}
 		}

+ 29 - 5
src/check_nrpe.c

@@ -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,7 @@ int show_license=FALSE;
 int show_version=FALSE;
 
 #ifdef HAVE_SSL
-SSL_METHOD *meth;
+const SSL_METHOD *meth;
 SSL_CTX *ctx;
 SSL *ssl;
 int use_ssl=TRUE;
@@ -84,12 +87,15 @@ int main(int argc, char **argv){
 
 	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(" <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");
@@ -143,7 +149,13 @@ int main(int argc, char **argv){
 	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 +332,13 @@ int process_arguments(int argc, char **argv){
 	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 +351,7 @@ int process_arguments(int argc, char **argv){
 	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 +369,9 @@ int process_arguments(int argc, char **argv){
 		case 'h':
 			show_help=TRUE;
 			break;
+		case 'b':
+			bind_address=strdup(optarg);
+			break;
 		case 'V':
 			show_version=TRUE;
 			break;
@@ -385,6 +403,12 @@ int process_arguments(int argc, char **argv){
 		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;

+ 287 - 160
src/nrpe.c

@@ -40,7 +40,7 @@ int deny_severity=LOG_WARNING;
 #endif
 
 #ifdef HAVE_SSL
-SSL_METHOD *meth;
+const SSL_METHOD *meth;
 SSL_CTX *ctx;
 int use_ssl=TRUE;
 #else
@@ -50,6 +50,9 @@ int use_ssl=FALSE;
 #define DEFAULT_COMMAND_TIMEOUT	60			/* default timeout for execution of plugins */
 #define MAXFD                   64
 #define NASTY_METACHARS         "|`&><'\"\\[]{};"
+#define howmany(x,y)	(((x)+((y)-1))/(y))
+#define MAX_LISTEN_SOCKS        16
+
 
 char    *command_name=NULL;
 char    *macro_argv[MAX_COMMAND_ARGUMENTS];
@@ -57,7 +60,11 @@ char    *macro_argv[MAX_COMMAND_ARGUMENTS];
 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;
+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;
@@ -148,11 +155,13 @@ int main(int argc, char **argv){
 
 	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 operating modes:\n");  
 		printf("   -i          =    Run as a service under inetd or xinetd\n");
 		printf("   -d          =    Run as a standalone daemon\n");
@@ -314,7 +323,7 @@ int main(int argc, char **argv){
 		/* make sure we're not root */
 		check_privileges();
 
-		do{
+		do {
 
 			/* reset flags */
 			sigrestart=FALSE;
@@ -338,7 +347,7 @@ int main(int argc, char **argv){
 				        }
 			        }
 
-		        }while(sigrestart==TRUE && sigshutdown==FALSE);
+			} while(sigrestart==TRUE && sigshutdown==FALSE);
 
 		/* remove pid file */
 		remove_pid_file();
@@ -384,7 +393,7 @@ int main(int argc, char **argv){
 		/* make sure we're not root */
 		check_privileges();
 
-		do{
+		do {
 
 			/* reset flags */
 			sigrestart=FALSE;
@@ -408,7 +417,7 @@ int main(int argc, char **argv){
 				        }
 			        }
 	
-		        }while(sigrestart==TRUE && sigshutdown==FALSE);
+			} while(sigrestart==TRUE && sigshutdown==FALSE);
 
 		/* remove pid file */
 		remove_pid_file();
@@ -774,17 +783,106 @@ command *find_command(char *command_name){
         }
 
 
+/*
+ * 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--;
+		}
+	}
+
+/* Start listen on a particular port */
+void create_listener(struct addrinfo *ai) {
+
+	int ret;
+	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+	int listen_sock;
+	int flag=1;
+
+	if(ai->ai_family != AF_INET && ai->ai_family != AF_INET6) return;
+
+	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));
+		return;
+		}
+
+	/* 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));
+		return;
+		}
+
+	/* 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));
+		return;
+		}
+
+#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 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);
+		return;
+		}
+	listen_socks[num_listen_socks] = listen_sock;
+	num_listen_socks++;
+
+	/* 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);
+	}
 
 /* wait for incoming connection requests */
 void wait_for_connections(void){
+	struct addrinfo *ai;
+	fd_set *fdset=NULL;
+	int maxfd=0;
+	struct sockaddr_storage from;
+	socklen_t fromlen;
+	char ipstr[INET6_ADDRSTRLEN];
+	int i;
+	int r;
+
 	struct sockaddr_in myname;
 	struct sockaddr_in *nptr;
-	struct sockaddr addr;
+	struct sockaddr_in6 *nptr6;
+	struct sockaddr_storage addr;
 	int rc;
 	int sock, new_sd;
 	socklen_t addrlen;
 	pid_t pid;
-	int flag=1;
 	fd_set fdread;
 	struct timeval timeout;
 	int retval;
@@ -792,48 +890,17 @@ void wait_for_connections(void){
 	struct request_info req;
 #endif
 
-	/* create a socket for listening */
-	sock=socket(AF_INET,SOCK_STREAM,0);
+	add_listen_addr(&listen_addrs, address_family, 
+			(strcmp(server_address, "") == 0) ? NULL : server_address, 
+			server_port);
 
-	/* 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) {
+		create_listener(ai);
 		}
 
-	/* 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);
-                }
-
-	/* 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);
-	        }
-
-	/* 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);
+	if (!num_listen_socks) {
+   		syslog(LOG_ERR, "Cannot bind to any address.");
+		exit(1);
 		}
 
 	/* log warning about command arguments */
@@ -858,26 +925,33 @@ void wait_for_connections(void){
 	/* listen for connection requests - fork() if we get one */
 	while(1){
 
-		/* wait for a connection request */
-	        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 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);
+		if(fdset != NULL) free(fdset);
+		fdset = (fd_set *)calloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
 
-			/* bail out if necessary */
-			if(sigrestart==TRUE || sigshutdown==TRUE)
-				break;
+		for (i = 0; i < num_listen_socks; i++) FD_SET(listen_socks[i], fdset);
 
-			/* error */
-			if(retval<0)
-				continue;
+		/* 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){
@@ -900,144 +974,189 @@ void wait_for_connections(void){
 
 				/* 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 */
+			/* child process should handle the connection */
     		pid=fork();
-    		if(pid==0){
+    		if(pid==0) {
 
-			/* fork again so we don't create zombies */
-			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){
+					/* hey, there was an error... */
+					if(new_sd<0) {
 
-					/* log error to syslog facility */
-					syslog(LOG_ERR,"Network server accept failure (%d: %s)",errno,strerror(errno));
+						/* log error to syslog facility */
+						syslog(LOG_ERR, "Network server accept failure (%d: %s)",
+								errno, strerror(errno));
 
-					/* close socket prioer to exiting */
-					close(sock);
+						/* close socket prioer to exiting */
+						close(sock);
 			
-					return;
-				        }
+						return;
+						}
 
-				/* handle signals */
-				signal(SIGQUIT,child_sighandler);
-				signal(SIGTERM,child_sighandler);
-				signal(SIGHUP,child_sighandler);
+					/* 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);
+					/* find out who just connected... */
+					addrlen=sizeof(addr);
+					rc=getpeername(new_sd, (struct sockaddr *)&addr, &addrlen);
 
-				if(rc<0){
+					if(rc<0) {
 
 				        /* log error to syslog facility */
-					syslog(LOG_ERR,"Error: Network server getpeername() failure (%d: %s)",errno,strerror(errno));
+						syslog(LOG_ERR, "Error: Network server getpeername() failure (%d: %s)", 
+								errno, strerror(errno));
 
 				        /* close socket prior to exiting */
-					close(new_sd);
-
-					return;
-		                        }
+						close(new_sd);
+
+						return;
+						}
+
+					/* is this is a blessed machine? */
+					if(allowed_hosts) {
+						switch(addr.ss_family) {
+						case AF_INET:
+							nptr = (struct sockaddr_in *)&addr;
+
+							/* log info to syslog facility */
+							if(debug==TRUE) {
+								syslog(LOG_DEBUG, "Connection from %s port %d",
+										inet_ntoa(nptr->sin_addr), 
+										nptr->sin_port);
+								}
+							if(!is_an_allowed_host(AF_INET,
+									(void *)&(nptr->sin_addr))) {
+								/* log error to syslog facility */
+								syslog(LOG_ERR,
+										"Host %s is not allowed to talk to us!",
+										inet_ntoa(nptr->sin_addr));
 
-				nptr=(struct sockaddr_in *)&addr;
+								/* log info to syslog facility */
+								if ( debug==TRUE ) {
+									syslog(LOG_DEBUG, 
+											"Connection from %s closed.",
+											inet_ntoa(nptr->sin_addr));
+									}
 
-				/* log info to syslog facility */
-				if(debug==TRUE)
-					syslog(LOG_DEBUG,"Connection from %s port %d",inet_ntoa(nptr->sin_addr),nptr->sin_port);
+								/* close socket prior to exiting */
+								close(new_sd);
+								exit(STATE_OK);
+								}
+							else {
+								/* log info to syslog facility */
+								if(debug==TRUE) {
+									syslog(LOG_DEBUG,
+											"Host address is in allowed_hosts");
+									}
 
-				/* is this is a blessed machine? */
-				if(allowed_hosts){
-                	switch(nptr->sin_family) {
-                    	case AF_INET:
-                        	if(!is_an_allowed_host(nptr->sin_addr)) {
-                            	/* log error to syslog facility */
-                            	syslog(LOG_ERR,"Host %s is not allowed to talk to us!",inet_ntoa(nptr->sin_addr));
+								}
+							break;
+						case AF_INET6:
+							nptr6 = (struct sockaddr_in6 *)&addr;
+							if(inet_ntop(AF_INET6, 
+									(const void *)&(nptr6->sin6_addr), ipstr, 
+									sizeof(ipstr)) == NULL) {
+								strncpy(ipstr, "Unknown", sizeof(ipstr));
+								} 
+
+							/* log info to syslog facility */
+							if(debug==TRUE) {
+								syslog(LOG_DEBUG, "Connection from %s port %d",
+										ipstr, nptr6->sin6_port);
+								}
+							if(!is_an_allowed_host(AF_INET6, 
+									(void *)&(nptr6->sin6_addr))) {
+								/* log error to syslog facility */
+								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));
+								if ( debug==TRUE ) {
+									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 */
-									if(debug==TRUE)
-										syslog(LOG_DEBUG,"Host address is in allowed_hosts");
+								}
+							else {
+								/* log info to syslog facility */
+								if(debug==TRUE) {
+									syslog(LOG_DEBUG,
+											"Host address is in allowed_hosts");
+									}
 
 								}
 							break;
-                           
-						case AF_INET6:
-							syslog(LOG_DEBUG,"Connection from %s closed. We don't support AF_INET6 addreess family in ACL\n", inet_ntoa(nptr->sin_addr));
-							break;
-					}
-				}
+							}
+						}
 
 #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);
 
-				/* Check whether or not connections are allowed from this host */
-				request_init(&req,RQ_DAEMON,"nrpe",RQ_FILE,new_sd,0);
-				fromhost(&req);
-
-				if(!hosts_access(&req)){
+					if(!hosts_access(&req)){
 
-					syslog(LOG_DEBUG,"Connection refused by TCP wrapper");
+						syslog(LOG_DEBUG,"Connection refused by TCP wrapper");
 
-					/* refuse the connection */
-					refuse(&req);
-					close(new_sd);
+						/* refuse the connection */
+						refuse(&req);
+						close(new_sd);
 
-					/* should not be reached */
-					syslog(LOG_ERR,"libwrap refuse() returns!");
-					exit(STATE_CRITICAL);
-					}
+						/* should not be reached */
+						syslog(LOG_ERR,"libwrap refuse() returns!");
+						exit(STATE_CRITICAL);
+						}
 #endif
 
-				/* handle the client connection */
-				handle_connection(new_sd);
+					/* 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));
+					/* log info to syslog facility */
+					if(debug==TRUE) {
+						syslog(LOG_DEBUG,"Connection from %s closed.",ipstr);
+						}
 
-				/* close socket prior to exiting */
-				close(new_sd);
+					/* close socket prior to exiting */
+					close(new_sd);
 
-				exit(STATE_OK);
-    			        }
+					exit(STATE_OK);
+					}
 
-			/* first child returns immediately, grandchild is inherited by INIT process -> no zombies... */
-			else
-				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 ... */
+			else {
+				/* parent doesn't need the new connection */
+				close(new_sd);
 
-			/* parent waits for first child to exit */
-			waitpid(pid,NULL,0);
-		        }
-  		}
+				/* 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;
 	}
@@ -1962,6 +2081,8 @@ int process_arguments(int argc, char **argv){
 		{"inetd", no_argument, 0, 'i'},
 		/* To compatibility between short and long options but not used on AIX */
 		{"src", no_argument, 0, 's'},
+		{"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'},
@@ -1974,7 +2095,7 @@ int process_arguments(int argc, char **argv){
 	if(argc<2)
 		return ERROR;
 
-	snprintf(optchars,MAX_INPUT_BUFFER,"c:hVldins");
+	snprintf(optchars,MAX_INPUT_BUFFER,"c:hVldi46n");
 
 	while(1){
 #ifdef HAVE_GETOPT_LONG
@@ -2010,6 +2131,12 @@ int process_arguments(int argc, char **argv){
 			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;

+ 114 - 165
src/utils.c

@@ -32,6 +32,14 @@
 #include "../include/common.h"
 #include "../include/utils.h"
 
+#ifndef NI_MAXSERV
+#define NI_MAXSERV 32
+#endif
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+
 static unsigned long crc32_table[256];
 
 
@@ -109,179 +117,120 @@ void randomize_buffer(char *buffer,int buffer_size){
         }
 
 
-/* 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);
-	        }
-
-	/* map transport protocol name to protocol number */
-	if(((ptrp=getprotobyname(proto)))==NULL){
-		printf("Cannot map \"%s\" to protocol number\n",proto);
-		return STATE_UNKNOWN;
-	        }
-
-	/* 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;
-	        }
-
-	/* 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");
-			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;
+/* 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);
+		}
 
-		if(c=='0'){
-			c=*++cp;
-			if(c=='x'||c=='X')
-				base=16,c=*++cp;
-			else
-				base=8;
-		        }
+	/*
+	* 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;
+		}
 
-		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;
-		        }
+		/* Create a socket for connecting. */
+		sock = my_create_socket(ai, bind_address);
+		if (sock < 0) {
+			/* Any error is already output */
+			continue;
+			}
 
-		if(c=='.'){
-
-			/*
-			 * 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
+		if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
+			/* Successful connection. */
+			memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen);
 			break;
-	        }
+			}
+		else {
+			fprintf(stderr,"connect to address %s port %s: %s\n", ntop, strport, 
+					strerror(errno));
+			close(sock);
+			sock = -1;
+			}
+		}
 
-	/* 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;
-	        }
+	freeaddrinfo(aitop);
 
-	if(addr)
-		addr->s_addr=htonl(val);
+	/* 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;
+		}
+	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){
 	int x;
 	int index;