ossp-pkg/sio/sio_hello.c
/*
** OSSP sio - Stream I/O
** Copyright (c) 2002-2005 Cable & Wireless <http://www.cw.com/>
** Copyright (c) 2002-2005 The OSSP Project <http://www.ossp.org/>
** Copyright (c) 2002-2005 Ralf S. Engelschall <rse@engelschall.com>
**
** This file is part of OSSP sio, a layered stream I/O library
** which can be found at http://www.ossp.org/pkg/lib/sio/.
**
** Permission to use, copy, modify, and distribute this software for
** any purpose with or without fee is hereby granted, provided that
** the above copyright notice and this permission notice appear in all
** copies.
**
** THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
** USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
** ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
** OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
** SUCH DAMAGE.
**
** sio_hello.c: hello world stage
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "al.h"
#include "sio.h"
#define PROMPT "Login: "
#define NPROMPT (sizeof(PROMPT)-1)
#define PASSWD "Geheim\r\n"
#define NPASS (sizeof(PASSWD)-1)
/*
* protocol states
*/
typedef enum {
INIT, /* setting up protocol, save output or switch to writer */
PROMPTING, /* sending prompt string as writer */
PROMPTED, /* continue sending, switch to reader when done */
WAIT, /* gather password as reader, flush buffer if good */
GOOD, /* default null operation */
BAD /* drop output, return eof on input */
} state_t;
typedef struct {
al_t *al_in, *al_out; /* cache input and output stream */
state_t state;
char passwd[NPASS]; /* input buffer */
int npass; /* characters in input buffer */
al_t *pre_in; /* saved during protocol */
al_t *pre_out; /* saved during protocol */
int isoutput; /* remember originator of protocol */
al_label_t data_label; /* al labels used by SIO */
al_label_t eof_label;
char eof; /* eof label buffer */
} private_t;
/***********************************************************************/
/*
* create stage
*
* allocate private instance data
*/
static
sio_rc_t hello_init(sio_t *sio, void **up)
{
private_t *my;
my = (private_t *)malloc(sizeof(private_t));
if (my == NULL)
return SIO_ERR_MEM;
sio_label(sio, SIO_LN_DATA, &my->data_label);
sio_label(sio, SIO_LN_EOF, &my->eof_label);
my->eof = '\0';
my->pre_in = NULL;
my->pre_out = NULL;
*up = my;
return SIO_OK;
}
/*
* configure stage
*
* pass two void pointers
*/
static
sio_rc_t hello_configure(sio_t *sio, void *u, void *obj, void *val)
{
return SIO_ERR_ARG;
}
/*
* destroy stage
*/
static
sio_rc_t hello_cleanup(sio_t *sio, void *u)
{
private_t *my = (private_t *)u;
free(my);
return SIO_OK;
}
static
void hello_setup(sio_t *sio, private_t *my)
{
my->state = INIT;
my->npass = 0;
}
static
sio_rc_t hello_openr(sio_t *sio, al_t *al, void *u)
{
private_t *my = (private_t *)u;
hello_setup(sio,my);
if (al_create(&my->pre_in) != AL_OK)
return SIO_ERR_INT;
my->al_in = al;
return SIO_OK;
}
static
sio_rc_t hello_closer(sio_t *sio, al_t *al, void *u)
{
private_t *my = (private_t *)u;
al_destroy(my->pre_in);
my->pre_in = NULL;
return SIO_OK;
}
static
sio_rc_t hello_openw(sio_t *sio, al_t *al, void *u)
{
private_t *my = (private_t *)u;
hello_setup(sio,my);
if (al_create(&my->pre_out) != AL_OK)
return SIO_ERR_INT;
my->al_out = al;
return SIO_OK;
}
static
sio_rc_t hello_closew(sio_t *sio, al_t *al, void *u)
{
private_t *my = (private_t *)u;
al_destroy(my->pre_out);
my->pre_out = NULL;
return SIO_OK;
}
/************************************************************************/
static
void hello_clearinput(private_t *my)
{
al_splice(my->al_in, 0, al_bytes(my->al_in), NULL, NULL);
}
static
void hello_clearoutput(private_t *my)
{
al_splice(my->al_out, 0, al_bytes(my->al_out), NULL, NULL);
}
/*
* gather input in password buffer
* return true if enough bytes or if no more data follows
*/
static
int hello_readpasswd(private_t *my)
{
size_t actual;
al_flatten(my->al_in, 0, NPASS - my->npass, AL_FORWARD_SPAN, my->data_label,
my->passwd, &actual);
al_splice(my->al_in, 0, actual, NULL, NULL);
my->npass += actual;
/* flush input when buffer full or labels are switching */
return my->npass == NPASS || al_bytes(my->al_in) > 0;
}
/*
* write eof token to input stream
*/
static
void hello_writeeof(private_t *my)
{
hello_clearinput(my);
al_append_bytes(my->al_in, &my->eof, sizeof(my->eof), my->eof_label);
}
/*
* defer initial data until protocol is done
*/
static
void hello_save(private_t *my)
{
al_splice(my->pre_in, al_bytes(my->pre_in), 0, my->al_in, NULL);
al_splice(my->pre_out, al_bytes(my->pre_out), 0, my->al_out, NULL);
}
/*
* restore saved output after handshake completed successfully
*/
static
void hello_restore(private_t *my)
{
al_splice(my->al_in, 0, 0, my->pre_in, NULL);
al_splice(my->al_out, 0, 0, my->pre_out, NULL);
}
/*
* kill saved data
*/
static
void hello_dropsaved(private_t *my)
{
al_splice(my->pre_in, 0, al_bytes(my->pre_in), NULL, NULL);
al_splice(my->pre_out, 0, al_bytes(my->pre_out), NULL, NULL);
}
/*
* write prompt string to output
*/
static
void hello_sendprompt(private_t *my)
{
al_prepend_bytes(my->al_out, PROMPT, NPROMPT, my->data_label);
}
/************************************************************************/
/*
*
* simple protocol
*
* send prompt to socket
* wait for password from socket
* if password correct -> normal communication
* else -> drop output, eof on input
*
*/
/* jump to next state */
#define GOTO(s, c) do { my->state = (s); rc = (c); } while(0)
static
sio_rc_t hello_protocol(sio_t *sio, private_t *my, int isoutput, sio_rc_t orc)
{
sio_rc_t rc = SIO_ERR_INT;
int good;
switch (my->state) {
case INIT:
assert(orc == SIO_SCHED_UP);
/*
* save origin (input, output) so that
* we can complete correctly after handshake
* is done
*
* save global assembly lines
*
* if caller starts with write, stay
*
* if caller starts with read, switch to writer
*
*/
hello_save(my);
my->isoutput = isoutput;
if (isoutput) {
GOTO(PROMPTING, SIO_SCHED_LOOP);
} else {
GOTO(PROMPTING, SIO_SCHED_CROSS);
}
break;
case PROMPTING:
assert(orc == SIO_SCHED_CROSS || orc == SIO_SCHED_LOOP);
assert(isoutput == 1);
/*
* only called on output stream
*
* either fall through from INIT as writer
* or scheduled via SIO_SCHED_CROSS from reader
* maybe there should be a SIO_STAY ?
*
* send prompt string, schedule upstream
*/
hello_sendprompt(my);
GOTO(PROMPTED, SIO_SCHED_UP);
break;
case PROMPTED:
assert(orc == SIO_SCHED_DOWN);
assert(isoutput == 1);
/*
* only called on output stream
*
* switch back to reader when output is flushed
* otherwise retry upstream
*
*/
if (al_bytes(my->al_out) > 0)
GOTO(PROMPTED, SIO_SCHED_UP);
else
GOTO(WAIT, SIO_SCHED_CROSS);
break;
case WAIT:
assert(orc == SIO_SCHED_CROSS || orc == SIO_SCHED_DOWN);
assert(isoutput == 0);
/*
* only called on input stream
*
* if not enough data and no signalling chunks,
* schedule upstream to deliver more
*
* check password
* if password bad (incomplete or wrong)
* state will be BAD
* -> if origin was writer, drop saved initial write
* schedule writer
* -> if origin was reader, signal EOF, send downstream
* else
* state will be GOOD
* -> if origin was writer, push saved initial write
* schedule writer
* -> if origin was reader, default action for reading
*
*/
if (!hello_readpasswd(my))
GOTO(WAIT, SIO_SCHED_UP);
else {
good = my->npass == NPASS &&
memcmp(my->passwd, PASSWD, NPASS) == 0;
if (!good) {
hello_dropsaved(my);
if (isoutput != my->isoutput) {
GOTO(BAD, SIO_SCHED_CROSS);
} else {
GOTO(BAD, SIO_SCHED_LOOP);
}
} else {
hello_restore(my);
if (isoutput != my->isoutput) {
GOTO(GOOD, SIO_SCHED_CROSS);
} else
GOTO(GOOD, SIO_SCHED_LOOP);
}
}
break;
case GOOD:
/*
* default action
*
* on input -> deliver data downstream, gather data upstream
* on output -> deliver data upstream, gather data downstream
*
*/
GOTO(GOOD, SIO_OK); /* default action */
break;
case BAD:
/*
* black hole
*
* on input -> signal EOF, send downstream
* on output -> drop data, return downstream
*
*/
if (isoutput)
hello_clearoutput(my);
else
hello_writeeof(my);
GOTO(BAD, SIO_SCHED_DOWN);
break;
}
return rc;
}
/************************************************************************/
static
sio_rc_t hello_input(sio_t *sio, al_t *al, void *u, sio_rc_t orc)
{
return hello_protocol(sio, (private_t *)u, 0, orc);
}
static
sio_rc_t hello_output(sio_t *sio, al_t *al, void *u, sio_rc_t orc)
{
return hello_protocol(sio, (private_t *)u, 1, orc);
}
sio_module_t sio_module_hello = {
"hello",
hello_init,
hello_configure,
hello_cleanup,
hello_openr,
hello_closer,
hello_openw,
hello_closew,
hello_input,
hello_output,
NULL
};