xref: /netbsd-src/external/bsd/tcpdump/dist/print-resp.c (revision 26ba0b503b498a5194a71ac319838b7f5497f3fe)
1784088dfSchristos /*
2784088dfSchristos  * Copyright (c) 2015 The TCPDUMP project
3784088dfSchristos  * All rights reserved.
4784088dfSchristos  *
5784088dfSchristos  * Redistribution and use in source and binary forms, with or without
6784088dfSchristos  * modification, are permitted provided that the following conditions
7784088dfSchristos  * are met:
8784088dfSchristos  * 1. Redistributions of source code must retain the above copyright
9784088dfSchristos  *    notice, this list of conditions and the following disclaimer.
10784088dfSchristos  * 2. Redistributions in binary form must reproduce the above copyright
11784088dfSchristos  *    notice, this list of conditions and the following disclaimer in the
12784088dfSchristos  *    documentation and/or other materials provided with the distribution.
13784088dfSchristos  *
14784088dfSchristos  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15784088dfSchristos  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16784088dfSchristos  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
17784088dfSchristos  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
18784088dfSchristos  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
19784088dfSchristos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20784088dfSchristos  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21784088dfSchristos  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22784088dfSchristos  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23784088dfSchristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24784088dfSchristos  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25784088dfSchristos  * POSSIBILITY OF SUCH DAMAGE.
26784088dfSchristos  *
27784088dfSchristos  * Initial contribution by Andrew Darqui (andrew.darqui@gmail.com).
28784088dfSchristos  */
29784088dfSchristos 
30fdccd7e4Schristos #include <sys/cdefs.h>
31fdccd7e4Schristos #ifndef lint
32*26ba0b50Schristos __RCSID("$NetBSD: print-resp.c,v 1.6 2024/09/02 16:15:32 christos Exp $");
33fdccd7e4Schristos #endif
34fdccd7e4Schristos 
35dc860a36Sspz /* \summary: REdis Serialization Protocol (RESP) printer */
36dc860a36Sspz 
37c74ad251Schristos #include <config.h>
38784088dfSchristos 
39c74ad251Schristos #include "netdissect-stdinc.h"
40784088dfSchristos #include "netdissect.h"
41784088dfSchristos #include <limits.h>
42784088dfSchristos 
43784088dfSchristos #include "extract.h"
44784088dfSchristos 
45784088dfSchristos 
46784088dfSchristos /*
47c74ad251Schristos  * For information regarding RESP, see: https://redis.io/topics/protocol
48784088dfSchristos  */
49784088dfSchristos 
50784088dfSchristos #define RESP_SIMPLE_STRING    '+'
51784088dfSchristos #define RESP_ERROR            '-'
52784088dfSchristos #define RESP_INTEGER          ':'
53784088dfSchristos #define RESP_BULK_STRING      '$'
54784088dfSchristos #define RESP_ARRAY            '*'
55784088dfSchristos 
56c74ad251Schristos #define resp_print_empty(ndo)            ND_PRINT(" empty")
57c74ad251Schristos #define resp_print_null(ndo)             ND_PRINT(" null")
58c74ad251Schristos #define resp_print_length_too_large(ndo) ND_PRINT(" length too large")
59c74ad251Schristos #define resp_print_length_negative(ndo)  ND_PRINT(" length negative and not -1")
60c74ad251Schristos #define resp_print_invalid(ndo)          ND_PRINT(" invalid")
61784088dfSchristos 
62c74ad251Schristos static int resp_parse(netdissect_options *, const u_char *, int);
63c74ad251Schristos static int resp_print_string_error_integer(netdissect_options *, const u_char *, int);
64c74ad251Schristos static int resp_print_simple_string(netdissect_options *, const u_char *, int);
65c74ad251Schristos static int resp_print_integer(netdissect_options *, const u_char *, int);
66c74ad251Schristos static int resp_print_error(netdissect_options *, const u_char *, int);
67c74ad251Schristos static int resp_print_bulk_string(netdissect_options *, const u_char *, int);
68c74ad251Schristos static int resp_print_bulk_array(netdissect_options *, const u_char *, int);
69c74ad251Schristos static int resp_print_inline(netdissect_options *, const u_char *, int);
70c74ad251Schristos static int resp_get_length(netdissect_options *, const u_char *, int, const u_char **);
71dc860a36Sspz 
72dc860a36Sspz #define LCHECK2(_tot_len, _len) \
73dc860a36Sspz     {                           \
74dc860a36Sspz         if (_tot_len < _len)    \
75dc860a36Sspz             goto trunc;         \
76dc860a36Sspz     }
77dc860a36Sspz 
78dc860a36Sspz #define LCHECK(_tot_len) LCHECK2(_tot_len, 1)
79784088dfSchristos 
80784088dfSchristos /*
81dc860a36Sspz  * FIND_CRLF:
82784088dfSchristos  * Attempts to move our 'ptr' forward until a \r\n is found,
83dc860a36Sspz  * while also making sure we don't exceed the buffer '_len'
84dc860a36Sspz  * or go past the end of the captured data.
85dc860a36Sspz  * If we exceed or go past the end of the captured data,
86dc860a36Sspz  * jump to trunc.
87784088dfSchristos  */
88dc860a36Sspz #define FIND_CRLF(_ptr, _len)                   \
89dc860a36Sspz     for (;;) {                                  \
90dc860a36Sspz         LCHECK2(_len, 2);                       \
91c74ad251Schristos         ND_TCHECK_2(_ptr);                      \
92c74ad251Schristos         if (GET_U_1(_ptr) == '\r' &&            \
93c74ad251Schristos             GET_U_1(_ptr+1) == '\n')            \
94dc860a36Sspz             break;                              \
95dc860a36Sspz         _ptr++;                                 \
96dc860a36Sspz         _len--;                                 \
97dc860a36Sspz     }
98784088dfSchristos 
99784088dfSchristos /*
100dc860a36Sspz  * CONSUME_CRLF
101dc860a36Sspz  * Consume a CRLF that we've just found.
102784088dfSchristos  */
103dc860a36Sspz #define CONSUME_CRLF(_ptr, _len) \
104dc860a36Sspz     _ptr += 2;                   \
105dc860a36Sspz     _len -= 2;
106dc860a36Sspz 
107dc860a36Sspz /*
108dc860a36Sspz  * FIND_CR_OR_LF
109dc860a36Sspz  * Attempts to move our '_ptr' forward until a \r or \n is found,
110dc860a36Sspz  * while also making sure we don't exceed the buffer '_len'
111dc860a36Sspz  * or go past the end of the captured data.
112dc860a36Sspz  * If we exceed or go past the end of the captured data,
113dc860a36Sspz  * jump to trunc.
114dc860a36Sspz  */
115dc860a36Sspz #define FIND_CR_OR_LF(_ptr, _len)           \
116dc860a36Sspz     for (;;) {                              \
117dc860a36Sspz         LCHECK(_len);                       \
118c74ad251Schristos         if (GET_U_1(_ptr) == '\r' ||        \
119c74ad251Schristos             GET_U_1(_ptr) == '\n')          \
120dc860a36Sspz             break;                          \
121dc860a36Sspz         _ptr++;                             \
122dc860a36Sspz         _len--;                             \
123dc860a36Sspz     }
124784088dfSchristos 
125784088dfSchristos /*
126784088dfSchristos  * CONSUME_CR_OR_LF
127784088dfSchristos  * Consume all consecutive \r and \n bytes.
128dc860a36Sspz  * If we exceed '_len' or go past the end of the captured data,
129dc860a36Sspz  * jump to trunc.
130784088dfSchristos  */
131dc860a36Sspz #define CONSUME_CR_OR_LF(_ptr, _len)             \
132dc860a36Sspz     {                                            \
133dc860a36Sspz         int _found_cr_or_lf = 0;                 \
134dc860a36Sspz         for (;;) {                               \
135dc860a36Sspz             /*                                   \
136dc860a36Sspz              * Have we hit the end of data?      \
137dc860a36Sspz              */                                  \
138c74ad251Schristos             if (_len == 0 || !ND_TTEST_1(_ptr)) {\
139dc860a36Sspz                 /*                               \
140dc860a36Sspz                  * Yes.  Have we seen a \r       \
141dc860a36Sspz                  * or \n?                        \
142dc860a36Sspz                  */                              \
143dc860a36Sspz                 if (_found_cr_or_lf) {           \
144dc860a36Sspz                     /*                           \
145dc860a36Sspz                      * Yes.  Just stop.          \
146dc860a36Sspz                      */                          \
147dc860a36Sspz                     break;                       \
148dc860a36Sspz                 }                                \
149dc860a36Sspz                 /*                               \
150dc860a36Sspz                  * No.  We ran out of packet.    \
151dc860a36Sspz                  */                              \
152dc860a36Sspz                 goto trunc;                      \
153dc860a36Sspz             }                                    \
154c74ad251Schristos             if (GET_U_1(_ptr) != '\r' &&         \
155c74ad251Schristos                 GET_U_1(_ptr) != '\n')           \
156dc860a36Sspz                 break;                           \
157dc860a36Sspz             _found_cr_or_lf = 1;                 \
158dc860a36Sspz             _ptr++;                              \
159dc860a36Sspz             _len--;                              \
160dc860a36Sspz         }                                        \
161dc860a36Sspz     }
162784088dfSchristos 
163784088dfSchristos /*
164dc860a36Sspz  * SKIP_OPCODE
165dc860a36Sspz  * Skip over the opcode character.
166dc860a36Sspz  * The opcode has already been fetched, so we know it's there, and don't
167dc860a36Sspz  * need to do any checks.
168784088dfSchristos  */
169dc860a36Sspz #define SKIP_OPCODE(_ptr, _tot_len) \
170dc860a36Sspz     _ptr++;                         \
171dc860a36Sspz     _tot_len--;
172784088dfSchristos 
173784088dfSchristos /*
174dc860a36Sspz  * GET_LENGTH
175dc860a36Sspz  * Get a bulk string or array length.
176784088dfSchristos  */
177dc860a36Sspz #define GET_LENGTH(_ndo, _tot_len, _ptr, _len)                \
178dc860a36Sspz     {                                                         \
179dc860a36Sspz         const u_char *_endp;                                  \
180dc860a36Sspz         _len = resp_get_length(_ndo, _ptr, _tot_len, &_endp); \
181dc860a36Sspz         _tot_len -= (_endp - _ptr);                           \
182dc860a36Sspz         _ptr = _endp;                                         \
183dc860a36Sspz     }
184784088dfSchristos 
185784088dfSchristos /*
186784088dfSchristos  * TEST_RET_LEN
187784088dfSchristos  * If ret_len is < 0, jump to the trunc tag which returns (-1)
188784088dfSchristos  * and 'bubbles up' to printing tstr. Otherwise, return ret_len.
189784088dfSchristos  */
190784088dfSchristos #define TEST_RET_LEN(rl) \
191784088dfSchristos     if (rl < 0) { goto trunc; } else { return rl; }
192784088dfSchristos 
193784088dfSchristos /*
194784088dfSchristos  * TEST_RET_LEN_NORETURN
195784088dfSchristos  * If ret_len is < 0, jump to the trunc tag which returns (-1)
196784088dfSchristos  * and 'bubbles up' to printing tstr. Otherwise, continue onward.
197784088dfSchristos  */
198784088dfSchristos #define TEST_RET_LEN_NORETURN(rl) \
199784088dfSchristos     if (rl < 0) { goto trunc; }
200784088dfSchristos 
201784088dfSchristos /*
202784088dfSchristos  * RESP_PRINT_SEGMENT
203784088dfSchristos  * Prints a segment in the form of: ' "<stuff>"\n"
204dc860a36Sspz  * Assumes the data has already been verified as present.
205784088dfSchristos  */
206784088dfSchristos #define RESP_PRINT_SEGMENT(_ndo, _bp, _len)            \
207c74ad251Schristos     ND_PRINT(" \"");                                   \
208c74ad251Schristos     if (nd_printn(_ndo, _bp, _len, _ndo->ndo_snapend)) \
209784088dfSchristos         goto trunc;                                    \
210784088dfSchristos     fn_print_char(_ndo, '"');
211784088dfSchristos 
212784088dfSchristos void
213784088dfSchristos resp_print(netdissect_options *ndo, const u_char *bp, u_int length)
214784088dfSchristos {
215c74ad251Schristos     int ret_len = 0;
216784088dfSchristos 
217c74ad251Schristos     ndo->ndo_protocol = "resp";
218784088dfSchristos 
219c74ad251Schristos     ND_PRINT(": RESP");
220c74ad251Schristos     while (length > 0) {
221784088dfSchristos         /*
222784088dfSchristos          * This block supports redis pipelining.
223784088dfSchristos          * For example, multiple operations can be pipelined within the same string:
224784088dfSchristos          * "*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"
225784088dfSchristos          * or
226784088dfSchristos          * "PING\r\nPING\r\nPING\r\n"
227784088dfSchristos          * In order to handle this case, we must try and parse 'bp' until
228784088dfSchristos          * 'length' bytes have been processed or we reach a trunc condition.
229784088dfSchristos          */
230c74ad251Schristos         ret_len = resp_parse(ndo, bp, length);
231784088dfSchristos         TEST_RET_LEN_NORETURN(ret_len);
232784088dfSchristos         bp += ret_len;
233c74ad251Schristos         length -= ret_len;
234784088dfSchristos     }
235784088dfSchristos 
236784088dfSchristos     return;
237784088dfSchristos 
238784088dfSchristos trunc:
239c74ad251Schristos     nd_print_trunc(ndo);
240784088dfSchristos }
241784088dfSchristos 
242784088dfSchristos static int
243c74ad251Schristos resp_parse(netdissect_options *ndo, const u_char *bp, int length)
244784088dfSchristos {
245dc860a36Sspz     u_char op;
246dc860a36Sspz     int ret_len;
247784088dfSchristos 
248dc860a36Sspz     LCHECK2(length, 1);
249c74ad251Schristos     op = GET_U_1(bp);
250784088dfSchristos 
251dc860a36Sspz     /* bp now points to the op, so these routines must skip it */
252784088dfSchristos     switch(op) {
253784088dfSchristos         case RESP_SIMPLE_STRING:  ret_len = resp_print_simple_string(ndo, bp, length);   break;
254784088dfSchristos         case RESP_INTEGER:        ret_len = resp_print_integer(ndo, bp, length);         break;
255784088dfSchristos         case RESP_ERROR:          ret_len = resp_print_error(ndo, bp, length);           break;
256784088dfSchristos         case RESP_BULK_STRING:    ret_len = resp_print_bulk_string(ndo, bp, length);     break;
257784088dfSchristos         case RESP_ARRAY:          ret_len = resp_print_bulk_array(ndo, bp, length);      break;
258784088dfSchristos         default:                  ret_len = resp_print_inline(ndo, bp, length);          break;
259784088dfSchristos     }
260784088dfSchristos 
261dc860a36Sspz     /*
262dc860a36Sspz      * This gives up with a "truncated" indicator for all errors,
263dc860a36Sspz      * including invalid packet errors; that's what we want, as
264dc860a36Sspz      * we have to give up on further parsing in that case.
265dc860a36Sspz      */
266784088dfSchristos     TEST_RET_LEN(ret_len);
267784088dfSchristos 
268784088dfSchristos trunc:
269784088dfSchristos     return (-1);
270784088dfSchristos }
271784088dfSchristos 
272784088dfSchristos static int
273c74ad251Schristos resp_print_simple_string(netdissect_options *ndo, const u_char *bp, int length) {
274784088dfSchristos     return resp_print_string_error_integer(ndo, bp, length);
275784088dfSchristos }
276784088dfSchristos 
277784088dfSchristos static int
278c74ad251Schristos resp_print_integer(netdissect_options *ndo, const u_char *bp, int length) {
279784088dfSchristos     return resp_print_string_error_integer(ndo, bp, length);
280784088dfSchristos }
281784088dfSchristos 
282784088dfSchristos static int
283c74ad251Schristos resp_print_error(netdissect_options *ndo, const u_char *bp, int length) {
284784088dfSchristos     return resp_print_string_error_integer(ndo, bp, length);
285784088dfSchristos }
286784088dfSchristos 
287784088dfSchristos static int
288c74ad251Schristos resp_print_string_error_integer(netdissect_options *ndo, const u_char *bp, int length) {
289dc860a36Sspz     int length_cur = length, len, ret_len;
290dc860a36Sspz     const u_char *bp_ptr;
291dc860a36Sspz 
292dc860a36Sspz     /* bp points to the op; skip it */
293dc860a36Sspz     SKIP_OPCODE(bp, length_cur);
294dc860a36Sspz     bp_ptr = bp;
295784088dfSchristos 
296784088dfSchristos     /*
297dc860a36Sspz      * bp now prints past the (+-;) opcode, so it's pointing to the first
298dc860a36Sspz      * character of the string (which could be numeric).
299784088dfSchristos      * +OK\r\n
300784088dfSchristos      * -ERR ...\r\n
301784088dfSchristos      * :02912309\r\n
302dc860a36Sspz      *
303dc860a36Sspz      * Find the \r\n with FIND_CRLF().
304784088dfSchristos      */
305dc860a36Sspz     FIND_CRLF(bp_ptr, length_cur);
306dc860a36Sspz 
307dc860a36Sspz     /*
308dc860a36Sspz      * bp_ptr points to the \r\n, so bp_ptr - bp is the length of text
309dc860a36Sspz      * preceding the \r\n.  That includes the opcode, so don't print
310dc860a36Sspz      * that.
311dc860a36Sspz      */
312*26ba0b50Schristos     len = ND_BYTES_BETWEEN(bp, bp_ptr);
313dc860a36Sspz     RESP_PRINT_SEGMENT(ndo, bp, len);
314dc860a36Sspz     ret_len = 1 /*<opcode>*/ + len /*<string>*/ + 2 /*<CRLF>*/;
315784088dfSchristos 
316784088dfSchristos     TEST_RET_LEN(ret_len);
317784088dfSchristos 
318784088dfSchristos trunc:
319784088dfSchristos     return (-1);
320784088dfSchristos }
321784088dfSchristos 
322784088dfSchristos static int
323c74ad251Schristos resp_print_bulk_string(netdissect_options *ndo, const u_char *bp, int length) {
324784088dfSchristos     int length_cur = length, string_len;
325784088dfSchristos 
326dc860a36Sspz     /* bp points to the op; skip it */
327dc860a36Sspz     SKIP_OPCODE(bp, length_cur);
328784088dfSchristos 
329dc860a36Sspz     /* <length>\r\n */
330dc860a36Sspz     GET_LENGTH(ndo, length_cur, bp, string_len);
331784088dfSchristos 
332dc860a36Sspz     if (string_len >= 0) {
333dc860a36Sspz         /* Byte string of length string_len, starting at bp */
334dc860a36Sspz         if (string_len == 0)
335dc860a36Sspz             resp_print_empty(ndo);
336dc860a36Sspz         else {
337dc860a36Sspz             LCHECK2(length_cur, string_len);
338c74ad251Schristos             ND_TCHECK_LEN(bp, string_len);
339784088dfSchristos             RESP_PRINT_SEGMENT(ndo, bp, string_len);
340dc860a36Sspz             bp += string_len;
341dc860a36Sspz             length_cur -= string_len;
342784088dfSchristos         }
343dc860a36Sspz 
344dc860a36Sspz         /*
345dc860a36Sspz          * Find the \r\n at the end of the string and skip past it.
346dc860a36Sspz          * XXX - report an error if the \r\n isn't immediately after
347dc860a36Sspz          * the item?
348dc860a36Sspz          */
349dc860a36Sspz         FIND_CRLF(bp, length_cur);
350dc860a36Sspz         CONSUME_CRLF(bp, length_cur);
351dc860a36Sspz     } else {
352dc860a36Sspz         /* null, truncated, or invalid for some reason */
353dc860a36Sspz         switch(string_len) {
354dc860a36Sspz             case (-1):  resp_print_null(ndo);             break;
355dc860a36Sspz             case (-2):  goto trunc;
356dc860a36Sspz             case (-3):  resp_print_length_too_large(ndo); break;
357dc860a36Sspz             case (-4):  resp_print_length_negative(ndo);  break;
358784088dfSchristos             default:    resp_print_invalid(ndo);          break;
359784088dfSchristos         }
360784088dfSchristos     }
361784088dfSchristos 
362dc860a36Sspz     return (length - length_cur);
363784088dfSchristos 
364784088dfSchristos trunc:
365784088dfSchristos     return (-1);
366784088dfSchristos }
367784088dfSchristos 
368784088dfSchristos static int
369c74ad251Schristos resp_print_bulk_array(netdissect_options *ndo, const u_char *bp, int length) {
370dc860a36Sspz     u_int length_cur = length;
371dc860a36Sspz     int array_len, i, ret_len;
372784088dfSchristos 
373dc860a36Sspz     /* bp points to the op; skip it */
374dc860a36Sspz     SKIP_OPCODE(bp, length_cur);
375784088dfSchristos 
376dc860a36Sspz     /* <array_length>\r\n */
377dc860a36Sspz     GET_LENGTH(ndo, length_cur, bp, array_len);
378784088dfSchristos 
379784088dfSchristos     if (array_len > 0) {
380784088dfSchristos         /* non empty array */
381784088dfSchristos         for (i = 0; i < array_len; i++) {
382784088dfSchristos             ret_len = resp_parse(ndo, bp, length_cur);
383784088dfSchristos 
384784088dfSchristos             TEST_RET_LEN_NORETURN(ret_len);
385784088dfSchristos 
386784088dfSchristos             bp += ret_len;
387784088dfSchristos             length_cur -= ret_len;
388784088dfSchristos         }
389784088dfSchristos     } else {
390dc860a36Sspz         /* empty, null, truncated, or invalid */
391784088dfSchristos         switch(array_len) {
392784088dfSchristos             case 0:     resp_print_empty(ndo);            break;
393784088dfSchristos             case (-1):  resp_print_null(ndo);             break;
394dc860a36Sspz             case (-2):  goto trunc;
395dc860a36Sspz             case (-3):  resp_print_length_too_large(ndo); break;
396dc860a36Sspz             case (-4):  resp_print_length_negative(ndo);  break;
397784088dfSchristos             default:    resp_print_invalid(ndo);          break;
398784088dfSchristos         }
399784088dfSchristos     }
400784088dfSchristos 
401dc860a36Sspz     return (length - length_cur);
402784088dfSchristos 
403784088dfSchristos trunc:
404784088dfSchristos     return (-1);
405784088dfSchristos }
406784088dfSchristos 
407784088dfSchristos static int
408c74ad251Schristos resp_print_inline(netdissect_options *ndo, const u_char *bp, int length) {
409dc860a36Sspz     int length_cur = length;
410dc860a36Sspz     int len;
411784088dfSchristos     const u_char *bp_ptr;
412784088dfSchristos 
413784088dfSchristos     /*
414784088dfSchristos      * Inline commands are simply 'strings' followed by \r or \n or both.
415dc860a36Sspz      * Redis will do its best to split/parse these strings.
416784088dfSchristos      * This feature of redis is implemented to support the ability of
417784088dfSchristos      * command parsing from telnet/nc sessions etc.
418784088dfSchristos      *
419784088dfSchristos      * <string><\r||\n||\r\n...>
420784088dfSchristos      */
421dc860a36Sspz 
422dc860a36Sspz     /*
423dc860a36Sspz      * Skip forward past any leading \r, \n, or \r\n.
424dc860a36Sspz      */
425784088dfSchristos     CONSUME_CR_OR_LF(bp, length_cur);
426784088dfSchristos     bp_ptr = bp;
427dc860a36Sspz 
428dc860a36Sspz     /*
429dc860a36Sspz      * Scan forward looking for \r or \n.
430dc860a36Sspz      */
431dc860a36Sspz     FIND_CR_OR_LF(bp_ptr, length_cur);
432dc860a36Sspz 
433dc860a36Sspz     /*
434dc860a36Sspz      * Found it; bp_ptr points to the \r or \n, so bp_ptr - bp is the
435c74ad251Schristos      * Length of the line text that precedes it.  Print it.
436dc860a36Sspz      */
437*26ba0b50Schristos     len = ND_BYTES_BETWEEN(bp, bp_ptr);
438784088dfSchristos     RESP_PRINT_SEGMENT(ndo, bp, len);
439dc860a36Sspz 
440dc860a36Sspz     /*
441dc860a36Sspz      * Skip forward past the \r, \n, or \r\n.
442dc860a36Sspz      */
443784088dfSchristos     CONSUME_CR_OR_LF(bp_ptr, length_cur);
444784088dfSchristos 
445dc860a36Sspz     /*
446dc860a36Sspz      * Return the number of bytes we processed.
447dc860a36Sspz      */
448dc860a36Sspz     return (length - length_cur);
449784088dfSchristos 
450784088dfSchristos trunc:
451784088dfSchristos     return (-1);
452784088dfSchristos }
453dc860a36Sspz 
454dc860a36Sspz static int
455c74ad251Schristos resp_get_length(netdissect_options *ndo, const u_char *bp, int len, const u_char **endp)
456dc860a36Sspz {
457dc860a36Sspz     int result;
458dc860a36Sspz     u_char c;
459dc860a36Sspz     int saw_digit;
460dc860a36Sspz     int neg;
461dc860a36Sspz     int too_large;
462dc860a36Sspz 
463dc860a36Sspz     if (len == 0)
464dc860a36Sspz         goto trunc;
465dc860a36Sspz     too_large = 0;
466dc860a36Sspz     neg = 0;
467c74ad251Schristos     if (GET_U_1(bp) == '-') {
468dc860a36Sspz         neg = 1;
469dc860a36Sspz         bp++;
470dc860a36Sspz         len--;
471dc860a36Sspz     }
472dc860a36Sspz     result = 0;
473dc860a36Sspz     saw_digit = 0;
474dc860a36Sspz 
475dc860a36Sspz     for (;;) {
476dc860a36Sspz         if (len == 0)
477dc860a36Sspz             goto trunc;
478c74ad251Schristos         c = GET_U_1(bp);
479dc860a36Sspz         if (!(c >= '0' && c <= '9')) {
48072c96ff3Schristos             if (!saw_digit) {
48172c96ff3Schristos                 bp++;
482dc860a36Sspz                 goto invalid;
48372c96ff3Schristos             }
484dc860a36Sspz             break;
485dc860a36Sspz         }
486dc860a36Sspz         c -= '0';
487dc860a36Sspz         if (result > (INT_MAX / 10)) {
488dc860a36Sspz             /* This will overflow an int when we multiply it by 10. */
489dc860a36Sspz             too_large = 1;
490dc860a36Sspz         } else {
491dc860a36Sspz             result *= 10;
49272c96ff3Schristos             if (result == ((INT_MAX / 10) * 10) && c > (INT_MAX % 10)) {
493dc860a36Sspz                 /* This will overflow an int when we add c */
494dc860a36Sspz                 too_large = 1;
495dc860a36Sspz             } else
496dc860a36Sspz                 result += c;
497dc860a36Sspz         }
498dc860a36Sspz         bp++;
499dc860a36Sspz         len--;
500dc860a36Sspz         saw_digit = 1;
501dc860a36Sspz     }
502dc860a36Sspz 
503dc860a36Sspz     /*
50472c96ff3Schristos      * OK, we found a non-digit character.  It should be a \r, followed
50572c96ff3Schristos      * by a \n.
506dc860a36Sspz      */
507c74ad251Schristos     if (GET_U_1(bp) != '\r') {
50872c96ff3Schristos         bp++;
509dc860a36Sspz         goto invalid;
51072c96ff3Schristos     }
511dc860a36Sspz     bp++;
512dc860a36Sspz     len--;
513dc860a36Sspz     if (len == 0)
514dc860a36Sspz         goto trunc;
515c74ad251Schristos     if (GET_U_1(bp) != '\n') {
51672c96ff3Schristos         bp++;
517dc860a36Sspz         goto invalid;
51872c96ff3Schristos     }
519dc860a36Sspz     bp++;
520dc860a36Sspz     len--;
521dc860a36Sspz     *endp = bp;
522dc860a36Sspz     if (neg) {
523dc860a36Sspz         /* -1 means "null", anything else is invalid */
524dc860a36Sspz         if (too_large || result != 1)
525dc860a36Sspz             return (-4);
526dc860a36Sspz         result = -1;
527dc860a36Sspz     }
528dc860a36Sspz     return (too_large ? -3 : result);
529dc860a36Sspz 
530dc860a36Sspz trunc:
53172c96ff3Schristos     *endp = bp;
532dc860a36Sspz     return (-2);
533dc860a36Sspz 
534dc860a36Sspz invalid:
53572c96ff3Schristos     *endp = bp;
536dc860a36Sspz     return (-5);
537dc860a36Sspz }
538