1*3340d773SGleb Smirnoff /* 2*3340d773SGleb Smirnoff * Copyright (c) 2015 The TCPDUMP project 3*3340d773SGleb Smirnoff * All rights reserved. 4*3340d773SGleb Smirnoff * 5*3340d773SGleb Smirnoff * Redistribution and use in source and binary forms, with or without 6*3340d773SGleb Smirnoff * modification, are permitted provided that the following conditions 7*3340d773SGleb Smirnoff * are met: 8*3340d773SGleb Smirnoff * 1. Redistributions of source code must retain the above copyright 9*3340d773SGleb Smirnoff * notice, this list of conditions and the following disclaimer. 10*3340d773SGleb Smirnoff * 2. Redistributions in binary form must reproduce the above copyright 11*3340d773SGleb Smirnoff * notice, this list of conditions and the following disclaimer in the 12*3340d773SGleb Smirnoff * documentation and/or other materials provided with the distribution. 13*3340d773SGleb Smirnoff * 14*3340d773SGleb Smirnoff * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 15*3340d773SGleb Smirnoff * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 16*3340d773SGleb Smirnoff * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 17*3340d773SGleb Smirnoff * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 18*3340d773SGleb Smirnoff * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 19*3340d773SGleb Smirnoff * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20*3340d773SGleb Smirnoff * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21*3340d773SGleb Smirnoff * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22*3340d773SGleb Smirnoff * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23*3340d773SGleb Smirnoff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 24*3340d773SGleb Smirnoff * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25*3340d773SGleb Smirnoff * POSSIBILITY OF SUCH DAMAGE. 26*3340d773SGleb Smirnoff * 27*3340d773SGleb Smirnoff * Initial contribution by Andrew Darqui (andrew.darqui@gmail.com). 28*3340d773SGleb Smirnoff */ 29*3340d773SGleb Smirnoff 30*3340d773SGleb Smirnoff /* \summary: REdis Serialization Protocol (RESP) printer */ 31*3340d773SGleb Smirnoff 32*3340d773SGleb Smirnoff #ifdef HAVE_CONFIG_H 33*3340d773SGleb Smirnoff #include "config.h" 34*3340d773SGleb Smirnoff #endif 35*3340d773SGleb Smirnoff 36*3340d773SGleb Smirnoff #include <netdissect-stdinc.h> 37*3340d773SGleb Smirnoff #include "netdissect.h" 38*3340d773SGleb Smirnoff #include <limits.h> 39*3340d773SGleb Smirnoff #include <string.h> 40*3340d773SGleb Smirnoff #include <stdlib.h> 41*3340d773SGleb Smirnoff #include <errno.h> 42*3340d773SGleb Smirnoff 43*3340d773SGleb Smirnoff #include "extract.h" 44*3340d773SGleb Smirnoff 45*3340d773SGleb Smirnoff static const char tstr[] = " [|RESP]"; 46*3340d773SGleb Smirnoff 47*3340d773SGleb Smirnoff /* 48*3340d773SGleb Smirnoff * For information regarding RESP, see: http://redis.io/topics/protocol 49*3340d773SGleb Smirnoff */ 50*3340d773SGleb Smirnoff 51*3340d773SGleb Smirnoff #define RESP_SIMPLE_STRING '+' 52*3340d773SGleb Smirnoff #define RESP_ERROR '-' 53*3340d773SGleb Smirnoff #define RESP_INTEGER ':' 54*3340d773SGleb Smirnoff #define RESP_BULK_STRING '$' 55*3340d773SGleb Smirnoff #define RESP_ARRAY '*' 56*3340d773SGleb Smirnoff 57*3340d773SGleb Smirnoff #define resp_print_empty(ndo) ND_PRINT((ndo, " empty")) 58*3340d773SGleb Smirnoff #define resp_print_null(ndo) ND_PRINT((ndo, " null")) 59*3340d773SGleb Smirnoff #define resp_print_length_too_large(ndo) ND_PRINT((ndo, " length too large")) 60*3340d773SGleb Smirnoff #define resp_print_length_negative(ndo) ND_PRINT((ndo, " length negative and not -1")) 61*3340d773SGleb Smirnoff #define resp_print_invalid(ndo) ND_PRINT((ndo, " invalid")) 62*3340d773SGleb Smirnoff 63*3340d773SGleb Smirnoff void resp_print(netdissect_options *, const u_char *, u_int); 64*3340d773SGleb Smirnoff static int resp_parse(netdissect_options *, register const u_char *, int); 65*3340d773SGleb Smirnoff static int resp_print_string_error_integer(netdissect_options *, register const u_char *, int); 66*3340d773SGleb Smirnoff static int resp_print_simple_string(netdissect_options *, register const u_char *, int); 67*3340d773SGleb Smirnoff static int resp_print_integer(netdissect_options *, register const u_char *, int); 68*3340d773SGleb Smirnoff static int resp_print_error(netdissect_options *, register const u_char *, int); 69*3340d773SGleb Smirnoff static int resp_print_bulk_string(netdissect_options *, register const u_char *, int); 70*3340d773SGleb Smirnoff static int resp_print_bulk_array(netdissect_options *, register const u_char *, int); 71*3340d773SGleb Smirnoff static int resp_print_inline(netdissect_options *, register const u_char *, int); 72*3340d773SGleb Smirnoff static int resp_get_length(netdissect_options *, register const u_char *, int, const u_char **); 73*3340d773SGleb Smirnoff 74*3340d773SGleb Smirnoff #define LCHECK2(_tot_len, _len) \ 75*3340d773SGleb Smirnoff { \ 76*3340d773SGleb Smirnoff if (_tot_len < _len) \ 77*3340d773SGleb Smirnoff goto trunc; \ 78*3340d773SGleb Smirnoff } 79*3340d773SGleb Smirnoff 80*3340d773SGleb Smirnoff #define LCHECK(_tot_len) LCHECK2(_tot_len, 1) 81*3340d773SGleb Smirnoff 82*3340d773SGleb Smirnoff /* 83*3340d773SGleb Smirnoff * FIND_CRLF: 84*3340d773SGleb Smirnoff * Attempts to move our 'ptr' forward until a \r\n is found, 85*3340d773SGleb Smirnoff * while also making sure we don't exceed the buffer '_len' 86*3340d773SGleb Smirnoff * or go past the end of the captured data. 87*3340d773SGleb Smirnoff * If we exceed or go past the end of the captured data, 88*3340d773SGleb Smirnoff * jump to trunc. 89*3340d773SGleb Smirnoff */ 90*3340d773SGleb Smirnoff #define FIND_CRLF(_ptr, _len) \ 91*3340d773SGleb Smirnoff for (;;) { \ 92*3340d773SGleb Smirnoff LCHECK2(_len, 2); \ 93*3340d773SGleb Smirnoff ND_TCHECK2(*_ptr, 2); \ 94*3340d773SGleb Smirnoff if (*_ptr == '\r' && *(_ptr+1) == '\n') \ 95*3340d773SGleb Smirnoff break; \ 96*3340d773SGleb Smirnoff _ptr++; \ 97*3340d773SGleb Smirnoff _len--; \ 98*3340d773SGleb Smirnoff } 99*3340d773SGleb Smirnoff 100*3340d773SGleb Smirnoff /* 101*3340d773SGleb Smirnoff * CONSUME_CRLF 102*3340d773SGleb Smirnoff * Consume a CRLF that we've just found. 103*3340d773SGleb Smirnoff */ 104*3340d773SGleb Smirnoff #define CONSUME_CRLF(_ptr, _len) \ 105*3340d773SGleb Smirnoff _ptr += 2; \ 106*3340d773SGleb Smirnoff _len -= 2; 107*3340d773SGleb Smirnoff 108*3340d773SGleb Smirnoff /* 109*3340d773SGleb Smirnoff * FIND_CR_OR_LF 110*3340d773SGleb Smirnoff * Attempts to move our '_ptr' forward until a \r or \n is found, 111*3340d773SGleb Smirnoff * while also making sure we don't exceed the buffer '_len' 112*3340d773SGleb Smirnoff * or go past the end of the captured data. 113*3340d773SGleb Smirnoff * If we exceed or go past the end of the captured data, 114*3340d773SGleb Smirnoff * jump to trunc. 115*3340d773SGleb Smirnoff */ 116*3340d773SGleb Smirnoff #define FIND_CR_OR_LF(_ptr, _len) \ 117*3340d773SGleb Smirnoff for (;;) { \ 118*3340d773SGleb Smirnoff LCHECK(_len); \ 119*3340d773SGleb Smirnoff ND_TCHECK(*_ptr); \ 120*3340d773SGleb Smirnoff if (*_ptr == '\r' || *_ptr == '\n') \ 121*3340d773SGleb Smirnoff break; \ 122*3340d773SGleb Smirnoff _ptr++; \ 123*3340d773SGleb Smirnoff _len--; \ 124*3340d773SGleb Smirnoff } 125*3340d773SGleb Smirnoff 126*3340d773SGleb Smirnoff /* 127*3340d773SGleb Smirnoff * CONSUME_CR_OR_LF 128*3340d773SGleb Smirnoff * Consume all consecutive \r and \n bytes. 129*3340d773SGleb Smirnoff * If we exceed '_len' or go past the end of the captured data, 130*3340d773SGleb Smirnoff * jump to trunc. 131*3340d773SGleb Smirnoff */ 132*3340d773SGleb Smirnoff #define CONSUME_CR_OR_LF(_ptr, _len) \ 133*3340d773SGleb Smirnoff { \ 134*3340d773SGleb Smirnoff int _found_cr_or_lf = 0; \ 135*3340d773SGleb Smirnoff for (;;) { \ 136*3340d773SGleb Smirnoff /* \ 137*3340d773SGleb Smirnoff * Have we hit the end of data? \ 138*3340d773SGleb Smirnoff */ \ 139*3340d773SGleb Smirnoff if (_len == 0 || !ND_TTEST(*_ptr)) { \ 140*3340d773SGleb Smirnoff /* \ 141*3340d773SGleb Smirnoff * Yes. Have we seen a \r \ 142*3340d773SGleb Smirnoff * or \n? \ 143*3340d773SGleb Smirnoff */ \ 144*3340d773SGleb Smirnoff if (_found_cr_or_lf) { \ 145*3340d773SGleb Smirnoff /* \ 146*3340d773SGleb Smirnoff * Yes. Just stop. \ 147*3340d773SGleb Smirnoff */ \ 148*3340d773SGleb Smirnoff break; \ 149*3340d773SGleb Smirnoff } \ 150*3340d773SGleb Smirnoff /* \ 151*3340d773SGleb Smirnoff * No. We ran out of packet. \ 152*3340d773SGleb Smirnoff */ \ 153*3340d773SGleb Smirnoff goto trunc; \ 154*3340d773SGleb Smirnoff } \ 155*3340d773SGleb Smirnoff if (*_ptr != '\r' && *_ptr != '\n') \ 156*3340d773SGleb Smirnoff break; \ 157*3340d773SGleb Smirnoff _found_cr_or_lf = 1; \ 158*3340d773SGleb Smirnoff _ptr++; \ 159*3340d773SGleb Smirnoff _len--; \ 160*3340d773SGleb Smirnoff } \ 161*3340d773SGleb Smirnoff } 162*3340d773SGleb Smirnoff 163*3340d773SGleb Smirnoff /* 164*3340d773SGleb Smirnoff * SKIP_OPCODE 165*3340d773SGleb Smirnoff * Skip over the opcode character. 166*3340d773SGleb Smirnoff * The opcode has already been fetched, so we know it's there, and don't 167*3340d773SGleb Smirnoff * need to do any checks. 168*3340d773SGleb Smirnoff */ 169*3340d773SGleb Smirnoff #define SKIP_OPCODE(_ptr, _tot_len) \ 170*3340d773SGleb Smirnoff _ptr++; \ 171*3340d773SGleb Smirnoff _tot_len--; 172*3340d773SGleb Smirnoff 173*3340d773SGleb Smirnoff /* 174*3340d773SGleb Smirnoff * GET_LENGTH 175*3340d773SGleb Smirnoff * Get a bulk string or array length. 176*3340d773SGleb Smirnoff */ 177*3340d773SGleb Smirnoff #define GET_LENGTH(_ndo, _tot_len, _ptr, _len) \ 178*3340d773SGleb Smirnoff { \ 179*3340d773SGleb Smirnoff const u_char *_endp; \ 180*3340d773SGleb Smirnoff _len = resp_get_length(_ndo, _ptr, _tot_len, &_endp); \ 181*3340d773SGleb Smirnoff _tot_len -= (_endp - _ptr); \ 182*3340d773SGleb Smirnoff _ptr = _endp; \ 183*3340d773SGleb Smirnoff } 184*3340d773SGleb Smirnoff 185*3340d773SGleb Smirnoff /* 186*3340d773SGleb Smirnoff * TEST_RET_LEN 187*3340d773SGleb Smirnoff * If ret_len is < 0, jump to the trunc tag which returns (-1) 188*3340d773SGleb Smirnoff * and 'bubbles up' to printing tstr. Otherwise, return ret_len. 189*3340d773SGleb Smirnoff */ 190*3340d773SGleb Smirnoff #define TEST_RET_LEN(rl) \ 191*3340d773SGleb Smirnoff if (rl < 0) { goto trunc; } else { return rl; } 192*3340d773SGleb Smirnoff 193*3340d773SGleb Smirnoff /* 194*3340d773SGleb Smirnoff * TEST_RET_LEN_NORETURN 195*3340d773SGleb Smirnoff * If ret_len is < 0, jump to the trunc tag which returns (-1) 196*3340d773SGleb Smirnoff * and 'bubbles up' to printing tstr. Otherwise, continue onward. 197*3340d773SGleb Smirnoff */ 198*3340d773SGleb Smirnoff #define TEST_RET_LEN_NORETURN(rl) \ 199*3340d773SGleb Smirnoff if (rl < 0) { goto trunc; } 200*3340d773SGleb Smirnoff 201*3340d773SGleb Smirnoff /* 202*3340d773SGleb Smirnoff * RESP_PRINT_SEGMENT 203*3340d773SGleb Smirnoff * Prints a segment in the form of: ' "<stuff>"\n" 204*3340d773SGleb Smirnoff * Assumes the data has already been verified as present. 205*3340d773SGleb Smirnoff */ 206*3340d773SGleb Smirnoff #define RESP_PRINT_SEGMENT(_ndo, _bp, _len) \ 207*3340d773SGleb Smirnoff ND_PRINT((_ndo, " \"")); \ 208*3340d773SGleb Smirnoff if (fn_printn(_ndo, _bp, _len, _ndo->ndo_snapend)) \ 209*3340d773SGleb Smirnoff goto trunc; \ 210*3340d773SGleb Smirnoff fn_print_char(_ndo, '"'); 211*3340d773SGleb Smirnoff 212*3340d773SGleb Smirnoff void 213*3340d773SGleb Smirnoff resp_print(netdissect_options *ndo, const u_char *bp, u_int length) 214*3340d773SGleb Smirnoff { 215*3340d773SGleb Smirnoff int ret_len = 0, length_cur = length; 216*3340d773SGleb Smirnoff 217*3340d773SGleb Smirnoff if(!bp || length <= 0) 218*3340d773SGleb Smirnoff return; 219*3340d773SGleb Smirnoff 220*3340d773SGleb Smirnoff ND_PRINT((ndo, ": RESP")); 221*3340d773SGleb Smirnoff while (length_cur > 0) { 222*3340d773SGleb Smirnoff /* 223*3340d773SGleb Smirnoff * This block supports redis pipelining. 224*3340d773SGleb Smirnoff * For example, multiple operations can be pipelined within the same string: 225*3340d773SGleb 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" 226*3340d773SGleb Smirnoff * or 227*3340d773SGleb Smirnoff * "PING\r\nPING\r\nPING\r\n" 228*3340d773SGleb Smirnoff * In order to handle this case, we must try and parse 'bp' until 229*3340d773SGleb Smirnoff * 'length' bytes have been processed or we reach a trunc condition. 230*3340d773SGleb Smirnoff */ 231*3340d773SGleb Smirnoff ret_len = resp_parse(ndo, bp, length_cur); 232*3340d773SGleb Smirnoff TEST_RET_LEN_NORETURN(ret_len); 233*3340d773SGleb Smirnoff bp += ret_len; 234*3340d773SGleb Smirnoff length_cur -= ret_len; 235*3340d773SGleb Smirnoff } 236*3340d773SGleb Smirnoff 237*3340d773SGleb Smirnoff return; 238*3340d773SGleb Smirnoff 239*3340d773SGleb Smirnoff trunc: 240*3340d773SGleb Smirnoff ND_PRINT((ndo, "%s", tstr)); 241*3340d773SGleb Smirnoff } 242*3340d773SGleb Smirnoff 243*3340d773SGleb Smirnoff static int 244*3340d773SGleb Smirnoff resp_parse(netdissect_options *ndo, register const u_char *bp, int length) 245*3340d773SGleb Smirnoff { 246*3340d773SGleb Smirnoff u_char op; 247*3340d773SGleb Smirnoff int ret_len; 248*3340d773SGleb Smirnoff 249*3340d773SGleb Smirnoff LCHECK2(length, 1); 250*3340d773SGleb Smirnoff ND_TCHECK(*bp); 251*3340d773SGleb Smirnoff op = *bp; 252*3340d773SGleb Smirnoff 253*3340d773SGleb Smirnoff /* bp now points to the op, so these routines must skip it */ 254*3340d773SGleb Smirnoff switch(op) { 255*3340d773SGleb Smirnoff case RESP_SIMPLE_STRING: ret_len = resp_print_simple_string(ndo, bp, length); break; 256*3340d773SGleb Smirnoff case RESP_INTEGER: ret_len = resp_print_integer(ndo, bp, length); break; 257*3340d773SGleb Smirnoff case RESP_ERROR: ret_len = resp_print_error(ndo, bp, length); break; 258*3340d773SGleb Smirnoff case RESP_BULK_STRING: ret_len = resp_print_bulk_string(ndo, bp, length); break; 259*3340d773SGleb Smirnoff case RESP_ARRAY: ret_len = resp_print_bulk_array(ndo, bp, length); break; 260*3340d773SGleb Smirnoff default: ret_len = resp_print_inline(ndo, bp, length); break; 261*3340d773SGleb Smirnoff } 262*3340d773SGleb Smirnoff 263*3340d773SGleb Smirnoff /* 264*3340d773SGleb Smirnoff * This gives up with a "truncated" indicator for all errors, 265*3340d773SGleb Smirnoff * including invalid packet errors; that's what we want, as 266*3340d773SGleb Smirnoff * we have to give up on further parsing in that case. 267*3340d773SGleb Smirnoff */ 268*3340d773SGleb Smirnoff TEST_RET_LEN(ret_len); 269*3340d773SGleb Smirnoff 270*3340d773SGleb Smirnoff trunc: 271*3340d773SGleb Smirnoff return (-1); 272*3340d773SGleb Smirnoff } 273*3340d773SGleb Smirnoff 274*3340d773SGleb Smirnoff static int 275*3340d773SGleb Smirnoff resp_print_simple_string(netdissect_options *ndo, register const u_char *bp, int length) { 276*3340d773SGleb Smirnoff return resp_print_string_error_integer(ndo, bp, length); 277*3340d773SGleb Smirnoff } 278*3340d773SGleb Smirnoff 279*3340d773SGleb Smirnoff static int 280*3340d773SGleb Smirnoff resp_print_integer(netdissect_options *ndo, register const u_char *bp, int length) { 281*3340d773SGleb Smirnoff return resp_print_string_error_integer(ndo, bp, length); 282*3340d773SGleb Smirnoff } 283*3340d773SGleb Smirnoff 284*3340d773SGleb Smirnoff static int 285*3340d773SGleb Smirnoff resp_print_error(netdissect_options *ndo, register const u_char *bp, int length) { 286*3340d773SGleb Smirnoff return resp_print_string_error_integer(ndo, bp, length); 287*3340d773SGleb Smirnoff } 288*3340d773SGleb Smirnoff 289*3340d773SGleb Smirnoff static int 290*3340d773SGleb Smirnoff resp_print_string_error_integer(netdissect_options *ndo, register const u_char *bp, int length) { 291*3340d773SGleb Smirnoff int length_cur = length, len, ret_len; 292*3340d773SGleb Smirnoff const u_char *bp_ptr; 293*3340d773SGleb Smirnoff 294*3340d773SGleb Smirnoff /* bp points to the op; skip it */ 295*3340d773SGleb Smirnoff SKIP_OPCODE(bp, length_cur); 296*3340d773SGleb Smirnoff bp_ptr = bp; 297*3340d773SGleb Smirnoff 298*3340d773SGleb Smirnoff /* 299*3340d773SGleb Smirnoff * bp now prints past the (+-;) opcode, so it's pointing to the first 300*3340d773SGleb Smirnoff * character of the string (which could be numeric). 301*3340d773SGleb Smirnoff * +OK\r\n 302*3340d773SGleb Smirnoff * -ERR ...\r\n 303*3340d773SGleb Smirnoff * :02912309\r\n 304*3340d773SGleb Smirnoff * 305*3340d773SGleb Smirnoff * Find the \r\n with FIND_CRLF(). 306*3340d773SGleb Smirnoff */ 307*3340d773SGleb Smirnoff FIND_CRLF(bp_ptr, length_cur); 308*3340d773SGleb Smirnoff 309*3340d773SGleb Smirnoff /* 310*3340d773SGleb Smirnoff * bp_ptr points to the \r\n, so bp_ptr - bp is the length of text 311*3340d773SGleb Smirnoff * preceding the \r\n. That includes the opcode, so don't print 312*3340d773SGleb Smirnoff * that. 313*3340d773SGleb Smirnoff */ 314*3340d773SGleb Smirnoff len = (bp_ptr - bp); 315*3340d773SGleb Smirnoff RESP_PRINT_SEGMENT(ndo, bp, len); 316*3340d773SGleb Smirnoff ret_len = 1 /*<opcode>*/ + len /*<string>*/ + 2 /*<CRLF>*/; 317*3340d773SGleb Smirnoff 318*3340d773SGleb Smirnoff TEST_RET_LEN(ret_len); 319*3340d773SGleb Smirnoff 320*3340d773SGleb Smirnoff trunc: 321*3340d773SGleb Smirnoff return (-1); 322*3340d773SGleb Smirnoff } 323*3340d773SGleb Smirnoff 324*3340d773SGleb Smirnoff static int 325*3340d773SGleb Smirnoff resp_print_bulk_string(netdissect_options *ndo, register const u_char *bp, int length) { 326*3340d773SGleb Smirnoff int length_cur = length, string_len; 327*3340d773SGleb Smirnoff 328*3340d773SGleb Smirnoff /* bp points to the op; skip it */ 329*3340d773SGleb Smirnoff SKIP_OPCODE(bp, length_cur); 330*3340d773SGleb Smirnoff 331*3340d773SGleb Smirnoff /* <length>\r\n */ 332*3340d773SGleb Smirnoff GET_LENGTH(ndo, length_cur, bp, string_len); 333*3340d773SGleb Smirnoff 334*3340d773SGleb Smirnoff if (string_len >= 0) { 335*3340d773SGleb Smirnoff /* Byte string of length string_len, starting at bp */ 336*3340d773SGleb Smirnoff if (string_len == 0) 337*3340d773SGleb Smirnoff resp_print_empty(ndo); 338*3340d773SGleb Smirnoff else { 339*3340d773SGleb Smirnoff LCHECK2(length_cur, string_len); 340*3340d773SGleb Smirnoff ND_TCHECK2(*bp, string_len); 341*3340d773SGleb Smirnoff RESP_PRINT_SEGMENT(ndo, bp, string_len); 342*3340d773SGleb Smirnoff bp += string_len; 343*3340d773SGleb Smirnoff length_cur -= string_len; 344*3340d773SGleb Smirnoff } 345*3340d773SGleb Smirnoff 346*3340d773SGleb Smirnoff /* 347*3340d773SGleb Smirnoff * Find the \r\n at the end of the string and skip past it. 348*3340d773SGleb Smirnoff * XXX - report an error if the \r\n isn't immediately after 349*3340d773SGleb Smirnoff * the item? 350*3340d773SGleb Smirnoff */ 351*3340d773SGleb Smirnoff FIND_CRLF(bp, length_cur); 352*3340d773SGleb Smirnoff CONSUME_CRLF(bp, length_cur); 353*3340d773SGleb Smirnoff } else { 354*3340d773SGleb Smirnoff /* null, truncated, or invalid for some reason */ 355*3340d773SGleb Smirnoff switch(string_len) { 356*3340d773SGleb Smirnoff case (-1): resp_print_null(ndo); break; 357*3340d773SGleb Smirnoff case (-2): goto trunc; 358*3340d773SGleb Smirnoff case (-3): resp_print_length_too_large(ndo); break; 359*3340d773SGleb Smirnoff case (-4): resp_print_length_negative(ndo); break; 360*3340d773SGleb Smirnoff default: resp_print_invalid(ndo); break; 361*3340d773SGleb Smirnoff } 362*3340d773SGleb Smirnoff } 363*3340d773SGleb Smirnoff 364*3340d773SGleb Smirnoff return (length - length_cur); 365*3340d773SGleb Smirnoff 366*3340d773SGleb Smirnoff trunc: 367*3340d773SGleb Smirnoff return (-1); 368*3340d773SGleb Smirnoff } 369*3340d773SGleb Smirnoff 370*3340d773SGleb Smirnoff static int 371*3340d773SGleb Smirnoff resp_print_bulk_array(netdissect_options *ndo, register const u_char *bp, int length) { 372*3340d773SGleb Smirnoff u_int length_cur = length; 373*3340d773SGleb Smirnoff int array_len, i, ret_len; 374*3340d773SGleb Smirnoff 375*3340d773SGleb Smirnoff /* bp points to the op; skip it */ 376*3340d773SGleb Smirnoff SKIP_OPCODE(bp, length_cur); 377*3340d773SGleb Smirnoff 378*3340d773SGleb Smirnoff /* <array_length>\r\n */ 379*3340d773SGleb Smirnoff GET_LENGTH(ndo, length_cur, bp, array_len); 380*3340d773SGleb Smirnoff 381*3340d773SGleb Smirnoff if (array_len > 0) { 382*3340d773SGleb Smirnoff /* non empty array */ 383*3340d773SGleb Smirnoff for (i = 0; i < array_len; i++) { 384*3340d773SGleb Smirnoff ret_len = resp_parse(ndo, bp, length_cur); 385*3340d773SGleb Smirnoff 386*3340d773SGleb Smirnoff TEST_RET_LEN_NORETURN(ret_len); 387*3340d773SGleb Smirnoff 388*3340d773SGleb Smirnoff bp += ret_len; 389*3340d773SGleb Smirnoff length_cur -= ret_len; 390*3340d773SGleb Smirnoff } 391*3340d773SGleb Smirnoff } else { 392*3340d773SGleb Smirnoff /* empty, null, truncated, or invalid */ 393*3340d773SGleb Smirnoff switch(array_len) { 394*3340d773SGleb Smirnoff case 0: resp_print_empty(ndo); break; 395*3340d773SGleb Smirnoff case (-1): resp_print_null(ndo); break; 396*3340d773SGleb Smirnoff case (-2): goto trunc; 397*3340d773SGleb Smirnoff case (-3): resp_print_length_too_large(ndo); break; 398*3340d773SGleb Smirnoff case (-4): resp_print_length_negative(ndo); break; 399*3340d773SGleb Smirnoff default: resp_print_invalid(ndo); break; 400*3340d773SGleb Smirnoff } 401*3340d773SGleb Smirnoff } 402*3340d773SGleb Smirnoff 403*3340d773SGleb Smirnoff return (length - length_cur); 404*3340d773SGleb Smirnoff 405*3340d773SGleb Smirnoff trunc: 406*3340d773SGleb Smirnoff return (-1); 407*3340d773SGleb Smirnoff } 408*3340d773SGleb Smirnoff 409*3340d773SGleb Smirnoff static int 410*3340d773SGleb Smirnoff resp_print_inline(netdissect_options *ndo, register const u_char *bp, int length) { 411*3340d773SGleb Smirnoff int length_cur = length; 412*3340d773SGleb Smirnoff int len; 413*3340d773SGleb Smirnoff const u_char *bp_ptr; 414*3340d773SGleb Smirnoff 415*3340d773SGleb Smirnoff /* 416*3340d773SGleb Smirnoff * Inline commands are simply 'strings' followed by \r or \n or both. 417*3340d773SGleb Smirnoff * Redis will do its best to split/parse these strings. 418*3340d773SGleb Smirnoff * This feature of redis is implemented to support the ability of 419*3340d773SGleb Smirnoff * command parsing from telnet/nc sessions etc. 420*3340d773SGleb Smirnoff * 421*3340d773SGleb Smirnoff * <string><\r||\n||\r\n...> 422*3340d773SGleb Smirnoff */ 423*3340d773SGleb Smirnoff 424*3340d773SGleb Smirnoff /* 425*3340d773SGleb Smirnoff * Skip forward past any leading \r, \n, or \r\n. 426*3340d773SGleb Smirnoff */ 427*3340d773SGleb Smirnoff CONSUME_CR_OR_LF(bp, length_cur); 428*3340d773SGleb Smirnoff bp_ptr = bp; 429*3340d773SGleb Smirnoff 430*3340d773SGleb Smirnoff /* 431*3340d773SGleb Smirnoff * Scan forward looking for \r or \n. 432*3340d773SGleb Smirnoff */ 433*3340d773SGleb Smirnoff FIND_CR_OR_LF(bp_ptr, length_cur); 434*3340d773SGleb Smirnoff 435*3340d773SGleb Smirnoff /* 436*3340d773SGleb Smirnoff * Found it; bp_ptr points to the \r or \n, so bp_ptr - bp is the 437*3340d773SGleb Smirnoff * Length of the line text that preceeds it. Print it. 438*3340d773SGleb Smirnoff */ 439*3340d773SGleb Smirnoff len = (bp_ptr - bp); 440*3340d773SGleb Smirnoff RESP_PRINT_SEGMENT(ndo, bp, len); 441*3340d773SGleb Smirnoff 442*3340d773SGleb Smirnoff /* 443*3340d773SGleb Smirnoff * Skip forward past the \r, \n, or \r\n. 444*3340d773SGleb Smirnoff */ 445*3340d773SGleb Smirnoff CONSUME_CR_OR_LF(bp_ptr, length_cur); 446*3340d773SGleb Smirnoff 447*3340d773SGleb Smirnoff /* 448*3340d773SGleb Smirnoff * Return the number of bytes we processed. 449*3340d773SGleb Smirnoff */ 450*3340d773SGleb Smirnoff return (length - length_cur); 451*3340d773SGleb Smirnoff 452*3340d773SGleb Smirnoff trunc: 453*3340d773SGleb Smirnoff return (-1); 454*3340d773SGleb Smirnoff } 455*3340d773SGleb Smirnoff 456*3340d773SGleb Smirnoff static int 457*3340d773SGleb Smirnoff resp_get_length(netdissect_options *ndo, register const u_char *bp, int len, const u_char **endp) 458*3340d773SGleb Smirnoff { 459*3340d773SGleb Smirnoff int result; 460*3340d773SGleb Smirnoff u_char c; 461*3340d773SGleb Smirnoff int saw_digit; 462*3340d773SGleb Smirnoff int neg; 463*3340d773SGleb Smirnoff int too_large; 464*3340d773SGleb Smirnoff 465*3340d773SGleb Smirnoff if (len == 0) 466*3340d773SGleb Smirnoff goto trunc; 467*3340d773SGleb Smirnoff ND_TCHECK(*bp); 468*3340d773SGleb Smirnoff too_large = 0; 469*3340d773SGleb Smirnoff neg = 0; 470*3340d773SGleb Smirnoff if (*bp == '-') { 471*3340d773SGleb Smirnoff neg = 1; 472*3340d773SGleb Smirnoff bp++; 473*3340d773SGleb Smirnoff len--; 474*3340d773SGleb Smirnoff } 475*3340d773SGleb Smirnoff result = 0; 476*3340d773SGleb Smirnoff saw_digit = 0; 477*3340d773SGleb Smirnoff 478*3340d773SGleb Smirnoff for (;;) { 479*3340d773SGleb Smirnoff if (len == 0) 480*3340d773SGleb Smirnoff goto trunc; 481*3340d773SGleb Smirnoff ND_TCHECK(*bp); 482*3340d773SGleb Smirnoff c = *bp; 483*3340d773SGleb Smirnoff if (!(c >= '0' && c <= '9')) { 484*3340d773SGleb Smirnoff if (!saw_digit) 485*3340d773SGleb Smirnoff goto invalid; 486*3340d773SGleb Smirnoff break; 487*3340d773SGleb Smirnoff } 488*3340d773SGleb Smirnoff c -= '0'; 489*3340d773SGleb Smirnoff if (result > (INT_MAX / 10)) { 490*3340d773SGleb Smirnoff /* This will overflow an int when we multiply it by 10. */ 491*3340d773SGleb Smirnoff too_large = 1; 492*3340d773SGleb Smirnoff } else { 493*3340d773SGleb Smirnoff result *= 10; 494*3340d773SGleb Smirnoff if (result == INT_MAX && c > (INT_MAX % 10)) { 495*3340d773SGleb Smirnoff /* This will overflow an int when we add c */ 496*3340d773SGleb Smirnoff too_large = 1; 497*3340d773SGleb Smirnoff } else 498*3340d773SGleb Smirnoff result += c; 499*3340d773SGleb Smirnoff } 500*3340d773SGleb Smirnoff bp++; 501*3340d773SGleb Smirnoff len--; 502*3340d773SGleb Smirnoff saw_digit = 1; 503*3340d773SGleb Smirnoff } 504*3340d773SGleb Smirnoff if (!saw_digit) 505*3340d773SGleb Smirnoff goto invalid; 506*3340d773SGleb Smirnoff 507*3340d773SGleb Smirnoff /* 508*3340d773SGleb Smirnoff * OK, the next thing should be \r\n. 509*3340d773SGleb Smirnoff */ 510*3340d773SGleb Smirnoff if (len == 0) 511*3340d773SGleb Smirnoff goto trunc; 512*3340d773SGleb Smirnoff ND_TCHECK(*bp); 513*3340d773SGleb Smirnoff if (*bp != '\r') 514*3340d773SGleb Smirnoff goto invalid; 515*3340d773SGleb Smirnoff bp++; 516*3340d773SGleb Smirnoff len--; 517*3340d773SGleb Smirnoff if (len == 0) 518*3340d773SGleb Smirnoff goto trunc; 519*3340d773SGleb Smirnoff ND_TCHECK(*bp); 520*3340d773SGleb Smirnoff if (*bp != '\n') 521*3340d773SGleb Smirnoff goto invalid; 522*3340d773SGleb Smirnoff bp++; 523*3340d773SGleb Smirnoff len--; 524*3340d773SGleb Smirnoff *endp = bp; 525*3340d773SGleb Smirnoff if (neg) { 526*3340d773SGleb Smirnoff /* -1 means "null", anything else is invalid */ 527*3340d773SGleb Smirnoff if (too_large || result != 1) 528*3340d773SGleb Smirnoff return (-4); 529*3340d773SGleb Smirnoff result = -1; 530*3340d773SGleb Smirnoff } 531*3340d773SGleb Smirnoff return (too_large ? -3 : result); 532*3340d773SGleb Smirnoff 533*3340d773SGleb Smirnoff trunc: 534*3340d773SGleb Smirnoff return (-2); 535*3340d773SGleb Smirnoff 536*3340d773SGleb Smirnoff invalid: 537*3340d773SGleb Smirnoff return (-5); 538*3340d773SGleb Smirnoff } 539