aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaurent Rabret <laurent.rabret@orange.com>2011-11-25 10:35:34 +0100
committerDaniel Stenberg <daniel@haxx.se>2011-11-25 10:46:49 +0100
commitb9223a17b8a500c88e8c5eddec7b261cf73514fc (patch)
tree1e9aec651d247cdcf85ddff4b0fb2d2a6529d6a9
parentf712ace9d702e5380a943caa48a18579a7cd8443 (diff)
TELNET: improved treatment of options
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
-rw-r--r--lib/arpa_telnet.h4
-rw-r--r--lib/telnet.c194
2 files changed, 172 insertions, 26 deletions
diff --git a/lib/arpa_telnet.h b/lib/arpa_telnet.h
index 6626928b5..098d9a92f 100644
--- a/lib/arpa_telnet.h
+++ b/lib/arpa_telnet.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -26,9 +26,11 @@
* 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 */
diff --git a/lib/telnet.c b/lib/telnet.c
index bbad08de1..0b63b12ae 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,9 +158,12 @@ 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 */
- struct curl_slist *telnet_vars; /* Environment variables */
+ 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 */
unsigned char subbuffer[SUBBUFSIZE];
@@ -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,9 @@ 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,6 +610,15 @@ 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);
@@ -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) {
@@ -698,20 +747,28 @@ static void printsub(struct SessionHandle *data,
if(CURL_TELOPT_OK(pointer[0])) {
switch(pointer[0]) {
- case CURL_TELOPT_TTYPE:
- case CURL_TELOPT_XDISPLOC:
- case CURL_TELOPT_NEW_ENVIRON:
- infof(data, "%s", CURL_TELOPT(pointer[0]));
- break;
- default:
- infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
- break;
+ 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:
+ infof(data, "%s (unsupported)", CURL_TELOPT(pointer[0]));
+ break;
}
}
else
infof(data, "%d (unknown)", pointer[i]);
- switch(pointer[1]) {
+ 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;
@@ -724,9 +781,9 @@ static void printsub(struct SessionHandle *data,
case CURL_TELQUAL_NAME:
infof(data, " NAME");
break;
- }
+ }
- switch(pointer[0]) {
+ switch(pointer[0]) {
case CURL_TELOPT_TTYPE:
case CURL_TELOPT_XDISPLOC:
pointer[length] = 0;
@@ -737,15 +794,15 @@ static void printsub(struct SessionHandle *data,
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;
+ case CURL_NEW_ENV_VAR:
+ infof(data, ", ");
+ break;
+ case CURL_NEW_ENV_VALUE:
+ infof(data, " = ");
+ break;
+ default:
+ infof(data, "%c", pointer[i]);
+ break;
}
}
}
@@ -754,8 +811,8 @@ static void printsub(struct SessionHandle *data,
for(i = 2; i < length; i++)
infof(data, " %.2x", pointer[i]);
break;
+ }
}
-
if(direction)
infof(data, "\n");
}
@@ -770,6 +827,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 +875,29 @@ 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 +994,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, (char *)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;
+ }
+}
+
+
static
CURLcode telrcv(struct connectdata *conn,
const unsigned char *inbuf, /* Data received from socket */