Newer
Older
/*****************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
* In order to be useful for every potential user, curl and libcurl are
* dual-licensed under the MPL and the MIT/X-derivate licenses.
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the MPL or the MIT/X-derivate
* licenses. You may pick one of these licenses.
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
* $Id$
*****************************************************************************/
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/* -- WIN32 approved -- */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#include <winsock.h>
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/resource.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <netdb.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#include <sys/ioctl.h>
#include <signal.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#endif
#include "urldata.h"
#include <curl/curl.h>
Daniel Stenberg
committed
#include "transfer.h"
#include "sendf.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
#define TELOPTS
#define TELCMDS
#include "arpa_telnet.h"
#define SUBBUFSIZE 512
#define SB_CLEAR(x) x->subpointer = x->subbuffer;
#define SB_TERM(x) { x->subend = x->subpointer; SB_CLEAR(x); }
#define SB_ACCUM(x,c) if (x->subpointer < (x->subbuffer+sizeof x->subbuffer)) { \
*x->subpointer++ = (c); \
#define SB_GET(x) ((*x->subpointer++)&0xff)
#define SB_PEEK(x) ((*x->subpointer)&0xff)
#define SB_EOF(x) (x->subpointer >= x->subend)
#define SB_LEN(x) (x->subend - x->subpointer)
static
void telrcv(struct connectdata *,
unsigned char *inbuf, /* Data received from socket */
int count); /* Number of bytes received */
static void printoption(struct UrlData *data,
const char *direction,
int cmd, int option);
static void negotiate(struct connectdata *);
static void send_negotiation(struct connectdata *, int cmd, int option);
static void set_local_option(struct connectdata *, int cmd, int option);
static void set_remote_option(struct connectdata *, int cmd, int option);
static void printsub(struct UrlData *data,
int direction, unsigned char *pointer, int length);
static void suboption(struct connectdata *);
/* For negotiation compliant to RFC 1143 */
#define NO 0
#define YES 1
#define WANTYES 2
#define WANTNO 3
#define EMPTY 0
#define OPPOSITE 1
{
TS_DATA = 0,
TS_IAC,
TS_WILL,
TS_WONT,
TS_DO,
TS_DONT,
TS_CR,
TS_SB, /* sub-option collection */
TS_SE /* looking for sub-option end */
} TelnetReceive;
struct TELNET {
int please_negotiate;
int already_negotiated;
int us[256];
int usq[256];
int us_preferred[256];
int him[256];
int himq[256];
int him_preferred[256];
char *subopt_ttype; /* Set with suboption TTYPE */
char *subopt_xdisploc; /* Set with suboption XDISPLOC */
struct curl_slist *telnet_vars; /* Environment variables */
/* suboptions */
char subbuffer[SUBBUFSIZE];
char *subpointer, *subend; /* buffer for sub-options */
TelnetReceive telrcv_state;
};
static
CURLcode init_telnet(struct connectdata *conn)
{
struct TELNET *tn;
tn = (struct TELNET *)malloc(sizeof(struct TELNET));
if(!tn)
return CURLE_OUT_OF_MEMORY;
conn->proto.telnet = (void *)tn; /* make us known */
memset(tn, 0, sizeof(struct TELNET));
/* Set all options to NO */
#if 0
/* NO is zero => default fill pattern */
memset(tn->us, NO, 256);
memset(tn->usq, NO, 256);
memset(tn->us_preferred, NO, 256);
memset(tn->him, NO, 256);
memset(tn->himq, NO, 256);
memset(tn->him_preferred, NO, 256);
#endif
/* Set the options we want by default */
tn->us_preferred[TELOPT_BINARY] = YES;
tn->us_preferred[TELOPT_SGA] = YES;
tn->him_preferred[TELOPT_BINARY] = YES;
tn->him_preferred[TELOPT_SGA] = YES;
return CURLE_OK;
static void negotiate(struct connectdata *conn)
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
for(i = 0;i < NTELOPTS;i++)
{
set_local_option(conn, i, YES);
set_remote_option(conn, i, YES);
}
}
static void printoption(struct UrlData *data,
const char *direction, int cmd, int option)
{
if (data->bits.verbose)
{
if (cmd == IAC)
{
if (TELCMD_OK(option))
printf("%s IAC %s\n", direction, TELCMD(option));
printf("%s IAC %d\n", direction, option);
}
else
{
fmt = (cmd == WILL) ? "WILL" : (cmd == WONT) ? "WONT" :
(cmd == DO) ? "DO" : (cmd == DONT) ? "DONT" : 0;
if (fmt)
if (TELOPT_OK(option))
opt = TELOPT(option);
else if (option == TELOPT_EXOPL)
opt = "EXOPL";
else
opt = NULL;
if(opt)
printf("%s %s %s\n", direction, fmt, opt);
else
printf("%s %s %d\n", direction, fmt, option);
else
printf("%s %d %d\n", direction, cmd, option);
}
}
static void send_negotiation(struct connectdata *conn, int cmd, int option)
{
unsigned char buf[3];
buf[0] = IAC;
buf[1] = cmd;
buf[2] = option;
swrite(conn->firstsocket, buf, 3);
printoption(conn->data, "SENT", cmd, option);
static
void set_remote_option(struct connectdata *conn, int option, int newstate)
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
send_negotiation(conn, DO, option);
break;
/* Already enabled */
break;
{
case EMPTY:
/* Already negotiating for YES, queue the request */
break;
case OPPOSITE:
/* Error: already queued an enable request */
break;
}
break;
{
case EMPTY:
/* Error: already negotiating for enable */
break;
case OPPOSITE:
break;
}
break;
}
}
else /* NO */
{
/* Already disabled */
break;
send_negotiation(conn, DONT, option);
break;
{
case EMPTY:
/* Already negotiating for NO */
break;
case OPPOSITE:
break;
case OPPOSITE:
break;
}
break;
}
}
static
void rec_will(struct connectdata *conn, int option)
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
switch(tn->him[option])
if(tn->him_preferred[option] == YES)
send_negotiation(conn, DO, option);
send_negotiation(conn, DONT, option);
case EMPTY:
/* Error: DONT answered by WILL */
break;
case OPPOSITE:
/* Error: DONT answered by WILL */
tn->him[option] = YES;
tn->himq[option] = EMPTY;
tn->him[option] = WANTNO;
tn->himq[option] = EMPTY;
send_negotiation(conn, DONT, option);
break;
static
void rec_wont(struct connectdata *conn, int option)
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
switch(tn->him[option])
send_negotiation(conn, DONT, option);
tn->him[option] = WANTYES;
tn->himq[option] = EMPTY;
send_negotiation(conn, DO, option);
break;
tn->him[option] = NO;
tn->himq[option] = EMPTY;
void set_local_option(struct connectdata *conn, int option, int newstate)
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
send_negotiation(conn, WILL, option);
break;
/* Already enabled */
break;
{
case EMPTY:
/* Already negotiating for YES, queue the request */
break;
case OPPOSITE:
/* Error: already queued an enable request */
break;
}
break;
{
case EMPTY:
/* Error: already negotiating for enable */
break;
case OPPOSITE:
break;
}
break;
}
}
else /* NO */
{
/* Already disabled */
break;
send_negotiation(conn, WONT, option);
break;
{
case EMPTY:
/* Already negotiating for NO */
break;
case OPPOSITE:
break;
case OPPOSITE:
break;
}
break;
}
}
static
void rec_do(struct connectdata *conn, int option)
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
switch(tn->us[option])
if(tn->us_preferred[option] == YES)
send_negotiation(conn, WILL, option);
send_negotiation(conn, WONT, option);
case EMPTY:
/* Error: DONT answered by WILL */
break;
case OPPOSITE:
/* Error: DONT answered by WILL */
tn->us[option] = YES;
tn->usq[option] = EMPTY;
tn->us[option] = WANTNO;
tn->himq[option] = EMPTY;
send_negotiation(conn, WONT, option);
break;
static
void rec_dont(struct connectdata *conn, int option)
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
switch(tn->us[option])
send_negotiation(conn, WONT, option);
tn->us[option] = WANTYES;
tn->usq[option] = EMPTY;
send_negotiation(conn, WILL, option);
break;
tn->us[option] = NO;
tn->usq[option] = EMPTY;
}
static void printsub(struct UrlData *data,
int direction, /* '<' or '>' */
unsigned char *pointer, /* where suboption data is */
int length) /* length of suboption data */
{
int i = 0;
if (data->bits.verbose)
{
if (direction)
{
printf("%s IAC SB ", (direction == '<')? "RCVD":"SENT");
if (length >= 3)
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
int j;
i = pointer[length-2];
j = pointer[length-1];
if (i != IAC || j != SE)
{
printf("(terminated by ");
if (TELOPT_OK(i))
printf("%s ", TELOPT(i));
else if (TELCMD_OK(i))
printf("%s ", TELCMD(i));
else
printf("%d ", i);
if (TELOPT_OK(j))
printf("%s", TELOPT(j));
else if (TELCMD_OK(j))
printf("%s", TELCMD(j));
else
printf("%d", j);
printf(", not IAC SE!) ");
}
}
length -= 2;
}
if (length < 1)
{
printf("(Empty suboption?)");
return;
}
if (TELOPT_OK(pointer[0])) {
switch(pointer[0]) {
case TELOPT_TTYPE:
case TELOPT_XDISPLOC:
case TELOPT_NEW_ENVIRON:
printf("%s", TELOPT(pointer[0]));
break;
default:
printf("%s (unsupported)", TELOPT(pointer[0]));
break;
}
}
else
printf("%d (unknown)", pointer[i]);
switch(pointer[1]) {
case TELQUAL_IS:
printf(" IS");
break;
case TELQUAL_SEND:
printf(" SEND");
break;
case TELQUAL_INFO:
printf(" INFO/REPLY");
break;
case TELQUAL_NAME:
printf(" NAME");
break;
}
switch(pointer[0]) {
case TELOPT_TTYPE:
case TELOPT_XDISPLOC:
pointer[length] = 0;
printf(" \"%s\"", &pointer[2]);
break;
case TELOPT_NEW_ENVIRON:
if(pointer[1] == TELQUAL_IS) {
printf(" ");
for(i = 3;i < length;i++) {
switch(pointer[i]) {
case NEW_ENV_VAR:
printf(", ");
break;
case NEW_ENV_VALUE:
printf(" = ");
break;
default:
printf("%c", pointer[i]);
break;
}
}
break;
default:
for (i = 2; i < length; i++)
printf(" %.2x", pointer[i]);
break;
}
if (direction)
{
printf("\n");
}
}
}
static int check_telnet_options(struct connectdata *conn)
{
struct curl_slist *head;
char option_keyword[128];
char option_arg[256];
char *buf;
struct UrlData *data = conn->data;
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
/* Add the user name as an environment variable if it
was given on the command line */
if(data->bits.user_passwd)
{
char *buf = malloc(256);
sprintf(buf, "USER,%s", data->user);
tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf);
tn->us_preferred[TELOPT_NEW_ENVIRON] = YES;
}
for(head = data->telnet_options; head; head=head->next) {
if(sscanf(head->data, "%127[^= ]%*[ =]%255s",
option_keyword, option_arg) == 2) {
/* Terminal type */
if(strequal(option_keyword, "TTYPE")) {
tn->subopt_ttype = option_arg;
tn->us_preferred[TELOPT_TTYPE] = YES;
/* Display variable */
if(strequal(option_keyword, "XDISPLOC")) {
tn->subopt_xdisploc = option_arg;
tn->us_preferred[TELOPT_XDISPLOC] = YES;
/* Environment variable */
if(strequal(option_keyword, "NEW_ENV")) {
buf = strdup(option_arg);
if(!buf)
return CURLE_OUT_OF_MEMORY;
tn->telnet_vars = curl_slist_append(tn->telnet_vars, buf);
tn->us_preferred[TELOPT_NEW_ENVIRON] = YES;
failf(data, "Unknown telnet option %s", head->data);
return CURLE_UNKNOWN_TELNET_OPTION;
} else {
failf(data, "Syntax error in telnet option: %s", head->data);
return CURLE_TELNET_OPTION_SYNTAX;
}
}
return CURLE_OK;
}
/*
* suboption()
*
* Look at the sub-option buffer, and try to be helpful to the other
* side.
* No suboptions are supported yet.
*/
static void suboption(struct connectdata *conn)
struct curl_slist *v;
unsigned char subchar;
unsigned char temp[2048];
int len;
int tmplen;
char varname[128];
char varval[128];
struct UrlData *data = conn->data;
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
printsub(data, '<', (unsigned char *)tn->subbuffer, SB_LEN(tn)+2);
switch (subchar = SB_GET(tn)) {
len = strlen(tn->subopt_ttype) + 4 + 2;
snprintf((char *)temp, sizeof(temp),
"%c%c%c%c%s%c%c", IAC, SB, TELOPT_TTYPE,
TELQUAL_IS, tn->subopt_ttype, IAC, SE);
swrite(conn->firstsocket, temp, len);
printsub(data, '>', &temp[2], len-2);
break;
case TELOPT_XDISPLOC:
len = strlen(tn->subopt_xdisploc) + 4 + 2;
snprintf((char *)temp, sizeof(temp),
"%c%c%c%c%s%c%c", IAC, SB, TELOPT_XDISPLOC,
TELQUAL_IS, tn->subopt_xdisploc, IAC, SE);
swrite(conn->firstsocket, temp, len);
printsub(data, '>', &temp[2], len-2);
break;
case TELOPT_NEW_ENVIRON:
snprintf((char *)temp, sizeof(temp),
"%c%c%c%c", IAC, SB, TELOPT_NEW_ENVIRON, TELQUAL_IS);
len = 4;
for(v = tn->telnet_vars;v;v = v->next) {
tmplen = (strlen(v->data) + 1);
/* Add the variable only if it fits */
if(len + tmplen < sizeof(temp)-6) {
sscanf(v->data, "%127[^,],%s", varname, varval);
snprintf((char *)&temp[len], sizeof(temp) - len,
"%c%s%c%s", NEW_ENV_VAR, varname,
NEW_ENV_VALUE, varval);
len += tmplen;
}
}
snprintf((char *)&temp[len], sizeof(temp) - len,
"%c%c", IAC, SE);
len += 2;
swrite(conn->firstsocket, temp, len);
printsub(data, '>', &temp[2], len-2);
break;
}
return;
static
void telrcv(struct connectdata *conn,
unsigned char *inbuf, /* Data received from socket */
int count) /* Number of bytes received */
unsigned char c;
int index = 0;
struct UrlData *data = conn->data;
struct TELNET *tn = (struct TELNET *)conn->proto.telnet;
while(count--)
{
c = inbuf[index++];
if (c == '\0')
{
break; /* Ignore \0 after CR */
}
Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1);
continue;
break;
}
else if(c == '\r')
{
}
Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1);
continue;
process_iac:
switch (c)
{
case WILL:
SB_CLEAR(tn);
tn->telrcv_state = TS_SB;
Curl_client_write(data, CLIENTWRITE_BODY, (char *)&c, 1);
case DM:
case NOP:
case GA:
default:
printoption(data, "RCVD", IAC, c);
break;
}
printoption(data, "RCVD", WILL, c);
printoption(data, "RCVD", WONT, c);
printoption(data, "RCVD", DO, c);
printoption(data, "RCVD", DONT, c);
if (c != SE)
{
if (c != IAC)
{
/*
* This is an error. We only expect to get
* "IAC IAC" or "IAC SE". Several things may
* have happend. An IAC was not doubled, the
* IAC SE was left off, or another option got
* inserted into the suboption are all possibilities.
* If we assume that the IAC was not doubled,
* and really the IAC SE was left off, we could
* get into an infinate loop here. So, instead,
* we terminate the suboption, and process the
* partial suboption if we can.
*/
SB_ACCUM(tn, (unsigned char)IAC);
SB_ACCUM(tn, c);
tn->subpointer -= 2;
SB_TERM(tn);
printoption(data, "In SUBOPTION processing, RCVD", IAC, c);
suboption(conn); /* handle sub-option */
SB_ACCUM(tn,c);
tn->telrcv_state = TS_SB;