1 /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
3 #include "proxy.h"
4
5 /*
6 * !!!WARNING!!!
7 * This is an experimental interface and is not to be used in production until
8 * after this warning has been removed.
9 * The req/res mutator system is currently an experimental draft, merged to
10 * allow code experiments and further information gathering before completing
11 * the interface.
12 *
13 * it currently has bugs, unfinished features, and will not immediately
14 * release request or result buffers after a request completes.
15 */
16
17 // space or \r\n
18 #define MIN_BUF_SPACE 2
19
20 enum mcp_mut_type {
21 MUT_REQ = 1,
22 MUT_RES,
23 };
24
25 enum mcp_mut_steptype {
26 mcp_mut_step_none = 0,
27 mcp_mut_step_cmdset,
28 mcp_mut_step_cmdcopy,
29 mcp_mut_step_keycopy,
30 mcp_mut_step_keyset,
31 mcp_mut_step_rescodeset,
32 mcp_mut_step_rescodecopy,
33 mcp_mut_step_reserr,
34 mcp_mut_step_flagset,
35 mcp_mut_step_flagcopy,
36 mcp_mut_step_valcopy,
37 mcp_mut_step_final, // not used.
38 };
39
40 enum mcp_mut_step_arg {
41 mcp_mut_step_arg_none = 0,
42 mcp_mut_step_arg_request,
43 mcp_mut_step_arg_response,
44 mcp_mut_step_arg_string,
45 mcp_mut_step_arg_int,
46 };
47
48 // START STEP STRUCTS
49
50 // struct forward declarations for entry/step function pointers
51 struct mcp_mut_step;
52 struct mcp_mutator;
53 struct mcp_mut_run;
54 struct mcp_mut_part;
55
56 typedef int (*mcp_mut_c)(lua_State *L, int tidx);
57 typedef int (*mcp_mut_i)(lua_State *L, int tidx, int sc, struct mcp_mutator *mut);
58 typedef int (*mcp_mut_r)(struct mcp_mut_run *run, struct mcp_mut_step *s, struct mcp_mut_part *p);
59
60 struct mcp_mut_entry {
61 const char *s; // string name
62 mcp_mut_c c; // argument checker
63 mcp_mut_i i; // argument initializer
64 mcp_mut_r n; // runtime length totaller
65 mcp_mut_r r; // runtime assembly
66 unsigned int t; // allowed object types
67 int rc; // number of results to expect
68 };
69
70 struct mcp_mut_string {
71 unsigned int str; // arena offset for match string.
72 unsigned int len;
73 };
74
75 struct mcp_mut_flag {
76 uint64_t bit; // flag converted for bitmask test
77 char f;
78 };
79
80 #define RESERR_ERROR 1
81 #define RESERR_ERROR_STR "ERROR"
82 #define RESERR_CLIENT 2
83 #define RESERR_CLIENT_STR "CLIENT_ERROR"
84 #define RESERR_SERVER 3
85 #define RESERR_SERVER_STR "SERVER_ERROR"
86
87 struct mcp_mut_flagval {
88 struct mcp_mut_flag flag;
89 struct mcp_mut_string str;
90 };
91
92 struct mcp_mut_step {
93 enum mcp_mut_steptype type;
94 unsigned int idx; // common: input argument position
95 enum mcp_mut_step_arg arg; // common: type of input argument
96 mcp_mut_r n; // totaller function
97 mcp_mut_r r; // data copy function
98 union {
99 struct mcp_mut_string string;
100 struct mcp_mut_flag flag;
101 struct mcp_mut_flagval flagval;
102 } c;
103 };
104
105 // END STEP STRUCTS
106
107 struct mcp_mutator {
108 enum mcp_mut_type type;
109 int scount;
110 unsigned int aused; // arena memory used
111 unsigned int rcount; // number of results to expect
112 char *arena; // string/data storage for steps
113 struct mcp_mut_step steps[];
114 };
115
116 // scratch space for steps between total and execution stages.
117 struct mcp_mut_part {
118 const char *src;
119 size_t slen;
120 };
121
122 // stack scratch variables for mutation execution
123 struct mcp_mut_run {
124 lua_State *L;
125 struct mcp_mutator *mut;
126 void *arg;
127 char *numbuf; // stack space for rendering numerics
128 char *d_pos; // current offset to the write string.
129
130 const char *vbuf; // buffer or ptr if a value is being attached
131 size_t vlen; // length of the actual value buffer.
132 };
133
134 #define mut_step_c(n) static int mcp_mutator_##n##_c(lua_State *L, int tidx)
135 #define mut_step_i(n) static int mcp_mutator_##n##_i(lua_State *L, int tidx, int sc, struct mcp_mutator *mut)
136 #define mut_step_r(n) static int mcp_mutator_##n##_r(struct mcp_mut_run *run, struct mcp_mut_step *s, struct mcp_mut_part *p)
137 #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)
138
139 // PRIVATE INTERFACE
140
141 // COMMON ARG HANDLERS
142
_mut_check_idx(lua_State * L,int tidx)143 static void _mut_check_idx(lua_State *L, int tidx) {
144 if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {
145 luaL_checktype(L, -1, LUA_TNUMBER);
146 int isnum = 0;
147 lua_Integer i = lua_tointegerx(L, -1, &isnum);
148 if (!isnum) {
149 proxy_lua_ferror(L, "mutator step %d: must provide 'idx' argument as an integer", tidx);
150 }
151 if (i < 2) {
152 proxy_lua_ferror(L, "mutator step %d: 'idx' argument must be greater than 1", tidx);
153 }
154 } else {
155 proxy_lua_ferror(L, "mutator step %d: must provide 'idx' argument", tidx);
156 }
157 lua_pop(L, 1);
158 }
159
_mut_check_strlen(lua_State * L,int tidx,const char * n)160 static size_t _mut_check_strlen(lua_State *L, int tidx, const char *n) {
161 size_t len = 0;
162
163 if (lua_getfield(L, tidx, n) != LUA_TNIL) {
164 lua_tolstring(L, -1, &len);
165 } else {
166 proxy_lua_ferror(L, "mutator step %d: must provide '%s' argument", tidx, n);
167 }
168 lua_pop(L, 1);
169
170 return len;
171 }
172
_mut_check_flag(lua_State * L,int tidx)173 static void _mut_check_flag(lua_State *L, int tidx) {
174 if (lua_getfield(L, tidx, "flag") != LUA_TNIL) {
175 size_t len = 0;
176 const char *flag = lua_tolstring(L, -1, &len);
177 if (len != 1) {
178 proxy_lua_ferror(L, "mutator step %d: 'flag' must be a single character", tidx);
179 }
180 if (mcp_is_flag_invalid(flag[0])) {
181 proxy_lua_ferror(L, "mutator step %d: 'flag' must be alphanumeric", tidx);
182 }
183 } else {
184 proxy_lua_ferror(L, "mutator step %d: must provide 'flag' argument", tidx);
185 }
186 lua_pop(L, 1); // val or nil
187 }
188
_mut_init_flag(lua_State * L,int tidx,struct mcp_mut_flag * c)189 static void _mut_init_flag(lua_State *L, int tidx, struct mcp_mut_flag *c) {
190 if (lua_getfield(L, tidx, "flag") != LUA_TNIL) {
191 const char *flag = lua_tostring(L, -1);
192 c->f = flag[0];
193 c->bit = (uint64_t)1 << (c->f - 65);
194 }
195 lua_pop(L, 1); // val or nil
196 }
197
198 // END COMMMON ARG HANDLERS
199
200 // START STEPS
201
202 // FIXME: decide on if this should take an mcp.CMD_etc instead, or at least
203 // optionally.
204 // we could also parse this into the int to avoid copying a string here.
205 // FIXME: track step progress and validate we're the first one.
mut_step_c(cmdset)206 mut_step_c(cmdset) {
207 size_t len = 0;
208
209 if (lua_getfield(L, tidx, "cmd") != LUA_TNIL) {
210 lua_tolstring(L, -1, &len);
211 // TODO: know either exact max length or parse for valid commands
212 // this small sanity check should help against user error for now.
213 if (len > 20) {
214 proxy_lua_ferror(L, "mutator step %d: 'cmd' too long", tidx);
215 }
216 } else {
217 proxy_lua_ferror(L, "mutator step %d: must provide 'cmd' argument", tidx);
218 }
219 lua_pop(L, 1);
220
221 return len;
222 }
223
mut_step_i(cmdset)224 mut_step_i(cmdset) {
225 struct mcp_mut_step *s = &mut->steps[sc];
226 struct mcp_mut_string *c = &s->c.string;
227 size_t len = 0;
228
229 if (lua_getfield(L, tidx, "cmd") != LUA_TNIL) {
230 const char *cmd = lua_tolstring(L, -1, &len);
231 c->str = mut->aused;
232 c->len = len;
233 char *a = mut->arena + mut->aused;
234 memcpy(a, cmd, len);
235 mut->aused += len;
236 }
237 lua_pop(L, 1);
238
239 return 0;
240 }
241
mut_step_n(cmdset)242 mut_step_n(cmdset) {
243 struct mcp_mut_string *c = &s->c.string;
244 return c->len;
245 }
246
mut_step_r(cmdset)247 mut_step_r(cmdset) {
248 struct mcp_mutator *mut = run->mut;
249 struct mcp_mut_string *c = &s->c.string;
250
251 const char *str = mut->arena + c->str;
252
253 memcpy(run->d_pos, str, c->len);
254 run->d_pos += c->len;
255
256 return 0;
257 }
258
259 // TODO: validate we're at the right stage to copy a command (no command set)
mut_step_c(cmdcopy)260 mut_step_c(cmdcopy) {
261 _mut_check_idx(L, tidx);
262 return 0;
263 }
264
mut_step_i(cmdcopy)265 mut_step_i(cmdcopy) {
266 struct mcp_mut_step *s = &mut->steps[sc];
267 if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {
268 s->idx = lua_tointeger(L, -1);
269 }
270 lua_pop(L, 1);
271
272 return 0;
273 }
274
mut_step_n(cmdcopy)275 mut_step_n(cmdcopy) {
276 unsigned idx = s->idx;
277 // TODO: validate metatable matches or pull from cached entry
278 mcp_request_t *srq = lua_touserdata(run->L, idx);
279
280 // command must be at the start
281 const char *cmd = srq->pr.request;
282 // command ends at the first token
283 int clen = srq->pr.tokens[1];
284 if (cmd[clen] == ' ') {
285 clen--;
286 }
287 p->src = cmd;
288 p->slen = clen;
289
290 return clen;
291 }
292
mut_step_r(cmdcopy)293 mut_step_r(cmdcopy) {
294 memcpy(run->d_pos, p->src, p->slen);
295 run->d_pos += p->slen;
296 return 0;
297 }
298
299 // TODO: validate a cmd is already slated to be set
300 // NOTE: we might need to know the integer CMD because key position can move
301 // with the stupid GAT command.
mut_step_c(keycopy)302 mut_step_c(keycopy) {
303 _mut_check_idx(L, tidx);
304 return 0;
305 }
306
307 // FIXME: idx_i_g?
mut_step_i(keycopy)308 mut_step_i(keycopy) {
309 struct mcp_mut_step *s = &mut->steps[sc];
310 if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {
311 s->idx = lua_tointeger(L, -1);
312 }
313 lua_pop(L, 1);
314 return 0;
315 }
316
mut_step_n(keycopy)317 mut_step_n(keycopy) {
318 unsigned idx = s->idx;
319 // TODO: validate metatable table matches or pull from cached entry
320 mcp_request_t *srq = lua_touserdata(run->L, idx);
321
322 p->src = MCP_PARSER_KEY(srq->pr);
323 p->slen = srq->pr.klen;
324
325 return p->slen;
326 }
327
mut_step_r(keycopy)328 mut_step_r(keycopy) {
329 memcpy(run->d_pos, p->src, p->slen);
330 run->d_pos += p->slen;
331 return 0;
332 }
333
334 // TODO: check we're okay to set a key
mut_step_c(keyset)335 mut_step_c(keyset) {
336 size_t len = 0;
337
338 if (lua_getfield(L, tidx, "str") != LUA_TNIL) {
339 lua_tolstring(L, -1, &len);
340 if (len < 1) {
341 proxy_lua_ferror(L, "mutator step %d: 'str' must have nonzero length", tidx);
342 }
343 } else {
344 proxy_lua_ferror(L, "mutator step %d: must provide 'str' argument", tidx);
345 }
346 lua_pop(L, 1); // val or nil
347
348 return len;
349 }
350
mut_step_i(keyset)351 mut_step_i(keyset) {
352 struct mcp_mut_step *s = &mut->steps[sc];
353 struct mcp_mut_string *c = &s->c.string;
354 size_t len = 0;
355
356 // store our match string in the arena space that we reserved before.
357 if (lua_getfield(L, tidx, "str") != LUA_TNIL) {
358 const char *str = lua_tolstring(L, -1, &len);
359 c->str = mut->aused;
360 c->len = len;
361 char *a = mut->arena + mut->aused;
362 memcpy(a, str, len);
363 mut->aused += len;
364 }
365 lua_pop(L, 1); // val or nil
366
367 return len;
368 }
369
mut_step_n(keyset)370 mut_step_n(keyset) {
371 struct mcp_mut_string *c = &s->c.string;
372 return c->len;
373 }
374
mut_step_r(keyset)375 mut_step_r(keyset) {
376 struct mcp_mutator *mut = run->mut;
377 struct mcp_mut_string *c = &s->c.string;
378
379 const char *str = mut->arena + c->str;
380
381 memcpy(run->d_pos, str, c->len);
382 run->d_pos += c->len;
383 return 0;
384 }
385
386 // TODO: ensure step is first
387 // TODO: pre-validate that it's an accepted code?
mut_step_c(rescodeset)388 mut_step_c(rescodeset) {
389 return _mut_check_strlen(L, tidx, "str");
390 }
391
mut_step_i(rescodeset)392 mut_step_i(rescodeset) {
393 struct mcp_mut_step *s = &mut->steps[sc];
394 struct mcp_mut_string *c = &s->c.string;
395 size_t len = 0;
396
397 if (lua_getfield(L, tidx, "str") != LUA_TNIL) {
398 const char *str = lua_tolstring(L, -1, &len);
399 c->str = mut->aused;
400 c->len = len;
401 char *a = mut->arena + mut->aused;
402 memcpy(a, str, len);
403 mut->aused += len;
404 }
405 lua_pop(L, 1);
406
407 return 0;
408 }
409
mut_step_n(rescodeset)410 mut_step_n(rescodeset) {
411 struct mcp_mut_string *c = &s->c.string;
412 return c->len;
413 }
414
mut_step_r(rescodeset)415 mut_step_r(rescodeset) {
416 struct mcp_mutator *mut = run->mut;
417 struct mcp_mut_string *c = &s->c.string;
418
419 const char *str = mut->arena + c->str;
420
421 memcpy(run->d_pos, str, c->len);
422 run->d_pos += c->len;
423 return 0;
424 }
425
426 // TODO: check we're the first step
mut_step_c(rescodecopy)427 mut_step_c(rescodecopy) {
428 _mut_check_idx(L, tidx);
429 return 0;
430 }
431
mut_step_i(rescodecopy)432 mut_step_i(rescodecopy) {
433 struct mcp_mut_step *s = &mut->steps[sc];
434 if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {
435 s->idx = lua_tointeger(L, -1);
436 }
437 lua_pop(L, 1);
438
439 return 0;
440 }
441
mut_step_n(rescodecopy)442 mut_step_n(rescodecopy) {
443 unsigned idx = s->idx;
444 // TODO: validate metatable matches or pull from cached entry
445 mcp_resp_t *srs = lua_touserdata(run->L, idx);
446
447 // TODO: can't recover the exact from the mcmc resp object, so lets make
448 // sure it's tokenized and copy the first token.
449 if (srs->resp.type == MCMC_RESP_META) {
450 mcmc_tokenize_res(srs->buf, srs->resp.reslen, &srs->tok);
451 } else {
452 // FIXME: above only does meta responses
453 assert(1 == 0);
454 }
455 int len = 0;
456 p->src = mcmc_token_get(srs->buf, &srs->tok, 0, &len);
457 p->slen = len;
458 return len;
459 }
460
461 // TODO: take a string or number from that position.
mut_step_r(rescodecopy)462 mut_step_r(rescodecopy) {
463 // FIXME: error propagation
464 // FIXME: can we do all the error handling in the totalling phase?
465 if (p->slen < 2) {
466 return -1;
467 }
468 memcpy(run->d_pos, p->src, p->slen);
469 run->d_pos += p->slen;
470 return 0;
471 }
472
473 // TODO: can be no other steps after an error is set.
mut_step_c(reserr)474 mut_step_c(reserr) {
475 size_t total = 0;
476 char *code = NULL;
477
478 // FIXME: add code length to len
479 if (lua_getfield(L, tidx, "code") != LUA_TNIL) {
480 const char *val = lua_tostring(L, -1);
481
482 if (strcmp(val, "error") == 0) {
483 code = RESERR_ERROR_STR;
484 } else if (strcmp(val, "server") == 0) {
485 code = RESERR_SERVER_STR;
486 } else if (strcmp(val, "client") == 0) {
487 code = RESERR_CLIENT_STR;
488 } else {
489 proxy_lua_ferror(L, "mutator step %d: mode must be 'error', server', or 'client'", tidx);
490 }
491
492 total += strlen(code);
493 } else {
494 proxy_lua_ferror(L, "mutator step %d: must provide 'mode' argument", tidx);
495 }
496 lua_pop(L, 1);
497
498 if (lua_getfield(L, tidx, "msg") != LUA_TNIL) {
499 size_t len = 0;
500 lua_tolstring(L, -1, &len);
501 if (len < 1) {
502 proxy_lua_ferror(L, "mutator step %d: 'msg' must be a nonzero length string", tidx);
503 }
504 total += len + 1; // room for space between code and msg
505 }
506 lua_pop(L, 1);
507
508 return total;
509 }
510
mut_step_i(reserr)511 mut_step_i(reserr) {
512 struct mcp_mut_step *s = &mut->steps[sc];
513 struct mcp_mut_string *c = &s->c.string;
514 size_t len = 0;
515 const char *code = NULL;
516
517 char *a = mut->arena + mut->aused;
518 if (lua_getfield(L, tidx, "code") != LUA_TNIL) {
519 const char *val = lua_tostring(L, -1);
520 if (strcmp(val, "error") == 0) {
521 code = RESERR_ERROR_STR;
522 } else if (strcmp(val, "server") == 0) {
523 code = RESERR_SERVER_STR;
524 } else if (strcmp(val, "client") == 0) {
525 code = RESERR_CLIENT_STR;
526 } else {
527 // shouldn't be possible
528 proxy_lua_ferror(L, "mutator step %d: code must be 'error', server', or 'client'", tidx);
529 }
530
531 size_t clen = strlen(code);
532 memcpy(a, code, clen);
533 a += clen;
534 *a = ' ';
535 a++;
536 mut->aused += clen + 1;
537 } else {
538 proxy_lua_ferror(L, "mutator step %d: must provide 'code' argument", tidx);
539 }
540 lua_pop(L, 1);
541
542 if (lua_getfield(L, tidx, "msg") != LUA_TNIL) {
543 const char *str = lua_tolstring(L, -1, &len);
544 c->str = mut->aused;
545 c->len = len;
546 memcpy(a, str, len);
547 mut->aused += len;
548 } else {
549 // TODO: note no error msg
550 }
551 lua_pop(L, 1);
552
553 return len;
554 }
555
mut_step_n(reserr)556 mut_step_n(reserr) {
557 struct mcp_mut_string *c = &s->c.string;
558 return c->len;
559 }
560
mut_step_r(reserr)561 mut_step_r(reserr) {
562 struct mcp_mutator *mut = run->mut;
563 struct mcp_mut_string *c = &s->c.string;
564
565 const char *str = mut->arena + c->str;
566 int len = c->len;
567
568 // set error code first
569 memcpy(run->d_pos, str, len);
570 run->d_pos += len;
571 return 0;
572 }
573
574 // TODO: track which flags we've already set and error on dupes
mut_step_c(flagset)575 mut_step_c(flagset) {
576 _mut_check_flag(L, tidx);
577 size_t len = 0;
578
579 int vtype = lua_getfield(L, tidx, "val");
580 if (vtype == LUA_TNUMBER || vtype == LUA_TSTRING) {
581 // this converts the arg into a string for us.
582 lua_tolstring(L, -1, &len);
583 } else if (vtype != LUA_TNIL) {
584 proxy_lua_ferror(L, "mutator step %d: unsupported type for 'val'", tidx);
585 }
586 lua_pop(L, 1);
587
588 return len;
589 }
590
mut_step_i(flagset)591 mut_step_i(flagset) {
592 struct mcp_mut_step *s = &mut->steps[sc];
593 struct mcp_mut_flagval *c = &s->c.flagval;
594 size_t len = 0;
595
596 _mut_init_flag(L, tidx, &c->flag);
597 if (lua_getfield(L, tidx, "val") != LUA_TNIL) {
598 const char *str = lua_tolstring(L, -1, &len);
599 c->str.str = mut->aused;
600 c->str.len = len;
601 char *a = mut->arena + mut->aused;
602 memcpy(a, str, len);
603 mut->aused += len;
604 }
605 lua_pop(L, 1);
606
607 return len;
608 }
609
mut_step_n(flagset)610 mut_step_n(flagset) {
611 struct mcp_mut_flagval *c = &s->c.flagval;
612 return c->str.len + 1; // room for flag
613 }
614
615 // TODO: validate we're okay to set flags.
616 // FIXME: triple check that this is actually the same code for req vs res?
617 // seems like it is.
mut_step_r(flagset)618 mut_step_r(flagset) {
619 struct mcp_mutator *mut = run->mut;
620 struct mcp_mut_flagval *c = &s->c.flagval;
621
622 const char *str = mut->arena + c->str.str;
623 int len = c->str.len;
624
625 *(run->d_pos) = c->flag.f;
626 run->d_pos++;
627 if (len > 0) {
628 memcpy(run->d_pos, str, len);
629 run->d_pos += len;
630 }
631
632 return 0;
633 }
634
mut_step_c(flagcopy)635 mut_step_c(flagcopy) {
636 _mut_check_flag(L, tidx);
637 _mut_check_idx(L, tidx);
638 return 0;
639 }
640
641 // TODO: maybe: optional default val if copy source missing
mut_step_i(flagcopy)642 mut_step_i(flagcopy) {
643 struct mcp_mut_step *s = &mut->steps[sc];
644 struct mcp_mut_flag *c = &s->c.flag;
645
646 _mut_init_flag(L, tidx, c);
647
648 if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {
649 s->idx = lua_tointeger(L, -1);
650 }
651 lua_pop(L, 1);
652
653 return 0;
654 }
655
656 // TODO: any reason for a req to pull from a res or vice versa?
657 // FIXME: handling when source flag doesn't exist might need a special case.
mut_step_n(flagcopy)658 mut_step_n(flagcopy) {
659 struct mcp_mutator *mut = run->mut;
660 struct mcp_mut_flag *c = &s->c.flag;
661
662 unsigned idx = s->idx;
663 // TODO: validate idx or use cached entry.
664 if (mut->type == MUT_REQ) {
665 mcp_request_t *srq = lua_touserdata(run->L, idx);
666 if (srq->pr.cmd_type != CMD_TYPE_META) {
667 return -1;
668 }
669 if (srq->pr.t.meta.flags & c->bit) {
670 const char *tok = NULL;
671 size_t tlen = 0;
672 mcp_request_find_flag_token(srq, c->f, &tok, &tlen);
673
674 if (tlen > 0) {
675 p->src = tok;
676 p->slen = tlen;
677 } else {
678 p->slen = 0;
679 }
680 }
681 } else {
682 mcp_resp_t *srs = lua_touserdata(run->L, idx);
683 if (srs->resp.type != MCMC_RESP_META) {
684 // FIXME: error, can't copy flag from non-meta res
685 return -1;
686 }
687 mcmc_tokenize_res(srs->buf, srs->resp.reslen, &srs->tok);
688 if (mcmc_token_has_flag_bit(&srs->tok, c->bit) == MCMC_OK) {
689 // flag exists, so copy that in.
690 // realistically this func is only used if we're also copying a
691 // token, so we look for that too.
692 // could add an option to avoid checking for a token?
693 int len = 0;
694 const char *tok = mcmc_token_get_flag(srs->buf, &srs->tok, c->f, &len);
695
696 if (len > 0) {
697 p->src = tok;
698 p->slen = len;
699 } else {
700 p->slen = 0;
701 }
702 }
703 }
704
705 return 0;
706 }
707
mut_step_r(flagcopy)708 mut_step_r(flagcopy) {
709 struct mcp_mut_flag *c = &s->c.flag;
710 *(run->d_pos) = c->f;
711 run->d_pos++;
712 if (p->slen) {
713 memcpy(run->d_pos, p->src, p->slen);
714 run->d_pos += p->slen;
715 }
716 return 0;
717 }
718
719 // TODO: check that the value hasn't been set yet.
mut_step_c(valcopy)720 mut_step_c(valcopy) {
721 _mut_check_idx(L, tidx);
722
723 // TODO: lift to common func
724 if (lua_getfield(L, tidx, "arg") == LUA_TSTRING) {
725 const char *a = lua_tostring(L, -1);
726 if (strcmp(a, "request") != 0 &&
727 strcmp(a, "response") != 0 &&
728 strcmp(a, "string") != 0 &&
729 strcmp(a, "int") != 0) {
730 proxy_lua_ferror(L, "mutator step %d: 'arg' must be request, response, string or int", tidx);
731 }
732 } else {
733 proxy_lua_ferror(L, "mutator step %d: missing 'arg' for input type", tidx);
734 }
735 lua_pop(L, 1);
736 return 0;
737 }
738
mut_step_i(valcopy)739 mut_step_i(valcopy) {
740 struct mcp_mut_step *s = &mut->steps[sc];
741
742 if (lua_getfield(L, tidx, "idx") != LUA_TNIL) {
743 s->idx = lua_tointeger(L, -1);
744 }
745 lua_pop(L, 1);
746
747 // TODO: lift to common func
748 if (lua_getfield(L, tidx, "arg") == LUA_TSTRING) {
749 const char *a = lua_tostring(L, -1);
750 if (strcmp(a, "request") == 0) {
751 s->arg = mcp_mut_step_arg_request;
752 } else if (strcmp(a, "response") == 0) {
753 s->arg = mcp_mut_step_arg_response;
754 } else if (strcmp(a, "string") == 0) {
755 s->arg = mcp_mut_step_arg_string;
756 } else if (strcmp(a, "int") == 0) {
757 s->arg = mcp_mut_step_arg_int;
758 } else {
759 proxy_lua_ferror(L, "mutator step %d: 'arg' must be request, response, string or int", tidx);
760 }
761 }
762 lua_pop(L, 1);
763
764 return 0;
765 }
766
mut_step_n(valcopy)767 mut_step_n(valcopy) {
768 lua_State *L = run->L;
769 unsigned idx = s->idx;
770 // extract the string + length we need to copy
771 mcp_request_t *srq;
772 //mcp_resp_t *srs;
773
774 // TODO: lift to common func?
775 // parts of it? with callback?
776 switch (s->arg) {
777 case mcp_mut_step_arg_none:
778 // can't get here.
779 break;
780 case mcp_mut_step_arg_request:
781 lua_getmetatable(L, idx);
782 lua_getiuservalue(L, 1, MUT_REQ);
783 if (lua_rawequal(L, -1, -2) == 0) {
784 // FIXME: error propagation
785 return -1;
786 }
787 lua_pop(L, 2);
788 srq = lua_touserdata(L, idx);
789 if (srq->pr.vbuf) {
790 run->vbuf = srq->pr.vbuf;
791 run->vlen = srq->pr.vlen;
792 }
793 break;
794 case mcp_mut_step_arg_response:
795 lua_getmetatable(L, idx);
796 lua_getiuservalue(L, 1, MUT_RES);
797 if (lua_rawequal(L, -1, -2) == 0) {
798 // FIXME: error propagation
799 return -1;
800 }
801 lua_pop(L, 2);
802 assert(1 == 0);
803 //srs = lua_touserdata(L, idx);
804 // TODO: need to locally parse the result to get the actual val
805 // offsets until later refactoring takes place.
806 break;
807 case mcp_mut_step_arg_string:
808 if (lua_isstring(L, idx)) {
809 // TODO: do we require the user supplies "\r\n"?
810 // detect it here and add a flag or adjust or etc?
811 run->vbuf = lua_tolstring(L, idx, &run->vlen);
812 }
813 break;
814 case mcp_mut_step_arg_int:
815 // TODO: just do number instead of int? meh.
816 // we want to not auto-convert this to a string.
817 lua_isnumber(L, idx);
818 assert(1 == 0); // TODO: unimplemented.
819 break;
820 }
821
822 // count the number of digits in vlen to reserve space.
823 //
824 // oddly algorithms to count digits and write digits are similar (outside
825 // of hyper optimization via bit math), so just reuse the same code here.
826 // if I can ever get arena allocations to work and remove the pre-calc
827 // steps this is moot anyway.
828 char temp[22];
829 const char *e = itoa_u64(run->vlen, temp);
830
831 return e - temp;
832 }
833
834 // print the vlen into the buffer
835 // we remove the \r\n from the protocol length
mut_step_r(valcopy)836 mut_step_r(valcopy) {
837 run->d_pos = itoa_u64(run->vlen-2, run->d_pos);
838 return 0;
839 }
840
841 // END STEPS
842
843 static const struct mcp_mut_entry mcp_mut_entries[] = {
844 [mcp_mut_step_none] = {NULL, NULL, NULL, NULL, NULL, 0, 0},
845 [mcp_mut_step_cmdset] = {"cmdset", mcp_mutator_cmdset_c, mcp_mutator_cmdset_i, mcp_mutator_cmdset_n, mcp_mutator_cmdset_r, MUT_REQ, 0},
846 [mcp_mut_step_cmdcopy] = {"cmdcopy", mcp_mutator_cmdcopy_c, mcp_mutator_cmdcopy_i, mcp_mutator_cmdcopy_n, mcp_mutator_cmdcopy_r, MUT_REQ, 0},
847 [mcp_mut_step_keycopy] = {"keycopy", mcp_mutator_keycopy_c, mcp_mutator_keycopy_i, mcp_mutator_keycopy_n, mcp_mutator_keycopy_r, MUT_REQ, 0},
848 [mcp_mut_step_keyset] = {"keyset", mcp_mutator_keyset_c, mcp_mutator_keyset_i, mcp_mutator_keyset_n, mcp_mutator_keyset_r, MUT_REQ, 0},
849 [mcp_mut_step_rescodeset] = {"rescodeset", mcp_mutator_rescodeset_c, mcp_mutator_rescodeset_i, mcp_mutator_rescodeset_n, mcp_mutator_rescodeset_r, MUT_RES, 0},
850 [mcp_mut_step_rescodecopy] = {"rescodecopy", mcp_mutator_rescodecopy_c, mcp_mutator_rescodecopy_i, mcp_mutator_rescodecopy_n, mcp_mutator_rescodecopy_r, MUT_RES, 0},
851 [mcp_mut_step_reserr] = {"reserr", mcp_mutator_reserr_c, mcp_mutator_reserr_i, mcp_mutator_reserr_n, mcp_mutator_reserr_r, MUT_RES, 0},
852 [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},
853 [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},
854 [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},
855 [mcp_mut_step_final] = {NULL, NULL, NULL, NULL, NULL, 0, 0},
856 };
857
858 // call with type string on top
mcp_mutator_steptype(lua_State * L)859 static enum mcp_mut_steptype mcp_mutator_steptype(lua_State *L) {
860 const char *type = luaL_checkstring(L, -1);
861 for (int x = 0; x < mcp_mut_step_final; x++) {
862 const struct mcp_mut_entry *e = &mcp_mut_entries[x];
863 if (e->s && strcmp(type, e->s) == 0) {
864 return x;
865 }
866 }
867 return mcp_mut_step_none;
868 }
869
mcp_mutator_new(lua_State * L,enum mcp_mut_type type)870 static int mcp_mutator_new(lua_State *L, enum mcp_mut_type type) {
871 int argc = lua_gettop(L);
872 size_t size = 0;
873 int scount = 0;
874
875 // loop argument tables once for validation and pre-calculations.
876 for (int x = 1; x <= argc; x++) {
877 luaL_checktype(L, x, LUA_TTABLE);
878 if (lua_getfield(L, x, "t") != LUA_TNIL) {
879 enum mcp_mut_steptype st = mcp_mutator_steptype(L);
880 const struct mcp_mut_entry *e = &mcp_mut_entries[st];
881 if (!(e->t & type)) {
882 proxy_lua_ferror(L, "mutator step %d: step incompatible with mutator type", x);
883 }
884 if ((st == mcp_mut_step_none) || e->c == NULL) {
885 proxy_lua_ferror(L, "mutator step %d: unknown step type", x);
886 }
887 size += e->c(L, x);
888 }
889 lua_pop(L, 1); // drop 't' or nil
890 scount++;
891 }
892
893 // we now know the size and number of steps. allocate some flat memory.
894
895 size_t extsize = sizeof(struct mcp_mut_step) * scount;
896 struct mcp_mutator *mut = lua_newuserdatauv(L, sizeof(*mut) + extsize, 2);
897 memset(mut, 0, sizeof(*mut));
898
899 mut->arena = malloc(size);
900 if (mut->arena == NULL) {
901 proxy_lua_error(L, "mutator_new: failed to allocate memory");
902 }
903 luaL_setmetatable(L, "mcp.mutator");
904 mut->type = type;
905
906 // Cache both request and result metatables for arg validation.
907 // Since a req mutator can take a res argument and vice versa
908 luaL_getmetatable(L, "mcp.request");
909 lua_setiuservalue(L, -2, MUT_REQ);
910 luaL_getmetatable(L, "mcp.response");
911 lua_setiuservalue(L, -2, MUT_RES);
912
913 // loop the arg tables again to fill in the steps
914 // skip checks since we did that during the first loop.
915 scount = 0;
916 for (int x = 1; x <= argc; x++) {
917 if (lua_getfield(L, x, "t") != LUA_TNIL) {
918 enum mcp_mut_steptype st = mcp_mutator_steptype(L);
919 mut->steps[scount].type = st;
920 mcp_mut_entries[st].i(L, x, scount, mut);
921 mut->rcount += mcp_mut_entries[st].rc;
922
923 // copy function pointers into the step so we don't have to skip
924 // around the much larger mcp_mut_entries at runtime.
925 mut->steps[scount].n = mcp_mut_entries[st].n;
926 mut->steps[scount].r = mcp_mut_entries[st].r;
927 mut->steps[scount].idx++; // actual args are "self, etc, etc"
928 }
929 lua_pop(L, 1); // drop t or nil
930 scount++;
931 }
932
933 if (size != mut->aused) {
934 proxy_lua_error(L, "mutator failed to properly initialize, memory not filled correctly");
935 }
936 mut->scount = scount;
937
938 return 1;
939 }
940
_mcp_mut_run_total(struct mcp_mut_run * run,struct mcp_mut_part * parts)941 static inline int _mcp_mut_run_total(struct mcp_mut_run *run, struct mcp_mut_part *parts) {
942 int total = 0;
943 struct mcp_mutator *mut = run->mut;
944 for (int x = 0; x < mut->scount; x++) {
945 struct mcp_mut_step *s = &mut->steps[x];
946 assert(s->type != mcp_mut_step_none);
947 int len = s->n(run, s, &parts[x]);
948 if (len < 0) {
949 assert(1 == 0);
950 break;
951 } else {
952 total += len;
953 }
954 }
955 // account for spaces between "steps" and \r\n
956 // note that steps that themselves can make multiple tokens _must_ account
957 // for this on their own.
958 total += mut->scount + MIN_BUF_SPACE;
959
960 return total;
961 }
962
_mcp_mut_run_assemble(struct mcp_mut_run * run,struct mcp_mut_part * parts)963 static inline int _mcp_mut_run_assemble(struct mcp_mut_run *run, struct mcp_mut_part *parts) {
964 struct mcp_mutator *mut = run->mut;
965 // assemble final req/res
966 for (int x = 0; x < mut->scount; x++) {
967 struct mcp_mut_step *s = &mut->steps[x];
968 assert(s->type != mcp_mut_step_none);
969 // TODO: handle -1 errors, halt.
970 // TODO: can we structure this so we don't have to check for errors at
971 // this stage, only the first one? would be nice to cut the branch out
972 if (s->r(run, s, &parts[x]) < 0) {
973 assert(1 == 0);
974 }
975
976 *(run->d_pos) = ' ';
977 run->d_pos++;
978 }
979
980 // TODO: any cases where we need to check if the final char is a space or
981 // not?
982 // add the \r\n after all steps
983 *(run->d_pos-1) = '\r';
984 *(run->d_pos) = '\n';
985 run->d_pos++;
986
987 return 0;
988 }
989
mcp_mut_run(struct mcp_mut_run * run)990 static int mcp_mut_run(struct mcp_mut_run *run) {
991 struct mcp_mutator *mut = run->mut;
992 LIBEVENT_THREAD *t = PROXY_GET_THR(run->L);
993 int ret = 0;
994 struct mcp_mut_part parts[mut->scount];
995
996 // first accumulate the length tally
997 int total = _mcp_mut_run_total(run, parts);
998 // FIXME: error handling from total call
999
1000 // ensure space and/or allocate memory then seed our destination pointer.
1001 if (mut->type == MUT_REQ) {
1002 mcp_request_t *rq = run->arg;
1003 // FIXME: cleanup should be managed by slot rctx.
1004 mcp_request_cleanup(t, rq);
1005 // future.. should be able to dynamically assign request buffer.
1006 if (total > MCP_REQUEST_MAXLEN) {
1007 // FIXME: proper error.
1008 assert(1 == 0);
1009 }
1010 run->d_pos = rq->request;
1011
1012 _mcp_mut_run_assemble(run, parts);
1013
1014 // TODO: process_request()
1015 // if run->vbuf, malloc/copy vbuf.
1016 if (process_request(&rq->pr, rq->request, run->d_pos - rq->request) != 0) {
1017 // TODO: throw error or return false?
1018 assert(1 == 0);
1019 }
1020
1021 if (run->vbuf) {
1022 rq->pr.vbuf = malloc(run->vlen);
1023 if (rq->pr.vbuf == NULL) {
1024 assert(1 == 0);
1025 }
1026 pthread_mutex_lock(&t->proxy_limit_lock);
1027 t->proxy_buffer_memory_used += rq->pr.vlen;
1028 pthread_mutex_unlock(&t->proxy_limit_lock);
1029
1030 rq->pr.vlen = run->vlen;
1031 memcpy(rq->pr.vbuf, run->vbuf, run->vlen);
1032 }
1033 } else {
1034 mcp_resp_t *rs = run->arg;
1035 // FIXME: cleanup should be managed by slot rctx.
1036 mcp_response_cleanup(t, rs);
1037
1038 // TODO: alloc big enough result buffer.
1039 rs->buf = malloc(total);
1040 if (rs->buf == NULL) {
1041 // FIXME: proper error.
1042 assert(1 == 0);
1043 }
1044 run->d_pos = rs->buf;
1045
1046 _mcp_mut_run_assemble(run, parts);
1047
1048 rs->tok.ntokens = 0; // TODO: handler from mcmc?
1049 if (mcmc_parse_buf(rs->buf, run->d_pos - rs->buf, &rs->resp) != MCMC_OK) {
1050 // TODO: throw error or return false?
1051 assert(1 == 0);
1052 }
1053
1054 // results are sequential buffers, copy the value in.
1055 if (run->vbuf) {
1056 memcpy(run->d_pos, run->vbuf, run->vlen);
1057 run->d_pos += run->vlen;
1058 }
1059
1060 rs->blen = run->d_pos - rs->buf;
1061 // NOTE: We increment but don't check the memory limits here. Any
1062 // incoming request or incoming response will alos check the memory
1063 // limits, and just doing an increment here removes some potential
1064 // error handling. Requests that are already started should be allowed
1065 // to complete to minimize impact of hitting memory limits.
1066 pthread_mutex_lock(&t->proxy_limit_lock);
1067 t->proxy_buffer_memory_used += rs->blen;
1068 pthread_mutex_unlock(&t->proxy_limit_lock);
1069 }
1070
1071 return ret;
1072 }
1073
1074 // PUBLIC INTERFACE
1075
mcplib_req_mutator_new(lua_State * L)1076 int mcplib_req_mutator_new(lua_State *L) {
1077 return mcp_mutator_new(L, MUT_REQ);
1078 }
1079
mcplib_res_mutator_new(lua_State * L)1080 int mcplib_res_mutator_new(lua_State *L) {
1081 return mcp_mutator_new(L, MUT_RES);
1082 }
1083
1084 // walk each step and free references/memory/etc
mcplib_mutator_gc(lua_State * L)1085 int mcplib_mutator_gc(lua_State *L) {
1086 struct mcp_mutator *mut = lua_touserdata(L, 1);
1087
1088 if (mut->arena) {
1089 free(mut->arena);
1090 mut->arena = NULL;
1091 }
1092
1093 // NOTE: leaving commented until I run into something that actually has
1094 // something extra to GC
1095 /*for (int x = 0; x < mut->scount; x++) {
1096 struct mcp_mut_step *s = &mut->steps[x];
1097 switch (s->type) {
1098 case mcp_mut_step_none:
1099 case mcp_mut_step_cmdset:
1100 case mcp_mut_step_cmdcopy:
1101 case mcp_mut_step_keycopy:
1102 case mcp_mut_step_keyset:
1103 case mcp_mut_step_rescodeset:
1104 case mcp_mut_step_rescodecopy:
1105 case mcp_mut_step_reserr:
1106 case mcp_mut_step_flagset:
1107 case mcp_mut_step_flagcopy:
1108 case mcp_mut_step_valcopy:
1109 case mcp_mut_step_final:
1110 break;
1111 }
1112 }*/
1113
1114 return 0;
1115 }
1116
mcplib_mutator_call(lua_State * L)1117 int mcplib_mutator_call(lua_State *L) {
1118 // since we're here from a __call, assume the type is correct.
1119 struct mcp_mutator *mut = lua_touserdata(L, 1);
1120 luaL_checktype(L, 2, LUA_TUSERDATA);
1121 if (lua_checkstack(L, mut->rcount + 3) == 0) {
1122 proxy_lua_error(L, "mutator ran out of stack space for results");
1123 }
1124
1125 lua_getmetatable(L, 2); // put dest obj arg metatable on stack
1126 lua_getiuservalue(L, 1, mut->type); // put stashed metatable on stack
1127 luaL_argcheck(L, lua_rawequal(L, -1, -2), 2,
1128 "invalid argument to mutator object");
1129 lua_pop(L, 2); // toss both metatables
1130
1131
1132 // we're valid now.
1133 void *arg = lua_touserdata(L, 2);
1134 // stack scratch space so we can avoid modifying the mut struct
1135 struct mcp_mut_run run = {L, mut, arg, NULL, NULL, 0};
1136 // TODO: numbuf space
1137 int ret = mcp_mut_run(&run);
1138
1139 return ret;
1140 }
1141