/* q931_decode.c - minimalistic Q.931 protocol decoder
 *
 * (C) 2022 by Harald Welte <laforge@osmocom.org>
 *
 * All Rights Reserved
 *
 * SPDX-License-Identifier: GPL-2.0+
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <errno.h>

#include <osmocom/gsm/tlv.h>

#include "q931.h"
#include "log.h"

/* Table 4-3/Q.931 */
const struct tlv_definition q931_tlv_def = {
	.def = {
	/* fixed-length */
	[0x80]				= { TLV_TYPE_SINGLE_TV, 0 },
	[0x90]				= { TLV_TYPE_SINGLE_TV, 0 },
	[Q931_IEI_MORE_DATA]		= { TLV_TYPE_T, 0 },
	[Q931_IEI_SENDING_COMPLETE]	= { TLV_TYPE_T, 0 },
	[0xB0]				= { TLV_TYPE_SINGLE_TV, 0 },
	[0xD0] 				= { TLV_TYPE_SINGLE_TV, 0 },
	/* variable-length */
	[Q931_IEI_SEGMENTED_MSG]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_BEARER_CAP]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CAUSE]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CALL_ID]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CALL_STATE]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CHANNEL_ID]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_PROGRESS_IND]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_NETWORK_SPEC_FAC]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_NOTIFICATION_IND]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_DISPLAY]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_DATE_TIME]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_KEYPAD_FACILITY]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_SIGNAL]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_INFORMATION_RATE]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_E2E_TRANSIT_DELAY]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_TRANSIT_DELAY_SEL_AND_IND] = { TLV_TYPE_TLV, 0 },
	[Q931_IEI_PKT_LAYER_BIN_PARAMS]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_PKT_LAYER_WIN_SIZE]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_PACKET_SIZE]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CLOSED_USER_GROUP]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_REV_CHARGING_IND]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CALLING_PARTY_NUM]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CALLING_PARTY_SUBADDR]= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CALLED_PARTY_NUM]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_CALLED_PARTY_SUBADDR]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_TRANSIT_NET_SEL]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_RESTART_IND]		= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_LOW_LAYER_COMPAT]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_HIGH_LAYER_COMPAT]	= { TLV_TYPE_TLV, 0 },
	[Q931_IEI_ESCAPE_FOR_EXT]	= { TLV_TYPE_TLV, 0 },
	},
};

/* parse one Q.931 message for signaling analysis */
int q931_msg_parse(struct q931_msg_parsed *out, const uint8_t *buf, size_t len)
{
	uint8_t cref_len;
	const uint8_t *other_ie;
	size_t other_ie_len;
	int rc;

	memset(out, 0, sizeof(*out));

	/* at least protocol discriminator + length of call-ref must be present */
	if (len < 2) {
		LOGP(DQ931, LOGL_ERROR, "short Q.931 message: %s\n", osmo_hexdump(buf, len));
		return -EMSGSIZE;
	}

	/* check protocol discriminator */
	if (buf[0] != 0x08) {
		LOGP(DQ931, LOGL_ERROR, "unknoqn Q.931 protocol discrimnator 0x%02x: %s", buf[0],
		     osmo_hexdump(buf, len));
		return -EINVAL;
	}

	/* parse [variable length] call reference */
	cref_len = buf[1] & 0x0F;
	if (len < 2U + cref_len) {
		LOGP(DQ931, LOGL_ERROR, "short Q.931 message: %s\n", osmo_hexdump(buf, len));
		return -EMSGSIZE;
	}
	out->call_ref = q931_decode_callref(buf+1, 1+cref_len);

	/* parse message type */
	if (len < 2U + cref_len + 1U) {
		LOGP(DQ931, LOGL_ERROR, "short Q.931 message: %s\n", osmo_hexdump(buf, len));
		return -EMSGSIZE;
	}
	out->msg_type = buf[2+cref_len] & 0x7f;

	/* parse 'other IEs' */
	other_ie = buf + 2 + cref_len + 1;
	other_ie_len = len - (2 + cref_len + 1);
	if (other_ie_len) {
		rc = tlv_parse(&out->ies, &q931_tlv_def, other_ie, other_ie_len, 0, 0);
		if (rc < 0) {
			LOGP(DQ931, LOGL_ERROR, "Can't TLV-parse Q.931 message: %s\n", osmo_hexdump(buf, len));
			return rc;
		}
	}

	return 0;
}

/* Q.931 Section 4.3 */
uint32_t q931_decode_callref(const uint8_t *data, uint8_t len)
{
	uint8_t len_of_callref;
	uint32_t callref = 0;
	bool flag = false;

	if (len < 1)
		goto err;

	len_of_callref = data[0] & 0x0f;
	if (len_of_callref == 0)
		goto err;

	if (len_of_callref > 4)
		goto err;

	if (len - 1 < len_of_callref)
		goto err;

	/* first octet contains flag, needs special handling */
	if (data[1] & 0x80)
		flag = true;
	callref = data[1] & 0x7f;

	/* all further octets ... */
	for (unsigned i = 1; i < len_of_callref; i++)
		callref = (callref << 8) | data[1+i];

	if (flag)
		callref |= 0x80000000;

	return callref;
err:
	LOGP(DQ931, LOGL_ERROR, "Unable to decode Q.931 call reference: %s\n",
	     osmo_hexdump(data, len));
	return 0;
}

/* Decode a (subset of) Q.931 channel identification IE from binary to struct representation */
int q931_decode_channel_id(struct q931_channel_id *out, const uint8_t *data, uint8_t len)
{
	const uint8_t *cur = data;

	memset(out, 0, sizeof(*out));

	if (len < 1)
		goto err;

	if (! (data[0] & 0x80))
		goto err;

	if (data[0] & 0x40) {
		out->interface_id_present = true;
		cur++;
		if (len < 1 + (cur - data))
			goto err;
		/* we only support single-octet interface ID */
		if (!(*cur & 0x80))
			goto err;
		out->interface_id = *cur & 0x7f;
	}

	if (data[0] & 0x20)
		out->interface_type_pri = true;

	if (data[0] & 0x08)
		out->exclusive = true;

	if (data[0] & 0x04) {
		out->d_channel = true;
	} else {
		switch (data[0] & 0x03) {
		case 0:
			out->info_chan_type = Q931_INFO_CHAN_T_NONE;
			break;
		case 1:
			if (out->interface_type_pri == false) {
				out->b_channel = 1;
			} else {
				/* Octet 3.2 */
				cur++;
				if (len < 1 + (cur - data))
					goto err;
				if (!(*cur & 0x80))
					goto err;
				/* do we care about the coding standard? */
				/* we only support single channel numbers */
				if (*cur & 0x10)
					goto err;
				/* we only support B-channels */
				if ((*cur & 0x0f) != 0x03)
					goto err;
				/* Octet 3.3 */
				cur++;
				if (len < 1 + (cur - data))
					goto err;
				/* we only support single channel numbers */
				if (!(*cur & 0x80))
					goto err;
				out->b_channel = *cur & 0x7f;
			}
			break;
		case 2:
			if (out->interface_type_pri == false)
				out->b_channel = 2;
			else
				goto err;
			break;
		case 3:
			out->info_chan_type = Q931_INFO_CHAN_T_ANY;
			break;
		}
	}

	return 0;
err:
	LOGP(DQ931, LOGL_ERROR, "Unable to decode Q.931 channel id: %s\n",
	     osmo_hexdump(data, len));
	return -1;

}

/* Q.931 Section 4.5.8 */
int q931_decode_called_party(struct q931_party_number *out, const uint8_t *buf, size_t len)
{
	memset(out, 0, sizeof(*out));

	if (len < 1)
		goto err;

	/* Octet 3 */
	if (!(*buf & 0x80))
		goto err;
	out->type_of_number = (*buf >> 4) & 0x7;
	out->numbering_plan_id = *buf & 0xf;

	for (unsigned int i = 0; i < len - 1; i++) {
		if (i >= sizeof(out->digits))
			goto err;
		out->digits[i] = buf[1+i] & 0x7f;
	}
	return 0;
err:
	LOGP(DQ931, LOGL_ERROR, "Unable to decode Q.931 called party: %s\n",
	     osmo_hexdump(buf, len));
	return -1;
}

/* Q.931 Section 4.5.10 */
int q931_decode_calling_party(struct q931_party_number *out, const uint8_t *buf, size_t len)
{
	const uint8_t *cur = buf;

	memset(out, 0, sizeof(*out));

	if (len < 1)
		goto err;

	/* Octet 3 */
	out->type_of_number = (*cur >> 4) & 0x7;
	out->numbering_plan_id = *cur & 0xf;
	if (!(*cur & 0x80)) {
		/* Octet 3a */
		cur++;
		if (len < 2)
			goto err;
		if (!(*cur & 0x80))
			goto err;
		out->presentation_ind = (*cur >> 5) & 0x3;
		out->screening_ind = *cur & 0x3;
	}
	cur++;

	for (unsigned int i = 0; i < len - (cur - buf); i++) {
		if (i >= sizeof(out->digits))
			goto err;
		out->digits[i] = cur[i] & 0x7f;
	}
	return 0;
err:
	LOGP(DQ931, LOGL_ERROR, "Unable to decode Q.931 calling party: %s\n",
	     osmo_hexdump(buf, len));
	return -1;
}
