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