diff options
Diffstat (limited to 'proxy_request.c')
-rw-r--r-- | proxy_request.c | 173 |
1 files changed, 73 insertions, 100 deletions
diff --git a/proxy_request.c b/proxy_request.c index e133240..cd520b0 100644 --- a/proxy_request.c +++ b/proxy_request.c @@ -440,7 +440,7 @@ int process_request(mcp_parser_t *pr, const char *command, size_t cmdlen) { // FIXME (v2): any reason to pass in command/cmdlen separately? mcp_request_t *mcp_new_request(lua_State *L, mcp_parser_t *pr, const char *command, size_t cmdlen) { // reserving an upvalue for key. - mcp_request_t *rq = lua_newuserdatauv(L, sizeof(mcp_request_t) + MCP_REQUEST_MAXLEN * 2 + KEY_MAX_LENGTH, 1); + mcp_request_t *rq = lua_newuserdatauv(L, sizeof(mcp_request_t) + MCP_REQUEST_MAXLEN + KEY_MAX_LENGTH, 1); // TODO (v2): memset only the non-data part? as the rest gets memcpy'd // over. memset(rq, 0, sizeof(mcp_request_t)); @@ -458,63 +458,68 @@ mcp_request_t *mcp_new_request(lua_State *L, mcp_parser_t *pr, const char *comma return rq; } -// TODO (v2): -// if modified, this will re-serialize every time it's accessed. -// a simple opt could copy back over the original space -// a "better" one could A/B the request ptr and clear the modified state -// each time it gets serialized. -void mcp_request_attach(lua_State *L, mcp_request_t *rq, io_pending_proxy_t *p) { +// Replaces a token inside a request and re-parses. +// Note that this has some optimization opportunities. Delaying until +// required. +// We should not guarantee order when updating meta flags, which would allow +// blanking tokens and appending new ones. +// TODO (v2): function doesn't allow appending. +// TODO (v2): much of the length is the key, avoid copying it. +int mcp_request_render(mcp_request_t *rq, int idx, const char *tok, size_t len) { + char temp[MCP_REQUEST_MAXLEN]; + char *p = temp; mcp_parser_t *pr = &rq->pr; - char *r = (char *) pr->request; - size_t len = pr->reqlen; - // one or more of the tokens were changed - if (rq->was_modified) { - assert(rq->tokent_ref); - // option table to top of stack. - lua_rawgeti(L, LUA_REGISTRYINDEX, rq->tokent_ref); - - // space was reserved in case of modification. - char *nr = rq->request + MCP_REQUEST_MAXLEN; - r = nr; - char *or = NULL; - - for (int x = 0; x < pr->ntokens; x++) { - const char *newtok = NULL; - size_t newlen = 0; - if (x != 0 && x != pr->keytoken) { - int type = lua_rawgeti(L, -1, x+1); - if (type != LUA_TNIL) { - newtok = lua_tolstring(L, -1, &newlen); - memcpy(nr, newtok, newlen); - nr += newlen; - } - lua_pop(L, 1); - } + if (pr->reqlen + len > MCP_REQUEST_MAXLEN) { + return -1; + } + // Cannot add/append tokens yet. + if (idx >= pr->ntokens) { + return -1; + } - if (newtok == NULL) { - // TODO (v2): if we add an extra "end" token that's just reqlen we can - // memcpy... however most args are short and that may not be worth - // it. - or = rq->request + pr->tokens[x]; - // will walk past the end without the \r test. - // if we add the end token trick this can be changed. - while (*or != ' ' && *or != '\r' && *or != '\n') { - *nr = *or; - nr++; - or++; - } - } - *nr = ' '; - nr++; + memcpy(p, pr->request, pr->tokens[idx]); + p += pr->tokens[idx]; + + memcpy(p, tok, len); + p += len; + + // Add a space and copy more tokens if there were more. + if (idx+1 < pr->ntokens) { + if (len != 0) { + // Only pre-space if not deleting the token. + *p = ' '; + p++; } - // tag the end bits. - memcpy(nr-1, "\r\n\0", 3); - nr++; + memcpy(p, &pr->request[pr->tokens[idx+1]], pr->tokens[pr->ntokens] - pr->tokens[idx+1]); + p += pr->tokens[pr->ntokens] - pr->tokens[idx+1]; + } + + memcpy(p, "\r\n\0", 3); + p += 2; - len = nr - (rq->request + MCP_REQUEST_MAXLEN); - lua_pop(L, 1); // pop the table + memcpy(rq->request, temp, p - temp); + + // Hold the vlen/vbuf and restore after re-parsing. Since we can only edit + // the command line, not the value here, we would otherwise allow sending + // arbitrary memory over the network if someone modifies a SET. + void *vbuf = pr->vbuf; + int vlen = pr->vlen; + + memset(pr, 0, sizeof(mcp_parser_t)); // TODO: required? + int ret = process_request(pr, rq->request, p - temp); + if (ret != 0) { + return ret; } + pr->vbuf = vbuf; + pr->vlen = vlen; + return 0; +} + +void mcp_request_attach(lua_State *L, mcp_request_t *rq, io_pending_proxy_t *p) { + mcp_parser_t *pr = &rq->pr; + char *r = (char *) pr->request; + size_t len = pr->reqlen; // The stringified request. This is also referencing into the coroutine // stack, which should be safe from gc. @@ -528,7 +533,6 @@ void mcp_request_attach(lua_State *L, mcp_request_t *rq, io_pending_proxy_t *p) p->iovcnt = 2; p->iovbytes += pr->vlen; } - } // second argument is optional, for building set requests. @@ -649,43 +653,23 @@ int mcplib_request_token(lua_State *L) { return 1; } - // we hold overwritten or parsed tokens in a lua table. - if (rq->tokent_ref == 0) { - // create a presized table that can hold our tokens. - lua_createtable(L, rq->pr.ntokens, 0); - // duplicate value to set back - lua_pushvalue(L, -1); - rq->tokent_ref = luaL_ref(L, LUA_REGISTRYINDEX); - } else { - lua_rawgeti(L, LUA_REGISTRYINDEX, rq->tokent_ref); - } - // top of stack should be token table. - size_t vlen = 0; if (argc > 2) { // overwriting a token. - luaL_checklstring(L, 3, &vlen); - lua_pushvalue(L, 3); // copy to top of stack - lua_rawseti(L, -2, token); - rq->was_modified = true; + size_t newlen = 0; + const char *newtok = lua_tolstring(L, 3, &newlen); + if (mcp_request_render(rq, token-1, newtok, newlen) != 0) { + proxy_lua_error(L, "token(): request malformed after edit"); + return 0; + } return 0; } else { // fetching a token. - if (lua_rawgeti(L, -1, token) != LUA_TSTRING) { - lua_pop(L, 1); // got a nil, drop it. - - // token not uploaded yet. find the len. - const char *start = rq->pr.request + rq->pr.tokens[token-1]; - vlen = _process_token_len(&rq->pr, token-1); + const char *start = rq->pr.request + rq->pr.tokens[token-1]; + vlen = _process_token_len(&rq->pr, token-1); - P_DEBUG("%s: pushing token of len: %lu\n", __func__, vlen); - lua_pushlstring(L, start, vlen); - lua_pushvalue(L, -1); // copy - - lua_rawseti(L, -3, token); // pops copy. - } - - // return fetched token or copy of new token. + P_DEBUG("%s: pushing token of len: %lu\n", __func__, vlen); + lua_pushlstring(L, start, vlen); return 1; } @@ -743,12 +727,6 @@ int mcplib_request_flag_token(lua_State *L) { } if (lua_isstring(L, 3)) { // overwriting a flag/token with the third argument. - // NOTE: semi duplicated from mcplib_request_token() - if (rq->tokent_ref == 0) { - // create a presized table that can hold our tokens. - lua_createtable(L, rq->pr.ntokens, 0); - rq->tokent_ref = luaL_ref(L, LUA_REGISTRYINDEX); - } replace = true; } uint64_t flagbit = (uint64_t)1 << (flagstr[0] - 65); @@ -770,14 +748,12 @@ int mcplib_request_flag_token(lua_State *L) { // Have something to replace the flag/token with. if (replace) { - // table is top of stack. - lua_rawgeti(L, LUA_REGISTRYINDEX, rq->tokent_ref); - // need to copy the string to top of stack. - lua_pushvalue(L, 3); - - lua_rawseti(L, -2, x+1); // pops copy - lua_pop(L, 1); // pop table - rq->was_modified = true; + size_t newlen = 0; + const char *newtok = lua_tolstring(L, 3, &newlen); + if (mcp_request_render(rq, x, newtok, newlen) != 0) { + proxy_lua_error(L, "token(): request malformed after edit"); + return 0; + } } break; } @@ -798,9 +774,6 @@ int mcplib_request_gc(lua_State *L) { free(rq->pr.vbuf); } - if (rq->tokent_ref != 0) { - luaL_unref(L, LUA_REGISTRYINDEX, rq->tokent_ref); - } return 0; } |