#include <stdio.h>
#include <stdlib.h>
#include <gc/gc.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <time.h>
#include <event.h>
#include <netdb.h>
#include <signal.h>
#define DEBUG
#include <debug.h>
#include <rbtree.h>
#include "network.h"
#include "telnet.h"
#include "client.h"
static void telnet_send_req(DESC * client, int how, int request);
static void telnet_send_subop(DESC * client, int how, int option);
static void telnet_handle_will(DESC * client, int option);
static void telnet_handle_wont(DESC * client, int option);
static int telnet_handle_subop(DESC * client, unsigned char *buffer,
int length);
int telnet_init(DESC * client)
{
dprintk("telnet initializing.");
strncpy(client->termtype, "vt100", 16);
client->telnet_win_width = 80;
client->telnet_win_height = 25;
telnet_send_req(client, TELNET_DO, TELNET_OPT_TERMTYPE);
telnet_send_req(client, TELNET_DO, TELNET_OPT_WINSIZE);
return 1;
}
/* Client Ring Buffer Handling */
static void telnet_send_req(DESC * client, int how, int request)
{
unsigned char buffer[16] = { TELNET_IAC, how, request };
network_write(client, (void *) buffer, 3);
}
static void telnet_send_subop(DESC * client, int option, int how)
{
unsigned char buffer[16] =
{ TELNET_IAC, TELNET_SB, option, how, TELNET_IAC, TELNET_SE };
network_write(client, (void *) buffer, 6);
}
static void telnet_handle_will(DESC * client, int option)
{
switch (option) {
case TELNET_OPT_TERMTYPE:
client->telnet_has_termtype = TSTATE_SUP;
dprintk("client acknowledged TERMTYPE");
break;
case TELNET_OPT_WINSIZE:
client->telnet_has_winsize = TSTATE_SUP;
dprintk("client acknowledged WINSIZE");
break;
}
}
static void telnet_handle_wont(DESC * client, int option)
{
switch (option) {
case TELNET_OPT_TERMTYPE:
client->telnet_has_termtype = TSTATE_UNSUP;
strncpy(client->termtype, "vt100", 16);
break;
case TELNET_OPT_WINSIZE:
client->telnet_has_winsize = TSTATE_UNSUP;
client->telnet_win_width = 80;
client->telnet_win_height = 25;
break;
}
}
static void telnet_check_options(DESC * client)
{
if(client->telnet_has_termtype == TSTATE_SUP) {
client->telnet_has_termtype = TSTATE_REQUEST;
telnet_send_subop(client, TELNET_OPT_TERMTYPE, TELNET_SUB_REQUIRED);
} else if(client->telnet_has_termtype == TSTATE_REQUEST) {
client->telnet_has_termtype = TSTATE_UNSUP;
}
if(client->telnet_has_winsize == TSTATE_SUP) {
client->telnet_has_winsize = TSTATE_REQUEST;
telnet_send_subop(client, TELNET_OPT_WINSIZE, TELNET_SUB_REQUIRED);
} else if(client->telnet_has_winsize == TSTATE_REQUEST) {
client->telnet_has_termtype = TSTATE_UNSUP;
}
}
static int telnet_handle_subop(DESC * client, unsigned char *buffer,
int length)
{
int iter;
if(buffer[1] != TELNET_SUB_SUPPLIED)
dfail("unexpected reply in telnet suboption negotiation.");
iter = 2;
switch (buffer[0]) {
case TELNET_OPT_TERMTYPE:
memset(client->termtype, 0, 16);
for(iter = 2; iter < length && iter < 18 && buffer[iter] != 0xFF;
iter++) {
client->termtype[iter - 2] = buffer[iter];
}
iter++;
dprintk("Received Terminal Type '%s'", client->termtype);
client->telnet_has_termtype = TSTATE_REPLIED;
break;
case TELNET_OPT_WINSIZE:
client->telnet_win_width = buffer[2];
client->telnet_win_height = buffer[4];
iter = 6;
dprintk("Received Window Size %dx%d", client->telnet_win_width,
client->telnet_win_height);
client->telnet_has_winsize = TSTATE_REPLIED;
break;
default:
dfail("unexpected reply in telnet usboption negotiation.");
}
return iter;
}
#define ring_avail(client) (client->ringtail > client->ringhead ? RING_LENGTH - client->ringtail + client->ringhead : \
client->ringhead - client->ringtail)
#define ring_char(client, x) (client->ringbuffer[(client->ringhead+x)%RING_LENGTH])
#define ring_length(client) (client->ringtail >= client->ringhead ? client->ringtail - client->ringhead : \
RING_LENGTH - client->ringhead + client->ringtail)
#define ring_eat(client, x) (client->ringhead = (client->ringhead+x) % RING_LENGTH)
void telnet_read_ring(DESC * client)
{
int iter, complete;
unsigned char subopt_buffer[32];
dprintk
("Ring Read started, input length = %d, ringhead = %d, ringtail = %d. ",
ring_length(client), client->ringhead, client->ringtail);
while (ring_length(client) > 0) {
dprintk
("Parsing data 0x%02x, %d bytes in ring, ringhead = %d, ringtail = %d",
ring_char(client, 0), ring_length(client), client->ringhead,
client->ringtail);
switch (ring_char(client, 0)) {
case TELNET_IAC:
if(ring_length(client) < 2)
return;
dprintk("TELNET COMMADN %d", ring_char(client, 1));
switch ((unsigned char) ring_char(client, 1)) {
case TELNET_WILL:
telnet_handle_will(client, ring_char(client, 2));
ring_eat(client, 3);
break;
case TELNET_WONT:
telnet_handle_wont(client, ring_char(client, 2));
ring_eat(client, 3);
break;
case TELNET_SB:
complete = 0;
for(iter = 2; !complete && iter < ring_avail(client); iter++) {
subopt_buffer[iter - 2] = ring_char(client, iter);
if(ring_char(client, iter) == TELNET_SE) {
telnet_handle_subop(client, subopt_buffer, iter - 2);
complete = 1;
ring_eat(client, iter + 1);
}
}
if(!complete)
return;
break;
case TELNET_IAC:
client->inputbuffer[client->inputtail++] = TELNET_IAC;
default:
ring_eat(client, 2);
break;
}
break;
case 0:
ring_eat(client, 1);
break;
case '\r':
case '\n':
if(ring_length(client) > 1 && (ring_char(client, 1) == '\r' ||
ring_char(client, 1) == '\n')) {
ring_eat(client, 1);
}
if(client->inputtail == 0) {
ring_eat(client, 1);
continue;
}
client->inputbuffer[client->inputtail] = 0;
client_accept_input(client);
dprintk("Would Parse: %s", client->inputbuffer);
client->inputtail = 0;
ring_eat(client, 1);
break;
default:
client->inputbuffer[client->inputtail++] = ring_char(client, 0);
ring_eat(client, 1);
break;
}
}
if(client->ringhead == client->ringtail) {
client->ringhead = client->ringtail = 0;
}
telnet_check_options(client);
dprintk
("Ring Read finished. RingState = { head = %d, tail = %d }, InputState = { tail = %d }",
client->ringhead, client->ringtail, client->inputtail);
}
void telnet_disconnect(DESC * client)
{
network_disconnect(client);
}
void telnet_write(DESC * client, char *buffer, int length)
{
dprintk("telnet_write");
// Color translation should occur here.
network_write(client, buffer, length);
}