aboutsummaryrefslogtreecommitdiff
path: root/lib/telnet.c
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 /lib/telnet.c
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
Diffstat (limited to 'lib/telnet.c')
-rw-r--r--lib/telnet.c194
1 files changed, 169 insertions, 25 deletions
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 */