Index: trunk/configure.ac
===================================================================
--- trunk/configure.ac	(revision 576)
+++ trunk/configure.ac	(revision 577)
@@ -12,5 +12,5 @@
 dnl start
 dnl
-AM_INIT_AUTOMAKE(samhain, 4.4.7)
+AM_INIT_AUTOMAKE(samhain, 4.4.8)
 AC_DEFINE([SAMHAIN], 1, [Application is samhain])
 AC_CANONICAL_HOST
Index: trunk/docs/Changelog
===================================================================
--- trunk/docs/Changelog	(revision 576)
+++ trunk/docs/Changelog	(revision 577)
@@ -1,3 +1,7 @@
-4.4.7:
+4.4.8:
+	* new server option Alias=alias@hostname (based on
+	patch by A. Hofland)
+
+4.4.7 (07-03-2022):
 	* fix compile error on MacOS
         * disable dnmalloc for gcc 11 (regexec does not work)
Index: trunk/include/sh_cat.h
===================================================================
--- trunk/include/sh_cat.h	(revision 576)
+++ trunk/include/sh_cat.h	(revision 577)
@@ -229,5 +229,6 @@
  MSG_TCP_EBGN,    
  		  
- MSG_TCP_CREG,    
+ MSG_TCP_CREG,
+ MSG_TCP_AREG,
  MSG_TCP_FAUTH,   
  MSG_TCP_TIMOUT,  
Index: trunk/include/sh_string.h
===================================================================
--- trunk/include/sh_string.h	(revision 576)
+++ trunk/include/sh_string.h	(revision 577)
@@ -68,4 +68,6 @@
  * lengths in 'lengths'.
  * A single delimiter will return two empty fields.
+ * The returned array is allocated memory, and its fields
+ * are modified parts of the 'line' parameter.
  */
 char ** split_array(char *line, unsigned int * nfields, 
@@ -78,4 +80,6 @@
  * An empty string will return zero fields.
  * If nfields < actual fields, last string will be remainder.
+ * The returned array is allocated memory, and its fields
+ * are modified parts of the 'line' parameter.
  */
 char ** split_array_ws(char *line, unsigned int * nfields, size_t * lengths);
Index: trunk/include/sh_xfer.h
===================================================================
--- trunk/include/sh_xfer.h	(revision 576)
+++ trunk/include/sh_xfer.h	(revision 577)
@@ -84,4 +84,8 @@
 void sh_xfer_html_write(void);
 
+/* register an alias
+ */
+int sh_xfer_register_alias (const char * str);
+
 /* register a client
  */
Index: trunk/src/sh_cat.c
===================================================================
--- trunk/src/sh_cat.c	(revision 576)
+++ trunk/src/sh_cat.c	(revision 577)
@@ -225,4 +225,5 @@
 
   { MSG_TCP_CREG,    SH_ERR_ALL,     TCP,   N_("msg=\"Registered %s, salt %s, verifier %s\"")},
+  { MSG_TCP_AREG,    SH_ERR_ALL,     TCP,   N_("msg=\"Registered %s, hostname %s\"")},
   { MSG_TCP_FAUTH,   SH_ERR_INFO,    TCP,   N_("msg=\"Force authentication\" host=\"%s\"")},
 
@@ -569,4 +570,5 @@
 
   { MSG_TCP_CREG,    SH_ERR_ALL,     TCP,   N_("msg=<Registered %s, salt %s, verifier %s>")},
+  { MSG_TCP_AREG,    SH_ERR_ALL,     TCP,   N_("msg=<Registered %s, hostname %s>")},
   { MSG_TCP_FAUTH,   SH_ERR_INFO,    TCP,   N_("msg=<Force authentication>, client=<%s>")},
 
Index: trunk/src/sh_readconf.c
===================================================================
--- trunk/src/sh_readconf.c	(revision 576)
+++ trunk/src/sh_readconf.c	(revision 577)
@@ -1148,4 +1148,6 @@
   { N_("setserverinterface"),  SH_SECTION_SRV,  SH_SECTION_MISC, 
     sh_xfer_set_interface },
+  { N_("alias"),               SH_SECTION_CLIENTS,           SH_SECTION_NONE, 
+    sh_xfer_register_alias },
   { N_("client"),              SH_SECTION_CLIENTS,           SH_SECTION_NONE, 
     sh_xfer_register_client },
Index: trunk/src/sh_xfer_server.c
===================================================================
--- trunk/src/sh_xfer_server.c	(revision 576)
+++ trunk/src/sh_xfer_server.c	(revision 577)
@@ -419,4 +419,9 @@
  */
 
+typedef struct client_alias {
+  char                  * alias;
+  char                  * hostname;
+} alias_t;
+
 #include "zAVLTree.h"
 
@@ -435,5 +440,5 @@
 
 /* Function to return the key for indexing
- * the argument 
+ * the argument (for the client list)
  */
 zAVLKey sh_avl_key (void const * arg)
@@ -444,4 +449,15 @@
 
 zAVLTree * all_clients = NULL;
+
+/* Function to return the key for indexing
+ * the argument (for the aliases list)
+ */
+zAVLKey sh_avl_alias (void const * arg)
+{
+  const alias_t * sa = (const alias_t *) arg;
+  return (zAVLKey) sa->alias;
+}
+
+zAVLTree * all_aliases = NULL;
 
 void sh_xfer_html_write()
@@ -470,5 +486,5 @@
 
 
-/* the destructor
+/* the destructor (client list item)
  */
 void free_client(void * inptr)
@@ -490,4 +506,98 @@
   SH_FREE(here);
   SL_RET0(_("free_client"));
+}
+
+/* the destructor (alias list item)
+ */
+void free_alias(void * inptr)
+{
+  alias_t * here;
+
+  SL_ENTER(_("free_alias"));
+  if (inptr == NULL)
+    SL_RET0(_("free_alias"));
+  else
+    here = (alias_t *) inptr;
+
+  if (here->alias != NULL)
+    SH_FREE(here->alias);
+  if (here->hostname != NULL)
+    SH_FREE(here->hostname);
+  SH_FREE(here);
+  SL_RET0(_("free_alias"));
+}
+
+int sh_xfer_register_alias (const char * str)
+{
+  alias_t    * newalias;
+  alias_t    * testalias;
+
+  const char * ptr;
+  int          sepnum  = 0;
+  int          sep     = 0;
+  register int i       = 0;
+  int          siz_str = 0;
+
+  SL_ENTER(_("sh_xfer_register_alias"));
+
+  ptr = str; 
+  while (*ptr) {
+    if (*ptr == '@' && sepnum < 1) 
+      { 
+	sep = i;
+	++sepnum;
+      } 
+    ++ptr; ++i; 
+  }
+
+  if (all_aliases == NULL)
+    {
+      all_aliases = zAVLAllocTree (sh_avl_alias, zAVL_KEY_STRING);
+      if (all_aliases == NULL) 
+	{
+	  (void) safe_logger (0, 0, NULL);
+	  aud__exit(FIL__, __LINE__, EXIT_FAILURE);
+	}
+    }
+
+  if ((sepnum == 1) && (sep > 0) && (i > (sep + 1)))
+    {
+      newalias                  = SH_ALLOC (sizeof(alias_t));
+      newalias->alias           = SH_ALLOC (sep+1);
+      newalias->hostname        = SH_ALLOC (sl_strlen(str)-sep);
+
+      /* truncate */
+      sl_strlcpy(newalias->alias,  &str[0],        sep+1);
+      sh_tolower(newalias->alias);
+
+      /* truncate */
+      sl_strlcpy(newalias->hostname,  &str[sep+1], sl_strlen(str)-sep);
+      sh_tolower(newalias->hostname);
+
+      testalias = (alias_t *) zAVLSearch (all_aliases, newalias->alias);
+
+      if (testalias != NULL)
+	{
+	  /* keep the alias but replace the hostname with the new one */
+	  SH_FREE(testalias->hostname);
+	  siz_str = strlen (newalias->hostname) + 1;
+	  testalias->hostname = SH_ALLOC (siz_str);
+	  sl_strlcpy(testalias->hostname, newalias->hostname, siz_str);
+	  
+	  free_alias(newalias);
+	  SL_RETURN( 0, _("sh_xfer_register_alias"));
+	}
+      else
+	{
+	  if (0 == zAVLInsert (all_aliases, newalias))
+	    {
+	      sh_error_handle((-1), FIL__, __LINE__, 0, MSG_TCP_AREG,
+			      newalias->alias, 
+			      newalias->hostname);
+	      SL_RETURN( 0, _("sh_xfer_register_alias"));
+	    }
+	}
+    }
+  SL_RETURN (-1, _("sh_xfer_register_alias"));
 }
 
@@ -1053,4 +1163,5 @@
 client_t * search_register(sh_conn_t * conn, int pos)
 {
+  alias_t  * this_alias;
   client_t * this_client;
   char       peer_ip[SH_IP_BUF];
@@ -1086,4 +1197,12 @@
 	{
 	  sl_strlcpy(peer_name, peer_ip, MAXHOSTNAMELEN + 1);
+	}
+      else
+	{
+	  this_alias = zAVLSearch(all_aliases, peer_name);
+	  if (this_alias)
+	    {
+	      sl_strlcpy(peer_name, this_alias->hostname, MAXHOSTNAMELEN + 1);
+	    }
 	}
 
@@ -3375,4 +3494,8 @@
 	    sh_xfer_mark_dead ();
 
+	    /* free the aliases list */
+	    zAVLFreeTree (all_aliases, free_alias);
+	    all_aliases = NULL;
+
 	    reset_count_dev_console();
 	    reset_count_dev_time();
Index: trunk/test/testrun_2g.sh
===================================================================
--- trunk/test/testrun_2g.sh	(revision 576)
+++ trunk/test/testrun_2g.sh	(revision 577)
@@ -204,5 +204,8 @@
 	UUID=$(uuidgen)
 	mv ./file.delta file.${SH_LOCALHOST}.${UUID}
-	cp file.${SH_LOCALHOST}.${UUID} "./file.${ALTHOST}.${UUID}"
+	if [ "x${SH_LOCALHOST}" != "x${ALTHOST}" ]
+	then
+	    cp file.${SH_LOCALHOST}.${UUID} "./file.${ALTHOST}.${UUID}"
+	fi
 	
 	#
@@ -223,4 +226,6 @@
 	fi
 
+	NHOSTS=1
+	
 	./yulectl -c "DELTA:${UUID}" ${SH_LOCALHOST}
 	if [ $? -ne 0 ]; then
@@ -229,12 +234,18 @@
 	    return 1
 	fi
-	./yulectl -c "DELTA:${UUID}" ${ALTHOST}
-	if [ $? -ne 0 ]; then
-	    [ -z "$verbose" ] || log_msg_fail "yulectl (2)";
-	    kill $PROC_S; kill $PROC_Y;
-	    return 1
-	fi
+
+	if [ "x${SH_LOCALHOST}" != "x${ALTHOST}" ]
+	then
+	    ./yulectl -c "DELTA:${UUID}" ${ALTHOST}
+	    NHOSTS=2
+	    if [ $? -ne 0 ]; then
+		[ -z "$verbose" ] || log_msg_fail "yulectl (2)";
+		kill $PROC_S; kill $PROC_Y;
+		return 1
+	    fi
+	fi
+	
 	NR=$( ./yulectl -c LIST | grep ${UUID} | grep -v grep | wc -l )
-	if [ $NR -ne 2 ]; then
+	if [ $NR -ne $NHOSTS ]; then
 	    [ -z "$verbose" ] || log_msg_fail "yulectl (3)";
 	    [ -z "$verbose" ] || ./yulectl -c LIST
@@ -249,5 +260,6 @@
 	done
 	#
-	NR=$( ./yulectl -c LIST | grep ${UUID} | grep -v grep | wc -l )
+	NR=$( ./yulectl -c LISTALL | grep ${UUID} | grep SENT | grep -v grep | wc -l )
+	# NR=$( ./yulectl -c LIST | grep ${UUID} | grep -v grep | wc -l )
 	if [ $NR -ne 1 ]; then
 	    [ -z "$verbose" ] || log_msg_fail "yulectl (4)";
@@ -330,5 +342,8 @@
 	else
 	    mv ./file.delta file.${SH_LOCALHOST}.${UUID}
-	    cp file.${SH_LOCALHOST}.${UUID} "./file.${ALTHOST}.${UUID}"
+	    if [ "x${SH_LOCALHOST}" != "x${ALTHOST}" ]
+	    then
+		cp file.${SH_LOCALHOST}.${UUID} "./file.${ALTHOST}.${UUID}"
+	    fi
 	fi
 	[ -z "$verbose" ] || log_msg_ok    "... DeltaDB copied as file.${SH_LOCALHOST}.${UUID} ...";
@@ -351,4 +366,6 @@
 	fi
 
+	NHOSTS=1
+	
 	./yulectl -c "DELTA:${UUID}" ${SH_LOCALHOST}
 	if [ $? -ne 0 ]; then
@@ -357,12 +374,18 @@
 	    return 1
 	fi
-	./yulectl -c "DELTA:${UUID}" ${ALTHOST}
-	if [ $? -ne 0 ]; then
-	    [ -z "$verbose" ] || log_msg_fail "yulectl (2)";
-	    kill $PROC_S; kill $PROC_Y;
-	    return 1
-	fi
+	
+	if [ "x${SH_LOCALHOST}" != "x${ALTHOST}" ]
+	then
+	    ./yulectl -c "DELTA:${UUID}" ${ALTHOST}
+	    NHOSTS=2
+	    if [ $? -ne 0 ]; then
+		[ -z "$verbose" ] || log_msg_fail "yulectl (2)";
+		kill $PROC_S; kill $PROC_Y;
+		return 1
+	    fi
+	fi
+	
 	NR=$( ./yulectl -c LIST | grep ${UUID} | grep -v grep | wc -l )
-	if [ $NR -ne 2 ]; then
+	if [ $NR -ne $NHOSTS ]; then
 	    [ -z "$verbose" ] || log_msg_fail "yulectl (3)";
 	    [ -z "$verbose" ] || ./yulectl -c LIST
@@ -378,12 +401,14 @@
 	done
 	#
-	NR=$( ./yulectl -c LIST | grep ${UUID} | grep -v grep | wc -l )
+	NR=$( ./yulectl -c LISTALL | grep ${UUID} | grep SENT | grep -v grep | wc -l )
 	if [ $NR -ne 1 ]; then
-	    [ -z "$verbose" ] || log_msg_fail "yulectl (4)";
+	    [ -z "$verbose" ] || log_msg_fail "yulectl (4): ${UUID}";
 	    [ -z "$verbose" ] || ./yulectl -c LISTALL
-	    kill $PROC_S; kill $PROC_Y;
-	    return 1
-	fi
-	[ -z "$verbose" ] || OLINE=$( ./yulectl -c LIST | grep ${UUID} )
+	    [ -z "$verbose" ] || echo "(now just LIST)"
+	    [ -z "$verbose" ] || ./yulectl -c LIST
+	    kill $PROC_S; kill $PROC_Y;
+	    return 1
+	fi
+	[ -z "$verbose" ] || OLINE=$( ./yulectl -c LISTALL | grep ${UUID} )
 	[ -z "$verbose" ] || echo "${OLINE}"
 
@@ -607,8 +632,11 @@
 	chmod 644 ./file.${SH_LOCALHOST}
 
-	cp    ./testrc_2       "./rc.${ALTHOST}"
-	cp    ./file.${SH_LOCALHOST} "./file.${ALTHOST}" 2>/dev/null
-	chmod 644 ./rc.${ALTHOST}
-	chmod 644 ./file.${ALTHOST}
+	if [ "x${SH_LOCALHOST}" != "x${ALTHOST}" ]
+	then
+	    cp    ./testrc_2       "./rc.${ALTHOST}"
+	    cp    ./file.${SH_LOCALHOST} "./file.${ALTHOST}" 2>/dev/null
+	    chmod 644 ./rc.${ALTHOST}
+	    chmod 644 ./file.${ALTHOST}
+	fi
 }
 
