tcpdump/print-ahcp.c
Denis Ovsienko 424961546a Introduce and use ND_LONGJMP_FROM_TCHECK.
Apply this to the 4 the recently converted protocols.

HOW TO USE:

Define this in a print-*.c file before including netdissect.h to make
all its ND_TCHECK*() instances longjmp(). Only do this after verifying
that every ND_TCHECK*() in the file is correct and that in every
possible case it is safe to call longjmp() without incurring resource
leaks or any other problems. If it is not safe or feasible to convert
whole file, consider converting one function at a time.
2020-09-27 21:45:46 +01:00

389 lines
10 KiB
C

/*
* Copyright (c) 2013 The TCPDUMP project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* \summary: Ad Hoc Configuration Protocol (AHCP) printer */
/* Based on draft-chroboczek-ahcp-00 and source code of ahcpd-0.53 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "netdissect-stdinc.h"
#define ND_LONGJMP_FROM_TCHECK
#include "netdissect.h"
#include "extract.h"
#include "addrtoname.h"
#define AHCP_MAGIC_NUMBER 43
#define AHCP_VERSION_1 1
#define AHCP1_HEADER_FIX_LEN 24
#define AHCP1_BODY_MIN_LEN 4
#define AHCP1_MSG_DISCOVER 0
#define AHCP1_MSG_OFFER 1
#define AHCP1_MSG_REQUEST 2
#define AHCP1_MSG_ACK 3
#define AHCP1_MSG_NACK 4
#define AHCP1_MSG_RELEASE 5
static const struct tok ahcp1_msg_str[] = {
{ AHCP1_MSG_DISCOVER, "Discover" },
{ AHCP1_MSG_OFFER, "Offer" },
{ AHCP1_MSG_REQUEST, "Request" },
{ AHCP1_MSG_ACK, "Ack" },
{ AHCP1_MSG_NACK, "Nack" },
{ AHCP1_MSG_RELEASE, "Release" },
{ 0, NULL }
};
#define AHCP1_OPT_PAD 0
#define AHCP1_OPT_MANDATORY 1
#define AHCP1_OPT_ORIGIN_TIME 2
#define AHCP1_OPT_EXPIRES 3
#define AHCP1_OPT_MY_IPV6_ADDRESS 4
#define AHCP1_OPT_MY_IPV4_ADDRESS 5
#define AHCP1_OPT_IPV6_PREFIX 6
#define AHCP1_OPT_IPV4_PREFIX 7
#define AHCP1_OPT_IPV6_ADDRESS 8
#define AHCP1_OPT_IPV4_ADDRESS 9
#define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10
#define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11
#define AHCP1_OPT_NAME_SERVER 12
#define AHCP1_OPT_NTP_SERVER 13
#define AHCP1_OPT_MAX 13
static const struct tok ahcp1_opt_str[] = {
{ AHCP1_OPT_PAD, "Pad" },
{ AHCP1_OPT_MANDATORY, "Mandatory" },
{ AHCP1_OPT_ORIGIN_TIME, "Origin Time" },
{ AHCP1_OPT_EXPIRES, "Expires" },
{ AHCP1_OPT_MY_IPV6_ADDRESS, "My-IPv6-Address" },
{ AHCP1_OPT_MY_IPV4_ADDRESS, "My-IPv4-Address" },
{ AHCP1_OPT_IPV6_PREFIX, "IPv6 Prefix" },
{ AHCP1_OPT_IPV4_PREFIX, "IPv4 Prefix" },
{ AHCP1_OPT_IPV6_ADDRESS, "IPv6 Address" },
{ AHCP1_OPT_IPV4_ADDRESS, "IPv4 Address" },
{ AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" },
{ AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" },
{ AHCP1_OPT_NAME_SERVER, "Name Server" },
{ AHCP1_OPT_NTP_SERVER, "NTP Server" },
{ 0, NULL }
};
static void
ahcp_time_print(netdissect_options *ndo,
const u_char *cp, uint8_t len)
{
time_t t;
struct tm *tm;
char buf[BUFSIZE];
if (len != 4)
goto invalid;
t = GET_BE_U_4(cp);
if (NULL == (tm = gmtime(&t)))
ND_PRINT(": gmtime() error");
else if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm))
ND_PRINT(": strftime() error");
else
ND_PRINT(": %s UTC", buf);
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}
static void
ahcp_seconds_print(netdissect_options *ndo,
const u_char *cp, uint8_t len)
{
if (len != 4)
goto invalid;
ND_PRINT(": %us", GET_BE_U_4(cp));
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}
static void
ahcp_ipv6_addresses_print(netdissect_options *ndo,
const u_char *cp, uint8_t len)
{
const char *sep = ": ";
while (len) {
if (len < 16)
goto invalid;
ND_PRINT("%s%s", sep, GET_IP6ADDR_STRING(cp));
cp += 16;
len -= 16;
sep = ", ";
}
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}
static void
ahcp_ipv4_addresses_print(netdissect_options *ndo,
const u_char *cp, uint8_t len)
{
const char *sep = ": ";
while (len) {
if (len < 4)
goto invalid;
ND_PRINT("%s%s", sep, GET_IPADDR_STRING(cp));
cp += 4;
len -= 4;
sep = ", ";
}
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}
static void
ahcp_ipv6_prefixes_print(netdissect_options *ndo,
const u_char *cp, uint8_t len)
{
const char *sep = ": ";
while (len) {
if (len < 17)
goto invalid;
ND_PRINT("%s%s/%u", sep, GET_IP6ADDR_STRING(cp), GET_U_1(cp + 16));
cp += 17;
len -= 17;
sep = ", ";
}
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}
static void
ahcp_ipv4_prefixes_print(netdissect_options *ndo,
const u_char *cp, uint8_t len)
{
const char *sep = ": ";
while (len) {
if (len < 5)
goto invalid;
ND_PRINT("%s%s/%u", sep, GET_IPADDR_STRING(cp), GET_U_1(cp + 4));
cp += 5;
len -= 5;
sep = ", ";
}
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}
static void
(* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, uint8_t) = {
/* [AHCP1_OPT_PAD] = */ NULL,
/* [AHCP1_OPT_MANDATORY] = */ NULL,
/* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print,
/* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print,
/* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
/* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
/* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print,
/* [AHCP1_OPT_IPV4_PREFIX] = */ NULL,
/* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
/* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
/* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print,
/* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print,
/* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print,
/* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print,
};
static void
ahcp1_options_print(netdissect_options *ndo,
const u_char *cp, uint16_t len)
{
while (len) {
uint8_t option_no, option_len;
/* Option no */
option_no = GET_U_1(cp);
cp += 1;
len -= 1;
ND_PRINT("\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no));
if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY)
continue;
/* Length */
if (!len)
goto invalid;
option_len = GET_U_1(cp);
cp += 1;
len -= 1;
if (option_len > len)
goto invalid;
/* Value */
if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) {
data_decoders[option_no](ndo, cp, option_len);
} else {
ND_PRINT(" (Length %u)", option_len);
ND_TCHECK_LEN(cp, option_len);
}
cp += option_len;
len -= option_len;
}
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}
static void
ahcp1_body_print(netdissect_options *ndo,
const u_char *cp, u_int len)
{
uint8_t type, mbz;
uint16_t body_len;
if (len < AHCP1_BODY_MIN_LEN)
goto invalid;
/* Type */
type = GET_U_1(cp);
cp += 1;
len -= 1;
/* MBZ */
mbz = GET_U_1(cp);
cp += 1;
len -= 1;
/* Length */
body_len = GET_BE_U_2(cp);
cp += 2;
len -= 2;
if (ndo->ndo_vflag) {
ND_PRINT("\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type));
if (mbz != 0)
ND_PRINT(", MBZ %u", mbz);
ND_PRINT(", Length %u", body_len);
}
if (body_len > len)
goto invalid;
/* Options */
/* Here use "body_len", not "len" (ignore any extra data). */
if (ndo->ndo_vflag >= 2)
ahcp1_options_print(ndo, cp, body_len);
else
ND_TCHECK_LEN(cp, body_len);
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}
void
ahcp_print(netdissect_options *ndo,
const u_char *cp, u_int len)
{
uint8_t version;
ndo->ndo_protocol = "ahcp";
nd_print_protocol_caps(ndo);
if (len < 2)
goto invalid;
/* Magic */
if (GET_U_1(cp) != AHCP_MAGIC_NUMBER)
goto invalid;
cp += 1;
len -= 1;
/* Version */
version = GET_U_1(cp);
cp += 1;
len -= 1;
switch (version) {
case AHCP_VERSION_1: {
ND_PRINT(" Version 1");
if (len < AHCP1_HEADER_FIX_LEN - 2)
goto invalid;
if (!ndo->ndo_vflag) {
ND_TCHECK_LEN(cp, AHCP1_HEADER_FIX_LEN - 2);
cp += AHCP1_HEADER_FIX_LEN - 2;
len -= AHCP1_HEADER_FIX_LEN - 2;
} else {
/* Hopcount */
ND_PRINT("\n\tHopcount %u", GET_U_1(cp));
cp += 1;
len -= 1;
/* Original Hopcount */
ND_PRINT(", Original Hopcount %u", GET_U_1(cp));
cp += 1;
len -= 1;
/* Nonce */
ND_PRINT(", Nonce 0x%08x", GET_BE_U_4(cp));
cp += 4;
len -= 4;
/* Source Id */
ND_PRINT(", Source Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8));
cp += 8;
len -= 8;
/* Destination Id */
ND_PRINT(", Destination Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8));
cp += 8;
len -= 8;
}
/* Body */
ahcp1_body_print(ndo, cp, len);
break;
}
default:
ND_PRINT(" Version %u (unknown)", version);
ND_TCHECK_LEN(cp, len);
break;
}
return;
invalid:
nd_print_invalid(ndo);
ND_TCHECK_LEN(cp, len);
}