Project

General

Profile

Download (41.6 KB) Statistics
| Branch: | Revision:
62045e98 Hamish Coleman
/*
* wconsd.c - serial port server service for Windows NT
*
83cf653a Hamish Coleman
* Copyright (c) 2008 Hamish Coleman <hamish@zot.org>
* 2003 Benjamin Schweizer <gopher at h07 dot org>
62045e98 Hamish Coleman
* 1998 Stephen Early <Stephen.Early@cl.cam.ac.uk>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
78d4c0b0 Hamish Coleman
b6970c98 Hamish Coleman
/* Note: winsock2.h MUST be included before windows.h */

#include <winsock2.h>
08ceb73b Hamish Coleman
#include <ws2tcpip.h>
b6970c98 Hamish Coleman
#include <windows.h>
#include <winsvc.h>
#include <stdio.h>
#include <stdlib.h>
de212a70 Hamish Coleman
#include <getopt.h>
b6970c98 Hamish Coleman
e592f8f7 Hamish Coleman
#include "scm.h"

5ddf869b Hamish Coleman
#include "libcli/libcli.h"

8d5aad54 Hamish Coleman
#include "module.h"

479f1136 Hamish Coleman
#define VERSION "0.2.6"
62045e98 Hamish Coleman
78d4c0b0 Hamish Coleman
/* Size of buffers for send and receive */
#define BUFSIZE 1024
62045e98 Hamish Coleman
#define MAXLEN 1024
78d4c0b0 Hamish Coleman
/* Sockets for listening and communicating */
3eb633b1 Hamish Coleman
SOCKET ls=INVALID_SOCKET;
78d4c0b0 Hamish Coleman
/* Event objects */
3eb633b1 Hamish Coleman
HANDLE stopEvent;
78d4c0b0 Hamish Coleman
HANDLE readEvent, writeEvent;
WSAEVENT listenSocketEvent;

/* COM port */
DCB dcb;
62045e98 Hamish Coleman
COMMTIMEOUTS timeouts;
61b53eda Hamish Coleman
/* Port default settings are here */
int com_port=1;
DWORD com_speed=9600;
BYTE com_data=8;
BYTE com_parity=NOPARITY;
BYTE com_stop=ONESTOPBIT;
78d4c0b0 Hamish Coleman
b9a4e090 Hamish Coleman
int default_tcpport = 23;
61b53eda Hamish Coleman
4cb40453 Hamish Coleman
/* TODO - these buffers are ugly and large */
char *hostname[BUFSIZE];
struct hostent *host_entry;

78d4c0b0 Hamish Coleman
/* Service status: our current status, and handle on service manager */
SERVICE_STATUS wconsd_status;
SERVICE_STATUS_HANDLE wconsd_statusHandle;

25fa1d20 Hamish Coleman
479f1136 Hamish Coleman
/* these match the official telnet codes */
#define TELNET_OPTION_SB 0xfa
#define TELNET_OPTION_WILL 0xfb
#define TELNET_OPTION_WONT 0xfc
#define TELNET_OPTION_DO 0xfd
#define TELNET_OPTION_DONT 0xfe
#define TELNET_OPTION_IAC 0xff

/* these are my local state-tracking codes */
#define TELNET_OPTION_SBXX 0xfa00 /* received IAC SB xx */

ef78cec5 Hamish Coleman
#define MAXCONNECTIONS 8

2d552dd4 Hamish Coleman
int next_connection_id = 1; /* lifetime unique connection id */
aaba0a4c Hamish Coleman
int next_connection_slot = 0; /* next slot to look at for new connection */
ef78cec5 Hamish Coleman
struct connection {
884971fc Hamish Coleman
int active; /* an active entry cannot be reused */
int id; /* connection identifier */
ef78cec5 Hamish Coleman
HANDLE menuThread;
SOCKET net;
aaba0a4c Hamish Coleman
int serialconnected;
ef78cec5 Hamish Coleman
HANDLE serial;
HANDLE serialThread;
3fa88c6f Hamish Coleman
int option_runmenu; /* are we at the menu? */
479f1136 Hamish Coleman
int option_binary; /* binary transmission requested */
int option_echo; /* will we echo chars received? */
3fa88c6f Hamish Coleman
int option_keepalive; /* will we send IAC NOPs all the time? */
ef78cec5 Hamish Coleman
int net_bytes_rx;
int net_bytes_tx;
3fa88c6f Hamish Coleman
struct sockaddr *sa;
479f1136 Hamish Coleman
int telnet_option; /* Set to indicate option processing status */
int telnet_option_param;/* saved parameters from telnet options */
ef78cec5 Hamish Coleman
};
struct connection connection[MAXCONNECTIONS];

5ddf869b Hamish Coleman
struct cli_def *cli;
4dcb97c2 Hamish Coleman
int wconsd_init(int argc, char **argv);
39736270 Hamish Coleman
int wconsd_main(int argc, char **argv);
int wconsd_stop(void *);
4dcb97c2 Hamish Coleman
struct SCM_def sd = {
9c69afef Hamish Coleman
.name = "wconsd",
.desc = "wconsd - Telnet to Serial server",
.init = wconsd_init,
.main = wconsd_main,
.stop = wconsd_stop,
4dcb97c2 Hamish Coleman
};

61b53eda Hamish Coleman
/*
e6a4efc5 Hamish Coleman
* output from OutputDebugStringA can be seen using sysinternals debugview
61b53eda Hamish Coleman
* http://technet.microsoft.com/en-us/sysinternals/bb896647.aspx
9c69afef Hamish Coleman
* http://live.sysinternals.com/Dbgview.exe
61b53eda Hamish Coleman
*/
e6a4efc5 Hamish Coleman
/*
* log a debug message
*/
int dprintf_level = 1;
6731132f Hamish Coleman
int dprintf_to_stdout = 0;
e6a4efc5 Hamish Coleman
int dprintf(unsigned char severity, const char *fmt, ...) {
va_list args;
c7c4a6f7 Hamish Coleman
char buf[MAXLEN];
e6a4efc5 Hamish Coleman
int i;

if (severity > dprintf_level)
return 0;

va_start(args,fmt);
c0d1e8fe Hamish Coleman
i=vsnprintf(buf,sizeof(buf),fmt,args);
e6a4efc5 Hamish Coleman
va_end(args);

6731132f Hamish Coleman
if (dprintf_to_stdout) {
25fa1d20 Hamish Coleman
printf("%s",buf);
} else {
OutputDebugStringA(buf);
}
e6a4efc5 Hamish Coleman
return i;
78d4c0b0 Hamish Coleman
}

c7c4a6f7 Hamish Coleman
/*
884971fc Hamish Coleman
* format a string and send it to a net connection
c7c4a6f7 Hamish Coleman
*/
884971fc Hamish Coleman
int netprintf(struct connection *conn, const char *fmt, ...) {
c7c4a6f7 Hamish Coleman
va_list args;
char buf[MAXLEN];
int i;
884971fc Hamish Coleman
int bytes;
c7c4a6f7 Hamish Coleman
va_start(args,fmt);
i=vsnprintf(buf,sizeof(buf),fmt,args);
va_end(args);

884971fc Hamish Coleman
bytes = send(conn->net,buf,(i>MAXLEN)?MAXLEN-1:i,0);

if (bytes==-1) {
08ceb73b Hamish Coleman
dprintf(1,"wconsd[%i]: netprintf: send error %i\n",conn->id,GetLastError());
884971fc Hamish Coleman
} else {
conn->net_bytes_tx += bytes;
}

c7c4a6f7 Hamish Coleman
return i;
}

62045e98 Hamish Coleman
/* open the com port */
4991f810 Hamish Coleman
int open_com_port(struct connection *conn) {
62045e98 Hamish Coleman
/* Open the COM port */
char portstr[12];
aaba0a4c Hamish Coleman
if (conn->serialconnected) {
dprintf(1,"wcons[%i]: open_com_port: serialconnected\n",conn->id);
}

62045e98 Hamish Coleman
sprintf(portstr, "\\\\.\\COM%d", com_port);
aaba0a4c Hamish Coleman
conn->serial = CreateFile(portstr,
62045e98 Hamish Coleman
GENERIC_READ | GENERIC_WRITE,
0, // Exclusive access
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
aaba0a4c Hamish Coleman
if (conn->serial == INVALID_HANDLE_VALUE) {
4991f810 Hamish Coleman
return -1;
62045e98 Hamish Coleman
}

aaba0a4c Hamish Coleman
if (!GetCommState(conn->serial, &dcb)) {
4991f810 Hamish Coleman
return -1;
62045e98 Hamish Coleman
}

// Fill in the device control block
dcb.BaudRate=com_speed;
dcb.ByteSize=com_data;
dcb.Parity=com_parity; // NOPARITY, ODDPARITY, EVENPARITY
dcb.StopBits=com_stop; // ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS
dcb.fBinary=TRUE;
dcb.fOutxCtsFlow=FALSE;
dcb.fOutxDsrFlow=FALSE;
dcb.fDtrControl=DTR_CONTROL_ENABLE; // Always on
dcb.fDsrSensitivity=FALSE;
dcb.fTXContinueOnXoff=FALSE;
dcb.fOutX=FALSE;
dcb.fInX=FALSE;
dcb.fErrorChar=FALSE;
dcb.fNull=FALSE;
dcb.fRtsControl=RTS_CONTROL_ENABLE; // Always on
dcb.fAbortOnError=FALSE;

aaba0a4c Hamish Coleman
if (!SetCommState(conn->serial, &dcb)) {
4991f810 Hamish Coleman
return -1;
62045e98 Hamish Coleman
}

4cb40453 Hamish Coleman
/* FIXME - these values need much more tuning */
62045e98 Hamish Coleman
timeouts.ReadIntervalTimeout=20;
timeouts.ReadTotalTimeoutMultiplier=0;
4cb40453 Hamish Coleman
/*
* Note that this means that the serial to net thread wakes
* each and ever 50 milliseconds
*/
timeouts.ReadTotalTimeoutConstant=50;
62045e98 Hamish Coleman
timeouts.WriteTotalTimeoutMultiplier=0;
timeouts.WriteTotalTimeoutConstant=0;
aaba0a4c Hamish Coleman
if (!SetCommTimeouts(conn->serial, &timeouts)) {
4991f810 Hamish Coleman
return -1;
62045e98 Hamish Coleman
}
aaba0a4c Hamish Coleman
conn->serialconnected=1;
62045e98 Hamish Coleman
return 0;
}

/* close the com port */
aaba0a4c Hamish Coleman
void close_com_port(struct connection *conn) {
CloseHandle(conn->serial);
2d552dd4 Hamish Coleman
conn->serial=INVALID_HANDLE_VALUE;
aaba0a4c Hamish Coleman
conn->serialconnected=0;
62045e98 Hamish Coleman
}

2d552dd4 Hamish Coleman
/*
* Given an active connection, force close its
* serial port. waiting for all relevant resources
*/
void close_serial_connection(struct connection *conn) {
if (!conn->active) {
dprintf(1,"wconsd: closing closed connection %i\n",conn->id);
return;
}
3fa88c6f Hamish Coleman
conn->option_runmenu=1;
2d552dd4 Hamish Coleman
close_com_port(conn);
WaitForSingleObject(conn->serialThread,INFINITE);
CloseHandle(conn->serialThread);
conn->serialThread=NULL;
}

8d5aad54 Hamish Coleman
/* show the config for this module */
static int this_showrun(struct cli_def *cli) {
cli_print(cli, "debug level %i",dprintf_level);
cli_print(cli, "listen port %i",default_tcpport);
return CLI_OK;
}

/* NOTE: this function is replicated in show_status */
static int cmd_showport(struct cli_def *cli, char *command, char *argv[], int argc) {

cli_print(cli, "status:");
cli_print(cli, " port=%d speed=%d data=%d parity=%d stop=%d",
com_port, com_speed, com_data, com_parity, com_stop);

/* FIXME - need to associate a connection object with a cli object */
#if 0
if(conn->serialconnected) {
cli_print(cli, " state=open");
} else {
cli_print(cli, " state=closed");
}
cli_print(cli," ");
cli_print(cli," connectionid=%i hostname=%s",conn->id,hostname);
cli_print(cli," echo=%i binary=%i keepalive=%i",
conn->option_echo,conn->option_binary,conn->option_keepalive);
#endif
cli_print(cli," ");
return CLI_OK;
}

static int cmd_debuglevel(struct cli_def *cli, char *command, char *argv[], int argc) {
cli_print(cli,"Current dprintf_level=%i",dprintf_level);
return CLI_OK;
}

static int cmd_cdebuglevel(struct cli_def *cli, char *command, char *argv[], int argc) {
dprintf_level = atoi(argv[0]);
return CLI_OK;
}

static int cmd_cidle(struct cli_def *cli, char *command, char *argv[], int argc) {
cli_set_idle_timeout(cli, atoi(argv[0]));
return CLI_OK;
}

static int cmd_conntable(struct cli_def *cli, char *command, char *argv[], int argc) {
int i;
cli_print(cli,
"Flags: A - Active Slot, S - Serial active,");
cli_print(cli,
" M - Run Menu, B - Binary transmission, E - Echo enabled,");
cli_print(cli,
" K - Telnet Keepalives, * - This connection");
cli_print(cli," ");
cli_print(cli, "s flags id mThr net serial serialTh netrx nettx peer address");
cli_print(cli, "- ------ -- ---- ---- ------ -------- ----- ----- ------------");
for (i=0;i<MAXCONNECTIONS;i++) {
cli_print(cli,"%i%c%c%c%c%c%c%c %2i %4i %4i %6i %8i %5i %5i %s:%i",
i,
' ',
connection[i].active?'A':' ',
connection[i].serialconnected?'S':' ',
connection[i].option_runmenu?'M':' ',
connection[i].option_binary?'B':' ',
connection[i].option_echo?'E':' ',
connection[i].option_keepalive?'K':' ',
connection[i].id,

connection[i].menuThread,
connection[i].net,

connection[i].serial,
connection[i].serialThread,
connection[i].net_bytes_rx,
connection[i].net_bytes_tx,
/* FIXME - IPv4 Specific */
connection[i].sa?inet_ntoa(((struct sockaddr_in*)connection[i].sa)->sin_addr):"",
connection[i].sa?htons(((struct sockaddr_in*)connection[i].sa)->sin_port):0
);
}
return CLI_OK;
}

/* Our local module definition */
static struct module_def this_module = {
.name = "wconsd",
.desc = "Main wconsd program",
.showrun = this_showrun,
};

/*
* Go through the available modules and initialise them all, causing
* them to register their resources
*
* TODO - decide if we want to get silly and use a magic initialiser list here
*/
static void initialise_all_modules(struct cli_def *cli) {
modules_init(cli); /* done first, to register the parents */

/*
* register stuff from the main program
* TODO - move appropriate things into modules
*/

cli_register_command(cli, lookup_parent("show"), "connection table", cmd_conntable,
PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Connection Table");

cli_register_command(cli, lookup_parent("show"), "port", cmd_showport,
PRIVILEGE_UNPRIVILEGED, MODE_EXEC, "Default serial configuration");

cli_register_command(cli, lookup_parent("debug"), "level", cmd_debuglevel,
PRIVILEGE_PRIVILEGED, MODE_EXEC, "Logging output level");

register_parent("config debug",
cli_register_command(cli, NULL, "debug", NULL, PRIVILEGE_PRIVILEGED,
MODE_CONFIG, "Debug Options"));

cli_register_command(cli, lookup_parent("config debug"), "level", cmd_cdebuglevel,
PRIVILEGE_PRIVILEGED, MODE_CONFIG, "Logging output level");

cli_register_command(cli, NULL, "idle", cmd_cidle,
PRIVILEGE_PRIVILEGED, MODE_CONFIG, "idle timeout");

register_module(&this_module);
}

de212a70 Hamish Coleman
static void usage(const char *name, struct option *option)
{
printf("Usage: %s [-i pathname | -r | -d | -p port ]\n",name);
printf("Just start with no options to start server\n");
printf(" -i install service 'wconsd'\n");
printf(" -r remove service 'wconsd'\n");
printf(" -d run wconsd in foreground mode\n");
printf(" -p port listen on the given port in foreground mode\n");

printf("\n");
while(option->name) {
printf("--%s%s ",option->name,option->has_arg?"=VAL":"");
option++;
}
printf("\n\n");

}

56177514 Hamish Coleman
static int do_getopt(const int argc, char **argv) {
de212a70 Hamish Coleman
static struct option long_options[] = {
{"install", 0, 0, 'i'},
{"remove", 0, 0, 'r'},
{"debug", 2, 0, 'd'},
{"port", 1, 0, 'p'},
{0,0,0,0}
};

while(1) {
int c = getopt_long(argc,argv, "ird::p:",
long_options,NULL);
if (c==-1)
break;

switch(c) {
case 'i': {
/* request service installation */
368d31d4 Hamish Coleman
char *path = SCM_Install(&sd,NULL);
de212a70 Hamish Coleman
if (!path) {
printf("Service installation failed\n");
51ddf380 Hamish Coleman
return 2;
de212a70 Hamish Coleman
}
printf("Service '%s' installed, binary path '%s'\n",sd.name,path);
printf("You should now start the service using the service manager.\n");
51ddf380 Hamish Coleman
return 1;
de212a70 Hamish Coleman
}
case 'r':
// request service removal
if (SCM_Remove(&sd)==0) {
printf("Deleted service '%s'\n",sd.name);
} else {
printf("Service removal failed\n");
}
51ddf380 Hamish Coleman
return 1;
de212a70 Hamish Coleman
case 'd':
if (optarg) {
dprintf_level = atoi(optarg);
}
break;
case 'p':
if (optarg) {
default_tcpport = atoi(optarg);
}
break;
default:
usage("wconsd",long_options);
51ddf380 Hamish Coleman
return 1;
de212a70 Hamish Coleman
}
}
51ddf380 Hamish Coleman
return 0;
de212a70 Hamish Coleman
}

39736270 Hamish Coleman
int wconsd_stop(void *param1) {
4dcb97c2 Hamish Coleman
SetEvent(stopEvent);
return 0;
}
62045e98 Hamish Coleman
5ddf869b Hamish Coleman
int libcli_test(struct cli_def *cli, char *command, char *argv[], int argc) {
int i;
cli_print(cli, "called %s with \"%s\"", __FUNCTION__, command);
cli_print(cli, "%d arguments:", argc);
for (i = 0; i < argc; i++)
cli_print(cli, " %s", argv[i]);

return CLI_OK;
}


78d4c0b0 Hamish Coleman
/* Initialise wconsd: open a listening socket and the COM port, and
* create lots of event objects. */
4dcb97c2 Hamish Coleman
int wconsd_init(int argc, char **argv) {
78d4c0b0 Hamish Coleman
struct sockaddr_in sin;
WORD wVersionRequested;
WSADATA wsaData;
int err;

9c69afef Hamish Coleman
if (sd.mode==SVC_CONSOLE) {
211ce618 Hamish Coleman
/* We are running in a command-line mode */
dprintf_to_stdout=1;

dprintf(1,"\n"
"wconsd: Serial Console server (version %s)\n",VERSION);
dprintf(1,
" (see http://wob.zot.org/2/wiki/wconsd for more info)\n\n");

dprintf(1,"wconsd: Foreground mode\n");
}

2e5a1c8d Hamish Coleman
/* setup the libcli early so that modules can use it */
if (!(cli = cli_init())) {
dprintf(1,"wconsd: wconsd_init: failed run cli_init\n");
return 13;
}
8d5aad54 Hamish Coleman
initialise_all_modules(cli);
2e5a1c8d Hamish Coleman
56177514 Hamish Coleman
/* handle commandline options */
51ddf380 Hamish Coleman
if (do_getopt(argc,argv)) {
/* do_getopt returns nonzero if we should not continue */
return 1;
}
de212a70 Hamish Coleman
78d4c0b0 Hamish Coleman
/* Start up sockets */
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
return 1;
}
/* Confirm that the WinSock DLL supports 2.2.*/
/* Note that if the DLL supports versions greater */
/* than 2.2 in addition to 2.2, it will still return */
/* 2.2 in wVersion since that is the version we */
/* requested. */
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
WSACleanup( );
return 2;
}

/* The WinSock DLL is acceptable. Proceed. */

// Create the event object used to signal service shutdown
stopEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (stopEvent==NULL) {
return 3;
}
// Event objects for overlapped IO
readEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (readEvent==NULL) {
return 6;
}
writeEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
if (writeEvent==NULL) {
return 7;
}
// Create the event object for socket operations
listenSocketEvent = WSACreateEvent();
if (listenSocketEvent==WSA_INVALID_EVENT) {
return 8;
}

306304a2 Hamish Coleman
if (gethostname((char *)hostname,sizeof(hostname))==SOCKET_ERROR) {
return 1;
}
dprintf(1,"wconsd: Hostname is %s\n",hostname);

host_entry=gethostbyname((char *)hostname);
if (host_entry->h_addrtype==AF_INET) {
dprintf(1,"wconsd: IP Address is %s\n",inet_ntoa (*(struct in_addr *)*host_entry->h_addr_list));
/* FIXME - enumerate all the IP addresses from the list */
} else {
host_entry=0;
return 0;
}

78d4c0b0 Hamish Coleman
/* Create a socket to listen for connections. */
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
62727af2 Hamish Coleman
sin.sin_port=htons(default_tcpport);
78d4c0b0 Hamish Coleman
ls=socket(AF_INET,SOCK_STREAM,0);
c4d01aaa Hamish Coleman
/*
FIXME:
you can open sockets on Windows so that they support read/write. Just create it with

fh = WSASocket (domain, type, protocol, NULL, 0, 0);


instead of socket.
(see http://lists.gnu.org/archive/html/qemu-devel/2010-01/msg00383.html)
*/
78d4c0b0 Hamish Coleman
if (ls==INVALID_SOCKET) {
return 9;
}
ef78cec5 Hamish Coleman
#ifndef MS_WINDOWS
{
int one=1;
setsockopt(ls,SOL_SOCKET,SO_REUSEADDR,(void*)&one,sizeof(one));
}
#endif
78d4c0b0 Hamish Coleman
if (bind(ls,(struct sockaddr *)&sin,sizeof(sin))==SOCKET_ERROR) {
ef78cec5 Hamish Coleman
dprintf(1,"wconsd: wconsd_init: failed to bind socket\n");
78d4c0b0 Hamish Coleman
return 10;
}
if (listen(ls,1)==SOCKET_ERROR) {
return 11;
}
306304a2 Hamish Coleman
dprintf(1,"wconsd: listening on port %i\n",default_tcpport);

78d4c0b0 Hamish Coleman
/* Mark the socket as non-blocking */
if (WSAEventSelect(ls,listenSocketEvent,FD_ACCEPT)==SOCKET_ERROR) {
return 12;
}
4cb40453 Hamish Coleman
5ddf869b Hamish Coleman
cli_set_banner(cli, "wconsd serial to telnet");
cli_set_hostname(cli, (char *)hostname);
cli_set_idle_timeout(cli, 60);
cli_register_command(cli, NULL, "test", libcli_test, PRIVILEGE_UNPRIVILEGED,
MODE_EXEC, NULL);
#if 0
cli_set_auth_callback(cli, check_auth);
cli_set_enable_callback(cli, check_enable);
#endif

78d4c0b0 Hamish Coleman
return 0;
}

3eb633b1 Hamish Coleman
/*
479f1136 Hamish Coleman
* telnet option receiver state machine.
* Called with the current char, it saves the intermediate state in the
* conn struct.
*
* Returns 0 to indicate no echo of this char, or 0xff to indicate that the
* char should be echoed.
3eb633b1 Hamish Coleman
*/
479f1136 Hamish Coleman
unsigned char process_telnet_option(struct connection*conn, unsigned char ch) {
dprintf(2,"wconsd[%i]: debug option (0x%02x) 0x%02x\n",conn->id,conn->telnet_option,ch);

switch (conn->telnet_option) {
case 0: /* received nothing */
if (ch==TELNET_OPTION_IAC) {
conn->telnet_option=ch;
return 0; /* dont echo */
997564ce Hamish Coleman
}
479f1136 Hamish Coleman
return 0xff; /* ECHO */

case TELNET_OPTION_IAC: /* recived IAC */
switch(ch) {
case 0xf0: /* suboption end */
case 0xf1: /* NOP */
case 0xf2: /* Data Mark */
case 0xf5: /* abort output */
case 0xf7: /* erase character */
case 0xf8: /* erase line */
case 0xf9: /* go ahead */
dprintf(1,"wconsd[%i]: option IAC %i\n",conn->id,ch);
conn->telnet_option=0;
return 0; /* dont echo */

case 0xf3: /* Break */
if (conn->serialconnected) {
dprintf(2,"wconsd[%i]: send break\n",conn->id);
Sleep(1000);
SetCommBreak(conn->serial);
Sleep(1000);
ClearCommBreak(conn->serial);
}
conn->telnet_option=0;
return 0; /* dont echo */

case 0xf4: /* Interrupt */
conn->option_runmenu=1;
conn->telnet_option=0;
return 0; /* dont echo */

case 0xf6: /* are you there */
dprintf(1,"wconsd[%i]: option IAC AYT\n",conn->id);
netprintf(conn,"yes\r\n");
conn->telnet_option=0;
return 0; /* dont echo */

case TELNET_OPTION_SB:
case TELNET_OPTION_WILL:
case TELNET_OPTION_WONT:
case TELNET_OPTION_DO:
case TELNET_OPTION_DONT:
conn->telnet_option=ch;
return 0; /* dont echo */

case 0xff: /* send ff */
conn->telnet_option=0;
return ch; /* ECHO */

default:
dprintf(1,"wconsd[%i]: option IAC %i invalid\n",conn->id,ch);
conn->telnet_option=0;
return 0; /* dont echo */
3eb633b1 Hamish Coleman
}
479f1136 Hamish Coleman
case TELNET_OPTION_SB: /* received IAC SB 0xfa */
conn->telnet_option=TELNET_OPTION_SBXX;
conn->telnet_option_param=ch;
return 0; /* dont echo */

case TELNET_OPTION_WILL: /* received IAC WILL 0xfb */
dprintf(2,"wconsd[%i]: option IAC WILL %i\n",conn->id,ch);
conn->telnet_option=0;
return 0; /* dont echo */
case TELNET_OPTION_WONT: /* received IAC WONT 0xfc */
dprintf(1,"wconsd[%i]: option IAC WONT %i\n",conn->id,ch);
conn->telnet_option=0;
return 0; /* dont echo */
case TELNET_OPTION_DO: /* received IAC DO 0xfd */
switch (ch) {
case 0x00: /* Binary */
dprintf(2,"wconsd[%i]: DO Binary\n",conn->id);
conn->option_binary=1;
3eb633b1 Hamish Coleman
case 0x01: /* ECHO */
4cb40453 Hamish Coleman
dprintf(2,"wconsd[%i]: DO ECHO\n",conn->id);
3eb633b1 Hamish Coleman
conn->option_echo=1;
break;
479f1136 Hamish Coleman
default:
dprintf(2,"wconsd[%i]: option IAC DO %i\n",conn->id,ch);
3eb633b1 Hamish Coleman
break;
}
479f1136 Hamish Coleman
conn->telnet_option=0;
return 0; /* dont echo */
case TELNET_OPTION_DONT: /* received IAC DONT 0xfe */
switch (ch) {
case 0x00: /* Binary */
dprintf(2,"wconsd[%i]: DONT Binary\n",conn->id);
conn->option_binary=0;
3eb633b1 Hamish Coleman
case 0x01: /* ECHO */
dprintf(1,"wconsd[%i]: DONT ECHO\n",conn->id);
conn->option_echo=0;
break;
default:
479f1136 Hamish Coleman
dprintf(2,"wconsd[%i]: option IAC DONT %i\n",conn->id,ch);
break;
3eb633b1 Hamish Coleman
}
479f1136 Hamish Coleman
conn->telnet_option=0;
return 0; /* dont echo */

3eb633b1 Hamish Coleman
default:
479f1136 Hamish Coleman
if (conn->telnet_option==TELNET_OPTION_SBXX) {
/* received IAC SB x */
int option = conn->telnet_option_param;

if (ch==0) {
/* IS */
dprintf(1,"wconsd[%i]: option IAC SB %i IS\n",
conn->id,option);
/*
* TODO - stay in telnet option mode while
* absorbing the IS buffer
*/
conn->telnet_option=0;
return 0; /* dont echo */
} else if (ch>1) {
/* error ? */
dprintf(1,"wconsd[%i]: option IAC SB %i %i\n",
conn->id,option,ch);
conn->telnet_option=0;
return 0; /* dont echo */
}

/* SEND */
if (option == 5) {
dprintf(1,"wconsd[%i]: option IAC SB 5 SEND\n",conn->id);
/* FIXME - add option_binary */
netprintf(conn,"%s%c%s%s%s",
"\xff\xfa\x05",
0,
"\xfb\x05",
conn->option_echo?"\xfb\x01":"",
"\xff\xf0");
conn->telnet_option=0;
return 0; /* dont echo */
} else {
/* error ? */
dprintf(1,"wconsd[%i]: option IAC SB %i %i\n",
conn->id,option,ch);
conn->telnet_option=0;
return 0; /* dont echo */
}
}

dprintf(1,"wconsd[%i]: invalid conn->telnet_option==%i \n",conn->id,conn->telnet_option);

3eb633b1 Hamish Coleman
}

/* not normally reached */
return 3;
}

/*
* Wrap up all the crazy file writing process in a function
*/
int serial_writefile(struct connection *conn,OVERLAPPED *o,unsigned char *buf,int size) {
DWORD wsize;

if (!conn->serialconnected) {
dprintf(1,"wconsd[%i]: serial_writefile but serial closed\n",conn->id);
return 0;
}

if (!WriteFile(conn->serial,buf,size,&wsize,o)) {
if (GetLastError()==ERROR_IO_PENDING) {
// Wait for it...
if (!GetOverlappedResult(conn->serial,o,&wsize,TRUE)) {
dprintf(1,"wconsd[%i]: Error %d (overlapped) writing to COM port\n",conn->id,GetLastError());
}
} else {
dprintf(1,"wconsd[%i]: Error %d writing to COM port\n",conn->id,GetLastError());
}
}
if (wsize!=size) {
dprintf(1,"wconsd[%i]: Eeek! WriteFile: wrote %d of %d\n",conn->id,wsize,size);
}
return wsize;
}

78d4c0b0 Hamish Coleman
DWORD WINAPI wconsd_net_to_com(LPVOID lpParam)
{
aaba0a4c Hamish Coleman
struct connection * conn = (struct connection*)lpParam;
c0d1e8fe Hamish Coleman
unsigned char buf[BUFSIZE];
3eb633b1 Hamish Coleman
unsigned char *pbuf;
int bytes_to_scan;
DWORD size;
78d4c0b0 Hamish Coleman
unsigned long zero=0;
fd_set s;
aaba0a4c Hamish Coleman
struct timeval tv;
78d4c0b0 Hamish Coleman
OVERLAPPED o={0};

4cb40453 Hamish Coleman
dprintf(1,"wconsd[%i]: debug: start wconsd_net_to_com\n",conn->id);
884971fc Hamish Coleman
78d4c0b0 Hamish Coleman
o.hEvent = writeEvent;
f6cc8fbf Hamish Coleman
while (conn->serialconnected) {
78d4c0b0 Hamish Coleman
/* There's a bug in some versions of Windows which leads
* to recv() returning -1 and indicating error WSAEWOULDBLOCK,
* even on a blocking socket. This select() is here to work
* around that bug. */
aaba0a4c Hamish Coleman
/*
* These sockets are non-blocking, so I am unsure that the
* above statement is still true
*/
FD_SET(conn->net,&s);
3eb633b1 Hamish Coleman
tv.tv_sec = 2;
aaba0a4c Hamish Coleman
tv.tv_usec = 0;
select(0,&s,NULL,NULL,&tv);
306304a2 Hamish Coleman
/* TODO - examine the retval for the select */
aaba0a4c Hamish Coleman
size=recv(conn->net,(void*)&buf,BUFSIZE,0);
78d4c0b0 Hamish Coleman
if (size==0) {
aaba0a4c Hamish Coleman
closesocket(conn->net);
conn->net=INVALID_SOCKET;
dprintf(1,"wconsd[%i]: wconsd_net_to_com size==0\n",conn->id);
f6cc8fbf Hamish Coleman
return 0;
78d4c0b0 Hamish Coleman
}
if (size==SOCKET_ERROR) {
306304a2 Hamish Coleman
int err = WSAGetLastError();
switch (err) {
case WSAEWOULDBLOCK:
/* ignore */
3fa88c6f Hamish Coleman
if (conn->option_keepalive) {
netprintf(conn,"\xff\xf1");
}
306304a2 Hamish Coleman
continue;
3fa88c6f Hamish Coleman
case WSAECONNRESET:
closesocket(conn->net);
conn->net=INVALID_SOCKET;
479f1136 Hamish Coleman
return 0;
306304a2 Hamish Coleman
default:
dprintf(1,"wconsd[%i]: net_to_com socket error (%i)\n",conn->id,err);
/* General paranoia about blocking sockets */
ioctlsocket(conn->net,FIONBIO,&zero);
}
aaba0a4c Hamish Coleman
continue;
78d4c0b0 Hamish Coleman
}
aaba0a4c Hamish Coleman
conn->net_bytes_rx+=size;

3eb633b1 Hamish Coleman
/*
* Scan for telnet options and process then remove them
* This loop is reasonably fast if there are no options,
* but recursively slow if there are options
479f1136 Hamish Coleman
*
* TODO - if an option is mid packet, this could change
* the semantics of processing at the wrong point
3eb633b1 Hamish Coleman
*/
pbuf=buf;
bytes_to_scan=size;
479f1136 Hamish Coleman
while(bytes_to_scan--) {
/* TODO - use ->telnet_option || 0xff to chose to call process_telnet_option */
if(!process_telnet_option(conn,*pbuf)) {
/* remove this byte from the buffer */
memmove(pbuf,pbuf+1,bytes_to_scan);
size--;
continue;
3eb633b1 Hamish Coleman
}
479f1136 Hamish Coleman
pbuf++;
3eb633b1 Hamish Coleman
}

/*
* Scan for CR NUL sequences and uncook them
201f6e65 Hamish Coleman
* it also appears that I need to uncook CR LF sequences
3eb633b1 Hamish Coleman
*/
479f1136 Hamish Coleman
if (!conn->option_binary) {
pbuf=buf;
bytes_to_scan=size;
while ((pbuf=memchr(pbuf,0x0d,bytes_to_scan))!=NULL) {
pbuf++;
if (*pbuf!=0x00&&*pbuf!=0x0a) {
continue;
}
3eb633b1 Hamish Coleman
479f1136 Hamish Coleman
bytes_to_scan = size-(pbuf-buf)+1;
size -= 1;
memmove(pbuf,pbuf+1,bytes_to_scan);
}
/* TODO - emulate cisco's ctrl-^,x sequence for exit to menu */
3eb633b1 Hamish Coleman
}

aaba0a4c Hamish Coleman
3fa88c6f Hamish Coleman
if (conn->option_runmenu) {
f6cc8fbf Hamish Coleman
/*
* If processing the telnet options has caused
* runmenu to be set, we exit the loop here
*/
3eb633b1 Hamish Coleman
return 0;
aaba0a4c Hamish Coleman
}

if (!conn->serialconnected) {
dprintf(1,"wconsd[%i]: data to send, but serial closed\n",conn->id);
continue;
}

3eb633b1 Hamish Coleman
/*
* we could check the return value to see if there was a
* short write, but what would our options be?
*/
serial_writefile(conn,&o,buf,size);
78d4c0b0 Hamish Coleman
}
4cb40453 Hamish Coleman
dprintf(1,"wconsd[%i]: debug: finish wconsd_net_to_com\n",conn->id);
78d4c0b0 Hamish Coleman
return 0;
}

DWORD WINAPI wconsd_com_to_net(LPVOID lpParam)
{
aaba0a4c Hamish Coleman
struct connection * conn = (struct connection*)lpParam;
c0d1e8fe Hamish Coleman
unsigned char buf[BUFSIZE];
78d4c0b0 Hamish Coleman
DWORD size;
OVERLAPPED o={0};

o.hEvent=readEvent;

4cb40453 Hamish Coleman
dprintf(1,"wconsd[%i]: debug: start wconsd_com_to_net\n",conn->id);
884971fc Hamish Coleman
f6cc8fbf Hamish Coleman
while (conn->serialconnected) {
aaba0a4c Hamish Coleman
if (!ReadFile(conn->serial,buf,BUFSIZE,&size,&o)) {
78d4c0b0 Hamish Coleman
if (GetLastError()==ERROR_IO_PENDING) {
// Wait for overlapped operation to complete
aaba0a4c Hamish Coleman
if (!GetOverlappedResult(conn->serial,&o,&size,TRUE)) {
9b2f3609 Hamish Coleman
dprintf(1,"wconsd: Error %d (overlapped) reading from COM port\n",GetLastError());
78d4c0b0 Hamish Coleman
}
} else {
aaba0a4c Hamish Coleman
dprintf(1,"wconsd[%i]: Error %d reading from COM port\n",conn->id,GetLastError());
conn->serialconnected=0;
continue;
78d4c0b0 Hamish Coleman
}
}
3eb633b1 Hamish Coleman
/* We might not have any data if the ReadFile timed out */
78d4c0b0 Hamish Coleman
if (size>0) {
f6cc8fbf Hamish Coleman
if (send(conn->net,(void*)&buf,size,0)==-1) {
dprintf(1,"wconsd[%i]: wconsd_com_to_net send failed\n",conn->id);
return 0;
3eb633b1 Hamish Coleman
}
aaba0a4c Hamish Coleman
conn->net_bytes_tx+=size;
78d4c0b0 Hamish Coleman
}
}
4cb40453 Hamish Coleman
dprintf(1,"wconsd[%i]: debug: finish wconsd_com_to_net\n",conn->id);
78d4c0b0 Hamish Coleman
return 0;
62045e98 Hamish Coleman
}

f6cc8fbf Hamish Coleman
void cmd_open(struct connection *conn) {
dprintf(1,"wconsd[%i]: debug: start cmd_open\n",conn->id);
4991f810 Hamish Coleman
if (!conn->serialconnected) {
if (open_com_port(conn)) {
netprintf(conn,"error: cannot open port\r\n\n");
f6cc8fbf Hamish Coleman
return;
4991f810 Hamish Coleman
}
}

netprintf(conn,"\r\n\n");

PurgeComm(conn->serial,PURGE_RXCLEAR|PURGE_RXABORT);
if (conn->serialThread==NULL) {
/* we might already have a com_to_net thread */
conn->serialThread=CreateThread(NULL,0,wconsd_com_to_net,conn,0,NULL);
}

f6cc8fbf Hamish Coleman
conn->option_runmenu=0;
wconsd_net_to_com(conn);
4991f810 Hamish Coleman
conn->option_runmenu=1;
}

884971fc Hamish Coleman
void send_help(struct connection *conn) {
aaba0a4c Hamish Coleman
netprintf(conn,
4cb40453 Hamish Coleman
"NOTE: the commands will be changing in the next version\r\n"
"\r\n"
"available commands:\r\n"
aaba0a4c Hamish Coleman
"\r\n"
479f1136 Hamish Coleman
"binary - toggle the binary comms mode\r\n"
4cb40453 Hamish Coleman
"close - Stop serial communications\r\n"
"copyright - Print the copyright notice\r\n"
"data - Set number of data bits\r\n"
"help - This guff\r\n"
"kill_conn - Stop a given connection's serial communications\r\n"
479f1136 Hamish Coleman
"keepalive - toggle the generation of keepalive packets\r\n"
4cb40453 Hamish Coleman
"open - Connect or resume communications with a serial port\r\n"
"parity - Set the serial parity\r\n"
"port - Set serial port number\r\n"
"quit - exit from this session\r\n"
"show_conn_table - Show the connections table\r\n"
"speed - Set serial port speed\r\n"
"status - Show current serial port status\r\n"
"stop - Set number of stop bits\r\n"
9c5b7bd0 Hamish Coleman
"\r\n"
"see http://wob.zot.org/2/wiki/wconsd for more information\r\n"
4cb40453 Hamish Coleman
"\r\n");
53110989 Hamish Coleman
}

8d5aad54 Hamish Coleman
/* NOTE: this function is replicated in cmd_showport */
c7c4a6f7 Hamish Coleman
void show_status(struct connection* conn) {
/* print the status to the net connection */

884971fc Hamish Coleman
netprintf(conn, "status:\r\n\n"
4cb40453 Hamish Coleman
" port=%d speed=%d data=%d parity=%d stop=%d\r\n",
c7c4a6f7 Hamish Coleman
com_port, com_speed, com_data, com_parity, com_stop);

aaba0a4c Hamish Coleman
if(conn->serialconnected) {
4cb40453 Hamish Coleman
netprintf(conn, " state=open\r\n\n");
c7c4a6f7 Hamish Coleman
} else {
4cb40453 Hamish Coleman
netprintf(conn, " state=closed\r\n\n");
c7c4a6f7 Hamish Coleman
}
3fa88c6f Hamish Coleman
netprintf(conn," connectionid=%i hostname=%s\r\n",conn->id,hostname);
479f1136 Hamish Coleman
netprintf(conn," echo=%i binary=%i keepalive=%i\r\n",
conn->option_echo,conn->option_binary,conn->option_keepalive);
3fa88c6f Hamish Coleman
netprintf(conn,"\r\n");
c7c4a6f7 Hamish Coleman
}

/*
* I thought that I would need this a lot, but it turns out that there
* was a lot of duplicated code
*/
884971fc Hamish Coleman
int check_atoi(char *p,int old_value,struct connection *conn,char *error) {
c7c4a6f7 Hamish Coleman
if (!p) {
884971fc Hamish Coleman
netprintf(conn,error);
c7c4a6f7 Hamish Coleman
return old_value;
}

return atoi(p);
53110989 Hamish Coleman
}

2d552dd4 Hamish Coleman
void process_menu_line(struct connection*conn, char *line) {
d51f658a Hamish Coleman
char *command;
char *parameter1;

c7c4a6f7 Hamish Coleman
/*
* FIXME - non re-entrant code
*
4450bbca Hamish Coleman
* my windows build environment does not have strtok_r
c7c4a6f7 Hamish Coleman
* and thus I am running these two strtok as close as possible.
* I am definitely not going to re-invent the wheel with my own
* code.
*/
d51f658a Hamish Coleman
command = strtok(line," ");
parameter1 = strtok(NULL," ");

if (!strcmp(command, "help") || !strcmp(command, "?")) {
// help
884971fc Hamish Coleman
send_help(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "status")) {
// status
c7c4a6f7 Hamish Coleman
show_status(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "copyright")) { // copyright
884971fc Hamish Coleman
netprintf(conn,
c7c4a6f7 Hamish Coleman
" Copyright (c) 2008 by Hamish Coleman <hamish@zot.org>\r\n"
" 2003 by Benjamin Schweizer <gopher at h07 dot org>\r\n"
" 1998 by Stephen Early <Stephen.Early@cl.cam.ac.uk>\r\n"
"\r\n"
"\r\n"
" This program is free software; you can redistribute it and/or modify\r\n"
" it under the terms of the GNU General Public License as published by\r\n"
" the Free Software Foundation; either version 2 of the License, or\r\n"
" (at your option) any later version.\r\n"
"\r\n"
" This program is distributed in the hope that it will be useful,\r\n"
" but WITHOUT ANY WARRANTY; without even the implied warranty of\r\n"
" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r\n"
" GNU General Public License for more details.\r\n"
"\r\n"
" You should have received a copy of the GNU General Public License\r\n"
" along with this program; if not, write to the Free Software\r\n"
" Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r\n"
"\n");
d51f658a Hamish Coleman
} else if (!strcmp(command, "port")) { // port
884971fc Hamish Coleman
int new = check_atoi(parameter1,com_port,conn,"must specify a port\r\n");
c7c4a6f7 Hamish Coleman
if (new >= 1 && new <= 16) {
com_port=new;
d51f658a Hamish Coleman
}
} else if (!strcmp(command, "speed")) { // speed
884971fc Hamish Coleman
com_speed = check_atoi(parameter1,com_port,conn,"must specify a speed\r\n");
d51f658a Hamish Coleman
} else if (!strcmp(command, "data")) { // data
4cb40453 Hamish Coleman
if (!parameter1) {
netprintf(conn,"Please specify number of data bits {5,6,7,8}\r\n");
return;
}
d51f658a Hamish Coleman
if (!strcmp(parameter1, "5")) {
com_data=5;
} else if (!strcmp(parameter1, "6")) {
com_data=6;
} else if (!strcmp(parameter1, "7")) {
com_data=7;
} else if (!strcmp(parameter1, "8")) {
com_data=8;
}
c7c4a6f7 Hamish Coleman
show_status(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "parity")) { // parity
4cb40453 Hamish Coleman
if (!parameter1) {
netprintf(conn,"Please specify the parity {no,even,odd,mark,space}\r\n");
return;
}
d51f658a Hamish Coleman
if (!strcmp(parameter1, "no") || !strcmp(parameter1, "0")) {
com_parity=NOPARITY;
} else if (!strcmp(parameter1, "even") || !strcmp(parameter1, "2")) {
com_parity=EVENPARITY;
} else if (!strcmp(parameter1, "odd") || !strcmp(parameter1, "1")) {
com_parity=ODDPARITY;
} else if (!strcmp(parameter1, "mark")) {
com_parity=MARKPARITY;
} else if (!strcmp(parameter1, "space")) {
com_parity=SPACEPARITY;
}
c7c4a6f7 Hamish Coleman
show_status(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "stop")) {
4cb40453 Hamish Coleman
if (!parameter1) {
netprintf(conn,"Please specify the number of stop bits {1,1.5,2}\r\n");
return;
}
d51f658a Hamish Coleman
if (!strcmp(parameter1, "one") || !strcmp(parameter1, "1")) {
com_stop=ONESTOPBIT;
} else if (!strcmp(parameter1, "one5") || !strcmp(parameter1, "1.5")) {
com_stop=ONE5STOPBITS;
} else if (!strcmp(parameter1, "two") || !strcmp(parameter1, "2")) {
com_stop=TWOSTOPBITS;
}
c7c4a6f7 Hamish Coleman
show_status(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "open")) { // open
4cb40453 Hamish Coleman
int new = check_atoi(parameter1,com_port,conn,"Opening default port\r\n");
c7c4a6f7 Hamish Coleman
if (new >= 1 && new <= 16) {
com_port=new;
d51f658a Hamish Coleman
}
f6cc8fbf Hamish Coleman
cmd_open(conn);
d51f658a Hamish Coleman
} else if (!strcmp(command, "close")) { // close
2d552dd4 Hamish Coleman
close_serial_connection(conn);
884971fc Hamish Coleman
netprintf(conn,"info: actual com port closed\r\n\n");
b0f1897b Hamish Coleman
} else if (!strcmp(command, "quit")) {
// quit the connection
3fa88c6f Hamish Coleman
conn->option_runmenu=0;
aaba0a4c Hamish Coleman
closesocket(conn->net);
conn->net=INVALID_SOCKET;
2d552dd4 Hamish Coleman
return;
3fa88c6f Hamish Coleman
} else if (!strcmp(command, "keepalive")) {
conn->option_keepalive=!conn->option_keepalive;
return;
479f1136 Hamish Coleman
} else if (!strcmp(command, "binary")) {
conn->option_binary=!conn->option_binary;
return;
884971fc Hamish Coleman
} else if (!strcmp(command, "show_conn_table")) {
int i;
netprintf(conn,
f6cc8fbf Hamish Coleman
"Flags: A - Active Slot, S - Serial active,\r\n"
479f1136 Hamish Coleman
" M - Run Menu, B - Binary transmission, E - Echo enabled,\r\n"
" K - Telnet Keepalives, * - This connection\r\n"
3fa88c6f Hamish Coleman
"\r\n");
08ceb73b Hamish Coleman
netprintf(conn,
f6cc8fbf Hamish Coleman
"s flags id mThr net serial serialTh netrx nettx peer address\r\n");
3fa88c6f Hamish Coleman
netprintf(conn,
f6cc8fbf Hamish Coleman
"- ------ -- ---- ---- ------ -------- ----- ----- ------------\r\n");
884971fc Hamish Coleman
for (i=0;i<MAXCONNECTIONS;i++) {
f6cc8fbf Hamish Coleman
netprintf(conn,"%i%c%c%c%c%c%c%c %2i %4i ",
3fa88c6f Hamish Coleman
i,
&connection[i]==conn?'*':' ',
connection[i].active?'A':' ',
connection[i].serialconnected?'S':' ',
connection[i].option_runmenu?'M':' ',
479f1136 Hamish Coleman
connection[i].option_binary?'B':' ',
3fa88c6f Hamish Coleman
connection[i].option_echo?'E':' ',
connection[i].option_keepalive?'K':' ',
connection[i].id,

connection[i].menuThread);
f6cc8fbf Hamish Coleman
netprintf(conn,"%4i ", connection[i].net);

3fa88c6f Hamish Coleman
if (connection[i].serialconnected) {
netprintf(conn,"%6i %8i ",
connection[i].serial,
connection[i].serialThread);
} else {
netprintf(conn," ");
}
netprintf(conn, "%5i %5i ",
connection[i].net_bytes_rx,
connection[i].net_bytes_tx);
if (connection[i].sa) {
/* FIXME - IPv4 Specific */
netprintf(conn,"%s:%i",
inet_ntoa(((struct sockaddr_in*)connection[i].sa)->sin_addr),
htons(((struct sockaddr_in*)connection[i].sa)->sin_port));
}
netprintf(conn, "\r\n");
884971fc Hamish Coleman
}
2d552dd4 Hamish Coleman
} else if (!strcmp(command, "kill_conn")) {
int connid = check_atoi(parameter1,0,conn,"must specify a connection id\r\n");
if (connid==0 || connid>next_connection_id) {
netprintf(conn,"Connection ID %i out of range\r\n",connid);
return;
}

int i=0;
while(connection[i].id!=connid && i<MAXCONNECTIONS) {
i++;
}
if (i>=MAXCONNECTIONS) {
netprintf(conn,"Connection ID %i not found\r\n",connid);
return;
}
997564ce Hamish Coleman
netprintf(&connection[i],"Serial Connection Closed by Connection ID %i\r\n",conn->id);
2d552dd4 Hamish Coleman
close_serial_connection(&connection[i]);
netprintf(conn,"Connection ID %i serial port closed\r\n",connid);
5ddf869b Hamish Coleman
} else if (!strcmp(command, "menu")) {
cli_loop(cli,conn->net);
c7c4a6f7 Hamish Coleman
} else {
/* other, unknown commands */
4cb40453 Hamish Coleman
netprintf(conn,"\r\nInvalid Command: '%s'\r\n\r\n",line);
d51f658a Hamish Coleman
}
53110989 Hamish Coleman
}

4cb40453 Hamish Coleman
void show_prompt(struct connection *conn) {
netprintf(conn,"%s> ",hostname);
}

2d552dd4 Hamish Coleman
void run_menu(struct connection * conn) {
08ceb73b Hamish Coleman
unsigned char buf[BUFSIZE+5]; /* ensure there is room for our kludge telnet options */
unsigned char line[MAXLEN];
62045e98 Hamish Coleman
DWORD size, linelen=0;
WORD i;

unsigned long zero=0;
89312c37 Hamish Coleman
fd_set set_read;
306304a2 Hamish Coleman
struct timeval tv;
53110989 Hamish Coleman
479f1136 Hamish Coleman
unsigned char last_ch;
unsigned char ch;

08ceb73b Hamish Coleman
/* IAC WILL ECHO */
/* IAC WILL suppress go ahead */
/* IAC WILL status */
201f6e65 Hamish Coleman
/* IAC WONT linemode */
netprintf(conn,"\xff\xfb\x01\xff\xfb\x03\xff\xfb\x05\xff\xfc\x22");
08ceb73b Hamish Coleman
aaba0a4c Hamish Coleman
netprintf(conn,"\r\nwconsd serial port server (version %s)\r\n\r\n",VERSION);
884971fc Hamish Coleman
send_help(conn);
4cb40453 Hamish Coleman
show_prompt(conn);
c7c4a6f7 Hamish Coleman
89312c37 Hamish Coleman
FD_ZERO(&set_read);
f6cc8fbf Hamish Coleman
while (conn->option_runmenu) {
89312c37 Hamish Coleman
FD_SET(conn->net,&set_read);
306304a2 Hamish Coleman
tv.tv_sec = 2;
tv.tv_usec = 0;
select(conn->net+1,&set_read,NULL,NULL,&tv);
/* TODO - examine the retval for the select */
c7c4a6f7 Hamish Coleman
size=recv(conn->net,(void*)&buf,BUFSIZE,0);
08ceb73b Hamish Coleman
62045e98 Hamish Coleman
if (size==0) {
c7c4a6f7 Hamish Coleman
closesocket(conn->net);
conn->net=INVALID_SOCKET;
2d552dd4 Hamish Coleman
return;
62045e98 Hamish Coleman
}
if (size==SOCKET_ERROR) {
306304a2 Hamish Coleman
int err = WSAGetLastError();
switch (err) {
case WSAEWOULDBLOCK:
/* ignore */
3fa88c6f Hamish Coleman
if (conn->option_keepalive) {
netprintf(conn,"\xff\xf1");
}
306304a2 Hamish Coleman
continue;
3fa88c6f Hamish Coleman
case WSAECONNRESET:
closesocket(conn->net);
conn->net=INVALID_SOCKET;
return;
306304a2 Hamish Coleman
default:
dprintf(1,"wconsd[%i]: run_menu socket error (%i)\n",conn->id,WSAGetLastError());
/* General paranoia about blocking sockets */
ioctlsocket(conn->net,FIONBIO,&zero);
}
53110989 Hamish Coleman
continue;
62045e98 Hamish Coleman
}
884971fc Hamish Coleman
conn->net_bytes_rx+=size;
62045e98 Hamish Coleman
53110989 Hamish Coleman
for (i = 0; i < size; i++) {
479f1136 Hamish Coleman
last_ch=ch;
ch = buf[i];

/* TODO - remove the second call to process_telnet_option */
if (conn->telnet_option) {
/* We are in the middle of handling an option */
process_telnet_option(conn,ch);
/*
* return value ignored, since we dont care to
* print any telnet option values in the menu.
*/
b9a4e090 Hamish Coleman
continue;
479f1136 Hamish Coleman
}
if (ch==0) {
/*
* NULLs could occur as the second char in a
* CR NUL telnet sequence
*/
continue;
} else if (ch==127 || ch==8) {
b9a4e090 Hamish Coleman
// backspace
53110989 Hamish Coleman
if (linelen > 0) {
884971fc Hamish Coleman
netprintf(conn,"\x08 \x08");
53110989 Hamish Coleman
linelen--;
} else {
c7c4a6f7 Hamish Coleman
/* if the linebuf is empty, ring the bell */
884971fc Hamish Coleman
netprintf(conn,"\x07");
53110989 Hamish Coleman
}
continue;
479f1136 Hamish Coleman
} else if (ch==0x0d || ch==0x0a) {
b9a4e090 Hamish Coleman
// detected cr or lf

479f1136 Hamish Coleman
if (last_ch == 0x0d && ch==0x0a) {
/* skip the second char in CR LF */
continue;
b9a4e090 Hamish Coleman
}

08ceb73b Hamish Coleman
if (conn->option_echo)
/* echo the endofline */
netprintf(conn,"\r\n");
53110989 Hamish Coleman
0bcbd4d3 Hamish Coleman
if (linelen!=0) {
line[linelen]=0; // ensure string is terminated
c7c4a6f7 Hamish Coleman
2d552dd4 Hamish Coleman
process_menu_line(conn,(char*)line);
3fa88c6f Hamish Coleman
if (!conn->option_runmenu) {
c7c4a6f7 Hamish Coleman
/* exiting the menu.. */
2d552dd4 Hamish Coleman
return;
0bcbd4d3 Hamish Coleman
}
53110989 Hamish Coleman
}

4cb40453 Hamish Coleman
show_prompt(conn);
53110989 Hamish Coleman
linelen=0;
b9a4e090 Hamish Coleman
continue;
479f1136 Hamish Coleman
} else if (ch<0x20) {
08ceb73b Hamish Coleman
/* ignore other ctrl chars */
continue;
479f1136 Hamish Coleman
} else if (ch==TELNET_OPTION_IAC) {
/* start a telnet option packet */
process_telnet_option(conn,ch);
3eb633b1 Hamish Coleman
/*
479f1136 Hamish Coleman
* no possible return values, since IAC is
* just the beginning of a telnet packet
3eb633b1 Hamish Coleman
*/
b9a4e090 Hamish Coleman
continue;
53110989 Hamish Coleman
} else {
// other chars
0bcbd4d3 Hamish Coleman
53110989 Hamish Coleman
if (linelen < MAXLEN - 1) {
479f1136 Hamish Coleman
line[linelen] = ch;
53110989 Hamish Coleman
linelen++;
08ceb73b Hamish Coleman
if (conn->option_echo) {
479f1136 Hamish Coleman
netprintf(conn,"%c",ch); /* echo */
08ceb73b Hamish Coleman
}
53110989 Hamish Coleman
} else {
884971fc Hamish Coleman
netprintf(conn,"\x07"); /* linebuf full bell */
62045e98 Hamish Coleman
}
b9a4e090 Hamish Coleman
continue;
62045e98 Hamish Coleman
}
}
}
c7c4a6f7 Hamish Coleman
/* not reached */
78d4c0b0 Hamish Coleman
}

ef78cec5 Hamish Coleman
DWORD WINAPI thread_new_connection(LPVOID lpParam) {
c7c4a6f7 Hamish Coleman
struct connection * conn = (struct connection*)lpParam;
ef78cec5 Hamish Coleman
f6cc8fbf Hamish Coleman
dprintf(1,"wconsd[%i]: debug: start thread_new_connection loop\n",conn->id);
run_menu(conn);
ef78cec5 Hamish Coleman
c7c4a6f7 Hamish Coleman
/* cleanup */
884971fc Hamish Coleman
dprintf(1,"wconsd[%i]: connection closing\n",conn->id);

c7c4a6f7 Hamish Coleman
/* TODO print bytecounts */
/* maybe close file descriptors? */
884971fc Hamish Coleman
shutdown(conn->net,SD_BOTH);
closesocket(conn->net);
3eb633b1 Hamish Coleman
2d552dd4 Hamish Coleman
close_serial_connection(conn);
3eb633b1 Hamish Coleman
884971fc Hamish Coleman
conn->active=0;
ef78cec5 Hamish Coleman
aaba0a4c Hamish Coleman
/* TODO - who closes menuThread ? */
ef78cec5 Hamish Coleman
return 0;
}

39736270 Hamish Coleman
int wconsd_main(int argc, char **argv)
78d4c0b0 Hamish Coleman
{
3eb633b1 Hamish Coleman
HANDLE wait_array[2];
78d4c0b0 Hamish Coleman
BOOL run=TRUE;
DWORD o;
SOCKET as;
c0d1e8fe Hamish Coleman
unsigned long zero=0;
78d4c0b0 Hamish Coleman
ef78cec5 Hamish Coleman
struct sockaddr_in sa;
int salen;

int i;
aaba0a4c Hamish Coleman
int count;
ef78cec5 Hamish Coleman
/* clear out any bogus data in the connections table */
for (i=0;i<MAXCONNECTIONS;i++) {
884971fc Hamish Coleman
connection[i].active = 0;
ef78cec5 Hamish Coleman
}

78d4c0b0 Hamish Coleman
/* Main loop: wait for a connection, service it, repeat
* until signalled that the service is terminating */
wait_array[0]=stopEvent;
wait_array[1]=listenSocketEvent;

while (run) {
4cb40453 Hamish Coleman
dprintf(1,"wconsd: debug: start wconsd_main loop\n");
bd5605dd Hamish Coleman
3eb633b1 Hamish Coleman
o=WaitForMultipleObjects(2,wait_array,FALSE,INFINITE);
78d4c0b0 Hamish Coleman
switch (o-WAIT_OBJECT_0) {
ef78cec5 Hamish Coleman
case 0: /* stopEvent */
78d4c0b0 Hamish Coleman
run=FALSE;
ResetEvent(stopEvent);
break;
ef78cec5 Hamish Coleman
case 1: /* listenSocketEvent */
/* There is an incoming connection */
78d4c0b0 Hamish Coleman
WSAResetEvent(listenSocketEvent);
ef78cec5 Hamish Coleman
salen = sizeof(sa);
as=accept(ls,(struct sockaddr*)&sa,&salen);

if (as==INVALID_SOCKET) {
break;
}
53110989 Hamish Coleman
4cb40453 Hamish Coleman
dprintf(1,"wconsd: new connection from %s\n",
inet_ntoa(sa.sin_addr));
53110989 Hamish Coleman
ef78cec5 Hamish Coleman
/* search for an empty connection slot */
aaba0a4c Hamish Coleman
i=next_connection_slot%MAXCONNECTIONS;
count=0;
while(connection[i].active && count<MAXCONNECTIONS) {
count++;
i = (i+1)%MAXCONNECTIONS;
ef78cec5 Hamish Coleman
}
if (i==MAXCONNECTIONS) {
dprintf(1,"wconsd: connection table overflow\n");
/* FIXME - properly reject the incoming connection */
/* for now, just close the socket */
closesocket(as);
break;
78d4c0b0 Hamish Coleman
}
aaba0a4c Hamish Coleman
next_connection_slot = (next_connection_slot+1)%MAXCONNECTIONS;
884971fc Hamish Coleman
connection[i].active=1; /* mark this entry busy */
connection[i].id = next_connection_id++;
ef78cec5 Hamish Coleman
connection[i].menuThread=NULL;
connection[i].net=as;
aaba0a4c Hamish Coleman
connection[i].serialconnected=0;
ef78cec5 Hamish Coleman
connection[i].serial=INVALID_HANDLE_VALUE;
connection[i].serialThread=NULL;
3fa88c6f Hamish Coleman
connection[i].option_runmenu=1; /* start in the menu */
479f1136 Hamish Coleman
connection[i].option_binary=0;
08ceb73b Hamish Coleman
connection[i].option_echo=0;
3fa88c6f Hamish Coleman
connection[i].option_keepalive=0;
ef78cec5 Hamish Coleman
connection[i].net_bytes_rx=0;
connection[i].net_bytes_tx=0;
479f1136 Hamish Coleman
connection[i].telnet_option=0;
connection[i].telnet_option_param=0;
ef78cec5 Hamish Coleman
3fa88c6f Hamish Coleman
if (connection[i].sa) {
/* Do lazy de-allocation so that the info is
* still visible to show conn table */
free(connection[i].sa);
}
if ((connection[i].sa=malloc(salen))==NULL) {
dprintf(1,"wconsd[%i]: malloc failed\n",
connection[i].id);
} else {
memcpy(connection[i].sa,&sa,salen);
}

4cb40453 Hamish Coleman
dprintf(1,"wconsd[%i]: accepted new connection in slot %i\n",connection[i].id,i);
ef78cec5 Hamish Coleman

/* we successfully accepted the connection */

aaba0a4c Hamish Coleman
ioctlsocket(connection[i].net,FIONBIO,&zero);
ef78cec5 Hamish Coleman
connection[i].menuThread = CreateThread(NULL,0,thread_new_connection,&connection[i],0,NULL);

78d4c0b0 Hamish Coleman
break;
default:
run=FALSE; // Stop the service - I want to get off!
break;
}
}

aaba0a4c Hamish Coleman
/* TODO - look through the connection table and close everything */
78d4c0b0 Hamish Coleman
closesocket(ls);
WSACleanup();
4dcb97c2 Hamish Coleman
return 0;
78d4c0b0 Hamish Coleman
}

int main(int argc, char **argv)
{
62727af2 Hamish Coleman
// debug info for when I test this as a service
dprintf(1,"wconsd: started with argc==%i\n",argc);

211ce618 Hamish Coleman
/* assume that our messages are going to the debug log */
dprintf_to_stdout=0;
62727af2 Hamish Coleman
211ce618 Hamish Coleman
if (SCM_Start(&sd,argc,argv)!=SVC_OK) {
8c98de67 Hamish Coleman
return 1;
62727af2 Hamish Coleman
}

return 0;
78d4c0b0 Hamish Coleman
}