1/*
2 * Copyright (c) 2009-2021, Redis Labs Ltd.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Redis nor the names of its contributors may be used
14 * to endorse or promote products derived from this software without
15 * specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "server.h"
31#include "call_reply.h"
32
33#define REPLY_FLAG_ROOT (1<<0)
34#define REPLY_FLAG_PARSED (1<<1)
35#define REPLY_FLAG_RESP3 (1<<2)
36
37/* --------------------------------------------------------
38 * An opaque struct used to parse a RESP protocol reply and
39 * represent it. Used when parsing replies such as in RM_Call
40 * or Lua scripts.
41 * -------------------------------------------------------- */
42struct CallReply {
43 void *private_data;
44 sds original_proto; /* Available only for root reply. */
45 const char *proto;
46 size_t proto_len;
47 int type; /* REPLY_... */
48 int flags; /* REPLY_FLAG... */
49 size_t len; /* Length of a string, or the number elements in an array. */
50 union {
51 const char *str; /* String pointer for string and error replies. This
52 * does not need to be freed, always points inside
53 * a reply->proto buffer of the reply object or, in
54 * case of array elements, of parent reply objects. */
55 struct {
56 const char *str;
57 const char *format;
58 } verbatim_str; /* Reply value for verbatim string */
59 long long ll; /* Reply value for integer reply. */
60 double d; /* Reply value for double reply. */
61 struct CallReply *array; /* Array of sub-reply elements. used for set, array, map, and attribute */
62 } val;
63 list *deferred_error_list; /* list of errors in sds form or NULL */
64 struct CallReply *attribute; /* attribute reply, NULL if not exists */
65};
66
67static void callReplySetSharedData(CallReply *rep, int type, const char *proto, size_t proto_len, int extra_flags) {
68 rep->type = type;
69 rep->proto = proto;
70 rep->proto_len = proto_len;
71 rep->flags |= extra_flags;
72}
73
74static void callReplyNull(void *ctx, const char *proto, size_t proto_len) {
75 CallReply *rep = ctx;
76 callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, REPLY_FLAG_RESP3);
77}
78
79static void callReplyNullBulkString(void *ctx, const char *proto, size_t proto_len) {
80 CallReply *rep = ctx;
81 callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, 0);
82}
83
84static void callReplyNullArray(void *ctx, const char *proto, size_t proto_len) {
85 CallReply *rep = ctx;
86 callReplySetSharedData(rep, REDISMODULE_REPLY_NULL, proto, proto_len, 0);
87}
88
89static void callReplyBulkString(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
90 CallReply *rep = ctx;
91 callReplySetSharedData(rep, REDISMODULE_REPLY_STRING, proto, proto_len, 0);
92 rep->len = len;
93 rep->val.str = str;
94}
95
96static void callReplyError(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
97 CallReply *rep = ctx;
98 callReplySetSharedData(rep, REDISMODULE_REPLY_ERROR, proto, proto_len, 0);
99 rep->len = len;
100 rep->val.str = str;
101}
102
103static void callReplySimpleStr(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
104 CallReply *rep = ctx;
105 callReplySetSharedData(rep, REDISMODULE_REPLY_STRING, proto, proto_len, 0);
106 rep->len = len;
107 rep->val.str = str;
108}
109
110static void callReplyLong(void *ctx, long long val, const char *proto, size_t proto_len) {
111 CallReply *rep = ctx;
112 callReplySetSharedData(rep, REDISMODULE_REPLY_INTEGER, proto, proto_len, 0);
113 rep->val.ll = val;
114}
115
116static void callReplyDouble(void *ctx, double val, const char *proto, size_t proto_len) {
117 CallReply *rep = ctx;
118 callReplySetSharedData(rep, REDISMODULE_REPLY_DOUBLE, proto, proto_len, REPLY_FLAG_RESP3);
119 rep->val.d = val;
120}
121
122static void callReplyVerbatimString(void *ctx, const char *format, const char *str, size_t len, const char *proto, size_t proto_len) {
123 CallReply *rep = ctx;
124 callReplySetSharedData(rep, REDISMODULE_REPLY_VERBATIM_STRING, proto, proto_len, REPLY_FLAG_RESP3);
125 rep->len = len;
126 rep->val.verbatim_str.str = str;
127 rep->val.verbatim_str.format = format;
128}
129
130static void callReplyBigNumber(void *ctx, const char *str, size_t len, const char *proto, size_t proto_len) {
131 CallReply *rep = ctx;
132 callReplySetSharedData(rep, REDISMODULE_REPLY_BIG_NUMBER, proto, proto_len, REPLY_FLAG_RESP3);
133 rep->len = len;
134 rep->val.str = str;
135}
136
137static void callReplyBool(void *ctx, int val, const char *proto, size_t proto_len) {
138 CallReply *rep = ctx;
139 callReplySetSharedData(rep, REDISMODULE_REPLY_BOOL, proto, proto_len, REPLY_FLAG_RESP3);
140 rep->val.ll = val;
141}
142
143static void callReplyParseCollection(ReplyParser *parser, CallReply *rep, size_t len, const char *proto, size_t elements_per_entry) {
144 rep->len = len;
145 rep->val.array = zcalloc(elements_per_entry * len * sizeof(CallReply));
146 for (size_t i = 0; i < len * elements_per_entry; i += elements_per_entry) {
147 for (size_t j = 0 ; j < elements_per_entry ; ++j) {
148 rep->val.array[i + j].private_data = rep->private_data;
149 parseReply(parser, rep->val.array + i + j);
150 rep->val.array[i + j].flags |= REPLY_FLAG_PARSED;
151 if (rep->val.array[i + j].flags & REPLY_FLAG_RESP3) {
152 /* If one of the sub-replies is RESP3, then the current reply is also RESP3. */
153 rep->flags |= REPLY_FLAG_RESP3;
154 }
155 }
156 }
157 rep->proto = proto;
158 rep->proto_len = parser->curr_location - proto;
159}
160
161static void callReplyAttribute(ReplyParser *parser, void *ctx, size_t len, const char *proto) {
162 CallReply *rep = ctx;
163 rep->attribute = zcalloc(sizeof(CallReply));
164
165 /* Continue parsing the attribute reply */
166 rep->attribute->len = len;
167 rep->attribute->type = REDISMODULE_REPLY_ATTRIBUTE;
168 callReplyParseCollection(parser, rep->attribute, len, proto, 2);
169 rep->attribute->flags |= REPLY_FLAG_PARSED | REPLY_FLAG_RESP3;
170 rep->attribute->private_data = rep->private_data;
171
172 /* Continue parsing the reply */
173 parseReply(parser, rep);
174
175 /* In this case we need to fix the proto address and len, it should start from the attribute */
176 rep->proto = proto;
177 rep->proto_len = parser->curr_location - proto;
178 rep->flags |= REPLY_FLAG_RESP3;
179}
180
181static void callReplyArray(ReplyParser *parser, void *ctx, size_t len, const char *proto) {
182 CallReply *rep = ctx;
183 rep->type = REDISMODULE_REPLY_ARRAY;
184 callReplyParseCollection(parser, rep, len, proto, 1);
185}
186
187static void callReplySet(ReplyParser *parser, void *ctx, size_t len, const char *proto) {
188 CallReply *rep = ctx;
189 rep->type = REDISMODULE_REPLY_SET;
190 callReplyParseCollection(parser, rep, len, proto, 1);
191 rep->flags |= REPLY_FLAG_RESP3;
192}
193
194static void callReplyMap(ReplyParser *parser, void *ctx, size_t len, const char *proto) {
195 CallReply *rep = ctx;
196 rep->type = REDISMODULE_REPLY_MAP;
197 callReplyParseCollection(parser, rep, len, proto, 2);
198 rep->flags |= REPLY_FLAG_RESP3;
199}
200
201static void callReplyParseError(void *ctx) {
202 CallReply *rep = ctx;
203 rep->type = REDISMODULE_REPLY_UNKNOWN;
204}
205
206/* Recursively free the current call reply and its sub-replies. */
207static void freeCallReplyInternal(CallReply *rep) {
208 if (rep->type == REDISMODULE_REPLY_ARRAY || rep->type == REDISMODULE_REPLY_SET) {
209 for (size_t i = 0 ; i < rep->len ; ++i) {
210 freeCallReplyInternal(rep->val.array + i);
211 }
212 zfree(rep->val.array);
213 }
214
215 if (rep->type == REDISMODULE_REPLY_MAP || rep->type == REDISMODULE_REPLY_ATTRIBUTE) {
216 for (size_t i = 0 ; i < rep->len ; ++i) {
217 freeCallReplyInternal(rep->val.array + i * 2);
218 freeCallReplyInternal(rep->val.array + i * 2 + 1);
219 }
220 zfree(rep->val.array);
221 }
222
223 if (rep->attribute) {
224 freeCallReplyInternal(rep->attribute);
225 zfree(rep->attribute);
226 }
227}
228
229/* Free the given call reply and its children (in case of nested reply) recursively.
230 * If private data was set when the CallReply was created it will not be freed, as it's
231 * the caller's responsibility to free it before calling freeCallReply(). */
232void freeCallReply(CallReply *rep) {
233 if (!(rep->flags & REPLY_FLAG_ROOT)) {
234 return;
235 }
236 if (rep->flags & REPLY_FLAG_PARSED) {
237 freeCallReplyInternal(rep);
238 }
239 sdsfree(rep->original_proto);
240 if (rep->deferred_error_list)
241 listRelease(rep->deferred_error_list);
242 zfree(rep);
243}
244
245static const ReplyParserCallbacks DefaultParserCallbacks = {
246 .null_callback = callReplyNull,
247 .bulk_string_callback = callReplyBulkString,
248 .null_bulk_string_callback = callReplyNullBulkString,
249 .null_array_callback = callReplyNullArray,
250 .error_callback = callReplyError,
251 .simple_str_callback = callReplySimpleStr,
252 .long_callback = callReplyLong,
253 .array_callback = callReplyArray,
254 .set_callback = callReplySet,
255 .map_callback = callReplyMap,
256 .double_callback = callReplyDouble,
257 .bool_callback = callReplyBool,
258 .big_number_callback = callReplyBigNumber,
259 .verbatim_string_callback = callReplyVerbatimString,
260 .attribute_callback = callReplyAttribute,
261 .error = callReplyParseError,
262};
263
264/* Parse the buffer located in rep->original_proto and update the CallReply
265 * structure to represent its contents. */
266static void callReplyParse(CallReply *rep) {
267 if (rep->flags & REPLY_FLAG_PARSED) {
268 return;
269 }
270
271 ReplyParser parser = {.curr_location = rep->proto, .callbacks = DefaultParserCallbacks};
272
273 parseReply(&parser, rep);
274 rep->flags |= REPLY_FLAG_PARSED;
275}
276
277/* Return the call reply type (REDISMODULE_REPLY_...). */
278int callReplyType(CallReply *rep) {
279 if (!rep) return REDISMODULE_REPLY_UNKNOWN;
280 callReplyParse(rep);
281 return rep->type;
282}
283
284/* Return reply string as buffer and len. Applicable to:
285 * - REDISMODULE_REPLY_STRING
286 * - REDISMODULE_REPLY_ERROR
287 *
288 * The return value is borrowed from CallReply, so it must not be freed
289 * explicitly or used after CallReply itself is freed.
290 *
291 * The returned value is not NULL terminated and its length is returned by
292 * reference through len, which must not be NULL.
293 */
294const char *callReplyGetString(CallReply *rep, size_t *len) {
295 callReplyParse(rep);
296 if (rep->type != REDISMODULE_REPLY_STRING &&
297 rep->type != REDISMODULE_REPLY_ERROR) return NULL;
298 if (len) *len = rep->len;
299 return rep->val.str;
300}
301
302/* Return a long long reply value. Applicable to:
303 * - REDISMODULE_REPLY_INTEGER
304 */
305long long callReplyGetLongLong(CallReply *rep) {
306 callReplyParse(rep);
307 if (rep->type != REDISMODULE_REPLY_INTEGER) return LLONG_MIN;
308 return rep->val.ll;
309}
310
311/* Return a double reply value. Applicable to:
312 * - REDISMODULE_REPLY_DOUBLE
313 */
314double callReplyGetDouble(CallReply *rep) {
315 callReplyParse(rep);
316 if (rep->type != REDISMODULE_REPLY_DOUBLE) return LLONG_MIN;
317 return rep->val.d;
318}
319
320/* Return a reply Boolean value. Applicable to:
321 * - REDISMODULE_REPLY_BOOL
322 */
323int callReplyGetBool(CallReply *rep) {
324 callReplyParse(rep);
325 if (rep->type != REDISMODULE_REPLY_BOOL) return INT_MIN;
326 return rep->val.ll;
327}
328
329/* Return reply length. Applicable to:
330 * - REDISMODULE_REPLY_STRING
331 * - REDISMODULE_REPLY_ERROR
332 * - REDISMODULE_REPLY_ARRAY
333 * - REDISMODULE_REPLY_SET
334 * - REDISMODULE_REPLY_MAP
335 * - REDISMODULE_REPLY_ATTRIBUTE
336 */
337size_t callReplyGetLen(CallReply *rep) {
338 callReplyParse(rep);
339 switch(rep->type) {
340 case REDISMODULE_REPLY_STRING:
341 case REDISMODULE_REPLY_ERROR:
342 case REDISMODULE_REPLY_ARRAY:
343 case REDISMODULE_REPLY_SET:
344 case REDISMODULE_REPLY_MAP:
345 case REDISMODULE_REPLY_ATTRIBUTE:
346 return rep->len;
347 default:
348 return 0;
349 }
350}
351
352static CallReply *callReplyGetCollectionElement(CallReply *rep, size_t idx, int elements_per_entry) {
353 if (idx >= rep->len * elements_per_entry) return NULL; // real len is rep->len * elements_per_entry
354 return rep->val.array+idx;
355}
356
357/* Return a reply array element at a given index. Applicable to:
358 * - REDISMODULE_REPLY_ARRAY
359 *
360 * The return value is borrowed from CallReply, so it must not be freed
361 * explicitly or used after CallReply itself is freed.
362 */
363CallReply *callReplyGetArrayElement(CallReply *rep, size_t idx) {
364 callReplyParse(rep);
365 if (rep->type != REDISMODULE_REPLY_ARRAY) return NULL;
366 return callReplyGetCollectionElement(rep, idx, 1);
367}
368
369/* Return a reply set element at a given index. Applicable to:
370 * - REDISMODULE_REPLY_SET
371 *
372 * The return value is borrowed from CallReply, so it must not be freed
373 * explicitly or used after CallReply itself is freed.
374 */
375CallReply *callReplyGetSetElement(CallReply *rep, size_t idx) {
376 callReplyParse(rep);
377 if (rep->type != REDISMODULE_REPLY_SET) return NULL;
378 return callReplyGetCollectionElement(rep, idx, 1);
379}
380
381static int callReplyGetMapElementInternal(CallReply *rep, size_t idx, CallReply **key, CallReply **val, int type) {
382 callReplyParse(rep);
383 if (rep->type != type) return C_ERR;
384 if (idx >= rep->len) return C_ERR;
385 if (key) *key = callReplyGetCollectionElement(rep, idx * 2, 2);
386 if (val) *val = callReplyGetCollectionElement(rep, idx * 2 + 1, 2);
387 return C_OK;
388}
389
390/* Retrieve a map reply key and value at a given index. Applicable to:
391 * - REDISMODULE_REPLY_MAP
392 *
393 * The key and value are returned by reference through key and val,
394 * which may also be NULL if not needed.
395 *
396 * Returns C_OK on success or C_ERR if reply type mismatches, or if idx is out
397 * of range.
398 *
399 * The returned values are borrowed from CallReply, so they must not be freed
400 * explicitly or used after CallReply itself is freed.
401 */
402int callReplyGetMapElement(CallReply *rep, size_t idx, CallReply **key, CallReply **val) {
403 return callReplyGetMapElementInternal(rep, idx, key, val, REDISMODULE_REPLY_MAP);
404}
405
406/* Return reply attribute, or NULL if it does not exist. Applicable to all replies.
407 *
408 * The returned values are borrowed from CallReply, so they must not be freed
409 * explicitly or used after CallReply itself is freed.
410 */
411CallReply *callReplyGetAttribute(CallReply *rep) {
412 return rep->attribute;
413}
414
415/* Retrieve attribute reply key and value at a given index. Applicable to:
416 * - REDISMODULE_REPLY_ATTRIBUTE
417 *
418 * The key and value are returned by reference through key and val,
419 * which may also be NULL if not needed.
420 *
421 * Returns C_OK on success or C_ERR if reply type mismatches, or if idx is out
422 * of range.
423 *
424 * The returned values are borrowed from CallReply, so they must not be freed
425 * explicitly or used after CallReply itself is freed.
426 */
427int callReplyGetAttributeElement(CallReply *rep, size_t idx, CallReply **key, CallReply **val) {
428 return callReplyGetMapElementInternal(rep, idx, key, val, REDISMODULE_REPLY_MAP);
429}
430
431/* Return a big number reply value. Applicable to:
432 * - REDISMODULE_REPLY_BIG_NUMBER
433 *
434 * The returned values are borrowed from CallReply, so they must not be freed
435 * explicitly or used after CallReply itself is freed.
436 *
437 * The return value is guaranteed to be a big number, as described in the RESP3
438 * protocol specifications.
439 *
440 * The returned value is not NULL terminated and its length is returned by
441 * reference through len, which must not be NULL.
442 */
443const char *callReplyGetBigNumber(CallReply *rep, size_t *len) {
444 callReplyParse(rep);
445 if (rep->type != REDISMODULE_REPLY_BIG_NUMBER) return NULL;
446 *len = rep->len;
447 return rep->val.str;
448}
449
450/* Return a verbatim string reply value. Applicable to:
451 * - REDISMODULE_REPLY_VERBATIM_STRING
452 *
453 * If format is non-NULL, the verbatim reply format is also returned by value.
454 *
455 * The optional output argument can be given to get a verbatim reply
456 * format, or can be set NULL if not needed.
457 *
458 * The return value is borrowed from CallReply, so it must not be freed
459 * explicitly or used after CallReply itself is freed.
460 *
461 * The returned value is not NULL terminated and its length is returned by
462 * reference through len, which must not be NULL.
463 */
464const char *callReplyGetVerbatim(CallReply *rep, size_t *len, const char **format){
465 callReplyParse(rep);
466 if (rep->type != REDISMODULE_REPLY_VERBATIM_STRING) return NULL;
467 *len = rep->len;
468 if (format) *format = rep->val.verbatim_str.format;
469 return rep->val.verbatim_str.str;
470}
471
472/* Return the current reply blob.
473 *
474 * The return value is borrowed from CallReply, so it must not be freed
475 * explicitly or used after CallReply itself is freed.
476 */
477const char *callReplyGetProto(CallReply *rep, size_t *proto_len) {
478 *proto_len = rep->proto_len;
479 return rep->proto;
480}
481
482/* Return CallReply private data, as set by the caller on callReplyCreate().
483 */
484void *callReplyGetPrivateData(CallReply *rep) {
485 return rep->private_data;
486}
487
488/* Return true if the reply or one of it sub-replies is RESP3 formatted. */
489int callReplyIsResp3(CallReply *rep) {
490 return rep->flags & REPLY_FLAG_RESP3;
491}
492
493/* Returns a list of errors in sds form, or NULL. */
494list *callReplyDeferredErrorList(CallReply *rep) {
495 return rep->deferred_error_list;
496}
497
498/* Create a new CallReply struct from the reply blob.
499 *
500 * The function will own the reply blob, so it must not be used or freed by
501 * the caller after passing it to this function.
502 *
503 * The reply blob will be freed when the returned CallReply struct is later
504 * freed using freeCallReply().
505 *
506 * The deferred_error_list is an optional list of errors that are present
507 * in the reply blob, if given, this function will take ownership on it.
508 *
509 * The private_data is optional and can later be accessed using
510 * callReplyGetPrivateData().
511 *
512 * NOTE: The parser used for parsing the reply and producing CallReply is
513 * designed to handle valid replies created by Redis itself. IT IS NOT
514 * DESIGNED TO HANDLE USER INPUT and using it to parse invalid replies is
515 * unsafe.
516 */
517CallReply *callReplyCreate(sds reply, list *deferred_error_list, void *private_data) {
518 CallReply *res = zmalloc(sizeof(*res));
519 res->flags = REPLY_FLAG_ROOT;
520 res->original_proto = reply;
521 res->proto = reply;
522 res->proto_len = sdslen(reply);
523 res->private_data = private_data;
524 res->attribute = NULL;
525 res->deferred_error_list = deferred_error_list;
526 return res;
527}
528
529/* Create a new CallReply struct from the reply blob representing an error message.
530 * Automatically creating deferred_error_list and set a copy of the reply in it.
531 * Refer to callReplyCreate for detailed explanation.
532 * Reply string can come in one of two forms:
533 * 1. A protocol reply starting with "-CODE" and ending with "\r\n"
534 * 2. A plain string, in which case this function adds the protocol header and footer. */
535CallReply *callReplyCreateError(sds reply, void *private_data) {
536 sds err_buff = reply;
537 if (err_buff[0] != '-') {
538 err_buff = sdscatfmt(sdsempty(), "-ERR %S\r\n", reply);
539 sdsfree(reply);
540 }
541 list *deferred_error_list = listCreate();
542 listSetFreeMethod(deferred_error_list, (void (*)(void*))sdsfree);
543 listAddNodeTail(deferred_error_list, sdsnew(err_buff));
544 return callReplyCreate(err_buff, deferred_error_list, private_data);
545}
546