Index: /trunk/docs/Changelog
===================================================================
--- /trunk/docs/Changelog	(revision 525)
+++ /trunk/docs/Changelog	(revision 526)
@@ -1,3 +1,7 @@
 4.2.2:
+	* fix bug with PortCheckSkip: for any given port, only first interface
+	specified in config is checked (reported by A. Hofland)
+	* fix PortCheck bug that occasionally causes spurious detections of
+	open ports (patch by A. Hofland)
 	* add success/failure message for closing baseline database at init
 
Index: /trunk/include/sh_ipvx.h
===================================================================
--- /trunk/include/sh_ipvx.h	(revision 525)
+++ /trunk/include/sh_ipvx.h	(revision 526)
@@ -48,5 +48,5 @@
 /* Get the port
  */
-int sh_ipvx_get_port(struct sockaddr * ss, int sa_family);
+int sh_ipvx_get_port(struct sh_sockaddr * ss);
 
 /* Save a sockaddress
Index: /trunk/src/sh_ipvx.c
===================================================================
--- /trunk/src/sh_ipvx.c	(revision 525)
+++ /trunk/src/sh_ipvx.c	(revision 526)
@@ -251,21 +251,20 @@
 }
 
-int sh_ipvx_get_port(struct sockaddr * sa, int sa_family)
+int sh_ipvx_get_port(struct sh_sockaddr * sa)
 {
   int port = 0;
 #if defined(USE_IPVX)
 
-  switch (sa_family)
+  switch (sa->ss_family)
     {
     case AF_INET:
-      port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+      port = ntohs((sa->sin).sin_port);
       break;
     case AF_INET6:
-      port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
-      break;
-    }
-#else
-  (void) sa_family;
-  port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+      port = ntohs((sa->sin6).sin6_port);
+      break;
+    }
+#else
+  port = ntohs((sa->sin).sin_port);
 #endif
   return port;
Index: /trunk/src/sh_portcheck.c
===================================================================
--- /trunk/src/sh_portcheck.c	(revision 525)
+++ /trunk/src/sh_portcheck.c	(revision 526)
@@ -46,5 +46,5 @@
 #include <fcntl.h>
 
-#define PORTCHK_VERSION "1.0"
+#define PORTCHK_VERSION "1.1"
 
 #if defined(TEST_ONLY) || (defined(SH_USE_PORTCHECK) && (defined (SH_WITH_CLIENT) || defined (SH_STANDALONE)))
@@ -140,7 +140,9 @@
 #define SH_PORTCHK_INTERVAL 300
 
-static int sh_portchk_check_udp = 1;
-static int sh_portchk_active    = 1;
-static int sh_portchk_interval  = SH_PORTCHK_INTERVAL;
+static int sh_portchk_check_udp  = 1;
+static int sh_portchk_active     = 1;
+static int sh_portchk_same_ports = 1;
+static int sh_portchk_transients = 1;
+static int sh_portchk_interval   = SH_PORTCHK_INTERVAL;
 
 static int sh_portchk_minport = -1;
@@ -156,4 +158,7 @@
 static struct sh_port * blacklist_udp = NULL;
 
+static struct sh_port * transient_tcp = NULL;
+static struct sh_port * transient_udp = NULL;
+
 SH_MUTEX_STATIC(mutex_port_check, PTHREAD_MUTEX_INITIALIZER);
 
@@ -196,4 +201,9 @@
  */
 static int sh_portchk_is_blacklisted(int port, struct sh_sockaddr * haddr, int proto);
+
+/* verify whether port/interface is transient (used as source port hence no check required)
+ */
+static int sh_portchk_is_transient(int port, struct sh_sockaddr * haddr, int proto);
+static int sh_portchk_transient(int port, struct sh_sockaddr * haddr, int proto);
 
 #ifndef TEST_ONLY
@@ -244,25 +254,35 @@
 
 
-static int sh_portchk_set_minport   (const char * str)
+static int sh_portchk_set_minport     (const char * str)
 {
   return sh_portchk_set_port_minmax (str, &sh_portchk_minport);
 }
 
-static int sh_portchk_set_maxport   (const char * str)
+static int sh_portchk_set_maxport     (const char * str)
 {
   return sh_portchk_set_port_minmax (str, &sh_portchk_maxport);
 }
 
-static int sh_portchk_set_active   (const char * str)
+static int sh_portchk_set_active     (const char * str)
 {
   return sh_util_flagval(str, &sh_portchk_active);
 }
 
-static int sh_portchk_set_udp      (const char * str)
+static int sh_portchk_set_udp        (const char * str)
 {
   return sh_util_flagval(str, &sh_portchk_check_udp);
 }
-
-static int sh_portchk_set_severity (const char * str)
+#if defined(SH_ALLOW_RESTORE)
+static int sh_portchk_set_transients (const char * str)
+{
+  return sh_util_flagval(str, &sh_portchk_transients);
+}
+
+static int sh_portchk_set_same_ports (const char * str)
+{
+  return sh_util_flagval(str, &sh_portchk_same_ports);
+}
+#endif
+static int sh_portchk_set_severity   (const char * str)
 {
   char tmp[32];
@@ -323,4 +343,14 @@
         sh_portchk_set_udp,
     },
+#if defined(SH_ALLOW_RESTORE)
+    {
+        N_("portchecktransients"),
+        sh_portchk_set_transients,
+    },
+    {
+        N_("portchecksameports"),
+        sh_portchk_set_same_ports,
+    },
+#endif
     {
         NULL,
@@ -534,4 +564,17 @@
       if (head->next)
 	sh_portchk_kill_blacklist (head->next);
+
+      SH_FREE(head->paddr);
+      SH_FREE(head);
+    }
+  return NULL;
+}
+  
+static struct sh_port * sh_portchk_kill_transient (struct sh_port * head)
+{
+  if (head)
+    {
+      if (head->next)
+	sh_portchk_kill_transient (head->next);
 
       SH_FREE(head->paddr);
@@ -647,4 +690,5 @@
   char str_addr[SH_IP_BUF];
 
+
   if (proto == IPPROTO_TCP)
     portlist = portlist_tcp;
@@ -911,8 +955,12 @@
 #ifndef TEST_ONLY
   char               errmsg[256];
-  int                nerr;
+  volatile int       nerr;
 #endif
   char errbuf[SH_ERRBUF_SIZE];
   char ipbuf[SH_IP_BUF];
+
+  struct sh_sockaddr	saddr;
+  socklen_t		slen  = 0;
+  volatile int		sport = 0;
 
   sh_ipvx_set_port(paddr, port);
@@ -941,66 +989,129 @@
   else
     {
-      do {
-	retval = send (fd, buf, 0, 0);
-      } while (retval < 0 && errno == EINTR);
-
-      if (retval == -1 && errno == ECONNREFUSED)
-	{
-	  sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
-	  if (portchk_debug)
-	    fprintf(stderr, _("check port_udp: %5d/udp on %15s established/time_wait\n"),
-		    port, ipbuf);
-	}
-      else 
-	{
-	  /* Only the second send() may catch the error 
-	   */
-	  do {
+      /* Register the used source port as transient. This will avoid
+       * the issue of lingering source ports being reported as a spurious
+       * service. The lingering source port is effectvely a race condition.
+       *
+       * Also use this code to obtain the source port. Sometimes Samhain
+       * reports on a port where it connects back to itself. In that case
+       * source and destination port are the same.
+       *
+       * AGH, 23 Apr 2017 (www.2024sight.com).
+       */
+
+#if defined(USE_IPVX)
+      if (paddr->ss_family == AF_INET)
+        {
+          saddr.ss_family = AF_INET;
+          slen = sizeof( struct sockaddr_in );
+          retval = getsockname(fd, (struct sockaddr *)&(saddr.sin), &slen);
+        }
+      else
+        {
+          saddr.ss_family = AF_INET6;
+          slen = sizeof( struct sockaddr_in6 );
+          retval = getsockname(fd, (struct sockaddr *)&(saddr.sin6), &slen);
+        }
+#else
+      saddr.ss_family = AF_INET;
+      slen = sizeof( struct sockaddr_in );
+      retval = getsockname(fd, (struct sockaddr *)&(saddr.sin), &slen);
+#endif
+
+      if ( retval == 0 )
+        {
+          sport = sh_ipvx_get_port(&saddr);
+          sh_portchk_transient(sport, &saddr, IPPROTO_UDP);
+        }
+      else
+        {
+#ifdef TEST_ONLY
+          if (portchk_debug)
+            perror(_("getsockname"));
+#else
+            sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
+            nerr = errno;
+            sl_snprintf(errmsg, sizeof(errmsg), _("source port transient for %15s:%d/udp: %s"),
+		            ipbuf, port, sh_error_message(errno, errbuf, sizeof(errbuf)));
+            SH_MUTEX_LOCK(mutex_thread_nolog);
+            sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN, errmsg, _("getsockname"));
+            SH_MUTEX_UNLOCK(mutex_thread_nolog);
+#endif
+        }
+
+      if (( sport != port ) || ( sh_portchk_same_ports == S_FALSE ))
+        {
+          do {
 	    retval = send (fd, buf, 0, 0);
-	  } while (retval < 0 && errno == EINTR);
-
-	  if (retval == -1 && errno == ECONNREFUSED)
+          } while (retval < 0 && errno == EINTR);
+
+          if (retval == -1 && errno == ECONNREFUSED)
 	    {
 	      sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
 	      if (portchk_debug)
-		fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"),
-			port, ipbuf);
+	        fprintf(stderr, _("check port_udp: %5d/udp on %15s established/time_wait\n"),
+		        port, ipbuf);
 	    }
-	  else if (retval != -1)
+          else 
 	    {
-	      /* Try to get service name from portmap
+	      /* Only the second send() may catch the error 
 	       */
-	      if (paddr->ss_family == AF_INET)
-		{
-		  p = check_rpc_list (port, 
-				      (struct sockaddr_in *) sh_ipvx_sockaddr_cast(paddr), 
-				      IPPROTO_UDP);
-		}
-
-	      sh_portchk_cmp_to_list (IPPROTO_UDP, port, paddr, p ? p : NULL);
+	      do {
+	        retval = send (fd, buf, 0, 0);
+	      } while (retval < 0 && errno == EINTR);
+
+	      if (retval == -1 && errno == ECONNREFUSED)
+	        {
+	          sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
+	          if (portchk_debug)
+		    fprintf(stderr, _("check port: %5d/udp on %15s established/time_wait\n"),
+			    port, ipbuf);
+	        }
+	      else if (retval != -1)
+	        {
+	          /* Try to get service name from portmap
+	           */
+	          if (paddr->ss_family == AF_INET)
+		    {
+		      p = check_rpc_list (port, 
+				          (struct sockaddr_in *) sh_ipvx_sockaddr_cast(paddr), 
+				          IPPROTO_UDP);
+		    }
+
+	          sh_portchk_cmp_to_list (IPPROTO_UDP, port, paddr, p ? p : NULL);
 	      
-	      /* If not an RPC service, try to get name from /etc/services
-	       */
-	      if (!p)
-		p = check_services(port, IPPROTO_UDP);
+	          /* If not an RPC service, try to get name from /etc/services
+	           */
+	          if (!p)
+		    p = check_services(port, IPPROTO_UDP);
 	      
-	      if (portchk_debug)
-		{
-		  sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
-		  fprintf(stderr, _("check port_udp: %5d/udp on %15s open %s\n"), 
-			  port, ipbuf, p);
-		}
+	          if (portchk_debug)
+		    {
+		      sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
+		      fprintf(stderr, _("check port_udp: %5d/udp on %15s open %s\n"), 
+			      port, ipbuf, p);
+		    }
 	      
+	        }
+	      else
+	        {
+	          if (portchk_debug)
+		    {
+		      sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
+		      fprintf(stderr, _("check port_udp: %5d/udp on %15s ERRNO %d\n"), 
+			      port, ipbuf, errno);
+		    }
+	        }
 	    }
-	  else
-	    {
-	      if (portchk_debug)
-		{
-		  sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
-		  fprintf(stderr, _("check port_udp: %5d/udp on %15s ERRNO %d\n"), 
-			  port, ipbuf, errno);
-		}
-	    }
-	}
+        }
+      else
+        {
+	  if (portchk_debug)
+            {
+              sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
+	      fprintf(stderr, _("check port_udp: %5d/udp on %15s same source and destination port\n"), 
+		      port, ipbuf);
+            }
+        }
     }
   sl_close_fd (FIL__, __LINE__, fd);
@@ -1010,13 +1121,17 @@
 static int check_port_tcp_internal (int fd, int port, struct sh_sockaddr * paddr)
 {
-  int                retval;
+  volatile int       retval;
   int                flags;
   char             * p = NULL;
 #ifndef TEST_ONLY
   char               errmsg[256];
-  int                nerr;
+  volatile int       nerr;
 #endif
   char errbuf[SH_ERRBUF_SIZE];
   char ipbuf[SH_IP_BUF];
+
+  struct sh_sockaddr	saddr;
+  socklen_t		slen  = 0;
+  volatile int		sport = 0;
 
   sh_ipvx_set_port(paddr, port);
@@ -1053,26 +1168,90 @@
   else
     {
-      /* Try to get service name from portmap
+      /* Register the used source port as transient. This will avoid
+       * the issue of lingering source ports being reported as a spurious
+       * service. The lingering source port is effectively a race condition.
+       *
+       * Also use this code to obtain the source port. Sometimes Samhain
+       * reports on a port where it connects back to itself. In that case
+       * source and destination port are the same.
+       *
+       * AGH, 23 Apr 2017 (www.2024sight.com).
        */
+
+#if defined(USE_IPVX)
       if (paddr->ss_family == AF_INET)
-	{
-	  p = check_rpc_list (port, 
-			      (struct sockaddr_in *) sh_ipvx_sockaddr_cast(paddr), 
-			      IPPROTO_TCP);
-	}
-
-      sh_portchk_cmp_to_list (IPPROTO_TCP, port, paddr, p ? p : NULL);
-
-      /* If not an RPC service, try to get name from /etc/services
-       */
-      if (!p)
-	p = check_services(port, IPPROTO_TCP);
-
-      if (portchk_debug)
-	{
-	  sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
-	  fprintf(stderr, _("check port_tcp: %5d on %15s open %s\n"), 
-		  port, ipbuf, p);
-	}
+        {
+          saddr.ss_family = AF_INET;
+          slen = sizeof( struct sockaddr_in );
+          retval = getsockname(fd, (struct sockaddr *)&(saddr.sin), &slen);
+        }
+      else
+        {
+          saddr.ss_family = AF_INET6;
+          slen = sizeof( struct sockaddr_in6 );
+          retval = getsockname(fd, (struct sockaddr *)&(saddr.sin6), &slen);
+        }
+#else
+      saddr.ss_family = AF_INET;
+      slen = sizeof( struct sockaddr_in );
+      retval = getsockname(fd, (struct sockaddr *)&(saddr.sin), &slen);
+#endif
+
+      if ( retval == 0 )
+        {
+          sport = sh_ipvx_get_port(&saddr);
+          sh_portchk_transient(sport, &saddr, IPPROTO_TCP);
+        }
+      else
+        {
+#ifdef TEST_ONLY
+          if (portchk_debug)
+	    perror(_("getsockname"));
+#else
+            sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
+            nerr = errno;
+            sl_snprintf(errmsg, sizeof(errmsg), _("source port transient for %15s:%d/tcp: %s"),
+		        ipbuf, port, sh_error_message(errno, errbuf, sizeof(errbuf)));
+            SH_MUTEX_LOCK(mutex_thread_nolog);
+            sh_error_handle((-1), FIL__, __LINE__, nerr, MSG_E_SUBGEN, 
+		            errmsg, _("getsockname"));
+            SH_MUTEX_UNLOCK(mutex_thread_nolog);
+#endif
+        }
+
+      if (( sport != port ) || ( sh_portchk_same_ports == S_FALSE ))
+        {
+          /* Try to get service name from portmap
+           */
+          if (paddr->ss_family == AF_INET)
+	    {
+	      p = check_rpc_list (port, 
+			          (struct sockaddr_in *) sh_ipvx_sockaddr_cast(paddr), 
+			          IPPROTO_TCP);
+	    }
+
+          sh_portchk_cmp_to_list (IPPROTO_TCP, port, paddr, p ? p : NULL);
+
+          /* If not an RPC service, try to get name from /etc/services
+           */
+          if (!p)
+	    p = check_services(port, IPPROTO_TCP);
+
+          if (portchk_debug)
+	    {
+	      sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
+	      fprintf(stderr, _("check port_tcp: %5d on %15s open %s\n"), 
+		      port, ipbuf, p);
+	    }
+        }
+      else
+        {
+	  if (portchk_debug)
+            {
+              sh_ipvx_ntoa(ipbuf, sizeof(ipbuf), paddr);
+	      fprintf(stderr, _("check port_udp: %5d/tcp on %15s same source and destination port\n"), 
+		      port, ipbuf);
+            }
+        }
 
 #if !defined(O_NONBLOCK)
@@ -1385,4 +1564,10 @@
 	}
 
+      if (0 != sh_portchk_is_transient(port, &paddr, protocol))
+	{
+	  ++i; 
+	  continue;
+	}
+
       if ((sock = socket(paddr.ss_family, type, protocol)) < 0 )
 	{
@@ -1574,4 +1759,10 @@
 	}
     }
+
+  if (protocol == IPPROTO_TCP)
+    transient_tcp=sh_portchk_kill_transient(transient_tcp);
+  else
+    transient_udp=sh_portchk_kill_transient(transient_udp);
+  
   return 0;
 }
@@ -1891,12 +2082,6 @@
   while (head)
     {
-      if (head->port == port)
-	{
-	  if (sh_ipvx_isany(head->paddr) || 
-	      0 == sh_ipvx_cmp(head->paddr, saddr))
-	    return 1;
-	  else
-	    return 0;
-	}
+      if (head->port == port && ( sh_ipvx_isany(head->paddr) ||  0 == sh_ipvx_cmp(head->paddr, saddr) ))
+	return 1;
       head = head->next;
     }
@@ -1944,5 +2129,73 @@
   return 0;
 }
+
+
+/* verify whether port/interface is transient (used as source port 
+ *hence no check required)
+ */
+static int sh_portchk_is_transient(int port, struct sh_sockaddr * saddr, 
+				     int proto)
+{
+  struct sh_port * head;
   
+  if (proto == IPPROTO_TCP)
+    head = transient_tcp;
+  else
+    head = transient_udp;
+  
+  while (head)
+    {
+      if (head->port == port && ( sh_ipvx_isany(head->paddr) || 0 == sh_ipvx_cmp(head->paddr, saddr) ))
+	return 1;
+      head = head->next;
+    }
+  return 0;
+}
+
+
+static int sh_portchk_transient(int port, struct sh_sockaddr * saddr, int proto)
+{
+  struct sh_port * transient;
+  struct sh_port * head;
+
+  if (sh_portchk_transients == S_FALSE)
+    return 0;
+
+  if (proto == IPPROTO_TCP)
+    head = transient_tcp;
+  else
+    head = transient_udp;
+
+  transient = head;
+
+  while (transient)
+    {
+      if (transient->port == port && 
+	  0 == sh_ipvx_cmp(head->paddr, saddr))
+	return -1;
+      transient = transient->next;
+    }
+
+  transient = SH_ALLOC (sizeof(struct sh_port));
+  transient->paddr = SH_ALLOC (sizeof(struct sh_sockaddr));
+  transient->port  = port;
+  memcpy(transient->paddr, saddr, sizeof(struct sh_sockaddr));
+  transient->next  = head;
+
+  if (proto == IPPROTO_TCP)
+    transient_tcp = transient;
+  else
+    transient_udp = transient;
+
+  if (portchk_debug)
+    {
+      int checkit = sh_portchk_is_transient(port, saddr, proto);
+      fprintf(stderr, _("port transient: %d %s\n"), port, 
+	      (checkit == 1) ? _("ok") : _("fail"));
+    }
+  return 0;
+}
+
+
 /* Subroutine to add a required or optional port/service
  */
