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