From fe3ef825e6d90cb2d4ebc31943de447f026d8201 Mon Sep 17 00:00:00 2001
From: Mats Erik Andersson <gnu@gisladisker.se>
Date: Mon, 27 Feb 2017 23:03:36 +0100
Subject: [PATCH 21/60] telnetd: Portable option debugging.

Also report locally unsupported editing characters
as free for the client to choose at his own will
---
 ChangeLog         | 19 +++++++++++++++++++
 telnetd/slc.c     | 33 ++++++++++++++++++++++++++++-----
 telnetd/utility.c | 21 +++++++++++++++++++--
 3 files changed, 66 insertions(+), 7 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 8c95a08f..fefaf4a7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+2017-02-27  Mats Erik Andersson  <gnu@gisladisker.se>
+
+	telnetd: Portable option debugging.
+	BSD systems assign IAC and _POSIX_VDISABLE the common decimal
+	value 255. Hence the NVT enforces value duplication more often
+	during their transmission, than GNU and Solaris systems do.
+
+	* telnetd/utility.c (printsub) <LM_SLC>: Increase suboption
+	pointer I sufficiently to counteract duplication.  This has
+	to distinguish between in/out/none.  In recursive mode,
+	suppress newline at the end of suboption printing.
+
+	* telnetd/slc.c (send_slc) <SLC_EC, SLC_EL, SLC_EW, SLC_RP>
+	<SLC_LNEXT>: Editing characters are reported using SLC_DEFAULT
+	rather than ignored due to SLC_NOSUPPORT, when they are not
+	supported locally.  This conforms with RFC 1184, section 2.4.
+	The test for support checks the set 'current', not 'defset',
+	which fits the wording found in RFC 1184.
+
 2017-02-23  Mats Erik Andersson  <gnu@gisladisker.se>
 
 	telnetd: Scrub USER from environment.
diff --git a/telnetd/slc.c b/telnetd/slc.c
index 197bc3e9..40b51e61 100644
--- a/telnetd/slc.c
+++ b/telnetd/slc.c
@@ -73,14 +73,37 @@ send_slc (void)
   /*
    * Send out list of triplets of special characters
    * to client.  We only send info on the characters
-   * that are currently supported.
+   * that are currently supported, except that editing
+   * characters without current support are conveyed
+   * using SLC_DEFAULT, thus urging the client to make
+   * a choice of his own.  This conforms to RFC 1184,
+   * section 2.4, page 6.
    */
   for (i = 1; i <= NSLC; i++)
     {
-      if ((slctab[i].defset.flag & SLC_LEVELBITS) == SLC_NOSUPPORT)
-	continue;
-      add_slc ((unsigned char) i, slctab[i].current.flag,
-	       slctab[i].current.val);
+      if ((slctab[i].current.flag & SLC_LEVELBITS) != SLC_NOSUPPORT)
+	add_slc ((unsigned char) i, slctab[i].current.flag,
+		 slctab[i].current.val);
+      else
+	{
+	  /* Urge the client to choose sensible editing characters.  */
+	  switch (i)
+	    {
+	    case SLC_EC:
+	    case SLC_EL:
+	    case SLC_EW:
+	    case SLC_RP:
+	    case SLC_LNEXT:
+	      add_slc ((unsigned char) i,
+		       (slctab[i].current.flag & ~SLC_LEVELBITS) | SLC_DEFAULT,
+			0);
+	      break;
+
+	    default:
+	      /* Be silent on other, unsupported characters.  */
+	      break;
+	    }
+	}
     }
 
 }				/* end of send_slc */
diff --git a/telnetd/utility.c b/telnetd/utility.c
index e493dff2..8227775d 100644
--- a/telnetd/utility.c
+++ b/telnetd/utility.c
@@ -1196,9 +1196,21 @@ printsub (int direction, unsigned char *pointer, int length)
 		debug_output_data ("(0x%x)", pointer[i + SLC_FLAGS]);
 	      debug_output_data (" %d;", pointer[i + SLC_VALUE]);
 
+	      /* Protocol enforced duplication of IAC depends on pre
+	       * and post modification of data, hence differs between
+	       * in, out, and no direction, i.e., recursive mode.
+	       * Some systems assign _POSIX_VDISABLE and IAC the same
+	       * value!  Heuristic experiments led to the following.
+	       * Recursive mode needs both steps as written here.
+	       */
 	      if ((pointer[i + SLC_VALUE] == IAC) &&
-		  (pointer[i + SLC_VALUE + 1] == IAC))
+		  (pointer[i + SLC_VALUE + 1] == IAC) &&
+		  (direction != '<'))
 		i++;
+	      if ((pointer[i + SLC_VALUE] == IAC) &&
+		  (pointer[i + SLC_VALUE + 1] == IAC) &&
+		  !direction)
+		i += 2;
 	    }
 
 	  for (; i < length; i++)
@@ -1572,7 +1584,12 @@ printsub (int direction, unsigned char *pointer, int length)
 	debug_output_data (" %d", pointer[i]);
       break;
     }
-  debug_output_data ("\r\n");
+
+  /* Without direction, we are doing a recursive suboption printing.
+   * Suppress NL, which would otherwise misplace the SE marker.
+   */
+  if (direction)
+    debug_output_data ("\r\n");
 }
 
 /*
-- 
2.26.0.292.g33ef6b2f38

