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