/* packet-coap.h
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#ifndef __PACKET_COAP_H__
#define __PACKET_COAP_H__

#include "packet-oscore.h"

/* bitmasks */
#define COAP_VERSION_MASK					0xC0
#define COAP_TYPE_MASK						0x30
#define COAP_TOKEN_LEN_MASK					0x0F
#define COAP_BLOCK_MFLAG_MASK					0x08
#define COAP_BLOCK_SIZE_MASK					0x07
#define COAP_OBJECT_SECURITY_RESERVED_MASK			0xE0
#define COAP_OBJECT_SECURITY_KID_CONTEXT_MASK			0x10
#define COAP_OBJECT_SECURITY_KID_MASK				0x08
#define COAP_OBJECT_SECURITY_PIVLEN_MASK			0x07

/* Parent protocol for CoAP */
typedef enum {
	PARENT_WEBSOCKETS,	/* WebSockets */
	PARENT_TCP_TLS,		/* TCP or TLS */
	PARENT_OTHER		/* UDP, WAP, other packet-based protocols */
} coap_parent_protocol;

/* CoAP Message information */
typedef struct {
	const gchar *ctype_str;
	guint ctype_value;
	guint block_option;                     /* Indicates Block1 or Block2 option */
	guint block_number;
	guint block_mflag;
	wmem_strbuf_t *uri_str_strbuf;		/* the maximum is 1024 > 510 = Uri-Host:255 + Uri-Path:255 x 2 */
	wmem_strbuf_t *uri_query_strbuf;	/* the maximum is 1024 >         765 = Uri-Query:255 x 3 */
	gboolean is_coap_for_tmf;		/* CoAP for Thread Management Framework */
	gboolean object_security;
	oscore_info_t *oscore_info;		/* OSCORE data needed to decrypt */
} coap_info;

/* CoAP Conversation information */
typedef struct {
	wmem_map_t *messages;
} coap_conv_info;

/* CoAP Transaction tracking information */
typedef struct {
	wmem_map_t    *req_rsp;
	wmem_strbuf_t *uri_str_strbuf;
	oscore_info_t *oscore_info;		/* OSCORE transaction to decrypt response */
} coap_transaction;

typedef struct {
	guint32  req_frame;
	guint32  rsp_frame;
	nstime_t req_time;
} coap_request_response;

/* common header fields, subtrees and expert info for SSL and DTLS dissectors */
typedef struct coap_common_dissect {
	struct {
		/* Header fields */
		int code;
		/* Payload fields */
		int payload;
		int payload_desc;
		int payload_length;

		/* Option fields */
		int opt_name;
		int opt_desc;
		int opt_delta;
		int opt_delta_ext;
		int opt_length;
		int opt_length_ext;
		int opt_end_marker;
		int opt_ctype;
		int opt_max_age;
		int opt_proxy_uri;
		int opt_proxy_scheme;
		int opt_size1;
		int opt_etag;
		int opt_uri_host;
		int opt_location_path;
		int opt_uri_port;
		int opt_location_query;
		int opt_uri_path;
		int opt_uri_path_recon;
		int opt_observe_req;
		int opt_observe_rsp;
		int opt_hop_limit;
		int opt_accept;
		int opt_if_match;
		int opt_block_number;
		int opt_block_mflag;
		int opt_block_size;
		int opt_uri_query;
		int opt_echo;
		int opt_no_response;
		int opt_request_tag;
		int opt_ocf_version;
		int opt_ocf_accept_version;
		int opt_unknown;
		int opt_object_security_reserved;
		int opt_object_security_kid_context_present;
		int opt_object_security_kid_present;
		int opt_object_security_piv_len;
		int opt_object_security_piv;
		int opt_object_security_kid_context_len;
		int opt_object_security_kid_context;
		int opt_object_security_kid;

	/* do not forget to update COAP_COMMON_LIST_T and COAP_COMMON_HF_LIST! */
	} hf;

	struct {
		gint payload;
		gint option;

	/* do not forget to update COAP_COMMON_LIST_T and COAP_COMMON_ETT_LIST! */
	} ett;

	struct {
		/* Generic expert info for malformed packets. */
		expert_field opt_unknown_number;
		expert_field opt_invalid_number;
		expert_field opt_invalid_range;
		expert_field opt_length_bad;
		expert_field opt_object_security_bad;

        /* do not forget to update COAP_COMMON_LIST_T and COAP_COMMON_EI_LIST! */
	} ei;
} coap_common_dissect_t;

guint8 dissect_coap_code(tvbuff_t *tvb, proto_tree *coap_tree, gint *offset, coap_common_dissect_t *dissect_hf, guint8 *code_class);
int dissect_coap_options(tvbuff_t *tvb, packet_info *pinfo, proto_tree *coap_tree, gint offset, gint offset_end, guint8 code_class, coap_info *coinfo, coap_common_dissect_t *dissect_hf);
void dissect_coap_payload(tvbuff_t *tvb, packet_info *pinfo, proto_tree *coap_tree, proto_tree *parent_tree, gint offset, gint offset_end, guint8 code_class, coap_info *coinfo, coap_common_dissect_t *dissect_hf, gboolean oscore);

extern const value_string coap_vals_observe_options[];
extern value_string_ext coap_vals_code_ext;

/* {{{ */
#define COAP_COMMON_LIST_T(name)						\
coap_common_dissect_t name = {							\
	/* hf */ {								\
		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,				\
		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,				\
		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,				\
		-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,				\
		-1, -1, -1, -1, -1, -1,						\
		},								\
	/* ett */ {								\
		-1, -1,								\
		},								\
	/* ei */ {								\
		EI_INIT, EI_INIT, EI_INIT, EI_INIT, EI_INIT,			\
		},								\
}
/* }}} */

/* {{{ */
#define COAP_COMMON_HF_LIST(name, prefix)					\
	{ & name .hf.code,							\
	  { "Code", prefix ".code",						\
	    FT_UINT8, BASE_DEC | BASE_EXT_STRING, &coap_vals_code_ext, 0x0,	\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.payload,							\
	  { "Payload",  prefix ".payload",					\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.payload_desc,						\
	  { "Payload Desc",  prefix ".payload_desc",				\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.payload_length,						\
	  { "Payload Length",  prefix ".payload_length",			\
	    FT_UINT32, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_name,							\
	  { "Opt Name",  prefix ".opt.name",					\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_desc,							\
	  { "Opt Desc",  prefix ".opt.desc",					\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_delta,							\
	  { "Opt Delta",  prefix ".opt.delta",					\
	    FT_UINT8, BASE_DEC, NULL, 0xf0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_delta_ext,						\
	  { "Opt Delta extended",  prefix ".opt.delta_ext",			\
	    FT_UINT16, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_length,						\
	  { "Opt Length",  prefix ".opt.length",				\
	    FT_UINT8, BASE_DEC, NULL, 0x0f,					\
	    "Option Length", HFILL }						\
	},									\
	{ & name .hf.opt_length_ext,						\
	  { "Opt Length extended",  prefix ".opt.length_ext",			\
	    FT_UINT16, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_end_marker,						\
	  { "End of options marker",  prefix ".opt.end_marker",			\
	    FT_UINT8, BASE_DEC, NULL, 0x00,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_ctype,							\
	  { "Content-type",  prefix ".opt.ctype",				\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_max_age,						\
	  { "Max-age",  prefix ".opt.max_age",					\
	    FT_UINT32, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_proxy_uri,						\
	  { "Proxy-Uri",  prefix ".opt.proxy_uri",				\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_proxy_scheme,						\
	  { "Proxy-Scheme",  prefix ".opt.proxy_scheme",			\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_size1,							\
	  { "Size1",  prefix ".opt.size1",					\
	    FT_UINT32, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_etag,							\
	  { "Etag",  prefix ".opt.etag",					\
	    FT_BYTES, BASE_NONE, NULL, 0x0,					\
	    "Option Etag", HFILL }						\
	},									\
	{ & name .hf.opt_uri_host,						\
	  { "Uri-Host",  prefix ".opt.uri_host",				\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_location_path,						\
	  { "Location-Path",  prefix ".opt.location_path",			\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_uri_port,						\
	  { "Uri-Port",  prefix ".opt.uri_port",				\
	    FT_UINT16, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_location_query,					\
	  { "Location-Query",  prefix ".opt.location_query",			\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_object_security_reserved,				\
	  { "Reserved",  prefix ".opt.object_security_reserved",		\
	    FT_BOOLEAN, 8, NULL, COAP_OBJECT_SECURITY_RESERVED_MASK,		\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_object_security_kid_context_present,			\
	  { "Key ID Context Present",  prefix ".opt.object_security_kid_context_present",\
	    FT_BOOLEAN, 8, NULL, COAP_OBJECT_SECURITY_KID_CONTEXT_MASK,		\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_object_security_kid_present,				\
	  { "Key ID Present",  prefix ".opt.object_security_kid_present",	\
	    FT_BOOLEAN, 8, NULL, COAP_OBJECT_SECURITY_KID_MASK,			\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_object_security_piv_len,				\
	  { "Partial IV Length",  prefix ".opt.object_security_piv_len",	\
	    FT_UINT8, BASE_DEC, NULL, COAP_OBJECT_SECURITY_PIVLEN_MASK,		\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_object_security_piv,					\
	  { "Partial IV",  prefix ".opt.object_security_piv",			\
	    FT_BYTES, BASE_NONE, NULL, 0x00,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_object_security_kid_context_len,			\
	  { "Key ID Context Length",  prefix ".opt.object_security_kid_context_len",\
	    FT_UINT8, BASE_DEC, NULL, 0x00,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_object_security_kid_context,				\
	  { "Key ID Context",  prefix ".opt.object_security_kid_context",	\
	    FT_BYTES, BASE_NONE, NULL, 0x00,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_object_security_kid,					\
	  { "Key ID",  prefix ".opt.object_security_kid",			\
	    FT_BYTES, BASE_NONE, NULL, 0x00,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_uri_path,						\
	  { "Uri-Path",  prefix ".opt.uri_path",				\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_uri_path_recon,					\
	  { "Uri-Path",  prefix ".opt.uri_path_recon",				\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_observe_req,						\
	  { "Observe",  prefix ".opt.observe",					\
	    FT_UINT32, BASE_DEC, VALS(coap_vals_observe_options), 0x0,		\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_observe_rsp,						\
	  { "Observe sequence number",  prefix ".opt.observe",			\
	    FT_UINT32, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_hop_limit,						\
	  { "Hop Limit",  prefix ".opt.hop_limit",				\
	    FT_UINT8, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_accept,						\
	  { "Accept",  prefix ".opt.accept",					\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_if_match,						\
	  { "If-Match",  prefix ".opt.if_match",				\
	    FT_BYTES, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_block_number,						\
	  { "Block Number",  prefix ".opt.block_number",			\
	    FT_UINT32, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_block_mflag,						\
	  { "More Flag",  prefix ".opt.block_mflag",				\
	    FT_UINT8, BASE_DEC, NULL, COAP_BLOCK_MFLAG_MASK,			\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_block_size,						\
	  { "Encoded Block Size",  prefix ".opt.block_size",			\
	    FT_UINT8, BASE_DEC, NULL, COAP_BLOCK_SIZE_MASK,			\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_uri_query,						\
	  { "Uri-Query",  prefix ".opt.uri_query",				\
	    FT_STRING, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_echo,							\
	  { "Echo",  prefix ".opt.opt_echo",					\
	    FT_BYTES, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_no_response,						\
	  { "No-Response",  prefix ".opt.opt_no_response",			\
	    FT_UINT8, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_request_tag,						\
	  { "Request-Tag",  prefix ".opt.opt_request_tag",			\
	    FT_BYTES, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_ocf_version,						\
	  { "OCF-Content-Format-Version",					\
	    prefix ".opt.opt_ocf_version",					\
	    FT_UINT8, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_ocf_accept_version,					\
	  { "OCF-Accept-Content-Format-Version",				\
	    prefix ".opt.opt_ocf_accept_version",				\
	    FT_UINT8, BASE_DEC, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
	{ & name .hf.opt_unknown,						\
	  { "Unknown",  prefix ".opt.unknown",					\
	    FT_BYTES, BASE_NONE, NULL, 0x0,					\
	    NULL, HFILL }							\
	},									\
/* }}} */

/* {{{ */
#define COAP_COMMON_ETT_LIST(name)						\
	& name .ett.payload,							\
	& name .ett.option,							\

/* }}} */

/* {{{ */
#define COAP_COMMON_EI_LIST(name, prefix)					\
	{ & name .ei.opt_unknown_number,					\
	  { prefix ".unknown_option_number", PI_UNDECODED, PI_WARN,		\
	    "Unknown Option Number", EXPFILL }					\
	},									\
	{ & name .ei.opt_invalid_number,					\
	  { prefix ".invalid_option_number", PI_MALFORMED, PI_WARN,		\
	    "Invalid Option Number", EXPFILL }					\
	},									\
	{ & name .ei.opt_invalid_range,						\
	  { prefix ".invalid_option_range", PI_MALFORMED, PI_WARN,		\
	    "Invalid Option Range", EXPFILL }					\
	},									\
	{ & name .ei.opt_length_bad,						\
	  { prefix ".option_length_bad", PI_MALFORMED, PI_WARN,			\
	    "Option length bad", EXPFILL }					\
	},									\
	{ & name .ei.opt_object_security_bad,					\
	  { prefix ".option_oscore_bad", PI_MALFORMED, PI_WARN,	\
	    "Invalid OSCORE Option Format", EXPFILL }			\
	},									\

/* }}} */

#endif /* __PACKET_COAP_H__ */

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */
