OSSP CVS Repository

ossp - ossp-pkg/sio/sio_hello.c 1.5
Not logged in
[Honeypot]  [Browse]  [Directory]  [Home]  [Login
[Reports]  [Search]  [Ticket]  [Timeline
  [Raw

ossp-pkg/sio/sio_hello.c 1.5

#include <stdlib.h>
#include <string.h>
#include <assert.h>

#include "al.h"
#include "sio.h"
#include "sio_module.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 **u)
{
    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;

    *u = 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 rc = SIO_ERR_INT;
    int good;

    switch (my->state) {

    case INIT:

        /*
         * 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_LOOP);
        } else {
            GOTO(PROMPTING, SIO_XSTREAM);
        }
        break;

    case PROMPTING:

        assert(isoutput == 1);
        /*
         *  only called on output stream
         *
         *  either fall through from INIT as writer
         *  or scheduled via SIO_XSTREAM from reader
         *  maybe there should be a SIO_STAY ?
         *
         *  send prompt string, schedule upstream
         */
        hello_sendprompt(my);
        GOTO(PROMPTED, SIO_UPSTREAM);
        break;


    case PROMPTED:

        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_UPSTREAM);
        else
            GOTO(WAIT, SIO_XSTREAM);
        break;

    case WAIT:

        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_UPSTREAM);
        else {
            good = my->npass == NPASS &&
                   memcmp(my->passwd, PASSWD, NPASS) == 0;
            if (!good) {
                hello_dropsaved(my);
                if (my->isoutput) {
                    GOTO(BAD, SIO_XSTREAM);
                } else {
                    hello_writeeof(my);
                    GOTO(BAD, SIO_DOWNSTREAM);
                }
            } else {
                hello_restore(my);
                if (isoutput != my->isoutput) {
                    GOTO(GOOD, SIO_XSTREAM);
                } else
                    GOTO(GOOD, SIO_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_DOWNSTREAM);
        break;
    }

    return rc;
}

/************************************************************************/

static
sio_rc_t hello_input(sio_t *sio, al_t *al, void *u)
{
    return hello_protocol(sio, (private_t *)u, 0);
}

static
sio_rc_t hello_output(sio_t *sio, al_t *al, void *u)
{
    return hello_protocol(sio, (private_t *)u, 1);
}

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
};


CVSTrac 2.0.1