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
This commit is contained in:
Laurent Rabret 2011-11-25 10:35:34 +01:00 committed by Daniel Stenberg
parent f712ace9d7
commit b9223a17b8
2 changed files with 172 additions and 26 deletions

View File

@ -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 */

View File

@ -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 */