13340d773SGleb Smirnoff /* 23340d773SGleb Smirnoff * Copyright (c) 2015 The TCPDUMP project 33340d773SGleb Smirnoff * All rights reserved. 43340d773SGleb Smirnoff * 53340d773SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 63340d773SGleb Smirnoff * modification, are permitted provided that the following conditions 73340d773SGleb Smirnoff * are met: 83340d773SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 93340d773SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 103340d773SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 113340d773SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 123340d773SGleb Smirnoff * documentation and/or other materials provided with the distribution. 133340d773SGleb Smirnoff * 143340d773SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 153340d773SGleb Smirnoff * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 163340d773SGleb Smirnoff * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 173340d773SGleb Smirnoff * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 183340d773SGleb Smirnoff * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 193340d773SGleb Smirnoff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 203340d773SGleb Smirnoff * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 213340d773SGleb Smirnoff * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 223340d773SGleb Smirnoff * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 233340d773SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 243340d773SGleb Smirnoff * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 253340d773SGleb Smirnoff * POSSIBILITY OF SUCH DAMAGE. 263340d773SGleb Smirnoff * 273340d773SGleb Smirnoff * Initial contribution by Andrew Darqui (andrew.darqui@gmail.com). 283340d773SGleb Smirnoff */ 293340d773SGleb Smirnoff 303340d773SGleb Smirnoff /* \summary: REdis Serialization Protocol (RESP) printer */ 313340d773SGleb Smirnoff 323340d773SGleb Smirnoff #ifdef HAVE_CONFIG_H 333340d773SGleb Smirnoff #include "config.h" 343340d773SGleb Smirnoff #endif 353340d773SGleb Smirnoff 363340d773SGleb Smirnoff #include <netdissect-stdinc.h> 373340d773SGleb Smirnoff #include "netdissect.h" 383340d773SGleb Smirnoff #include <limits.h> 393340d773SGleb Smirnoff #include <string.h> 403340d773SGleb Smirnoff #include <stdlib.h> 413340d773SGleb Smirnoff #include <errno.h> 423340d773SGleb Smirnoff 433340d773SGleb Smirnoff #include "extract.h" 443340d773SGleb Smirnoff 453340d773SGleb Smirnoff static const char tstr[] = " [|RESP]"; 463340d773SGleb Smirnoff 473340d773SGleb Smirnoff /* 483340d773SGleb Smirnoff * For information regarding RESP, see: http://redis.io/topics/protocol 493340d773SGleb Smirnoff */ 503340d773SGleb Smirnoff 513340d773SGleb Smirnoff #define RESP_SIMPLE_STRING '+' 523340d773SGleb Smirnoff #define RESP_ERROR '-' 533340d773SGleb Smirnoff #define RESP_INTEGER ':' 543340d773SGleb Smirnoff #define RESP_BULK_STRING '$' 553340d773SGleb Smirnoff #define RESP_ARRAY '*' 563340d773SGleb Smirnoff 573340d773SGleb Smirnoff #define resp_print_empty(ndo) ND_PRINT((ndo, " empty")) 583340d773SGleb Smirnoff #define resp_print_null(ndo) ND_PRINT((ndo, " null")) 593340d773SGleb Smirnoff #define resp_print_length_too_large(ndo) ND_PRINT((ndo, " length too large")) 603340d773SGleb Smirnoff #define resp_print_length_negative(ndo) ND_PRINT((ndo, " length negative and not -1")) 613340d773SGleb Smirnoff #define resp_print_invalid(ndo) ND_PRINT((ndo, " invalid")) 623340d773SGleb Smirnoff 633340d773SGleb Smirnoff void resp_print(netdissect_options *, const u_char *, u_int); 643340d773SGleb Smirnoff static int resp_parse(netdissect_options *, register const u_char *, int); 653340d773SGleb Smirnoff static int resp_print_string_error_integer(netdissect_options *, register const u_char *, int); 663340d773SGleb Smirnoff static int resp_print_simple_string(netdissect_options *, register const u_char *, int); 673340d773SGleb Smirnoff static int resp_print_integer(netdissect_options *, register const u_char *, int); 683340d773SGleb Smirnoff static int resp_print_error(netdissect_options *, register const u_char *, int); 693340d773SGleb Smirnoff static int resp_print_bulk_string(netdissect_options *, register const u_char *, int); 703340d773SGleb Smirnoff static int resp_print_bulk_array(netdissect_options *, register const u_char *, int); 713340d773SGleb Smirnoff static int resp_print_inline(netdissect_options *, register const u_char *, int); 723340d773SGleb Smirnoff static int resp_get_length(netdissect_options *, register const u_char *, int, const u_char **); 733340d773SGleb Smirnoff 743340d773SGleb Smirnoff #define LCHECK2(_tot_len, _len) \ 753340d773SGleb Smirnoff { \ 763340d773SGleb Smirnoff if (_tot_len < _len) \ 773340d773SGleb Smirnoff goto trunc; \ 783340d773SGleb Smirnoff } 793340d773SGleb Smirnoff 803340d773SGleb Smirnoff #define LCHECK(_tot_len) LCHECK2(_tot_len, 1) 813340d773SGleb Smirnoff 823340d773SGleb Smirnoff /* 833340d773SGleb Smirnoff * FIND_CRLF: 843340d773SGleb Smirnoff * Attempts to move our 'ptr' forward until a \r\n is found, 853340d773SGleb Smirnoff * while also making sure we don't exceed the buffer '_len' 863340d773SGleb Smirnoff * or go past the end of the captured data. 873340d773SGleb Smirnoff * If we exceed or go past the end of the captured data, 883340d773SGleb Smirnoff * jump to trunc. 893340d773SGleb Smirnoff */ 903340d773SGleb Smirnoff #define FIND_CRLF(_ptr, _len) \ 913340d773SGleb Smirnoff for (;;) { \ 923340d773SGleb Smirnoff LCHECK2(_len, 2); \ 933340d773SGleb Smirnoff ND_TCHECK2(*_ptr, 2); \ 943340d773SGleb Smirnoff if (*_ptr == '\r' && *(_ptr+1) == '\n') \ 953340d773SGleb Smirnoff break; \ 963340d773SGleb Smirnoff _ptr++; \ 973340d773SGleb Smirnoff _len--; \ 983340d773SGleb Smirnoff } 993340d773SGleb Smirnoff 1003340d773SGleb Smirnoff /* 1013340d773SGleb Smirnoff * CONSUME_CRLF 1023340d773SGleb Smirnoff * Consume a CRLF that we've just found. 1033340d773SGleb Smirnoff */ 1043340d773SGleb Smirnoff #define CONSUME_CRLF(_ptr, _len) \ 1053340d773SGleb Smirnoff _ptr += 2; \ 1063340d773SGleb Smirnoff _len -= 2; 1073340d773SGleb Smirnoff 1083340d773SGleb Smirnoff /* 1093340d773SGleb Smirnoff * FIND_CR_OR_LF 1103340d773SGleb Smirnoff * Attempts to move our '_ptr' forward until a \r or \n is found, 1113340d773SGleb Smirnoff * while also making sure we don't exceed the buffer '_len' 1123340d773SGleb Smirnoff * or go past the end of the captured data. 1133340d773SGleb Smirnoff * If we exceed or go past the end of the captured data, 1143340d773SGleb Smirnoff * jump to trunc. 1153340d773SGleb Smirnoff */ 1163340d773SGleb Smirnoff #define FIND_CR_OR_LF(_ptr, _len) \ 1173340d773SGleb Smirnoff for (;;) { \ 1183340d773SGleb Smirnoff LCHECK(_len); \ 1193340d773SGleb Smirnoff ND_TCHECK(*_ptr); \ 1203340d773SGleb Smirnoff if (*_ptr == '\r' || *_ptr == '\n') \ 1213340d773SGleb Smirnoff break; \ 1223340d773SGleb Smirnoff _ptr++; \ 1233340d773SGleb Smirnoff _len--; \ 1243340d773SGleb Smirnoff } 1253340d773SGleb Smirnoff 1263340d773SGleb Smirnoff /* 1273340d773SGleb Smirnoff * CONSUME_CR_OR_LF 1283340d773SGleb Smirnoff * Consume all consecutive \r and \n bytes. 1293340d773SGleb Smirnoff * If we exceed '_len' or go past the end of the captured data, 1303340d773SGleb Smirnoff * jump to trunc. 1313340d773SGleb Smirnoff */ 1323340d773SGleb Smirnoff #define CONSUME_CR_OR_LF(_ptr, _len) \ 1333340d773SGleb Smirnoff { \ 1343340d773SGleb Smirnoff int _found_cr_or_lf = 0; \ 1353340d773SGleb Smirnoff for (;;) { \ 1363340d773SGleb Smirnoff /* \ 1373340d773SGleb Smirnoff * Have we hit the end of data? \ 1383340d773SGleb Smirnoff */ \ 1393340d773SGleb Smirnoff if (_len == 0 || !ND_TTEST(*_ptr)) { \ 1403340d773SGleb Smirnoff /* \ 1413340d773SGleb Smirnoff * Yes. Have we seen a \r \ 1423340d773SGleb Smirnoff * or \n? \ 1433340d773SGleb Smirnoff */ \ 1443340d773SGleb Smirnoff if (_found_cr_or_lf) { \ 1453340d773SGleb Smirnoff /* \ 1463340d773SGleb Smirnoff * Yes. Just stop. \ 1473340d773SGleb Smirnoff */ \ 1483340d773SGleb Smirnoff break; \ 1493340d773SGleb Smirnoff } \ 1503340d773SGleb Smirnoff /* \ 1513340d773SGleb Smirnoff * No. We ran out of packet. \ 1523340d773SGleb Smirnoff */ \ 1533340d773SGleb Smirnoff goto trunc; \ 1543340d773SGleb Smirnoff } \ 1553340d773SGleb Smirnoff if (*_ptr != '\r' && *_ptr != '\n') \ 1563340d773SGleb Smirnoff break; \ 1573340d773SGleb Smirnoff _found_cr_or_lf = 1; \ 1583340d773SGleb Smirnoff _ptr++; \ 1593340d773SGleb Smirnoff _len--; \ 1603340d773SGleb Smirnoff } \ 1613340d773SGleb Smirnoff } 1623340d773SGleb Smirnoff 1633340d773SGleb Smirnoff /* 1643340d773SGleb Smirnoff * SKIP_OPCODE 1653340d773SGleb Smirnoff * Skip over the opcode character. 1663340d773SGleb Smirnoff * The opcode has already been fetched, so we know it's there, and don't 1673340d773SGleb Smirnoff * need to do any checks. 1683340d773SGleb Smirnoff */ 1693340d773SGleb Smirnoff #define SKIP_OPCODE(_ptr, _tot_len) \ 1703340d773SGleb Smirnoff _ptr++; \ 1713340d773SGleb Smirnoff _tot_len--; 1723340d773SGleb Smirnoff 1733340d773SGleb Smirnoff /* 1743340d773SGleb Smirnoff * GET_LENGTH 1753340d773SGleb Smirnoff * Get a bulk string or array length. 1763340d773SGleb Smirnoff */ 1773340d773SGleb Smirnoff #define GET_LENGTH(_ndo, _tot_len, _ptr, _len) \ 1783340d773SGleb Smirnoff { \ 1793340d773SGleb Smirnoff const u_char *_endp; \ 1803340d773SGleb Smirnoff _len = resp_get_length(_ndo, _ptr, _tot_len, &_endp); \ 1813340d773SGleb Smirnoff _tot_len -= (_endp - _ptr); \ 1823340d773SGleb Smirnoff _ptr = _endp; \ 1833340d773SGleb Smirnoff } 1843340d773SGleb Smirnoff 1853340d773SGleb Smirnoff /* 1863340d773SGleb Smirnoff * TEST_RET_LEN 1873340d773SGleb Smirnoff * If ret_len is < 0, jump to the trunc tag which returns (-1) 1883340d773SGleb Smirnoff * and 'bubbles up' to printing tstr. Otherwise, return ret_len. 1893340d773SGleb Smirnoff */ 1903340d773SGleb Smirnoff #define TEST_RET_LEN(rl) \ 1913340d773SGleb Smirnoff if (rl < 0) { goto trunc; } else { return rl; } 1923340d773SGleb Smirnoff 1933340d773SGleb Smirnoff /* 1943340d773SGleb Smirnoff * TEST_RET_LEN_NORETURN 1953340d773SGleb Smirnoff * If ret_len is < 0, jump to the trunc tag which returns (-1) 1963340d773SGleb Smirnoff * and 'bubbles up' to printing tstr. Otherwise, continue onward. 1973340d773SGleb Smirnoff */ 1983340d773SGleb Smirnoff #define TEST_RET_LEN_NORETURN(rl) \ 1993340d773SGleb Smirnoff if (rl < 0) { goto trunc; } 2003340d773SGleb Smirnoff 2013340d773SGleb Smirnoff /* 2023340d773SGleb Smirnoff * RESP_PRINT_SEGMENT 2033340d773SGleb Smirnoff * Prints a segment in the form of: ' "<stuff>"\n" 2043340d773SGleb Smirnoff * Assumes the data has already been verified as present. 2053340d773SGleb Smirnoff */ 2063340d773SGleb Smirnoff #define RESP_PRINT_SEGMENT(_ndo, _bp, _len) \ 2073340d773SGleb Smirnoff ND_PRINT((_ndo, " \"")); \ 2083340d773SGleb Smirnoff if (fn_printn(_ndo, _bp, _len, _ndo->ndo_snapend)) \ 2093340d773SGleb Smirnoff goto trunc; \ 2103340d773SGleb Smirnoff fn_print_char(_ndo, '"'); 2113340d773SGleb Smirnoff 2123340d773SGleb Smirnoff void 2133340d773SGleb Smirnoff resp_print(netdissect_options *ndo, const u_char *bp, u_int length) 2143340d773SGleb Smirnoff { 2153340d773SGleb Smirnoff int ret_len = 0, length_cur = length; 2163340d773SGleb Smirnoff 2173340d773SGleb Smirnoff if(!bp || length <= 0) 2183340d773SGleb Smirnoff return; 2193340d773SGleb Smirnoff 2203340d773SGleb Smirnoff ND_PRINT((ndo, ": RESP")); 2213340d773SGleb Smirnoff while (length_cur > 0) { 2223340d773SGleb Smirnoff /* 2233340d773SGleb Smirnoff * This block supports redis pipelining. 2243340d773SGleb Smirnoff * For example, multiple operations can be pipelined within the same string: 2253340d773SGleb Smirnoff * "*2\r\n\$4\r\nINCR\r\n\$1\r\nz\r\n*2\r\n\$4\r\nINCR\r\n\$1\r\nz\r\n*2\r\n\$4\r\nINCR\r\n\$1\r\nz\r\n" 2263340d773SGleb Smirnoff * or 2273340d773SGleb Smirnoff * "PING\r\nPING\r\nPING\r\n" 2283340d773SGleb Smirnoff * In order to handle this case, we must try and parse 'bp' until 2293340d773SGleb Smirnoff * 'length' bytes have been processed or we reach a trunc condition. 2303340d773SGleb Smirnoff */ 2313340d773SGleb Smirnoff ret_len = resp_parse(ndo, bp, length_cur); 2323340d773SGleb Smirnoff TEST_RET_LEN_NORETURN(ret_len); 2333340d773SGleb Smirnoff bp += ret_len; 2343340d773SGleb Smirnoff length_cur -= ret_len; 2353340d773SGleb Smirnoff } 2363340d773SGleb Smirnoff 2373340d773SGleb Smirnoff return; 2383340d773SGleb Smirnoff 2393340d773SGleb Smirnoff trunc: 2403340d773SGleb Smirnoff ND_PRINT((ndo, "%s", tstr)); 2413340d773SGleb Smirnoff } 2423340d773SGleb Smirnoff 2433340d773SGleb Smirnoff static int 2443340d773SGleb Smirnoff resp_parse(netdissect_options *ndo, register const u_char *bp, int length) 2453340d773SGleb Smirnoff { 2463340d773SGleb Smirnoff u_char op; 2473340d773SGleb Smirnoff int ret_len; 2483340d773SGleb Smirnoff 2493340d773SGleb Smirnoff LCHECK2(length, 1); 2503340d773SGleb Smirnoff ND_TCHECK(*bp); 2513340d773SGleb Smirnoff op = *bp; 2523340d773SGleb Smirnoff 2533340d773SGleb Smirnoff /* bp now points to the op, so these routines must skip it */ 2543340d773SGleb Smirnoff switch(op) { 2553340d773SGleb Smirnoff case RESP_SIMPLE_STRING: ret_len = resp_print_simple_string(ndo, bp, length); break; 2563340d773SGleb Smirnoff case RESP_INTEGER: ret_len = resp_print_integer(ndo, bp, length); break; 2573340d773SGleb Smirnoff case RESP_ERROR: ret_len = resp_print_error(ndo, bp, length); break; 2583340d773SGleb Smirnoff case RESP_BULK_STRING: ret_len = resp_print_bulk_string(ndo, bp, length); break; 2593340d773SGleb Smirnoff case RESP_ARRAY: ret_len = resp_print_bulk_array(ndo, bp, length); break; 2603340d773SGleb Smirnoff default: ret_len = resp_print_inline(ndo, bp, length); break; 2613340d773SGleb Smirnoff } 2623340d773SGleb Smirnoff 2633340d773SGleb Smirnoff /* 2643340d773SGleb Smirnoff * This gives up with a "truncated" indicator for all errors, 2653340d773SGleb Smirnoff * including invalid packet errors; that's what we want, as 2663340d773SGleb Smirnoff * we have to give up on further parsing in that case. 2673340d773SGleb Smirnoff */ 2683340d773SGleb Smirnoff TEST_RET_LEN(ret_len); 2693340d773SGleb Smirnoff 2703340d773SGleb Smirnoff trunc: 2713340d773SGleb Smirnoff return (-1); 2723340d773SGleb Smirnoff } 2733340d773SGleb Smirnoff 2743340d773SGleb Smirnoff static int 2753340d773SGleb Smirnoff resp_print_simple_string(netdissect_options *ndo, register const u_char *bp, int length) { 2763340d773SGleb Smirnoff return resp_print_string_error_integer(ndo, bp, length); 2773340d773SGleb Smirnoff } 2783340d773SGleb Smirnoff 2793340d773SGleb Smirnoff static int 2803340d773SGleb Smirnoff resp_print_integer(netdissect_options *ndo, register const u_char *bp, int length) { 2813340d773SGleb Smirnoff return resp_print_string_error_integer(ndo, bp, length); 2823340d773SGleb Smirnoff } 2833340d773SGleb Smirnoff 2843340d773SGleb Smirnoff static int 2853340d773SGleb Smirnoff resp_print_error(netdissect_options *ndo, register const u_char *bp, int length) { 2863340d773SGleb Smirnoff return resp_print_string_error_integer(ndo, bp, length); 2873340d773SGleb Smirnoff } 2883340d773SGleb Smirnoff 2893340d773SGleb Smirnoff static int 2903340d773SGleb Smirnoff resp_print_string_error_integer(netdissect_options *ndo, register const u_char *bp, int length) { 2913340d773SGleb Smirnoff int length_cur = length, len, ret_len; 2923340d773SGleb Smirnoff const u_char *bp_ptr; 2933340d773SGleb Smirnoff 2943340d773SGleb Smirnoff /* bp points to the op; skip it */ 2953340d773SGleb Smirnoff SKIP_OPCODE(bp, length_cur); 2963340d773SGleb Smirnoff bp_ptr = bp; 2973340d773SGleb Smirnoff 2983340d773SGleb Smirnoff /* 2993340d773SGleb Smirnoff * bp now prints past the (+-;) opcode, so it's pointing to the first 3003340d773SGleb Smirnoff * character of the string (which could be numeric). 3013340d773SGleb Smirnoff * +OK\r\n 3023340d773SGleb Smirnoff * -ERR ...\r\n 3033340d773SGleb Smirnoff * :02912309\r\n 3043340d773SGleb Smirnoff * 3053340d773SGleb Smirnoff * Find the \r\n with FIND_CRLF(). 3063340d773SGleb Smirnoff */ 3073340d773SGleb Smirnoff FIND_CRLF(bp_ptr, length_cur); 3083340d773SGleb Smirnoff 3093340d773SGleb Smirnoff /* 3103340d773SGleb Smirnoff * bp_ptr points to the \r\n, so bp_ptr - bp is the length of text 3113340d773SGleb Smirnoff * preceding the \r\n. That includes the opcode, so don't print 3123340d773SGleb Smirnoff * that. 3133340d773SGleb Smirnoff */ 3143340d773SGleb Smirnoff len = (bp_ptr - bp); 3153340d773SGleb Smirnoff RESP_PRINT_SEGMENT(ndo, bp, len); 3163340d773SGleb Smirnoff ret_len = 1 /*<opcode>*/ + len /*<string>*/ + 2 /*<CRLF>*/; 3173340d773SGleb Smirnoff 3183340d773SGleb Smirnoff TEST_RET_LEN(ret_len); 3193340d773SGleb Smirnoff 3203340d773SGleb Smirnoff trunc: 3213340d773SGleb Smirnoff return (-1); 3223340d773SGleb Smirnoff } 3233340d773SGleb Smirnoff 3243340d773SGleb Smirnoff static int 3253340d773SGleb Smirnoff resp_print_bulk_string(netdissect_options *ndo, register const u_char *bp, int length) { 3263340d773SGleb Smirnoff int length_cur = length, string_len; 3273340d773SGleb Smirnoff 3283340d773SGleb Smirnoff /* bp points to the op; skip it */ 3293340d773SGleb Smirnoff SKIP_OPCODE(bp, length_cur); 3303340d773SGleb Smirnoff 3313340d773SGleb Smirnoff /* <length>\r\n */ 3323340d773SGleb Smirnoff GET_LENGTH(ndo, length_cur, bp, string_len); 3333340d773SGleb Smirnoff 3343340d773SGleb Smirnoff if (string_len >= 0) { 3353340d773SGleb Smirnoff /* Byte string of length string_len, starting at bp */ 3363340d773SGleb Smirnoff if (string_len == 0) 3373340d773SGleb Smirnoff resp_print_empty(ndo); 3383340d773SGleb Smirnoff else { 3393340d773SGleb Smirnoff LCHECK2(length_cur, string_len); 3403340d773SGleb Smirnoff ND_TCHECK2(*bp, string_len); 3413340d773SGleb Smirnoff RESP_PRINT_SEGMENT(ndo, bp, string_len); 3423340d773SGleb Smirnoff bp += string_len; 3433340d773SGleb Smirnoff length_cur -= string_len; 3443340d773SGleb Smirnoff } 3453340d773SGleb Smirnoff 3463340d773SGleb Smirnoff /* 3473340d773SGleb Smirnoff * Find the \r\n at the end of the string and skip past it. 3483340d773SGleb Smirnoff * XXX - report an error if the \r\n isn't immediately after 3493340d773SGleb Smirnoff * the item? 3503340d773SGleb Smirnoff */ 3513340d773SGleb Smirnoff FIND_CRLF(bp, length_cur); 3523340d773SGleb Smirnoff CONSUME_CRLF(bp, length_cur); 3533340d773SGleb Smirnoff } else { 3543340d773SGleb Smirnoff /* null, truncated, or invalid for some reason */ 3553340d773SGleb Smirnoff switch(string_len) { 3563340d773SGleb Smirnoff case (-1): resp_print_null(ndo); break; 3573340d773SGleb Smirnoff case (-2): goto trunc; 3583340d773SGleb Smirnoff case (-3): resp_print_length_too_large(ndo); break; 3593340d773SGleb Smirnoff case (-4): resp_print_length_negative(ndo); break; 3603340d773SGleb Smirnoff default: resp_print_invalid(ndo); break; 3613340d773SGleb Smirnoff } 3623340d773SGleb Smirnoff } 3633340d773SGleb Smirnoff 3643340d773SGleb Smirnoff return (length - length_cur); 3653340d773SGleb Smirnoff 3663340d773SGleb Smirnoff trunc: 3673340d773SGleb Smirnoff return (-1); 3683340d773SGleb Smirnoff } 3693340d773SGleb Smirnoff 3703340d773SGleb Smirnoff static int 3713340d773SGleb Smirnoff resp_print_bulk_array(netdissect_options *ndo, register const u_char *bp, int length) { 3723340d773SGleb Smirnoff u_int length_cur = length; 3733340d773SGleb Smirnoff int array_len, i, ret_len; 3743340d773SGleb Smirnoff 3753340d773SGleb Smirnoff /* bp points to the op; skip it */ 3763340d773SGleb Smirnoff SKIP_OPCODE(bp, length_cur); 3773340d773SGleb Smirnoff 3783340d773SGleb Smirnoff /* <array_length>\r\n */ 3793340d773SGleb Smirnoff GET_LENGTH(ndo, length_cur, bp, array_len); 3803340d773SGleb Smirnoff 3813340d773SGleb Smirnoff if (array_len > 0) { 3823340d773SGleb Smirnoff /* non empty array */ 3833340d773SGleb Smirnoff for (i = 0; i < array_len; i++) { 3843340d773SGleb Smirnoff ret_len = resp_parse(ndo, bp, length_cur); 3853340d773SGleb Smirnoff 3863340d773SGleb Smirnoff TEST_RET_LEN_NORETURN(ret_len); 3873340d773SGleb Smirnoff 3883340d773SGleb Smirnoff bp += ret_len; 3893340d773SGleb Smirnoff length_cur -= ret_len; 3903340d773SGleb Smirnoff } 3913340d773SGleb Smirnoff } else { 3923340d773SGleb Smirnoff /* empty, null, truncated, or invalid */ 3933340d773SGleb Smirnoff switch(array_len) { 3943340d773SGleb Smirnoff case 0: resp_print_empty(ndo); break; 3953340d773SGleb Smirnoff case (-1): resp_print_null(ndo); break; 3963340d773SGleb Smirnoff case (-2): goto trunc; 3973340d773SGleb Smirnoff case (-3): resp_print_length_too_large(ndo); break; 3983340d773SGleb Smirnoff case (-4): resp_print_length_negative(ndo); break; 3993340d773SGleb Smirnoff default: resp_print_invalid(ndo); break; 4003340d773SGleb Smirnoff } 4013340d773SGleb Smirnoff } 4023340d773SGleb Smirnoff 4033340d773SGleb Smirnoff return (length - length_cur); 4043340d773SGleb Smirnoff 4053340d773SGleb Smirnoff trunc: 4063340d773SGleb Smirnoff return (-1); 4073340d773SGleb Smirnoff } 4083340d773SGleb Smirnoff 4093340d773SGleb Smirnoff static int 4103340d773SGleb Smirnoff resp_print_inline(netdissect_options *ndo, register const u_char *bp, int length) { 4113340d773SGleb Smirnoff int length_cur = length; 4123340d773SGleb Smirnoff int len; 4133340d773SGleb Smirnoff const u_char *bp_ptr; 4143340d773SGleb Smirnoff 4153340d773SGleb Smirnoff /* 4163340d773SGleb Smirnoff * Inline commands are simply 'strings' followed by \r or \n or both. 4173340d773SGleb Smirnoff * Redis will do its best to split/parse these strings. 4183340d773SGleb Smirnoff * This feature of redis is implemented to support the ability of 4193340d773SGleb Smirnoff * command parsing from telnet/nc sessions etc. 4203340d773SGleb Smirnoff * 4213340d773SGleb Smirnoff * <string><\r||\n||\r\n...> 4223340d773SGleb Smirnoff */ 4233340d773SGleb Smirnoff 4243340d773SGleb Smirnoff /* 4253340d773SGleb Smirnoff * Skip forward past any leading \r, \n, or \r\n. 4263340d773SGleb Smirnoff */ 4273340d773SGleb Smirnoff CONSUME_CR_OR_LF(bp, length_cur); 4283340d773SGleb Smirnoff bp_ptr = bp; 4293340d773SGleb Smirnoff 4303340d773SGleb Smirnoff /* 4313340d773SGleb Smirnoff * Scan forward looking for \r or \n. 4323340d773SGleb Smirnoff */ 4333340d773SGleb Smirnoff FIND_CR_OR_LF(bp_ptr, length_cur); 4343340d773SGleb Smirnoff 4353340d773SGleb Smirnoff /* 4363340d773SGleb Smirnoff * Found it; bp_ptr points to the \r or \n, so bp_ptr - bp is the 4373340d773SGleb Smirnoff * Length of the line text that preceeds it. Print it. 4383340d773SGleb Smirnoff */ 4393340d773SGleb Smirnoff len = (bp_ptr - bp); 4403340d773SGleb Smirnoff RESP_PRINT_SEGMENT(ndo, bp, len); 4413340d773SGleb Smirnoff 4423340d773SGleb Smirnoff /* 4433340d773SGleb Smirnoff * Skip forward past the \r, \n, or \r\n. 4443340d773SGleb Smirnoff */ 4453340d773SGleb Smirnoff CONSUME_CR_OR_LF(bp_ptr, length_cur); 4463340d773SGleb Smirnoff 4473340d773SGleb Smirnoff /* 4483340d773SGleb Smirnoff * Return the number of bytes we processed. 4493340d773SGleb Smirnoff */ 4503340d773SGleb Smirnoff return (length - length_cur); 4513340d773SGleb Smirnoff 4523340d773SGleb Smirnoff trunc: 4533340d773SGleb Smirnoff return (-1); 4543340d773SGleb Smirnoff } 4553340d773SGleb Smirnoff 4563340d773SGleb Smirnoff static int 4573340d773SGleb Smirnoff resp_get_length(netdissect_options *ndo, register const u_char *bp, int len, const u_char **endp) 4583340d773SGleb Smirnoff { 4593340d773SGleb Smirnoff int result; 4603340d773SGleb Smirnoff u_char c; 4613340d773SGleb Smirnoff int saw_digit; 4623340d773SGleb Smirnoff int neg; 4633340d773SGleb Smirnoff int too_large; 4643340d773SGleb Smirnoff 4653340d773SGleb Smirnoff if (len == 0) 4663340d773SGleb Smirnoff goto trunc; 4673340d773SGleb Smirnoff ND_TCHECK(*bp); 4683340d773SGleb Smirnoff too_large = 0; 4693340d773SGleb Smirnoff neg = 0; 4703340d773SGleb Smirnoff if (*bp == '-') { 4713340d773SGleb Smirnoff neg = 1; 4723340d773SGleb Smirnoff bp++; 4733340d773SGleb Smirnoff len--; 4743340d773SGleb Smirnoff } 4753340d773SGleb Smirnoff result = 0; 4763340d773SGleb Smirnoff saw_digit = 0; 4773340d773SGleb Smirnoff 4783340d773SGleb Smirnoff for (;;) { 4793340d773SGleb Smirnoff if (len == 0) 4803340d773SGleb Smirnoff goto trunc; 4813340d773SGleb Smirnoff ND_TCHECK(*bp); 4823340d773SGleb Smirnoff c = *bp; 4833340d773SGleb Smirnoff if (!(c >= '0' && c <= '9')) { 484*0bff6a5aSEd Maste if (!saw_digit) { 485*0bff6a5aSEd Maste bp++; 4863340d773SGleb Smirnoff goto invalid; 487*0bff6a5aSEd Maste } 4883340d773SGleb Smirnoff break; 4893340d773SGleb Smirnoff } 4903340d773SGleb Smirnoff c -= '0'; 4913340d773SGleb Smirnoff if (result > (INT_MAX / 10)) { 4923340d773SGleb Smirnoff /* This will overflow an int when we multiply it by 10. */ 4933340d773SGleb Smirnoff too_large = 1; 4943340d773SGleb Smirnoff } else { 4953340d773SGleb Smirnoff result *= 10; 496*0bff6a5aSEd Maste if (result == ((INT_MAX / 10) * 10) && c > (INT_MAX % 10)) { 4973340d773SGleb Smirnoff /* This will overflow an int when we add c */ 4983340d773SGleb Smirnoff too_large = 1; 4993340d773SGleb Smirnoff } else 5003340d773SGleb Smirnoff result += c; 5013340d773SGleb Smirnoff } 5023340d773SGleb Smirnoff bp++; 5033340d773SGleb Smirnoff len--; 5043340d773SGleb Smirnoff saw_digit = 1; 5053340d773SGleb Smirnoff } 5063340d773SGleb Smirnoff 5073340d773SGleb Smirnoff /* 508*0bff6a5aSEd Maste * OK, we found a non-digit character. It should be a \r, followed 509*0bff6a5aSEd Maste * by a \n. 5103340d773SGleb Smirnoff */ 511*0bff6a5aSEd Maste if (*bp != '\r') { 512*0bff6a5aSEd Maste bp++; 5133340d773SGleb Smirnoff goto invalid; 514*0bff6a5aSEd Maste } 5153340d773SGleb Smirnoff bp++; 5163340d773SGleb Smirnoff len--; 5173340d773SGleb Smirnoff if (len == 0) 5183340d773SGleb Smirnoff goto trunc; 5193340d773SGleb Smirnoff ND_TCHECK(*bp); 520*0bff6a5aSEd Maste if (*bp != '\n') { 521*0bff6a5aSEd Maste bp++; 5223340d773SGleb Smirnoff goto invalid; 523*0bff6a5aSEd Maste } 5243340d773SGleb Smirnoff bp++; 5253340d773SGleb Smirnoff len--; 5263340d773SGleb Smirnoff *endp = bp; 5273340d773SGleb Smirnoff if (neg) { 5283340d773SGleb Smirnoff /* -1 means "null", anything else is invalid */ 5293340d773SGleb Smirnoff if (too_large || result != 1) 5303340d773SGleb Smirnoff return (-4); 5313340d773SGleb Smirnoff result = -1; 5323340d773SGleb Smirnoff } 5333340d773SGleb Smirnoff return (too_large ? -3 : result); 5343340d773SGleb Smirnoff 5353340d773SGleb Smirnoff trunc: 536*0bff6a5aSEd Maste *endp = bp; 5373340d773SGleb Smirnoff return (-2); 5383340d773SGleb Smirnoff 5393340d773SGleb Smirnoff invalid: 540*0bff6a5aSEd Maste *endp = bp; 5413340d773SGleb Smirnoff return (-5); 5423340d773SGleb Smirnoff } 543