xref: /freebsd/contrib/tcpdump/print-resp.c (revision 3340d77368116708ab5b5b95acf6c9c710528300)
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