From 1934c297871ddb9affc8361b6c7d51546d0163d5 Mon Sep 17 00:00:00 2001
Date: Tue, 22 Nov 2011 23:16:23 +0100
Subject: [PATCH] This patch:
1) enables the Window Size option
2) allows the server to enable the echo mode
3) allows an app using libcurl to disable the default binary mode

Signed-off-by: Laurent Rabret
---
 lib/arpa_telnet.h |    3 +
 lib/telnet.c      |  239 ++++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 195 insertions(+), 47 deletions(-)

diff --git a/lib/arpa_telnet.h b/lib/arpa_telnet.h
index 6626928..7b7ac84 100644
--- a/lib/arpa_telnet.h
+++ b/lib/arpa_telnet.h
@@ -26,11 +26,14 @@
  * Telnet option defines. Add more here if in need.
  */
 #define CURL_TELOPT_BINARY   0  /* binary 8bit data */
+#define CURL_TELOPT_ECHO	 1  /* just echo! */
 #define CURL_TELOPT_SGA      3  /* Suppress Go Ahead */
 #define CURL_TELOPT_EXOPL  255  /* EXtended OPtions List */
 #define CURL_TELOPT_TTYPE   24  /* Terminal TYPE */
+#define CURL_TELOPT_NAWS	31  /* Negotiate About Window Size */
 #define CURL_TELOPT_XDISPLOC 35 /* X DISPlay LOCation */
 
+
 #define CURL_TELOPT_NEW_ENVIRON 39  /* NEW ENVIRONment variables */
 #define CURL_NEW_ENV_VAR   0
 #define CURL_NEW_ENV_VALUE 1
diff --git a/lib/telnet.c b/lib/telnet.c
index 59094b6..f0c083b 100644
--- a/lib/telnet.c
+++ b/lib/telnet.c
@@ -116,10 +116,13 @@ static void printsub(struct SessionHandle *data,
                      int direction, unsigned char *pointer,
                      size_t length);
 static void suboption(struct connectdata *);
+static void sendsuboption(struct connectdata *conn, int option);
 
 static CURLcode telnet_do(struct connectdata *conn, bool *done);
 static CURLcode telnet_done(struct connectdata *conn,
                                  CURLcode, bool premature);
+static CURLcode send_telnet_data(struct connectdata *conn,
+                                 char *buffer, ssize_t nread);
 
 /* For negotiation compliant to RFC 1143 */
 #define CURL_NO          0
@@ -155,8 +158,11 @@ struct TELNET {
   int him[256];
   int himq[256];
   int him_preferred[256];
+  int subnegotiation[256];
   char subopt_ttype[32];             /* Set with suboption TTYPE */
   char subopt_xdisploc[128];          /* Set with suboption XDISPLOC */
+  unsigned short subopt_wsx;	     /* Set with suboption NAWS */
+  unsigned short subopt_wsy;		 /* Set with suboption NAWS */
   struct curl_slist *telnet_vars; /* Environment variables */
 
   /* suboptions */
@@ -249,11 +255,37 @@ CURLcode init_telnet(struct connectdata *conn)
   CURL_SB_CLEAR(tn);
 
   /* Set the options we want by default */
-  tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
   tn->us_preferred[CURL_TELOPT_SGA] = CURL_YES;
-  tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
   tn->him_preferred[CURL_TELOPT_SGA] = CURL_YES;
 
+  /* To be compliant with previous releases of libcurl
+     we enable this option by default. This behaviour
+	 can be changed thanks to the "BINARY" option in
+	 CURLOPT_TELNETOPTIONS
+  */
+  tn->us_preferred[CURL_TELOPT_BINARY] = CURL_YES;
+  tn->him_preferred[CURL_TELOPT_BINARY] = CURL_YES;
+
+  /* We must allow the server to echo what we sent 
+	 but it is not necessary to request the server
+	 to do so (it might forces the server to close
+	 the connection). Hence, we ignore ECHO in the
+	 negotiate function
+  */
+  tn->him_preferred[CURL_TELOPT_ECHO] = CURL_YES;
+
+  /* Set the subnegotiation fields to send information
+    just after negotiation passed (do/will)
+  
+     Default values are (0,0) initialized by calloc.
+     According to the RFC1013 it is valid:
+     A value equal to zero is acceptable for the width (or height),
+	 and means that no character width (or height) is being sent.
+	 In this case, the width (or height) that will be assumed by the
+	 Telnet server is operating system specific (it will probably be
+	 based upon the terminal type information that may have been sent
+	 using the TERMINAL TYPE Telnet option). */
+  tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
   return CURLE_OK;
 }
 
@@ -263,6 +295,8 @@ static void negotiate(struct connectdata *conn)
   struct TELNET *tn = (struct TELNET *) conn->data->state.proto.telnet;
 
   for(i = 0;i < CURL_NTELOPTS;i++) {
+	  if(i==CURL_TELOPT_ECHO ) continue;
+
     if(tn->us_preferred[i] == CURL_YES)
       set_local_option(conn, i, CURL_YES);
 
@@ -575,7 +609,17 @@ void rec_do(struct connectdata *conn, int option)
     if(tn->us_preferred[option] == CURL_YES) {
       tn->us[option] = CURL_YES;
       send_negotiation(conn, CURL_WILL, option);
+	  if(tn->subnegotiation[option] == CURL_YES) {
+		  /* transmission of data option */
+		  sendsuboption(conn, option);
+	  }
     }
+    else if (tn->subnegotiation[option] == CURL_YES) {
+		/* send information to achieve this option*/
+		tn->us[option] = CURL_YES;
+		send_negotiation(conn, CURL_WILL, option);
+		sendsuboption(conn, option);
+	}
     else
       send_negotiation(conn, CURL_WONT, option);
     break;
@@ -602,6 +646,10 @@ void rec_do(struct connectdata *conn, int option)
     switch(tn->usq[option]) {
     case CURL_EMPTY:
       tn->us[option] = CURL_YES;
+	  if (tn->subnegotiation[option] == CURL_YES) {
+		  /* transmission of data option */
+		  sendsuboption(conn, option);
+	  }
       break;
     case CURL_OPPOSITE:
       tn->us[option] = CURL_WANTNO;
@@ -662,6 +710,7 @@ static void printsub(struct SessionHandle *data,
                      size_t length)             /* length of suboption data */
 {
   unsigned int i = 0;
+  unsigned short * pval;
 
   if(data->set.verbose) {
     if(direction) {
@@ -701,6 +750,7 @@ static void printsub(struct SessionHandle *data,
         case CURL_TELOPT_TTYPE:
         case CURL_TELOPT_XDISPLOC:
         case CURL_TELOPT_NEW_ENVIRON:
+		case CURL_TELOPT_NAWS:
           infof(data, "%s", CURL_TELOPT(pointer[0]));
           break;
         default:
@@ -711,51 +761,57 @@ static void printsub(struct SessionHandle *data,
     else
       infof(data, "%d (unknown)", pointer[i]);
 
-    switch(pointer[1]) {
-      case CURL_TELQUAL_IS:
-        infof(data, " IS");
-        break;
-      case CURL_TELQUAL_SEND:
-        infof(data, " SEND");
-        break;
-      case CURL_TELQUAL_INFO:
-        infof(data, " INFO/REPLY");
-        break;
-      case CURL_TELQUAL_NAME:
-        infof(data, " NAME");
-        break;
-    }
-
-    switch(pointer[0]) {
-      case CURL_TELOPT_TTYPE:
-      case CURL_TELOPT_XDISPLOC:
-        pointer[length] = 0;
-        infof(data, " \"%s\"", &pointer[2]);
-        break;
-      case CURL_TELOPT_NEW_ENVIRON:
-        if(pointer[1] == CURL_TELQUAL_IS) {
-          infof(data, " ");
-          for(i = 3;i < length;i++) {
-            switch(pointer[i]) {
-              case CURL_NEW_ENV_VAR:
-                infof(data, ", ");
-                break;
-              case CURL_NEW_ENV_VALUE:
-                infof(data, " = ");
-                break;
-              default:
-                infof(data, "%c", pointer[i]);
-                break;
-            }
-          }
-        }
-        break;
-      default:
-        for(i = 2; i < length; i++)
-          infof(data, " %.2x", pointer[i]);
-        break;
-    }
-
+	switch(pointer[0]){
+	  case CURL_TELOPT_NAWS:
+		  pval = (unsigned short*)(pointer+1);
+		  infof(data, "Width: %hu ; Height: %hu", ntohs(pval[0]), ntohs(pval[1]));
+		break;
+	  default:
+		switch(pointer[1]) {
+		  case CURL_TELQUAL_IS:
+			infof(data, " IS");
+			break;
+		  case CURL_TELQUAL_SEND:
+			infof(data, " SEND");
+			break;
+		  case CURL_TELQUAL_INFO:
+			infof(data, " INFO/REPLY");
+			break;
+		  case CURL_TELQUAL_NAME:
+			infof(data, " NAME");
+			break;
+		}
+
+		switch(pointer[0]) {
+		  case CURL_TELOPT_TTYPE:
+		  case CURL_TELOPT_XDISPLOC:
+			pointer[length] = 0;
+			infof(data, " \"%s\"", &pointer[2]);
+			break;
+		  case CURL_TELOPT_NEW_ENVIRON:
+			if(pointer[1] == CURL_TELQUAL_IS) {
+			  infof(data, " ");
+			  for(i = 3;i < length;i++) {
+				switch(pointer[i]) {
+				  case CURL_NEW_ENV_VAR:
+					infof(data, ", ");
+					break;
+				  case CURL_NEW_ENV_VALUE:
+					infof(data, " = ");
+					break;
+				  default:
+					infof(data, "%c", pointer[i]);
+					break;
+				}
+			  }
+			}
+			break;
+		  default:
+			for(i = 2; i < length; i++)
+			  infof(data, " %.2x", pointer[i]);
+			  break;
+		}
+	}
     if(direction)
       infof(data, "\n");
   }
@@ -770,6 +826,7 @@ static CURLcode check_telnet_options(struct connectdata *conn)
   struct SessionHandle *data = conn->data;
   struct TELNET *tn = (struct TELNET *)conn->data->state.proto.telnet;
   CURLcode result = CURLE_OK;
+  int binary_option;
 
   /* Add the user name as an environment variable if it
      was given on the command line */
@@ -817,6 +874,31 @@ static CURLcode check_telnet_options(struct connectdata *conn)
         continue;
       }
 
+	  /* Window Size */
+      if(Curl_raw_equal(option_keyword, "WS")) {
+		if(sscanf(option_arg, "%hu%*[xX]%hu",
+              &tn->subopt_wsx, &tn->subopt_wsy) == 2) {
+				  tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
+		}
+		else {
+			failf(data, "Syntax error in telnet option: %s", head->data);
+			result = CURLE_TELNET_OPTION_SYNTAX;
+			break;
+		}
+		continue;
+	  }
+		
+	  /* To take care or not of the 8th bit in data exchange */
+      if(Curl_raw_equal(option_keyword, "BINARY")) {
+		binary_option=atoi(option_arg);
+		if(binary_option!=1) {
+			tn->us_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+			tn->him_preferred[CURL_TELOPT_BINARY] = CURL_NO;
+		}
+        continue;
+      }
+
+
       failf(data, "Unknown telnet option %s", head->data);
       result = CURLE_UNKNOWN_TELNET_OPTION;
       break;
@@ -913,6 +995,69 @@ static void suboption(struct connectdata *conn)
   return;
 }
 
+
+/*
+ * sendsuboption()
+ *
+ * Send suboption information to the server side.
+ */
+
+static void sendsuboption(struct connectdata *conn, int option)
+{
+  ssize_t bytes_written;
+  int err;
+  unsigned short x, y;
+  unsigned char*uc1, *uc2;
+
+  struct SessionHandle *data = conn->data;
+  struct TELNET *tn = (struct TELNET *)data->state.proto.telnet;
+
+  switch (option) {
+	case CURL_TELOPT_NAWS:
+	  /* We prepare data to be sent */
+	  CURL_SB_CLEAR(tn)
+	  CURL_SB_ACCUM(tn,CURL_IAC)
+	  CURL_SB_ACCUM(tn,CURL_SB)
+	  CURL_SB_ACCUM(tn,CURL_TELOPT_NAWS)
+	  /* We must deal either with litte or big endien processors */
+	  /* Window size must be sent according to the 'network order' */
+	  x=htons(tn->subopt_wsx);
+	  y=htons(tn->subopt_wsy);
+	  uc1 = (unsigned char*)&x;
+	  uc2 = (unsigned char*)&y;
+	  CURL_SB_ACCUM(tn,uc1[0])
+	  CURL_SB_ACCUM(tn,uc1[1])
+	  CURL_SB_ACCUM(tn,uc2[0])
+	  CURL_SB_ACCUM(tn,uc2[1])
+
+	  CURL_SB_ACCUM(tn,CURL_IAC)
+	  CURL_SB_ACCUM(tn,CURL_SE)
+	  CURL_SB_TERM(tn)
+	  /* data suboption is now ready */
+
+	  printsub(data, '>', (unsigned char *)tn->subbuffer+2, CURL_SB_LEN(tn)-2);
+
+	  /* we send the header of the suboption... */
+	  bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer, 3);
+      if(bytes_written < 0) {
+        err = SOCKERRNO;
+        failf(data,"Sending data failed (%d)",err);
+      }
+	  /* ... then the window size with the send_telnet_data() function 
+	     to deal with 0xFF cases ... */
+	  send_telnet_data(conn, tn->subbuffer+3, 4);
+	  /* ... and the footer */
+	  bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer+7, 2);
+      if(bytes_written < 0) {
+        err = SOCKERRNO;
+        failf(data,"Sending data failed (%d)",err);
+      }
+      break;
+  }
+  return;
+}
+
+
 static
 CURLcode telrcv(struct connectdata *conn,
                 const unsigned char *inbuf, /* Data received from socket */
-- 
1.7.7.1.msysgit.0

