proxy_mutator.c C 1,337 lines View on github.com → Search inside
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */23#include "proxy.h"45/*6 * !!!WARNING!!!7 * This is an experimental interface and is not to be used in production until8 * after this warning has been removed.9 * The req/res mutator system is currently an experimental draft, merged to10 * allow code experiments and further information gathering before completing11 * the interface.12 *13 */1415// space or \r\n16#define MIN_BUF_SPACE 21718enum mcp_mut_type {19    MUT_REQ = 1,20    MUT_RES,21};2223enum mcp_mut_steptype {24    mcp_mut_step_none = 0,25    mcp_mut_step_cmdset,26    mcp_mut_step_cmdcopy,27    mcp_mut_step_keycopy,28    mcp_mut_step_keyset,29    mcp_mut_step_resnull,30    mcp_mut_step_rescodeset,31    mcp_mut_step_rescodecopy,32    mcp_mut_step_reserr,33    mcp_mut_step_flagset,34    mcp_mut_step_flagcopy,35    mcp_mut_step_flagcopyall,36    mcp_mut_step_valcopy,37    mcp_mut_step_final, // not used.38};3940// START STEP STRUCTS4142// struct forward declarations for entry/step function pointers43struct mcp_mut_step;44struct mcp_mutator;45struct mcp_mut_run;46struct mcp_mut_part;4748typedef int (*mcp_mut_c)(lua_State *L, int tidx);49typedef int (*mcp_mut_i)(lua_State *L, int tidx, int sc, struct mcp_mutator *mut);50typedef int (*mcp_mut_n)(struct mcp_mut_run *run, struct mcp_mut_step *s, struct mcp_mut_part *p);51typedef bool (*mcp_mut_r)(struct mcp_mut_run *run, struct mcp_mut_step *s, struct mcp_mut_part *p);5253struct mcp_mut_entry {54    const char *s; // string name55    mcp_mut_c c; // argument checker56    mcp_mut_i i; // argument initializer57    mcp_mut_n n; // runtime length totaller58    mcp_mut_r r; // runtime assembly59    unsigned int t; // allowed object types60    int rc; // number of results to expect61};6263struct mcp_mut_string {64    unsigned int str; // arena offset for match string.65    unsigned int len;66};6768struct mcp_mut_flag {69    uint64_t bit; // flag converted for bitmask test70    char f;71};7273#define RESERR_ERROR 174#define RESERR_ERROR_STR "ERROR"75#define RESERR_CLIENT 276#define RESERR_CLIENT_STR "CLIENT_ERROR"77#define RESERR_SERVER 378#define RESERR_SERVER_STR "SERVER_ERROR"7980struct mcp_mut_flagval {81    struct mcp_mut_flag flag;82    struct mcp_mut_string str;83};8485struct mcp_mut_step {86    enum mcp_mut_steptype type;87    unsigned int idx; // common: input argument position88    mcp_mut_n n; // totaller function89    mcp_mut_r r; // data copy function90    union {91        struct mcp_mut_string string;92        struct mcp_mut_flag flag;93        struct mcp_mut_flagval flagval;94    } c;95};9697// END STEP STRUCTS9899struct mcp_mutator {100    enum mcp_mut_type type;101    int scount;102    unsigned int aused; // arena memory used103    unsigned int rcount; // number of results to expect104    char *arena; // string/data storage for steps105    struct mcp_mut_step steps[];106};107108// scratch space for steps between total and execution stages.109struct mcp_mut_part {110    const char *src;111    size_t slen;112};113114// stack scratch variables for mutation execution115struct mcp_mut_run {116    lua_State *L;117    struct mcp_mutator *mut;118    void *arg;119    char *numbuf; // stack space for rendering numerics120    char *d_pos; // current offset to the write string.121122    const char *vbuf; // buffer or ptr if a value is being attached123    size_t vlen; // length of the actual value buffer.124};125126#define mut_step_c(n) static int mcp_mutator_##n##_c(lua_State *L, int tidx)127#define mut_step_i(n) static int mcp_mutator_##n##_i(lua_State *L, int tidx, int sc, struct mcp_mutator *mut)128#define mut_step_r(n) static bool mcp_mutator_##n##_r(struct mcp_mut_run *run, struct mcp_mut_step *s, struct mcp_mut_part *p)129#define mut_step_n(n) static int mcp_mutator_##n##_n(struct mcp_mut_run *run, struct mcp_mut_step *s, struct mcp_mut_part *p)130131// PRIVATE INTERFACE132133#define res_buf(r) (r->cresp ? r->cresp->iov[0].iov_base : r->buf)134135// COMMON ARG HANDLERS136137static void _mut_check_idx(lua_State *L, int tidx) {138    if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {139        luaL_checktype(L, -1, LUA_TNUMBER);140        int isnum = 0;141        lua_Integer i = lua_tointegerx(L, -1, &isnum);142        if (!isnum) {143            proxy_lua_ferror(L, "mutator step %d: must provide 'idx' argument as an integer", tidx);144        }145        if (i < 1) {146            proxy_lua_ferror(L, "mutator step %d: 'idx' argument must be greater than 0", tidx);147        }148    } else {149        proxy_lua_ferror(L, "mutator step %d: must provide 'idx' argument", tidx);150    }151    lua_pop(L, 1);152}153154static size_t _mut_check_strlen(lua_State *L, int tidx, const char *n) {155    size_t len = 0;156157    if (lua_getfield(L, tidx, n) != LUA_TNIL) {158        lua_tolstring(L, -1, &len);159    } else {160        proxy_lua_ferror(L, "mutator step %d: must provide '%s' argument", tidx, n);161    }162    lua_pop(L, 1);163164    return len;165}166167static void _mut_check_flag(lua_State *L, int tidx) {168    if (lua_getfield(L, tidx, "flag") != LUA_TNIL) {169        size_t len = 0;170        const char *flag = lua_tolstring(L, -1, &len);171        if (len != 1) {172            proxy_lua_ferror(L, "mutator step %d: 'flag' must be a single character", tidx);173        }174        if (mcp_is_flag_invalid(flag[0])) {175            proxy_lua_ferror(L, "mutator step %d: 'flag' must be alphanumeric", tidx);176        }177    } else {178        proxy_lua_ferror(L, "mutator step %d: must provide 'flag' argument", tidx);179    }180    lua_pop(L, 1); // val or nil181}182183static void _mut_init_flag(lua_State *L, int tidx, struct mcp_mut_flag *c) {184    if (lua_getfield(L, tidx, "flag") != LUA_TNIL) {185        const char *flag = lua_tostring(L, -1);186        c->f = flag[0];187        c->bit = (uint64_t)1 << (c->f - 65);188    }189    lua_pop(L, 1); // val or nil190}191192// mutator must be at position 1. index to check at idx.193// pulls metatables and compares them to see if we have a request or response194// object. then fills the appropriate pointer and returns.195static inline void _mut_checkudata(lua_State *L, unsigned int idx, mcp_request_t **srq, mcp_resp_t **srs) {196    lua_getmetatable(L, idx);197    lua_getiuservalue(L, 1, MUT_REQ);198    if (lua_rawequal(L, -1, -2)) {199        lua_pop(L, 2);200        *srq = lua_touserdata(L, idx);201        return;202    }203    lua_getiuservalue(L, 1, MUT_RES);204    if (lua_rawequal(L, -1, -3)) {205        *srs = lua_touserdata(L, idx);206    }207    lua_pop(L, 3);208}209210// END COMMMON ARG HANDLERS211212// START STEPS213214mut_step_c(cmdset) {215    size_t len = 0;216217    if (lua_getfield(L, tidx, "cmd") != LUA_TNIL) {218        lua_tolstring(L, -1, &len);219        // TODO: know either exact max length or parse for valid commands220        // this small sanity check should help against user error for now.221        if (len > 20) {222            proxy_lua_ferror(L, "mutator step %d: 'cmd' too long", tidx);223        }224    } else {225        proxy_lua_ferror(L, "mutator step %d: must provide 'cmd' argument", tidx);226    }227    lua_pop(L, 1);228229    return len;230}231232mut_step_i(cmdset) {233    struct mcp_mut_step *s = &mut->steps[sc];234    struct mcp_mut_string *c = &s->c.string;235    size_t len = 0;236237    if (lua_getfield(L, tidx, "cmd") != LUA_TNIL) {238        const char *cmd = lua_tolstring(L, -1, &len);239        c->str = mut->aused;240        c->len = len;241        char *a = mut->arena + mut->aused;242        memcpy(a, cmd, len);243        mut->aused += len;244    }245    lua_pop(L, 1);246247    return 0;248}249250mut_step_n(cmdset) {251    struct mcp_mut_string *c = &s->c.string;252    return c->len;253}254255mut_step_r(cmdset) {256    struct mcp_mutator *mut = run->mut;257    struct mcp_mut_string *c = &s->c.string;258259    const char *str = mut->arena + c->str;260261    memcpy(run->d_pos, str, c->len);262    run->d_pos += c->len;263    return true;264}265266mut_step_c(cmdcopy) {267    _mut_check_idx(L, tidx);268    return 0;269}270271mut_step_i(cmdcopy) {272    struct mcp_mut_step *s = &mut->steps[sc];273    if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {274        s->idx = lua_tointeger(L, -1);275    }276    lua_pop(L, 1);277278    return 0;279}280281mut_step_n(cmdcopy) {282    lua_State *L = run->L;283    unsigned idx = s->idx;284    mcp_request_t *srq = NULL;285    mcp_resp_t *srs = NULL;286287    switch (lua_type(L, idx)) {288        case LUA_TSTRING:289            p->src = lua_tolstring(L, idx, &p->slen);290            break;291        case LUA_TUSERDATA:292            _mut_checkudata(L, idx, &srq, &srs);293            p->slen = 0;294            if (srq) {295                // command must be at the start296                const char *cmd = srq->pr.request;297                // command ends at the first token298                // FIXME: use the mcmc API to pull the token.299                int clen = srq->pr.tok.tokens[1];300                if (cmd[clen] == ' ') {301                    clen--;302                }303                p->src = cmd;304                p->slen = clen;305            } else {306                // can only use a request object307                return -1;308            }309            break;310        default:311            // ints/etc unsupported312            return -1;313    }314315    return p->slen;316}317318mut_step_r(cmdcopy) {319    memcpy(run->d_pos, p->src, p->slen);320    run->d_pos += p->slen;321    return true;322}323324// TODO: validate a cmd is already slated to be set325// NOTE: we might need to know the integer CMD because key position can move326// with the stupid GAT command.327mut_step_c(keycopy) {328    _mut_check_idx(L, tidx);329    return 0;330}331332mut_step_i(keycopy) {333    struct mcp_mut_step *s = &mut->steps[sc];334    if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {335        s->idx = lua_tointeger(L, -1);336    }337    lua_pop(L, 1);338    return 0;339}340341mut_step_n(keycopy) {342    lua_State *L = run->L;343    unsigned idx = s->idx;344    mcp_request_t *srq = NULL;345    mcp_resp_t *srs = NULL;346347    switch (lua_type(L, idx)) {348        case LUA_TSTRING:349            p->src = lua_tolstring(L, idx, &p->slen);350            break;351        case LUA_TUSERDATA:352            _mut_checkudata(L, idx, &srq, &srs);353            if (srq) {354                p->src = MCP_PARSER_KEY(&srq->pr);355                p->slen = srq->pr.klen;356            } else {357                // TODO: if a result object:358                // - if meta, and 'k' flag, copy flag token.359                return -1;360            }361            break;362        default:363            // ints/etc unsupported364            return -1;365    }366367    return p->slen;368}369370mut_step_r(keycopy) {371    memcpy(run->d_pos, p->src, p->slen);372    run->d_pos += p->slen;373    return true;374}375376mut_step_c(keyset) {377    size_t len = 0;378379    if (lua_getfield(L, tidx, "val") != LUA_TNIL) {380        lua_tolstring(L, -1, &len);381        if (len < 1) {382            proxy_lua_ferror(L, "mutator step %d: 'val' must have nonzero length", tidx);383        }384    } else {385        proxy_lua_ferror(L, "mutator step %d: must provide 'val' argument", tidx);386    }387    lua_pop(L, 1); // val or nil388389    return len;390}391392mut_step_i(keyset) {393    struct mcp_mut_step *s = &mut->steps[sc];394    struct mcp_mut_string *c = &s->c.string;395    size_t len = 0;396397    // store our match string in the arena space that we reserved before.398    if (lua_getfield(L, tidx, "val") != LUA_TNIL) {399        const char *str = lua_tolstring(L, -1, &len);400        c->str = mut->aused;401        c->len = len;402        char *a = mut->arena + mut->aused;403        memcpy(a, str, len);404        mut->aused += len;405    }406    lua_pop(L, 1); // val or nil407408    return len;409}410411mut_step_n(keyset) {412    struct mcp_mut_string *c = &s->c.string;413    return c->len;414}415416mut_step_r(keyset) {417    struct mcp_mutator *mut = run->mut;418    struct mcp_mut_string *c = &s->c.string;419420    const char *str = mut->arena + c->str;421422    memcpy(run->d_pos, str, c->len);423    run->d_pos += c->len;424    return true;425}426427mut_step_c(resnull) {428    return 0;429}430431mut_step_i(resnull) {432    struct mcp_mut_step *s = &mut->steps[sc];433    if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {434        s->idx = lua_tointeger(L, -1);435    }436    lua_pop(L, 1);437438    return 0;439}440441mut_step_n(resnull) {442    lua_State *L = run->L;443    unsigned int idx = s->idx;444    mcp_request_t *srq = NULL;445    if (lua_type(L, idx) != LUA_TUSERDATA) {446        return -1;447    }448    // validate the ud is a request object.449    lua_getmetatable(L, idx);450    lua_getiuservalue(L, 1, MUT_REQ);451    if (!lua_rawequal(L, -1, -2)) {452        lua_pop(L, 2);453        return -1;454    } else {455        srq = lua_touserdata(L, idx);456        lua_pop(L, 2);457    }458459    switch (srq->pr.command) {460        case CMD_MG:461            p->src = "EN";462            break;463        case CMD_MS:464            p->src = "NS";465            break;466        case CMD_MD:467            p->src = "NF";468            break;469        case CMD_MA:470            p->src = "NF";471            break;472        case CMD_GET:473        case CMD_GAT:474        case CMD_GETS:475        case CMD_GATS:476            p->src = "END";477            break;478        case CMD_SET:479        case CMD_CAS:480        case CMD_ADD:481        case CMD_REPLACE:482        case CMD_APPEND:483        case CMD_PREPEND:484            p->src = "NOT_STORED";485            break;486        case CMD_INCR:487        case CMD_DECR:488        case CMD_TOUCH:489        case CMD_DELETE:490            p->src = "NOT_FOUND";491            break;492        default:493            return -1;494    }495    p->slen = strlen(p->src);496497    return p->slen;498}499500mut_step_r(resnull) {501    memcpy(run->d_pos, p->src, p->slen);502    run->d_pos += p->slen;503    return true;504}505506// TODO: pre-validate that it's an accepted code?507mut_step_c(rescodeset) {508    return _mut_check_strlen(L, tidx, "val");509}510511mut_step_i(rescodeset) {512    struct mcp_mut_step *s = &mut->steps[sc];513    struct mcp_mut_string *c = &s->c.string;514    size_t len = 0;515516    if (lua_getfield(L, tidx, "val") != LUA_TNIL) {517        const char *str = lua_tolstring(L, -1, &len);518        c->str = mut->aused;519        c->len = len;520        char *a = mut->arena + mut->aused;521        memcpy(a, str, len);522        mut->aused += len;523    }524    lua_pop(L, 1);525526    return 0;527}528529mut_step_n(rescodeset) {530    struct mcp_mut_string *c = &s->c.string;531    return c->len;532}533534mut_step_r(rescodeset) {535    struct mcp_mutator *mut = run->mut;536    struct mcp_mut_string *c = &s->c.string;537538    const char *str = mut->arena + c->str;539540    memcpy(run->d_pos, str, c->len);541    run->d_pos += c->len;542    return true;543}544545mut_step_c(rescodecopy) {546    _mut_check_idx(L, tidx);547    return 0;548}549550mut_step_i(rescodecopy) {551    struct mcp_mut_step *s = &mut->steps[sc];552    if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {553        s->idx = lua_tointeger(L, -1);554    }555    lua_pop(L, 1);556557    return 0;558}559560mut_step_n(rescodecopy) {561    lua_State *L = run->L;562    unsigned idx = s->idx;563    mcp_request_t *srq = NULL;564    mcp_resp_t *srs = NULL;565566    switch (lua_type(L, idx)) {567        case LUA_TSTRING:568            p->src = lua_tolstring(L, idx, &p->slen);569            break;570        case LUA_TUSERDATA:571            _mut_checkudata(L, idx, &srq, &srs);572            if (srs) {573                // can't recover the exact code from the mcmc resp object, so lets make574                // sure it's tokenized and copy the first token.575                if (srs->resp.type == MCMC_RESP_META) {576                    mcmc_tokenize_res(res_buf(srs), srs->resp.reslen, &srs->tok);577                } else {578                    // FIXME: only supports meta responses579                    return -1;580                }581                int len = 0;582                p->src = mcmc_token_get(res_buf(srs), &srs->tok, 0, &len);583                p->slen = len;584                if (len < 2) {585                    return -1;586                }587            } else {588                return -1;589            }590            break;591        default:592            // ints/etc unsupported593            return -1;594    }595596    return p->slen;597}598599mut_step_r(rescodecopy) {600    memcpy(run->d_pos, p->src, p->slen);601    run->d_pos += p->slen;602    return true;603}604605// TODO: can be no other steps after an error is set.606// TODO: if idx given instead of msg copy an input string.607mut_step_c(reserr) {608    size_t total = 0;609    char *code = NULL;610611    // FIXME: add code length to len612    if (lua_getfield(L, tidx, "code") != LUA_TNIL) {613        const char *val = lua_tostring(L, -1);614615        if (strcmp(val, "error") == 0) {616            code = RESERR_ERROR_STR;617        } else if (strcmp(val, "server") == 0) {618            code = RESERR_SERVER_STR;619        } else if (strcmp(val, "client") == 0) {620            code = RESERR_CLIENT_STR;621        } else {622            proxy_lua_ferror(L, "mutator step %d: code must be 'error', server', or 'client'", tidx);623        }624625        total += strlen(code);626    } else {627        proxy_lua_ferror(L, "mutator step %d: must provide 'code' argument", tidx);628    }629    lua_pop(L, 1);630631    if (lua_getfield(L, tidx, "msg") != LUA_TNIL) {632        size_t len = 0;633        lua_tolstring(L, -1, &len);634        if (len < 1) {635            proxy_lua_ferror(L, "mutator step %d: 'msg' must be a nonzero length string", tidx);636        }637        total += len + 1; // room for space between code and msg638    }639    lua_pop(L, 1);640641    return total;642}643644mut_step_i(reserr) {645    struct mcp_mut_step *s = &mut->steps[sc];646    struct mcp_mut_string *c = &s->c.string;647    size_t len = 0;648    const char *code = NULL;649650    char *a = mut->arena + mut->aused;651    if (lua_getfield(L, tidx, "code") != LUA_TNIL) {652        const char *val = lua_tostring(L, -1);653        if (strcmp(val, "error") == 0) {654            code = RESERR_ERROR_STR;655        } else if (strcmp(val, "server") == 0) {656            code = RESERR_SERVER_STR;657        } else if (strcmp(val, "client") == 0) {658            code = RESERR_CLIENT_STR;659        } else {660            // shouldn't be possible661            proxy_lua_ferror(L, "mutator step %d: code must be 'error', server', or 'client'", tidx);662        }663664        // Start our string here.665        c->str = mut->aused;666        size_t clen = strlen(code);667        memcpy(a, code, clen);668        a += clen;669        *a = ' ';670        a++;671        clen++;672        mut->aused += clen;673        c->len += clen;674    } else {675        proxy_lua_ferror(L, "mutator step %d: must provide 'code' argument", tidx);676    }677    lua_pop(L, 1);678679    if (lua_getfield(L, tidx, "msg") != LUA_TNIL) {680        // Extend the string with a msg if supplied.681        const char *str = lua_tolstring(L, -1, &len);682        c->len += len;683        memcpy(a, str, len);684        mut->aused += len;685    } else {686        // TODO: note no error msg687    }688    lua_pop(L, 1);689690    return c->len;691}692693mut_step_n(reserr) {694    struct mcp_mut_string *c = &s->c.string;695    return c->len;696}697698mut_step_r(reserr) {699    struct mcp_mutator *mut = run->mut;700    struct mcp_mut_string *c = &s->c.string;701702    const char *str = mut->arena + c->str;703    int len = c->len;704705    // set error code first706    memcpy(run->d_pos, str, len);707    run->d_pos += len;708    return true;709}710711// TODO: track which flags we've already set and error on dupes712mut_step_c(flagset) {713    _mut_check_flag(L, tidx);714    size_t len = 0;715716    int vtype = lua_getfield(L, tidx, "val");717    if (vtype == LUA_TNUMBER || vtype == LUA_TSTRING) {718        // this converts the arg into a string for us.719        lua_tolstring(L, -1, &len);720    } else if (vtype != LUA_TNIL) {721        proxy_lua_ferror(L, "mutator step %d: unsupported type for 'val'", tidx);722    }723    lua_pop(L, 1);724725    return len;726}727728mut_step_i(flagset) {729    struct mcp_mut_step *s = &mut->steps[sc];730    struct mcp_mut_flagval *c = &s->c.flagval;731    size_t len = 0;732733    _mut_init_flag(L, tidx, &c->flag);734    if (lua_getfield(L, tidx, "val") != LUA_TNIL) {735        const char *str = lua_tolstring(L, -1, &len);736        c->str.str = mut->aused;737        c->str.len = len;738        char *a = mut->arena + mut->aused;739        memcpy(a, str, len);740        mut->aused += len;741    }742    lua_pop(L, 1);743744    return len;745}746747mut_step_n(flagset) {748    struct mcp_mut_flagval *c = &s->c.flagval;749    return c->str.len + 1; // room for flag750}751752// FIXME: triple check that this is actually the same code for req vs res?753// seems like it is.754mut_step_r(flagset) {755    struct mcp_mutator *mut = run->mut;756    struct mcp_mut_flagval *c = &s->c.flagval;757758    const char *str = mut->arena + c->str.str;759    int len = c->str.len;760761    *(run->d_pos) = c->flag.f;762    run->d_pos++;763    if (len > 0) {764        memcpy(run->d_pos, str, len);765        run->d_pos += len;766    }767    return true;768}769770mut_step_c(flagcopy) {771    _mut_check_flag(L, tidx);772    _mut_check_idx(L, tidx);773    return 0;774}775776// TODO: maybe: optional default val if copy source missing777mut_step_i(flagcopy) {778    struct mcp_mut_step *s = &mut->steps[sc];779    struct mcp_mut_flag *c = &s->c.flag;780781    _mut_init_flag(L, tidx, c);782783    if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {784        s->idx = lua_tointeger(L, -1);785    }786    lua_pop(L, 1);787788    return 0;789}790791mut_step_n(flagcopy) {792    struct mcp_mut_flag *c = &s->c.flag;793794    lua_State *L = run->L;795    unsigned int idx = s->idx;796    mcp_request_t *srq = NULL;797    mcp_resp_t *srs = NULL;798799    switch (lua_type(L, idx)) {800        case LUA_TNIL:801            p->src = NULL;802            p->slen = 0;803            break;804        case LUA_TBOOLEAN:805            p->src = NULL;806            if (lua_toboolean(L, idx)) {807                p->slen = 1;808            } else {809                p->slen = 0;810            }811            break;812        case LUA_TSTRING:813            p->src = lua_tolstring(L, idx, &p->slen);814            p->slen++;815            break;816        case LUA_TNUMBER:817            // TODO: unimplemented818            return -1;819            break;820        case LUA_TUSERDATA:821            _mut_checkudata(L, idx, &srq, &srs);822            p->slen = 0;823            if (srq) {824                if (srq->pr.cmd_type != CMD_TYPE_META) {825                    return -1;826                }827                int tlen = 0;828                if (mcmc_token_has_flag_bit(&srq->pr.tok, c->bit) == MCMC_OK) {829                    const char *tok = mcmc_token_get_flag(srq->pr.request, &srq->pr.tok, c->f, &tlen);830                    p->slen = 1;831                    if (tok && tlen > 0) {832                        p->src = tok;833                        p->slen += tlen;834                    }835                }836            } else if (srs) {837                if (srs->resp.type != MCMC_RESP_META) {838                    // FIXME: error, can't copy flag from non-meta res839                    return -1;840                }841                mcmc_tokenize_res(res_buf(srs), srs->resp.reslen, &srs->tok);842                if (mcmc_token_has_flag_bit(&srs->tok, c->bit) == MCMC_OK) {843                    // flag exists, so copy that in.844                    // realistically this func is only used if we're also copying a845                    // token, so we look for that too.846                    // could add an option to avoid checking for a token?847                    int len = 0;848                    p->slen = 1;849                    const char *tok = mcmc_token_get_flag(res_buf(srs), &srs->tok, c->f, &len);850851                    if (len > 0) {852                        p->src = tok;853                        p->slen += len;854                    }855                }856            } else {857                return -1;858            }859            break;860        default:861            return -1;862    }863864    return p->slen;865}866867mut_step_r(flagcopy) {868    struct mcp_mut_flag *c = &s->c.flag;869    // slen of 1 means the flag existed in source.870    if (p->slen) {871        *(run->d_pos) = c->f;872        run->d_pos++;873        if (p->src) {874            memcpy(run->d_pos, p->src, p->slen-1);875            run->d_pos += p->slen-1;876        }877        return true;878    }879    return false;880}881882// TODO: except = "flags"883// compile to bitflags and check when looping source flags884// maybe: different command as copyallexcept so we don't have to branch as885// much in this code.886mut_step_c(flagcopyall) {887    return 0;888}889890mut_step_i(flagcopyall) {891    struct mcp_mut_step *s = &mut->steps[sc];892893    if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {894        s->idx = lua_tointeger(L, -1);895    }896    lua_pop(L, 1);897898    return 0;899}900901mut_step_n(flagcopyall) {902    lua_State *L = run->L;903    unsigned idx = s->idx;904    mcp_request_t *srq = NULL;905    mcp_resp_t *srs = NULL;906907    switch (lua_type(L, idx)) {908        case LUA_TSTRING:909            // FIXME: note that we're copying a pre-fabbed string?910            p->src = lua_tolstring(L, idx, &p->slen);911            break;912        // TODO: allow a table of flags?913        case LUA_TUSERDATA:914            _mut_checkudata(L, idx, &srq, &srs);915            if (srq) {916                // TODO: doesn't support requests917                return -1;918            } else {919                // TODO: srs->resp.rline srs->resp.rlen920                // is this set from res = mcp.internal?921                if (srs->resp.type == MCMC_RESP_META && srs->resp.rline) {922                    /* TODO: mstart ends up indexing VA [len]923                    mcmc_tokenizer_t *tok = &srs->tok;924                    mcmc_tokenize_res(res_buf(srs), srs->resp.reslen, &srs->tok);925                    p->src = res_buf(srs);926                    p->src += tok->tokens[tok->mstart];927                    p->slen = tok->tokens[tok->ntokens] - tok->tokens[tok->mstart];928                    */929                    p->src = srs->resp.rline;930                    p->slen = srs->resp.rlen;931                } else {932                    return -1;933                }934            }935            break;936        default:937            // ints/etc unsupported938            return -1;939    }940941    return p->slen;942}943944mut_step_r(flagcopyall) {945    memcpy(run->d_pos, p->src, p->slen);946    run->d_pos += p->slen;947948    return true;949}950951// TODO: check that the value hasn't been set yet.952mut_step_c(valcopy) {953    _mut_check_idx(L, tidx);954955    return 0;956}957958mut_step_i(valcopy) {959    struct mcp_mut_step *s = &mut->steps[sc];960961    if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {962        s->idx = lua_tointeger(L, -1);963    }964    lua_pop(L, 1);965966    return 0;967}968969mut_step_n(valcopy) {970    lua_State *L = run->L;971    unsigned int idx = s->idx;972    // extract the string + length we need to copy973    mcp_request_t *srq = NULL;974    mcp_resp_t *srs = NULL;975976    switch (lua_type(L, idx)) {977        case LUA_TSTRING:978            run->vbuf = lua_tolstring(L, idx, &run->vlen);979            break;980        case LUA_TNUMBER:981            // TODO: unimplemented982            break;983        case LUA_TUSERDATA:984            _mut_checkudata(L, idx, &srq, &srs);985            if (srq) {986                if (srq->pr.vbuf) {987                    run->vbuf = srq->pr.vbuf;988                    run->vlen = srq->pr.vlen;989                }990            } else if (srs) {991                // TODO: need to locally parse the result to get the actual val992                // offsets until later refactoring takes place.993            } else {994                return -1;995            }996            break;997        default:998            return -1;999    }10001001    if (run->vlen < 2 || memcmp(run->vbuf+run->vlen-2, "\r\n", 2) != 0) {1002        return -1;1003    }10041005    // count the number of digits in vlen to reserve space.1006    //1007    // oddly algorithms to count digits and write digits are similar (outside1008    // of hyper optimization via bit math), so just reuse the same code here.1009    // if I can ever get arena allocations to work and remove the pre-calc1010    // steps this is moot anyway.1011    char temp[22];1012    const char *e = itoa_u64(run->vlen, temp);10131014    return e - temp;1015}10161017// print the vlen into the buffer1018// we remove the \r\n from the protocol length1019mut_step_r(valcopy) {1020    // Note: vlen must not be < 2 during the _n stage to avoid underflowing.1021    run->d_pos = itoa_u64(run->vlen-2, run->d_pos);1022    return true;1023}10241025// END STEPS10261027static const struct mcp_mut_entry mcp_mut_entries[] = {1028    [mcp_mut_step_none] = {NULL, NULL, NULL, NULL, NULL, 0, 0},1029    [mcp_mut_step_cmdset] = {"cmdset", mcp_mutator_cmdset_c, mcp_mutator_cmdset_i, mcp_mutator_cmdset_n, mcp_mutator_cmdset_r, MUT_REQ, 0},1030    [mcp_mut_step_cmdcopy] = {"cmdcopy", mcp_mutator_cmdcopy_c, mcp_mutator_cmdcopy_i, mcp_mutator_cmdcopy_n, mcp_mutator_cmdcopy_r, MUT_REQ, 0},1031    [mcp_mut_step_keycopy] = {"keycopy", mcp_mutator_keycopy_c, mcp_mutator_keycopy_i, mcp_mutator_keycopy_n, mcp_mutator_keycopy_r, MUT_REQ, 0},1032    [mcp_mut_step_keyset] = {"keyset", mcp_mutator_keyset_c, mcp_mutator_keyset_i, mcp_mutator_keyset_n, mcp_mutator_keyset_r, MUT_REQ, 0},1033    [mcp_mut_step_resnull] = {"resnull", mcp_mutator_resnull_c, mcp_mutator_resnull_i, mcp_mutator_resnull_n, mcp_mutator_resnull_r, MUT_RES, 0},1034    [mcp_mut_step_rescodeset] = {"rescodeset", mcp_mutator_rescodeset_c, mcp_mutator_rescodeset_i, mcp_mutator_rescodeset_n, mcp_mutator_rescodeset_r, MUT_RES, 0},1035    [mcp_mut_step_rescodecopy] = {"rescodecopy", mcp_mutator_rescodecopy_c, mcp_mutator_rescodecopy_i, mcp_mutator_rescodecopy_n, mcp_mutator_rescodecopy_r, MUT_RES, 0},1036    [mcp_mut_step_reserr] = {"reserr", mcp_mutator_reserr_c, mcp_mutator_reserr_i, mcp_mutator_reserr_n, mcp_mutator_reserr_r, MUT_RES, 0},1037    [mcp_mut_step_flagset] = {"flagset", mcp_mutator_flagset_c, mcp_mutator_flagset_i, mcp_mutator_flagset_n, mcp_mutator_flagset_r, MUT_REQ|MUT_RES, 0},1038    [mcp_mut_step_flagcopy] = {"flagcopy", mcp_mutator_flagcopy_c, mcp_mutator_flagcopy_i, mcp_mutator_flagcopy_n, mcp_mutator_flagcopy_r, MUT_REQ|MUT_RES, 0},1039    [mcp_mut_step_flagcopyall] = {"flagcopyall", mcp_mutator_flagcopyall_c, mcp_mutator_flagcopyall_i, mcp_mutator_flagcopyall_n, mcp_mutator_flagcopyall_r, MUT_REQ|MUT_RES, 0},1040    [mcp_mut_step_valcopy] = {"valcopy", mcp_mutator_valcopy_c, mcp_mutator_valcopy_i, mcp_mutator_valcopy_n, mcp_mutator_valcopy_r, MUT_REQ|MUT_RES, 0},1041    [mcp_mut_step_final] = {NULL, NULL, NULL, NULL, NULL, 0, 0},1042};10431044// call with type string on top1045static enum mcp_mut_steptype mcp_mutator_steptype(lua_State *L) {1046    const char *type = luaL_checkstring(L, -1);1047    for (int x = 0; x < mcp_mut_step_final; x++) {1048        const struct mcp_mut_entry *e = &mcp_mut_entries[x];1049        if (e->s && strcmp(type, e->s) == 0) {1050            return x;1051        }1052    }1053    return mcp_mut_step_none;1054}10551056static int mcp_mutator_new(lua_State *L, enum mcp_mut_type type) {1057    int argc = lua_gettop(L);1058    size_t size = 0;1059    int scount = 0;10601061    // loop argument tables once for validation and pre-calculations.1062    for (int x = 1; x <= argc; x++) {1063        luaL_checktype(L, x, LUA_TTABLE);1064        if (lua_getfield(L, x, "t") != LUA_TNIL) {1065            enum mcp_mut_steptype st = mcp_mutator_steptype(L);1066            const struct mcp_mut_entry *e = &mcp_mut_entries[st];1067            if (!(e->t & type)) {1068                proxy_lua_ferror(L, "mutator step %d: step incompatible with mutator type", x);1069            }1070            if ((st == mcp_mut_step_none) || e->c == NULL) {1071                proxy_lua_ferror(L, "mutator step %d: unknown step type", x);1072            }1073            size += e->c(L, x);1074        }1075        lua_pop(L, 1); // drop 't' or nil1076        scount++;1077    }10781079    // we now know the size and number of steps. allocate some flat memory.10801081    size_t extsize = sizeof(struct mcp_mut_step) * scount;1082    struct mcp_mutator *mut = lua_newuserdatauv(L, sizeof(*mut) + extsize, 2);1083    memset(mut, 0, sizeof(*mut) + extsize);10841085    mut->arena = malloc(size);1086    if (mut->arena == NULL) {1087        proxy_lua_error(L, "mutator_new: failed to allocate memory");1088    }1089    luaL_setmetatable(L, "mcp.mutator");1090    mut->type = type;10911092    // Cache both request and result metatables for arg validation.1093    // Since a req mutator can take a res argument and vice versa1094    luaL_getmetatable(L, "mcp.request");1095    lua_setiuservalue(L, -2, MUT_REQ);1096    luaL_getmetatable(L, "mcp.response");1097    lua_setiuservalue(L, -2, MUT_RES);10981099    // loop the arg tables again to fill in the steps1100    // skip checks since we did that during the first loop.1101    scount = 0;1102    for (int x = 1; x <= argc; x++) {1103        if (lua_getfield(L, x, "t") != LUA_TNIL) {1104            enum mcp_mut_steptype st = mcp_mutator_steptype(L);1105            mut->steps[scount].type = st;1106            mcp_mut_entries[st].i(L, x, scount, mut);1107            mut->rcount += mcp_mut_entries[st].rc;11081109            // copy function pointers into the step so we don't have to skip1110            // around the much larger mcp_mut_entries at runtime.1111            mut->steps[scount].n = mcp_mut_entries[st].n;1112            mut->steps[scount].r = mcp_mut_entries[st].r;1113            // actual args are "self, dst, args". start user idx's at 31114            mut->steps[scount].idx += 2;1115        }1116        lua_pop(L, 1); // drop t or nil1117        scount++;1118    }11191120    if (size != mut->aused) {1121        proxy_lua_error(L, "mutator failed to properly initialize, memory not filled correctly");1122    }1123    if (scount == 0) {1124        proxy_lua_error(L, "mutator failed to initialize, no steps given");1125    }1126    mut->scount = scount;11271128    return 1;1129}11301131static inline int _mcp_mut_run_total(struct mcp_mut_run *run, struct mcp_mut_part *parts) {1132    int total = 0;1133    struct mcp_mutator *mut = run->mut;1134    for (int x = 0; x < mut->scount; x++) {1135        struct mcp_mut_step *s = &mut->steps[x];1136        assert(s->type != mcp_mut_step_none);1137        int len = s->n(run, s, &parts[x]);1138        if (len < 0) {1139            return -1;1140        } else {1141            total += len;1142        }1143    }1144    // account for spaces between "steps" and \r\n1145    // note that steps that themselves can make multiple tokens _must_ account1146    // for this on their own.1147    total += mut->scount + MIN_BUF_SPACE;11481149    return total;1150}11511152static inline int _mcp_mut_run_assemble(struct mcp_mut_run *run, struct mcp_mut_part *parts) {1153    struct mcp_mutator *mut = run->mut;1154    // assemble final req/res1155    for (int x = 0; x < mut->scount; x++) {1156        struct mcp_mut_step *s = &mut->steps[x];1157        assert(s->type != mcp_mut_step_none);1158        // Error handling is pushed to the totalling phase.1159        // In this phase we should just be copying data and cannot fail.1160        if (s->r(run, s, &parts[x])) {1161            *(run->d_pos) = ' ';1162            run->d_pos++;1163        }1164    }11651166    // TODO: any cases where we need to check if the final char is a space or1167    // not?1168    // add the \r\n after all steps1169    *(run->d_pos-1) = '\r';1170    *(run->d_pos) = '\n';1171    run->d_pos++;11721173    return 0;1174}11751176static int mcp_mut_run(struct mcp_mut_run *run) {1177    struct mcp_mutator *mut = run->mut;1178    LIBEVENT_THREAD *t = PROXY_GET_THR(run->L);1179    int ret = 0;1180    struct mcp_mut_part parts[mut->scount];11811182    // first accumulate the length tally1183    // FIXME: noticed off-by-one's sometimes.1184    // maybe add a debug assert to verify the written total (d_pos - etc)1185    // matches total?1186    // This isn't critical so long as total is > actual, which it has been1187    int total = _mcp_mut_run_total(run, parts);1188    if (total < 0) {1189        lua_pushboolean(run->L, 0);1190        return 1;1191    }11921193    // ensure space and/or allocate memory then seed our destination pointer.1194    if (mut->type == MUT_REQ) {1195        mcp_request_t *rq = run->arg;1196        if (rq->pr.vbuf) {1197            // FIXME: maybe NULL rq->pr.request in cleanup phase and test that1198            // instead? this check will only fire if req had a vbuf.1199            proxy_lua_error(run->L, "mutator: request has already been rendered");1200            return 0;1201        }1202        // future.. should be able to dynamically assign request buffer.1203        if (total > MCP_REQUEST_MAXLEN) {1204            proxy_lua_error(run->L, "mutator: new request is too long");1205            return 0;1206        }1207        run->d_pos = rq->request;12081209        _mcp_mut_run_assemble(run, parts);12101211        if (process_request(&rq->pr, rq->request, run->d_pos - rq->request) != 0) {1212            proxy_lua_error(run->L, "mutator: failed to parse new request");1213            return 0;1214        }12151216        if (run->vbuf) {1217            rq->pr.vbuf = malloc(run->vlen);1218            if (rq->pr.vbuf == NULL) {1219                proxy_lua_error(run->L, "mutator: failed to allocate value buffer");1220                return 0;1221            }1222            t->proxy_buffer_memory_used += rq->pr.vlen;12231224            rq->pr.vlen = run->vlen;1225            memcpy(rq->pr.vbuf, run->vbuf, run->vlen);1226        }1227    } else {1228        mcp_resp_t *rs = run->arg;1229        if (rs->buf) {1230            proxy_lua_error(run->L, "mutator: result has already been rendered");1231            return 0;1232        }12331234        // value is inlined in result buffers. future intention to allow more1235        // complex objects so we can refcount values.1236        rs->buf = malloc(total + run->vlen);1237        if (rs->buf == NULL) {1238            proxy_lua_error(run->L, "mutator: failed to allocate result buffer");1239            return 0;1240        }1241        run->d_pos = rs->buf;12421243        _mcp_mut_run_assemble(run, parts);12441245        rs->tok.ntokens = 0; // TODO: handler from mcmc?1246        rs->status = mcmc_parse_buf(rs->buf, run->d_pos - rs->buf, &rs->resp);1247        if (rs->resp.type == MCMC_RESP_FAIL) {1248            proxy_lua_error(run->L, "mutator: failed to parse new result");1249            return 0;1250        }12511252        // results are sequential buffers, copy the value in.1253        if (run->vbuf) {1254            memcpy(run->d_pos, run->vbuf, run->vlen);1255            run->d_pos += run->vlen;1256        }12571258        rs->blen = run->d_pos - rs->buf;1259        // NOTE: We increment but don't check the memory limits here. Any1260        // incoming request or incoming response will also check the memory1261        // limits, and just doing an increment here removes some potential1262        // error handling. Requests that are already started should be allowed1263        // to complete to minimize impact of hitting memory limits.1264        t->proxy_buffer_memory_used += rs->blen;1265    }12661267    return ret;1268}12691270// PUBLIC INTERFACE12711272int mcplib_req_mutator_new(lua_State *L) {1273    return mcp_mutator_new(L, MUT_REQ);1274}12751276int mcplib_res_mutator_new(lua_State *L) {1277    return mcp_mutator_new(L, MUT_RES);1278}12791280// walk each step and free references/memory/etc1281int mcplib_mutator_gc(lua_State *L) {1282    struct mcp_mutator *mut = lua_touserdata(L, 1);12831284    if (mut->arena) {1285        free(mut->arena);1286        mut->arena = NULL;1287    }12881289    // NOTE: leaving commented until I run into something that actually has1290    // something extra to GC1291    /*for (int x = 0; x < mut->scount; x++) {1292        struct mcp_mut_step *s = &mut->steps[x];1293        switch (s->type) {1294            case mcp_mut_step_none:1295            case mcp_mut_step_cmdset:1296            case mcp_mut_step_cmdcopy:1297            case mcp_mut_step_keycopy:1298            case mcp_mut_step_keyset:1299            case mcp_mut_step_rescodeset:1300            case mcp_mut_step_rescodecopy:1301            case mcp_mut_step_reserr:1302            case mcp_mut_step_flagset:1303            case mcp_mut_step_flagcopy:1304            case mcp_mut_step_valcopy:1305            case mcp_mut_step_final:1306                break;1307        }1308    }*/13091310    return 0;1311}13121313int mcplib_mutator_call(lua_State *L) {1314    // since we're here from a __call, assume the type is correct.1315    struct mcp_mutator *mut = lua_touserdata(L, 1);1316    luaL_checktype(L, 2, LUA_TUSERDATA);1317    if (lua_checkstack(L, mut->rcount + 3) == 0) {1318        proxy_lua_error(L, "mutator ran out of stack space for results");1319    }13201321    lua_getmetatable(L, 2); // put dest obj arg metatable on stack1322    lua_getiuservalue(L, 1, mut->type); // put stashed metatable on stack1323    luaL_argcheck(L, lua_rawequal(L, -1, -2), 2,1324            "invalid argument to mutator object");1325    lua_pop(L, 2); // toss both metatables132613271328    // we're valid now.1329    void *arg = lua_touserdata(L, 2);1330    // stack scratch space so we can avoid modifying the mut struct1331    struct mcp_mut_run run = {L, mut, arg, NULL, NULL, 0};1332    // TODO: numbuf space1333    int ret = mcp_mut_run(&run);13341335    return ret;1336}

Code quality findings 1

Info: Magic number detected in condition or control flow. Use named constants via #define or const for better readability and maintainability.
info maintainability magic-number
if (len > 20) {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.

Generated by searchcode.com · powered by code_get_file + code_get_findings