#include #include #include #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; /* saved output 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'; *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; al_destroy(my->pre); 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); my->al_in = al; return SIO_OK; } static sio_rc_t hello_closer(sio_t *sio, al_t *al, void *u) { 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); al_create(&my->pre); 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); my->pre = 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 output until protocol is done */ static void hello_saveoutput(private_t *my) { al_splice(my->pre, al_bytes(my->pre), 0, my->al_out, NULL); } /* * restore saved output after handshake completed successfully */ static void hello_restoreoutput(private_t *my) { al_splice(my->al_out, 0, 0, my->pre, NULL); } /* * kill saved output */ static void hello_dropsaved(private_t *my) { al_splice(my->pre, 0, al_bytes(my->pre), 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 * * if caller starts with write, preserve data * * if caller starts with read, switch to writer * */ if (isoutput) { my->isoutput = 1; hello_saveoutput(my); GOTO(PROMPTING, SIO_UPSTREAM); /* * FALL THROUGH to next state * * XXX = fall through is ugly * XXX + efficient, no extra pass through scheduler * XXX - scheduler doesn't support loops yet, we * could simulate this with extra states that * ping-pong back to the "right" side (ugh!) */ } else { my->isoutput = 0; GOTO(PROMPTING, SIO_XSTREAM); break; } /* FALL THROUGH */ 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) { if (my->isoutput) { hello_dropsaved(my); GOTO(BAD, SIO_XSTREAM); } else { hello_writeeof(my); GOTO(BAD, SIO_DOWNSTREAM); } } else if (my->isoutput) { hello_restoreoutput(my); GOTO(GOOD, SIO_XSTREAM); } else GOTO(GOOD, SIO_OK); } 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 };