#include #include #include #include #include #include "internal.h" // This file knows we always use big-endian CARD16 r_card16(const unsigned char *pp) { return((pp[0]*256U)+pp[1]); } void w_card16(unsigned char *pp, CARD16 val) { if (val > 65535) lx_abort(); pp[0] = val >> 8; pp[1] = val & 0xff; } void w_int16(unsigned char *pp, INT16 val) { /* * I would normally put a firewall * if ((val < -32768) || (val > 32767)) lx_abort(); * here, but it draws "warning: comparison is always 0 due to limited * range of data type". I'm not sure what to do about that. The * similar tests in w_card16 and w_card32 don't, for reasons I don't * understand, and I like this kind of firewalling. I'm skipping it * for now, to make it build until I figure out what the right thing * to do is. */ pp[0] = (CARD16)val >> 8; pp[1] = (CARD16)val & 0xff; } CARD32 r_card32(const unsigned char *pp) { return((pp[0]*16777216U)+(pp[1]*65536U)+(pp[2]*256U)+pp[3]); } void w_card32(unsigned char *pp, CARD32 val) { if (val > 0xffffffff) lx_abort(); pp[0] = val >> 24 ; pp[1] = (val >> 16) & 0xff; pp[2] = (val >> 8) & 0xff; pp[3] = val & 0xff; } void lx__send_req(LX_CONN *xc, const unsigned char *req, int len) { if (aio_oq_queue_copy(&xc->oq,req,len) < 0) { lx__nomem_fail(xc); return; } xc->next_serial ++; } void lx__send_req_free(LX_CONN *xc, unsigned char *req, int len) { if (aio_oq_queue_free(&xc->oq,req,len) < 0) { lx__nomem_fail(xc); return; } xc->next_serial ++; } static void op_link(LX_OP *op) { if (op->flags & LXOF_LINK) lx_abort(); op->flink = 0; op->blink = op->conn->pendingb; if (op->blink) op->blink->flink = op; else op->conn->pendingf = op; op->conn->pendingb = op; op->flags |= LXOF_LINK; } static void op_unlink(LX_OP *op) { if (! (op->flags & LXOF_LINK)) lx_abort(); if (op->flink) op->flink->blink = op->blink; else op->conn->pendingb = op->blink; if (op->blink) op->blink->flink = op->flink; else op->conn->pendingf = op->flink; op->flags &= ~LXOF_LINK; } static void op_free(LX_OP *op) { free(op); } void lx_op_callback(LX_OP *op, void (*cb)(void *), void *arg, unsigned int flags) { if (op->flags & LXOF_DROP) lx_abort(); if (flags & LX_OP_KEEP) op->flags |= LXOF_KEEP; else op->flags &= ~LXOF_KEEP; op->usercb = cb; op->usercbarg = arg; } int lx_op_test(LX_OP *op) { if (op->flags & LXOF_DROP) lx_abort(); return((op->flags&LXOF_DONE)?1:0); } void lx_op_drop(LX_OP *op) { if (op->flags & LXOF_DROP) lx_abort(); if (op->flags & LXOF_DONE) { op_unlink(op); op_free(op); } op->flags = (op->flags & ~LXOF_KEEP) | LXOF_DROP; } void lx_op_set_udata(LX_OP *op, void *ud) { if (op->flags & LXOF_DROP) lx_abort(); op->udata = ud; } void *lx_op_udata(LX_OP *op) { if (op->flags & LXOF_DROP) lx_abort(); return(op->udata); } void lx__protoerr(LX_CONN *xc, const char *fmt, ...) { va_list ap; char *s; int l; LX_LIB_ERR e; va_start(ap,fmt); l = vasprintf(&s,fmt,ap); va_end(ap); if (xc->flags & XCF_OPENING) { if (l < 0) { (*xc->os->err)(&lx__nomem_err,xc->os->cbarg); } else { e.type = LXLE_PROTO_ERR; e.u.proto_err.msg = s; (*xc->os->err)(&e,xc->os->cbarg); } } else { if (l < 0) { (*xc->lib_err)(xc,&lx__nomem_err); } else { e.type = LXLE_PROTO_ERR; e.u.proto_err.msg = s; (*xc->lib_err)(xc,&e); } } xc->flags |= XCF_FAIL; } LX_OP *lx__expect_reply( LX_CONN *xc, const unsigned char *req, int reqlen, void (*done)(LX_OP *, const unsigned char *), void *priv, int extra ) { LX_OP *op; CARD32 ser; ser = xc->next_serial; lx__send_req(xc,req,reqlen); if (xc->flags & XCF_FAIL) return(0); op = malloc(sizeof(LX_OP)); if (op == 0) { lx__nomem_fail(xc); return(0); } op->conn = xc; op->serial = ser; op->extra = extra; op->usercb = 0; op->usercbarg = 0; op->done = done; op->reqpriv = priv; op->flags = LXOF_KEEP; op->udata = 0; op_link(op); return(op); } static void rb_room(LX_CONN *xc, int n) { if (xc->rba < n) { xc->rba = n; xc->rb = realloc(xc->rb,n); } } void lx__proto_newpkt(LX_CONN *xc) { rb_room(xc,32); xc->rbl = 0; xc->rbw = 32; } static void dumphex(const char *tag, const unsigned char *data, int len) { int i; if (tag) printf("%s\n",tag); for (i=0;irb[0],32); } static void process_event(LX_CONN *xc) { dumphex("Event",&xc->rb[0],32); } static void process_reply(LX_CONN *xc) { LX_OP *op; CARD16 seq; if (xc->rbl < 32) lx_abort(); op = xc->pendingf; if (! op) { lx__protoerr(xc,"response when expecting nothing"); return; } seq = r_card16(&xc->rb[2]); if (seq != (op->serial&0xffff)) lx__protoerr(xc,"response to %04x when expecting (%04x)%04x\n",seq,op->serial>>16,op->serial&0xffff); (*op->done)(op,xc->rb); if (op->flags & LXOF_MORE) { op->flags &= ~LXOF_MORE; } else if (op->flags & LXOF_DROP) { op_unlink(op); op_free(op); } else { if (op->usercb) (*op->usercb)(op->usercbarg); op->flags |= LXOF_DONE; if (! (op->flags & LXOF_KEEP)) { op_unlink(op); op_free(op); } } } int lx__proto_pkt(LX_CONN *xc, const void *data, int len) { const unsigned char *dp; int n; int o; CARD32 l; if (xc->flags & XCF_FAIL) return(len); dp = data; o = 0; while (o < len) { n = xc->rbw - xc->rbl; if (n < 1) lx_abort(); if (n > len-o) { bcopy(dp+o,xc->rb+xc->rbl,len-o); xc->rbl += len - o; break; } bcopy(dp+o,xc->rb+xc->rbl,n); xc->rbl += n; o += n; switch (xc->rb[0]) { case 0: // Error if (xc->rbl != 32) lx_abort(); process_error(xc); break; case 1: // Reply l = r_card32(xc->rb+4); if (l > 0x3fffffff-8) lx__protoerr(xc,"insanely long reply (length field %lu)\n",(unsigned long int)l); xc->rbw = (l * 4) + 32; if (xc->rbl < xc->rbw) continue; process_reply(xc); break; default: // Event if (xc->rbl != 32) lx_abort(); process_event(xc); break; } lx__proto_newpkt(xc); } return(len); } void lx__nomem_fail(LX_CONN *xc) { (*xc->lib_err)(xc,&lx__nomem_err); xc->flags |= XCF_FAIL; } void lx__bad_call(LX_CONN *xc, const char *call) { LX_LIB_ERR e; e.type = LXLE_BAD_CALL; e.u.bad_call.fn = call; (*xc->lib_err)(xc,&e); }