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