aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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 */