[Date Prev] [Date Index] [Date Next] [Thread Prev] [Thread Index] [Thread Next]

[PATCH] FreeIPMI support, multi masters and multi-line comments

Anton D. Kachalov mouse@yandex-team.ru
Mon, 13 Dec 2010 19:31:16 GMT


Good day.

I prepared a patch that introduces support for:

1. FreeIPMI with new config keywords: "username", "password", "interface" ("lanplus" value only) and "workaround" flags ("payloadsize" flag only). Currently tested with SM X8DTU-F and Asus Z8NR-D12.

2. Multi masters. To use the same configuration file across the numbers of conservers.

 3. Multi-line comments in a C-way: /* ... */

4. Basic support for `clientSSLSocket' to operate over secured channel with remote console.

--
Anton D. Kachalov

ITO, System Administrator

diff --git a/autologin/Makefile b/autologin/Makefile
index 9827045..b4ea74a 100644
diff --git a/compat.h b/compat.h
index fe49bf0..686f5e3 100644
--- a/compat.h
+++ b/compat.h
@@ -331,3 +331,7 @@ typedef int socklen_t;
 #if HAVE_DMALLOC
 #include <dmalloc.h>
 #endif
+
+#if HAVE_FREEIPMI
+#include <ipmiconsole.h>
+#endif
diff --git a/config.h.in b/config.h.in
index bac39de..6c99f1f 100644
--- a/config.h.in
+++ b/config.h.in
@@ -33,6 +33,9 @@
 /* have dmalloc support */
 #undef HAVE_DMALLOC
 
+/* have freeipmi support */
+#undef HAVE_FREEIPMI
+
 /* Define to 1 if you have the `getaudit' function. */
 #undef HAVE_GETAUDIT
 
@@ -315,6 +318,9 @@
 /* Define to the one symbol short name of this package. */
 #undef PACKAGE_TARNAME
 
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
 
diff --git a/configure b/configure
index f0a003a..6a54edb 100755
diff --git a/configure.in b/configure.in
index f4e667a..181cd61 100644
--- a/configure.in
+++ b/configure.in
@@ -15,6 +15,7 @@ dnl AH_TEMPLATE([HAVE_POSIX_REGCOMP], [have POSIX regcomp])
 AH_TEMPLATE([HAVE_PAM], [have PAM support])
 AH_TEMPLATE([HAVE_OPENSSL], [have openssl support])
 AH_TEMPLATE([HAVE_GSSAPI], [have gss-api support])
+AH_TEMPLATE([HAVE_FREEIPMI], [have freeipmi support])
 AH_TEMPLATE([STRIP_REALM], [retry username without @REALM with gss-api authentication])
 AH_TEMPLATE([HAVE_DMALLOC], [have dmalloc support])
 AH_TEMPLATE([HAVE_SA_LEN],[Defined if sa_len member exists in struct sockaddr])
@@ -578,6 +579,50 @@ AC_ARG_WITH(gssapi,
     fi]
 )
 
+cons_with_freeipmi="NO"
+AC_ARG_WITH(freeipmi,
+    AS_HELP_STRING([--with-freeipmi@<:@=PATH@:>@],
+	[Compile in FreeIPMI support]),
+    [if test "$withval" != "no"; then
+	if test "$withval" != "yes"; then
+	    FREEIPMICPPFLAGS="-I$withval/include"
+	    if test "$use_dash_r" != "yes"; then
+		FREEIPMILDFLAGS="-L$withval/lib"
+	    else
+		FREEIPMIDFLAGS="-L$withval/lib -R$withval/lib"
+	    fi
+	else
+	    FREEIPMICPPFLAGS=""
+	    FREEIPMILDFLAGS=""
+	fi
+
+	oCPPFLAGS="$CPPFLAGS"
+	oLDFLAGS="$LDFLAGS"
+	oLIBS="$LIBS"
+	have_freeipmi=no
+
+	CPPFLAGS="$CPPFLAGS $FREEIPMICPPFLAGS"
+	LDFLAGS="$LDFLAGS $FREEIPMILDFLAGS"
+
+	AC_CHECK_HEADER([ipmiconsole.h],
+	    [LIBS="$LIBS -lipmiconsole"
+	    AC_MSG_CHECKING(for freeipmi libraries -lipmiconsole)
+	    AC_TRY_LINK([#include <ipmiconsole.h>
+		],[ipmiconsole_ctx_fd(0)],
+		[AC_MSG_RESULT(yes)
+		cons_with_freeipmi="YES"
+		AC_DEFINE(HAVE_FREEIPMI)
+		have_freeipmi=yes],
+		[AC_MSG_RESULT(no)])],)
+
+	if test $have_freeipmi = no; then
+	    LIBS="$oLIBS"
+	    CPPFLAGS="$oCPPFLAGS"
+	    LDFLAGS="$oLDFLAGS"
+	fi
+    fi]
+)
+
 cons_with_dmalloc="NO"
 AC_ARG_WITH(dmalloc,
     AS_HELP_STRING([--with-dmalloc@<:@=PATH@:>@],
@@ -737,6 +782,7 @@ echo "  Unix domain sockets (--with-uds)       : $cons_with_uds"
 echo "         TCP wrappers (--with-libwrap)   : $cons_with_libwrap"
 echo "              OpenSSL (--with-openssl)   : $cons_with_openssl"
 echo "              GSS-API (--with-gssapi)    : $cons_with_gssapi"
+echo "             FreeIPMI (--with-freeipmi)  : $cons_with_freeipmi"
 if [ $cons_with_gssapi = "YES" ]; then
 echo "         strip @REALM (--with-striprealm): $cons_strip_realm"
 fi
diff --git a/conserver/consent.c b/conserver/consent.c
index ca3161d..65e90cf 100644
--- a/conserver/consent.c
+++ b/conserver/consent.c
@@ -339,6 +339,48 @@ StopInit(pCE)
     }
 }
 
+ipmiconsole_ctx_t
+#if PROTOTYPES
+IpmiSOLCreate(CONSENT *pCE)
+#else
+IpmiSOLCreate(pCE)
+    CONSENT *pCE;
+#endif
+{
+    ipmiconsole_ctx_t ctx;
+    struct ipmiconsole_ipmi_config ipmi;
+    struct ipmiconsole_protocol_config protocol;
+    struct ipmiconsole_engine_config engine;
+
+    if (ipmiconsole_engine_init(1, 0) < 0)
+	return 0;
+
+    ipmi.username = pCE->username;
+    ipmi.password = pCE->password;
+    ipmi.k_g = NULL;
+    ipmi.k_g_len = 0;
+    ipmi.privilege_level = -1;
+    ipmi.cipher_suite_id = -1;
+    ipmi.workaround_flags = pCE->ipmi_wrndflags;
+
+    protocol.session_timeout_len = -1;
+
+    protocol.retransmission_timeout_len = -1;
+    protocol.retransmission_backoff_count = -1;
+    protocol.keepalive_timeout_len = -1;
+    protocol.retransmission_keepalive_timeout_len = -1;
+    protocol.acceptable_packet_errors_count = -1;
+    protocol.maximum_retransmission_count = -1;
+
+    engine.engine_flags = 0;
+    engine.behavior_flags = 0;
+    engine.debug_flags = 0;
+
+    ctx = ipmiconsole_ctx_create(pCE->host, &ipmi, &protocol, &engine);
+
+    return ctx;
+}
+
 /* invoke the initcmd command */
 void
 #if PROTOTYPES
@@ -1032,6 +1074,62 @@ ConsInit(pCE)
 	    TtyDev(pCE);
 	    pCE->ioState = ISNORMAL;
 	    break;
+
+	case IPMI:
+	    switch (pCE->intftype) {
+#if HAVE_FREEIPMI
+		case IPMIF_LANPLUS:
+		    if (!(pCE->ipmi_ctx = IpmiSOLCreate(pCE))) {
+			Error("[%s] Could not create IPMI context: forcing down",
+			  pCE->server);
+			goto ipmi_sol_error;
+		    }
+
+		    if (ipmiconsole_engine_submit_block(pCE->ipmi_ctx) < 0) {
+			Error("[%s] Could not connect to IPMI host `%s': forcing down",
+			  pCE->server, pCE->host);
+			goto ipmi_sol_destroy;
+		    }
+
+		    if (ipmiconsole_ctx_status(pCE->ipmi_ctx) !=
+			IPMICONSOLE_CTX_STATUS_SOL_ESTABLISHED) {
+			Error("[%s] Could not establish SOL connection: forcing down",
+			  pCE->server);
+			goto ipmi_sol_destroy;
+		    }
+
+		    cofile = ipmiconsole_ctx_fd(pCE->ipmi_ctx);
+		    if (!SetFlags(cofile, O_NONBLOCK, 0)) {
+		        goto ipmi_sol_destroy;
+		    }
+
+		    if ((pCE->cofile =
+			 FileOpenFD(cofile, simpleFile)) == (CONSFILE *)0) {
+			Error("[%s] FileOpenFD(simpleFile) failed: forcing down",
+			  pCE->server);
+			goto ipmi_sol_destroy;
+		    }
+
+		    pCE->ioState = ISNORMAL;
+		    pCE->stateTimer = 0;
+		    pCE->fup = 1;
+		    break;
+ipmi_sol_destroy:
+		    ipmiconsole_ctx_destroy(pCE->ipmi_ctx);
+ipmi_sol_error:
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    return;
+#endif
+		default:
+		    Error("[%s] unknown IPMI interface type (%d): forcing down",
+			  pCE->server, pCE->intftype);
+		    ConsDown(pCE, FLAGTRUE, FLAGTRUE);
+		    return;
+	    }
+	    /*
+	    ipmi_intf_session_set_sol_escape_char(pCE->intf, SOL_ESCAPE_CHARACTER_DEFAULT);
+	    */
+	    break;
     }
 
     if (!pCE->fup) {
@@ -1050,6 +1148,9 @@ ConsInit(pCE)
 	    Verbose("[%s] port %hu on %s", pCE->server, pCE->netport,
 		    pCE->host);
 	    break;
+	case IPMI:
+	    Verbose("[%s] on %s", pCE->server);
+	    break;
 	case NOOP:
 	    Verbose("[%s] noop", pCE->server);
 	    break;
diff --git a/conserver/consent.h b/conserver/consent.h
index dec5f52..51e8cf6 100644
--- a/conserver/consent.h
+++ b/conserver/consent.h
@@ -57,9 +57,14 @@ typedef enum consType {
     EXEC,
     HOST,
     NOOP,
-    UDS
+    UDS,
+    IPMI
 } CONSTYPE;
 
+typedef enum ipmiIntf {
+    IPMIF_LANPLUS = 0
+} IPMIF;
+
 typedef struct names {
     char *name;
     struct names *next;
@@ -92,6 +97,16 @@ typedef struct consent {	/* console information                  */
 #if defined(CRTSCTS)
     FLAG crtscts;		/* use hardware flow control            */
 #endif
+    FLAG secured;
+#if HAVE_OPENSSL
+    SSL_CTX *ssl_ctx;
+#endif
+    /* type == IPMI */
+    IPMIF intftype;		/* IPMI interface type                  */
+    ipmiconsole_ctx_t ipmi_ctx; /* IPMI ctx                             */
+    int ipmi_wrndflags;		/* IPMI workaround flags                */
+    char *username;		/* Username to log as                   */
+    char *password;		/* Login Password                       */
     /* type == HOST */
     char *host;			/* hostname                             */
     unsigned short netport;	/* final port    | netport = portbase + */
diff --git a/conserver/cutil.c b/conserver/cutil.c
index 7a51aee..676dabf 100644
--- a/conserver/cutil.c
+++ b/conserver/cutil.c
@@ -860,6 +860,7 @@ FileUnopen(cfp)
 	    break;
 #if HAVE_OPENSSL
 	case SSLSocket:
+	case clientSSLSocket:
 	    retval = -1;
 	    break;
 #endif
@@ -1000,6 +1001,7 @@ FileClose(pcfp)
 	    break;
 #if HAVE_OPENSSL
 	case SSLSocket:
+	case clientSSLSocket:
 	    CONDDEBUG((2,
 		       "FileClose(): performing a SSL_shutdown() on fd %d",
 		       cfp->fd));
@@ -1084,6 +1086,7 @@ FileRead(cfp, buf, len)
 	    break;
 #if HAVE_OPENSSL
 	case SSLSocket:
+	case clientSSLSocket:
 	    if (cfp->waitForWrite == FLAGTRUE) {
 		cfp->waitForWrite = FLAGFALSE;
 		if (cfp->wbuf->used <= 1)
@@ -1283,6 +1286,7 @@ FileWrite(cfp, bufferonly, buf, len)
 	    break;
 #if HAVE_OPENSSL
 	case SSLSocket:
+	case clientSSLSocket:
 	    if (cfp->waitForRead == FLAGTRUE)
 		cfp->waitForRead = FLAGFALSE;
 	    while (len > 0) {
@@ -1781,6 +1785,7 @@ FileStat(cfp, buf)
 	    break;
 #if HAVE_OPENSSL
 	case SSLSocket:
+	case clientSSLSocket:
 	    retval = -1;
 	    break;
 #endif
@@ -1823,6 +1828,7 @@ FileSeek(cfp, offset, whence)
 	    break;
 #if HAVE_OPENSSL
 	case SSLSocket:
+	case clientSSLSocket:
 	    retval = -1;
 	    break;
 #endif
@@ -1863,6 +1869,7 @@ FileFDNum(cfp)
 	    break;
 #if HAVE_OPENSSL
 	case SSLSocket:
+	case clientSSLSocket:
 	    retval = cfp->fd;
 	    break;
 #endif
@@ -1908,6 +1915,8 @@ FileGetType(cfp)
 #if HAVE_OPENSSL
 	case SSLSocket:
 	    return SSLSocket;
+	case clientSSLSocket:
+	    return clientSSLSocket;
 #endif
 	default:
 	    return nothing;
@@ -2122,6 +2131,7 @@ FileSend(cfp, msg, len, flags)
 	    break;
 #if HAVE_OPENSSL
 	case SSLSocket:
+	case clientSSLSocket:
 	    retval = send(fdout, msg, len, flags);
 	    break;
 #endif
@@ -2390,9 +2400,38 @@ ProbeInterfaces(bindAddr)
 
 int
 #if PROTOTYPES
-IsMe(char *id)
+IsMe(char *ids)
 #else
-IsMe(id)
+IsMe(ids)
+    char *ids;
+#endif
+{
+    int   rc;
+    char *id;
+    char *str;
+
+    rc  = 0;
+    str = strdup(ids);
+    for (; ; str = NULL) {
+        id = strtok(str, ",");
+        if (id == NULL)
+            break;
+        if (*id == '\0')
+            continue;
+        if (IsMeOne(id)) {
+            rc = 1;
+            break;
+        }
+    }
+    free(str);
+    return rc;
+}
+
+int
+#if PROTOTYPES
+IsMeOne(char *id)
+#else
+IsMeOne(id)
     char *id;
 #endif
 {
@@ -2726,7 +2765,7 @@ GetWord(fp, line, spaceok, word)
 	    if (checkInc == -2)
 		checkInc = -1;
 	}
-	if (comment) {
+	if (comment == 1) {
 	    if (c == '\n')
 		comment = 0;
 	    if (checkInc >= 0) {
@@ -2762,6 +2801,20 @@ GetWord(fp, line, spaceok, word)
 		}
 	    }
 	    continue;
+	} else if (comment == 2) {
+	    if (c == '*') {
+		comment = 3;
+		continue;
+	    }
+	    comment = 0;
+	    BuildStringChar('/', word);
+	} else if (comment == 3) {
+	    if (c == '*')
+		comment = 4;
+	    continue;
+	} else if (comment == 4) {
+	    comment = c == '/' ? 0 : 3;
+	    continue;
 	}
 	if (backslash) {
 	    BuildStringChar(c, word);
@@ -2793,6 +2846,9 @@ GetWord(fp, line, spaceok, word)
 	    comment = 1;
 	    if (checkInc == -1)
 		checkInc = 0;
+	} else if (c == '/') {
+	    comment = 2;
+	    continue;
 	} else if (c == '"') {
 	    quote = 1;
 	    sawQuote = 1;
diff --git a/conserver/cutil.h b/conserver/cutil.h
index 51a429b..8476bc9 100644
--- a/conserver/cutil.h
+++ b/conserver/cutil.h
@@ -37,6 +37,7 @@ enum consFileType {
     simplePipe,
 #if HAVE_OPENSSL
     SSLSocket,
+    clientSSLSocket,
 #endif
     nothing
 };
@@ -192,6 +193,7 @@ extern FLAG FileSawQuoteGoto PARAMS((CONSFILE *));
 extern void Bye PARAMS((int));
 extern void DestroyDataStructures PARAMS((void));
 extern int IsMe PARAMS((char *));
+extern int IsMeOne PARAMS((char *));
 extern char *PruneSpace PARAMS((char *));
 extern int FileCanRead PARAMS((CONSFILE *, fd_set *, fd_set *));
 extern int FileCanWrite PARAMS((CONSFILE *, fd_set *, fd_set *));
diff --git a/conserver/group.c b/conserver/group.c
index e5d396f..017c78b 100644
--- a/conserver/group.c
+++ b/conserver/group.c
@@ -1866,6 +1866,68 @@ SendBreak(pCLServing, pCEServing, bt)
 #if HAVE_OPENSSL
 int
 #if PROTOTYPES
+AttemptClientSSL(CONSENT *pCE)
+#else
+AttemptClientSSL(pCE)
+    CONSENT *pCE;
+#endif
+{
+    int ret;
+    SSL *ssl;
+
+    if ((ssl = FileGetSSL(pCE->cofile)) == NULL) {
+	pCE->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+	if (pCE->ssl_ctx == (SSL_CTX *)0) {
+	    Error
+		("[%s] SSL_CTX_new() failed: forcing down",
+		     pCE->server);
+	    return 0;
+	}
+	SSL_CTX_set_default_verify_paths(pCE->ssl_ctx);
+
+	/* SSL_VERIFY_NONE instructs OpenSSL not to abort SSL_connect if the
+	   certificate is invalid.  We verify the certificate separately in
+	   ssl_check_certificate, which provides much better diagnostics
+	   than examining the error stack after a failed SSL_connect.  */
+	SSL_CTX_set_verify (pCE->ssl_ctx, SSL_VERIFY_NONE, NULL);
+
+	/* Since fd_write unconditionally assumes partial writes (and
+	   handles them correctly), allow them in OpenSSL.  */
+	SSL_CTX_set_mode(pCE->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+
+	/* The OpenSSL library can handle renegotiations automatically, so
+	   tell it to do so.  */
+	SSL_CTX_set_mode(pCE->ssl_ctx, SSL_MODE_AUTO_RETRY);
+
+	ssl = SSL_new(pCE->ssl_ctx);
+	if (ssl == (SSL *)0) {
+	    Error
+		("[%s] SSL_new() failed: forcing down",
+		     pCE->server);
+	    return 0;
+	}
+
+	FileSetSSL(pCE->cofile, ssl);
+	SSL_set_fd(ssl, FileFDNum(pCE->cofile));
+	SSL_set_connect_state(ssl);
+    }
+
+    if ((ret = SSL_connect(ssl)) <= 0) {
+        ret = SSL_get_error(ssl, ret);
+        if (ret != SSL_ERROR_WANT_READ && ret != SSL_ERROR_WANT_READ) {
+	    Error
+		("[%s] SSL_connect(%d): %d failed: forcing down",
+		     pCE->server, FileFDNum(pCE->cofile), ret);
+	    return 0;
+	}
+	return -ret;
+    }
+
+    return 1;
+}
+
+int
+#if PROTOTYPES
 AttemptSSL(CONSCLIENT *pCL)
 #else
 AttemptSSL(pCL)
@@ -2167,6 +2229,11 @@ CommandExamine(pGE, pCLServing, pCEServing, tyme, args)
 		b = pCE->baud->acrate;
 		p = pCE->parity->key[0];
 		break;
+	    case IPMI:
+		d = BuildTmpStringPrint("%s", pCE->host);
+		b = "IPMI";
+		p = ' ';
+		break;
 	    case HOST:
 		d = BuildTmpStringPrint("%s/%hu", pCE->host, pCE->netport);
 		b = "Netwk";
@@ -2357,6 +2424,11 @@ CommandInfo(pGE, pCLServing, pCEServing, tyme, args)
 			  (unsigned long)pCE->ipid, pCE->execSlave,
 			  FileFDNum(pCE->cofile));
 		break;
+	    case IPMI:
+		FilePrint(pCLServing->fd, FLAGTRUE, "!:%s,%hu",
+			  pCE->host,
+			  FileFDNum(pCE->cofile));
+		break;
 	    case HOST:
 		FilePrint(pCLServing->fd, FLAGTRUE, "!:%s,%hu,%s,%d:",
 			  pCE->host, pCE->netport,
@@ -2613,6 +2685,7 @@ DoConsoleRead(pCEServing)
 	FD_CLR(cofile, &winit);
 	return;
     }
+
     /* read terminal line */
     if ((nr =
 	 FileRead(pCEServing->cofile, acInOrig, sizeof(acInOrig))) < 0) {
@@ -4610,6 +4683,27 @@ Kiddie(pGE, sfd)
 			int flags = 0;
 			int cofile = FileFDNum(pCEServing->cofile);
 			slen = sizeof(flags);
+
+#if HAVE_OPENSSL
+			if (pCEServing->secured == FLAGTRUE) {
+			    int r;
+			    char buf[10];
+			    r = AttemptClientSSL(pCEServing);
+			    if (r == 0) {
+				Error
+				    ("[%s] AttemptClientSSL(%d) failed: forcing down",
+				     pCEServing->server, FileFDNum(pCEServing->cofile));
+				/* no ConsoleError() for same reason as above */
+				SendIWaitClientsMsg(pCEServing, "down]\r\n");
+				ConsDown(pCEServing, FLAGTRUE, FLAGTRUE);
+				break;
+			    } else if (r == -SSL_ERROR_WANT_READ || r == -SSL_ERROR_WANT_WRITE) {
+				/* Data not ready yet in NON_BLOCKing mode */
+				continue;
+			    }
+			}
+#endif
+
 			/* So, getsockopt seems to return -1 if there is
 			 * something interesting in SO_ERROR under
 			 * solaris...sheesh.  So, the error message has
diff --git a/conserver/group.h b/conserver/group.h
index 3bda519..e23d8c3 100644
--- a/conserver/group.h
+++ b/conserver/group.h
@@ -85,4 +85,5 @@ extern void ClientWantsWrite PARAMS((CONSCLIENT *));
 extern void SendIWaitClientsMsg PARAMS((CONSENT *, char *));
 #if HAVE_OPENSSL
 extern int AttemptSSL PARAMS((CONSCLIENT *));
+extern int AttemptClientSSL PARAMS((CONSENT *));
 #endif
diff --git a/conserver/main.c b/conserver/main.c
index 50cdf41..58a3134 100644
--- a/conserver/main.c
+++ b/conserver/main.c
@@ -945,6 +945,14 @@ DumpDataStructures()
 			       pCE->execuid, pCE->execgid));
 
 		    break;
+		case IPMI:
+		    CONDDEBUG((1,
+			       "DumpDataStructures():  server=%s, type=IPMI",
+			       EMPTYSTR(pCE->server)));
+		    CONDDEBUG((1,
+			       "DumpDataStructures():  host=%s",
+			       EMPTYSTR(pCE->host)));
+		    break;
 		case HOST:
 		    CONDDEBUG((1,
 			       "DumpDataStructures():  server=%s, type=HOST",
diff --git a/conserver/master.c b/conserver/master.c
index d719346..0526279 100644
--- a/conserver/master.c
+++ b/conserver/master.c
@@ -239,18 +239,58 @@ FindRemoteConsole(args)
     char *args;
 #endif
 {
-    REMOTE *pRC;
+    REMOTE  *pRC;
+    REMOTE  *pRChead;
+    REMOTE **ppRCcurr;
+    REMOTE  *pRCtemp;
     NAMES *name;
 
+    pRCtemp  = (REMOTE *)0;
+    pRChead  = (REMOTE *)0;
+    ppRCcurr = &pRCtemp;
     for (pRC = pRCList; (REMOTE *)0 != pRC; pRC = pRC->pRCnext) {
-	if (strcasecmp(args, pRC->rserver) == 0)
-	    return pRC;
+	if (strcasecmp(args, pRC->rserver) == 0) {
+	    if ((pRCtemp = (REMOTE *)malloc(sizeof(REMOTE)))
+		== (REMOTE *)0)
+		OutOfMem();
+	    memcpy(pRCtemp, pRC, sizeof(REMOTE));
+	    pRCtemp->pRCnext = (REMOTE *)0;
+	    *ppRCcurr = pRCtemp;
+	    if (pRChead == (REMOTE *)0)
+		pRChead = *ppRCcurr;
+	    ppRCcurr = &pRCtemp->pRCnext;
+	}
 	for (name = pRC->aliases; name != (NAMES *)0; name = name->next) {
-	    if (strcasecmp(args, name->name) == 0)
-		return pRC;
+	    if (strcasecmp(args, name->name) == 0) {
+		if ((pRCtemp = (REMOTE *)malloc(sizeof(REMOTE)))
+		    == (REMOTE *)0)
+		    OutOfMem();
+		memcpy(pRCtemp, pRC, sizeof(REMOTE));
+		pRCtemp->pRCnext = (REMOTE *)0;
+		*ppRCcurr = pRCtemp;
+		if (pRChead == (REMOTE *)0)
+		    pRChead = *ppRCcurr;
+		ppRCcurr = &pRCtemp->pRCnext;
+	    }
 	}
     }
-    return pRC;
+    return pRChead;
+}
+
+void
+#if PROTOTYPES
+FreeRemoteConsole(REMOTE *pRChead)
+#else
+FindRemoteConsole(pRChead)
+    REMOTE *pRChead;
+#endif
+{
+    while (pRChead != (REMOTE *)0) {
+	REMOTE *pRCtmp;
+	pRCtmp = pRChead->pRCnext;
+	free(pRChead);
+	pRChead = pRCtmp;
+    }
 }
 
 void
@@ -263,6 +303,7 @@ CommandCall(pCL, args)
 #endif
 {
     int found;
+    int found_to_free    = 0;
     REMOTE *pRC, *pRCFound;
     unsigned short prnum = 0;
     char *ambiguous = (char *)0;
@@ -290,6 +331,7 @@ CommandCall(pCL, args)
 	    ambiguous = BuildTmpString(", ");
 	    ++found;
 	    pRCFound = pRC;
+	    found_to_free = 1;
 	}
     }
     if (found == 0 && config->autocomplete == FLAGTRUE) {
@@ -363,8 +405,14 @@ CommandCall(pCL, args)
 			      "automatic redirection disabled - console on master `%s'\r\n",
 			      pRCFound->rhost);
 		} else {
-		    FilePrint(pCL->fd, FLAGFALSE, "@%s\r\n",
-			      pRCFound->rhost);
+		    REMOTE *pRCtmp = pRCFound;
+		    while (pRCtmp != (REMOTE *)0) {
+			FilePrint(pCL->fd, FLAGFALSE,
+				  ":@%s"+(pRCtmp == pRCFound ? 1 : 0),
+				  pRCtmp->rhost);
+			pRCtmp = pRCtmp->pRCnext;
+		    }
+		    FilePrint(pCL->fd, FLAGFALSE, "\r\n");
 		}
 	    } else {
 		FilePrint(pCL->fd, FLAGFALSE, "%hu\r\n", prnum);
@@ -380,6 +428,8 @@ CommandCall(pCL, args)
     }
     BuildTmpString((char *)0);	/* we're done - clean up */
     ambiguous = (char *)0;
+    if (pRCFound != (REMOTE *)0 && found_to_free)
+	FreeRemoteConsole(pRCFound);
 }
 
 void
diff --git a/conserver/readcfg.c b/conserver/readcfg.c
index 32a76ae..3bbd863 100644
--- a/conserver/readcfg.c
+++ b/conserver/readcfg.c
@@ -1254,6 +1254,33 @@ DefaultItemInitrunas(id)
 
 void
 #if PROTOTYPES
+ProcessInterface(CONSENT *c, char *id)
+#else
+ProcessInterface(c, id)
+    CONSENT *c;
+    char *id;
+#endif
+{
+    if (!strcasecmp("lanplus", id))
+	c->intftype = IPMIF_LANPLUS;
+    else
+	Error("invalid interface type `%s' [%s:%d]", id, file, line);
+}
+
+void
+#if PROTOTYPES
+DefaultItemInterface(char *id)
+#else
+DefaultItemInterface(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "DefaultItemInterface(%s) [%s:%d]", id, file, line));
+    ProcessInterface(parserDefaultTemp, id);
+}
+
+void
+#if PROTOTYPES
 DefaultItemExecrunas(char *id)
 #else
 DefaultItemExecrunas(id)
@@ -1388,6 +1415,80 @@ DefaultItemUds(id)
 
 void
 #if PROTOTYPES
+ProcessUsername(CONSENT *c, char *id)
+#else
+ProcessUsername(c, id)
+    CONSENT *c;
+    char *id;
+#endif
+{
+    if ((id == (char *)0) || (*id == '\000')) {
+	c->username = (char *)0;
+	return;
+    }
+    c->username = strdup(id);
+}
+
+void
+#if PROTOTYPES
+DefaultItemUsername(char *id)
+#else
+DefaultItemUsername(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "DefaultItemUsername(%s) [%s:%d]", id, file, line));
+    ProcessUsername(parserDefaultTemp, id);
+}
+
+void
+#if PROTOTYPES
+ProcessWorkaround(CONSENT *c, char *id)
+#else
+ProcessWorkaround(c, id)
+    CONSENT *c;
+    char *id;
+#endif
+{
+    int flag;
+    char *token = (char *)0;
+
+    for (token = strtok(id, ALLWORDSEP); token != (char *)0;
+	 token = strtok(NULL, ALLWORDSEP)) {
+	short not;
+	if (token[0] == '!') {
+	    token++;
+	    not = 1;
+	} else
+	    not = 0;
+	flag = 0;
+	if (!strcmp(token, "payloadsize")) {
+	    flag = IPMICONSOLE_WORKAROUND_IGNORE_SOL_PAYLOAD_SIZE;
+	} else {
+	    continue;
+	}
+	if (not) {
+	    c->ipmi_wrndflags &= ~flag;
+	} else {
+	    c->ipmi_wrndflags |= flag;
+	}
+    }
+}
+
+void
+#if PROTOTYPES
+DefaultItemWorkaround(char *id)
+#else
+DefaultItemWorkaround(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "DefaultItemWorkaround(%s) [%s:%d]", id, file, line));
+    ProcessWorkaround(parserDefaultTemp, id);
+}
+
+void
+#if PROTOTYPES
 ProcessInclude(CONSENT *c, char *id)
 #else
 ProcessInclude(c, id)
@@ -1612,22 +1713,48 @@ DefaultItemIdlestring(id)
 
 void
 #if PROTOTYPES
-ProcessMaster(CONSENT *c, char *id)
+ProcessMaster(CONSENT *c, char *ids)
 #else
-ProcessMaster(c, id)
+ProcessMaster(c, ids)
     CONSENT *c;
-    char *id;
+    char *ids;
 #endif
 {
+    char *p;
+    char *d;
+    int   len;
+    int   is_delim;
+
     if (c->master != (char *)0) {
 	free(c->master);
 	c->master = (char *)0;
     }
-    if ((id == (char *)0) || (*id == '\000'))
+    if ((ids == (char *)0) || (*ids == '\000'))
 	return;
-    if ((c->master = StrDup(id))
+    if ((c->master = StrDup(ids))
 	== (char *)0)
 	OutOfMem();
+    /* Normalize master's hostnames */
+    d   = (char *)0;
+    p   = c->master;
+    len = strlen(c->master);
+    is_delim = 0;
+    while (*p != '\0') {
+	if (*p == ':' || *p == ',' || *p == ' ') {
+	    if (!is_delim) {
+		is_delim = 1;
+		d  = p;
+		*p = ',';
+	    }
+	} else if (is_delim) {
+	    memmove(d+1, p, len-(p-c->master)+1);
+	    is_delim = 0;
+	    len -= p-d-1;
+	    p = d;
+	}
+	p++;
+    }
+    fprintf(stderr, "Master [%s]\n", c->master);
 }
 
 void
@@ -1757,6 +1884,34 @@ DefaultItemParity(id)
 
 void
 #if PROTOTYPES
+ProcessPassword(CONSENT *c, char *id)
+#else
+ProcessPassword(c, id)
+    CONSENT *c;
+    char *id;
+#endif
+{
+    if ((id == (char *)0) || (*id == '\000')) {
+	c->password = (char *)0;
+	return;
+    }
+    c->password = strdup(id);
+}
+
+void
+#if PROTOTYPES
+DefaultItemPassword(char *id)
+#else
+DefaultItemPassword(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "DefaultItemPassword(%s) [%s:%d]", id, file, line));
+    ProcessPassword(parserDefaultTemp, id);
+}
+
+void
+#if PROTOTYPES
 ProcessPort(CONSENT *c, char *id)
 #else
 ProcessPort(c, id)
@@ -2163,6 +2318,37 @@ DefaultItemRw(id)
 
 void
 #if PROTOTYPES
+ProcessSecured(char *id, FLAG *flag)
+#else
+ProcessSecured(id, flag)
+    char *id;
+    FLAG *flag;
+#endif
+{
+    if (id == (char *)0 || id[0] == '\000')
+	*flag = FLAGFALSE;
+    else if (strcasecmp("yes", id) == 0 || strcasecmp("true", id) == 0 ||
+	     strcasecmp("on", id) == 0)
+	*flag = FLAGTRUE;
+    else if (strcasecmp("no", id) == 0 || strcasecmp("false", id) == 0 ||
+	     strcasecmp("off", id) == 0)
+	*flag = FLAGFALSE;
+}
+
+void
+#if PROTOTYPES
+DefaultItemSecured(char *id)
+#else
+DefaultItemSecured(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "DefaultItemSecured(%s) [%s:%d]", id, file, line));
+    ProcessSecured(id, &(parserDefaultTemp->secured));
+}
+
+void
+#if PROTOTYPES
 ProcessTimestamp(CONSENT *c, char *id)
 #else
 ProcessTimestamp(c, id)
@@ -2326,6 +2512,8 @@ ProcessType(c, id)
     }
     if (strcasecmp("device", id) == 0)
 	t = DEVICE;
+    else if (strcasecmp("ipmi", id) == 0)
+	t = IPMI;
     else if (strcasecmp("exec", id) == 0)
 	t = EXEC;
     else if (strcasecmp("host", id) == 0)
@@ -2484,6 +2672,14 @@ ConsoleEnd()
 		}
 	    }
 	    break;
+	case IPMI:
+	    if (parserConsoleTemp->host == (char *)0) {
+		if (isMaster)
+		    Error("[%s] console missing 'host' attribute [%s:%d]",
+			  parserConsoleTemp->server, file, line);
+		invalid = 1;
+	    }
+	    break;
 	case HOST:
 	    if (parserConsoleTemp->host == (char *)0) {
 		if (isMaster)
@@ -2516,8 +2712,8 @@ ConsoleEnd()
 	    break;
 	case UNKNOWNTYPE:
 	    if (isMaster)
-		Error("[%s] console type unknown [%s:%d]",
-		      parserConsoleTemp->server, file, line);
+		Error("[%s] console type unknown %d [%s:%d]",
+		      parserConsoleTemp->server, parserConsoleTemp->type, file, line);
 	    invalid = 1;
 	    break;
     }
@@ -2635,21 +2831,28 @@ ConsoleAdd(c)
     /* check for remote consoles */
     if (!IsMe(c->master)) {
 	if (isMaster) {
-	    REMOTE *pRCTemp;
-	    if ((pRCTemp = (REMOTE *)calloc(1, sizeof(REMOTE)))
-		== (REMOTE *)0)
-		OutOfMem();
-	    if ((pRCTemp->rhost = StrDup(c->master))
-		== (char *)0)
-		OutOfMem();
-	    if ((pRCTemp->rserver = StrDup(c->server))
-		== (char *)0)
-		OutOfMem();
-	    pRCTemp->aliases = c->aliases;
-	    c->aliases = (NAMES *)0;
-	    *ppRC = pRCTemp;
-	    ppRC = &pRCTemp->pRCnext;
-	    CONDDEBUG((1, "[%s] remote on %s", c->server, c->master));
+	    char *host;
+	    for (host = strtok(c->master, ",");
+		 host != (char *)0;
+		 host = strtok(NULL, ",")) {
+		REMOTE *pRCTemp;
+		if (IsMeOne(host))
+		    continue;
+		if ((pRCTemp = (REMOTE *)calloc(1, sizeof(REMOTE)))
+		    == (REMOTE *)0)
+		    OutOfMem();
+		if ((pRCTemp->rhost = StrDup(host))
+		    == (char *)0)
+		    OutOfMem();
+		if ((pRCTemp->rserver = StrDup(c->server))
+		    == (char *)0)
+		    OutOfMem();
+		pRCTemp->aliases = c->aliases;
+		c->aliases = (NAMES *)0;
+		*ppRC = pRCTemp;
+		ppRC = &pRCTemp->pRCnext;
+		CONDDEBUG((1, "[%s] remote on %s", c->server, pRCTemp->rhost));
+	    }
 	}
 	return;
     }
@@ -2859,6 +3062,7 @@ ConsoleAdd(c)
 	    if (!FileBufEmpty(pCEmatch->cofile))
 		FD_SET(cofile, &winit);
 	}
+
 	if (pCEmatch->initfile != (CONSFILE *)0) {
 	    int initfile = FileFDNum(pCEmatch->initfile);
 	    FD_SET(initfile, &rinit);
@@ -2987,6 +3191,30 @@ ConsoleAdd(c)
 		}
 #endif
 		break;
+	    case IPMI:
+		if (pCEmatch->host != (char *)0 && c->host != (char *)0) {
+		    if (strcasecmp(pCEmatch->host, c->host) != 0) {
+			SwapStr(&pCEmatch->host, &c->host);
+			closeMatch = 0;
+		    }
+		} else if (pCEmatch->host != (char *)0 ||
+			   c->host != (char *)0) {
+		    SwapStr(&pCEmatch->host, &c->host);
+		    closeMatch = 0;
+		} else if (pCEmatch->username != (char *)0 ||
+			   c->username != (char *)0) {
+		    SwapStr(&pCEmatch->username, &c->username);
+		    closeMatch = 0;
+		} else if (pCEmatch->password != (char *)0 ||
+			   c->password != (char *)0) {
+		    SwapStr(&pCEmatch->password, &c->password);
+		    closeMatch = 0;
+		}
+		if (pCEmatch->intftype != c->intftype) {
+		    pCEmatch->intftype = c->intftype;
+		    closeMatch = 0;
+		}
+		break;
 	    case HOST:
 		if (pCEmatch->host != (char *)0 && c->host != (char *)0) {
 		    if (strcasecmp(pCEmatch->host, c->host) != 0) {
@@ -3634,6 +3862,30 @@ ConsoleItemUds(id)
 
 void
 #if PROTOTYPES
+ConsoleItemUsername(char *id)
+#else
+ConsoleItemUsername(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "ConsoleItemUsername(%s) [%s:%d]", id, file, line));
+    ProcessUsername(parserConsoleTemp, id);
+}
+
+void
+#if PROTOTYPES
+ConsoleItemWorkaround(char *id)
+#else
+ConsoleItemWorkaround(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "ConsoleItemWorkaround(%s) [%s:%d]", id, file, line));
+    ProcessWorkaround(parserConsoleTemp, id);
+}
+
+void
+#if PROTOTYPES
 ConsoleItemInclude(char *id)
 #else
 ConsoleItemInclude(id)
@@ -3742,6 +3994,18 @@ ConsoleItemParity(id)
 
 void
 #if PROTOTYPES
+ConsoleItemPassword(char *id)
+#else
+ConsoleItemPassword(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "ConsoleItemPassword(%s) [%s:%d]", id, file, line));
+    ProcessPassword(parserConsoleTemp, id);
+}
+
+void
+#if PROTOTYPES
 ConsoleItemPort(char *id)
 #else
 ConsoleItemPort(id)
@@ -4571,6 +4835,30 @@ ProcessYesNo(id, flag)
 
 void
 #if PROTOTYPES
+ConsoleItemSecured(char *id)
+#else
+ConsoleItemSecured(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "ConsoleItemSecured(%s) [%s:%d]", id, file, line));
+    ProcessSecured(id, &(parserConsoleTemp->secured));
+}
+
+void
+#if PROTOTYPES
+ConsoleItemInterface(char *id)
+#else
+ConsoleItemInterface(id)
+    char *id;
+#endif
+{
+    CONDDEBUG((1, "ConsoleItemInterface(%s) [%s:%d]", id, file, line));
+    ProcessInterface(parserConsoleTemp, id);
+}
+
+void
+#if PROTOTYPES
 ConfigItemAutocomplete(char *id)
 #else
 ConfigItemAutocomplete(id)
@@ -4874,6 +5162,7 @@ ITEM keyDefault[] = {
     {"idlestring", DefaultItemIdlestring},
     {"idletimeout", DefaultItemIdletimeout},
     {"include", DefaultItemInclude},
+    {"interface", DefaultItemInterface},
     {"initcmd", DefaultItemInitcmd},
     {"initrunas", DefaultItemInitrunas},
     {"initspinmax", DefaultItemInitspinmax},
@@ -4885,6 +5174,7 @@ ITEM keyDefault[] = {
     {"motd", DefaultItemMOTD},
     {"options", DefaultItemOptions},
     {"parity", DefaultItemParity},
+    {"password", DefaultItemPassword},
     {"port", DefaultItemPort},
     {"portbase", DefaultItemPortbase},
     {"portinc", DefaultItemPortinc},
@@ -4892,10 +5182,13 @@ ITEM keyDefault[] = {
     {"replstring", DefaultItemReplstring},
     {"ro", DefaultItemRo},
     {"rw", DefaultItemRw},
+    {"secured", DefaultItemSecured},
     {"timestamp", DefaultItemTimestamp},
     {"type", DefaultItemType},
     {"uds", DefaultItemUds},
     {"udssubst", DefaultItemUdssubst},
+    {"username", DefaultItemUsername},
+    {"workaround", DefaultItemWorkaround},
     {(char *)0, (void *)0}
 };
 
@@ -4913,17 +5206,20 @@ ITEM keyConsole[] = {
     {"idlestring", ConsoleItemIdlestring},
     {"idletimeout", ConsoleItemIdletimeout},
     {"include", ConsoleItemInclude},
+    {"interface", ConsoleItemInterface},
     {"initcmd", ConsoleItemInitcmd},
     {"initrunas", ConsoleItemInitrunas},
     {"initspinmax", ConsoleItemInitspinmax},
     {"initspintimer", ConsoleItemInitspintimer},
     {"initsubst", ConsoleItemInitsubst},
+    {"interface", ConsoleItemInterface},
     {"logfile", ConsoleItemLogfile},
     {"logfilemax", ConsoleItemLogfilemax},
     {"master", ConsoleItemMaster},
     {"motd", ConsoleItemMOTD},
     {"options", ConsoleItemOptions},
     {"parity", ConsoleItemParity},
+    {"password", ConsoleItemPassword},
     {"port", ConsoleItemPort},
     {"portbase", ConsoleItemPortbase},
     {"portinc", ConsoleItemPortinc},
@@ -4931,10 +5227,13 @@ ITEM keyConsole[] = {
     {"replstring", ConsoleItemReplstring},
     {"ro", ConsoleItemRo},
     {"rw", ConsoleItemRw},
+    {"secured", ConsoleItemSecured},
     {"timestamp", ConsoleItemTimestamp},
     {"type", ConsoleItemType},
     {"uds", ConsoleItemUds},
     {"udssubst", ConsoleItemUdssubst},
+    {"username", ConsoleItemUsername},
+    {"workaround", ConsoleItemWorkaround},
     {(char *)0, (void *)0}
 };
 
diff --git a/contrib/chat/Makefile b/contrib/chat/Makefile
index 8fd93b6..7485c73 100644
diff --git a/contrib/yaconserv-8.1.18-mouse-moxa-realcom-tty.patch b/contrib/yaconserv-8.1.18-mouse-moxa-realcom-tty.patch
new file mode 100644
index 0000000..e46af55