view uptools/sms-pdu-decode/pdu-common.c @ 1012:11391cb6bdc0

patch from fixeria: doc change from SE K2x0 to K2xx Since their discovery in late 2022, Sony Ericsson K200 and K220 phones were collectively referred to as SE K2x0 in FreeCalypso documentation. However, now that SE K205 has been discovered as yet another member of the same family (same PCBA in different case), it makes more sense to refer to the whole family as SE K2xx.
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 23 Sep 2024 12:23:20 +0000
parents b515a97e5dff
children
line wrap: on
line source

#include <sys/types.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

int ascii_ext_mode, global_hexdump_mode;

u_char pdu[176];
unsigned pdu_length;

static u_char first_octet;
static unsigned pdu_ptr;
static int dcs_distilled;

static
handle_sca()
{
	unsigned sca_len;
	char digits[21];

	sca_len = pdu[0];
	pdu_ptr = 1;
	if (!sca_len)
		return(0);
	if (sca_len < 2 || sca_len > 11) {
		printf("Decode-Error: invalid SCA length\n");
		return(-1);
	}
	if (pdu_ptr + sca_len > pdu_length) {
		printf("Decode-Error: SCA goes past PDU end\n");
		return(-1);
	}
	pdu_ptr += sca_len;
	decode_address_digits(pdu + 2, digits, sc_addr_ndigits(pdu));
	printf("SCA: %s%s (type 0x%02X)\n", pdu[1] == 0x91 ? "+" : "", digits,
		pdu[1]);
	return(0);
}

static
handle_first_octet()
{
	if (pdu_ptr >= pdu_length) {
		printf("Decode-Error: end of PDU before FO\n");
		return(-1);
	}
	first_octet = pdu[pdu_ptr++];
	printf("First-Octet: 0x%02X\n", first_octet);
	return(0);
}

static
handle_mr()
{
	if (pdu_ptr >= pdu_length) {
		printf("Decode-Error: end of PDU before MR\n");
		return(-1);
	}
	printf("MR: 0x%02X\n", pdu[pdu_ptr++]);
	return(0);
}

static
handle_user_addr(direction)
	char *direction;
{
	unsigned addr_field_len, alpha_nsep;
	char digits[21];
	u_char alpha_gsm7[11];

	if (pdu_ptr >= pdu_length) {
		printf("Decode-Error: end of PDU before %s address\n",
			direction);
		return(-1);
	}
	if (pdu[pdu_ptr] > 20) {
		printf("Decode-Error: %s address > 20 digits\n", direction);
		return(-1);
	}
	addr_field_len = ((pdu[pdu_ptr] + 1) >> 1) + 2;
	if (pdu_ptr + addr_field_len > pdu_length) {
		printf("Decode-Error: %s address goes past PDU end\n",
			direction);
		return(-1);
	}
	if (!pdu[pdu_ptr])
		printf("%s: empty-addr (type 0x%02X)\n", direction,
			pdu[pdu_ptr+1]);
	else if ((pdu[pdu_ptr+1] & 0x70) == 0x50 &&
		 alpha_addr_valid(pdu[pdu_ptr], &alpha_nsep)) {
		gsm7_unpack(pdu + pdu_ptr + 2, alpha_gsm7, alpha_nsep);
		printf("%s: ", direction);
		print_gsm7_string_to_file(alpha_gsm7, alpha_nsep, stdout);
		printf(" (type 0x%02X)\n", pdu[pdu_ptr+1]);
	} else {
		decode_address_digits(pdu + pdu_ptr + 2, digits, pdu[pdu_ptr]);
		printf("%s: %s%s (type 0x%02X)\n", direction,
			pdu[pdu_ptr+1] == 0x91 ? "+" : "", digits,
			pdu[pdu_ptr+1]);
	}
	pdu_ptr += addr_field_len;
	return(0);
}

static
handle_pid()
{
	if (pdu_ptr >= pdu_length) {
		printf("Decode-Error: end of PDU before PID\n");
		return(-1);
	}
	printf("PID: 0x%02X\n", pdu[pdu_ptr++]);
	return(0);
}

static
handle_dcs()
{
	u_char dcs;
	char *strtype;

	if (pdu_ptr >= pdu_length) {
		printf("Decode-Error: end of PDU before DCS\n");
		return(-1);
	}
	dcs = pdu[pdu_ptr++];
	dcs_distilled = sms_dcs_classify(dcs);
	switch (dcs_distilled) {
	case 7:
		strtype = "7-bit";
		break;
	case 8:
		strtype = "raw octets";
		break;
	case 9:
		strtype = "compressed";
		break;
	case 16:
		strtype = "UCS-2";
		break;
	}
	printf("DCS: 0x%02X (%s)\n", dcs, strtype);
	return(0);
}

static
handle_scts()
{
	char str[21];

	if (pdu_ptr + 7 > pdu_length) {
		printf("Decode-Error: end of PDU before SCTS\n");
		return(-1);
	}
	gsm_timestamp_decode(pdu + pdu_ptr, str);
	printf("SC-Timestamp: %s\n", str);
	pdu_ptr += 7;
	return(0);
}

static
handle_vp_abs()
{
	char str[21];

	if (pdu_ptr + 7 > pdu_length) {
		printf("Decode-Error: end of PDU before VP-abs\n");
		return(-1);
	}
	gsm_timestamp_decode(pdu + pdu_ptr, str);
	printf("VP-Absolute: %s\n", str);
	pdu_ptr += 7;
	return(0);
}

static
handle_vp_rel()
{
	unsigned vprel, hours, min;

	if (pdu_ptr >= pdu_length) {
		printf("Decode-Error: end of PDU before VP-rel\n");
		return(-1);
	}
	vprel = pdu[pdu_ptr++];
	if (vprel <= 143) {
		min = (vprel + 1) * 5;
		goto hhmm;
	} else if (vprel <= 167) {
		min = (vprel - 143) * 30 + 12 * 60;
		goto hhmm;
	} else if (vprel <= 196) {
		printf("VP-Relative: %u days\n", vprel - 166);
		return(0);
	} else {
		printf("VP-Relative: %u weeks\n", vprel - 192);
		return(0);
	}

hhmm:	hours = min / 60;
	min %= 60;
	printf("VP-Relative:");
	if (hours)
		printf(" %u hour%s", hours, hours != 1 ? "s" : "");
	if (min)
		printf(" %u min", min);
	putchar('\n');
	return(0);
}

static
handle_vp_enh()
{
	if (pdu_ptr + 7 > pdu_length) {
		printf("Decode-Error: end of PDU before VP-enh\n");
		return(-1);
	}
	printf("VP-Enhanced: %02X %02X %02X %02X %02X %02X %02X\n",
		pdu[pdu_ptr], pdu[pdu_ptr+1], pdu[pdu_ptr+2], pdu[pdu_ptr+3],
		pdu[pdu_ptr+4], pdu[pdu_ptr+5], pdu[pdu_ptr+6]);
	pdu_ptr += 7;
	return(0);
}

static
handle_vp()
{
	int rc;

	switch (first_octet & 0x18) {
	case 0x00:
		rc = 0;
		break;
	case 0x08:
		rc = handle_vp_enh();
		break;
	case 0x10:
		rc = handle_vp_rel();
		break;
	case 0x18:
		rc = handle_vp_abs();
		break;
	}
	return(rc);
}

process_pdu(require_exact_length, expect_sca)
{
	unsigned udl, udl_octets;
	unsigned udhl, udh_octets, udh_chars, ud_chars;
	u_char ud7[160], decode_buf[481];
	int do_hexdump;
	unsigned decoded_len;

	if (expect_sca) {
		if (handle_sca() < 0)
			return(-1);
	} else
		pdu_ptr = 0;
	if (handle_first_octet() < 0)
		return(-1);
	if (first_octet & 2) {
		printf("Decode-Error: MTI not supported\n");
		return(-1);
	}
	if (first_octet & 1) {
		if (handle_mr() < 0)
			return(-1);
	}
	if (handle_user_addr(first_octet & 1 ? "To" : "From") < 0)
		return(-1);
	if (handle_pid() < 0)
		return(-1);
	if (handle_dcs() < 0)
		return(-1);
	if (first_octet & 1) {
		if (handle_vp() < 0)
			return(-1);
	} else {
		if (handle_scts() < 0)
			return(-1);
	}
	if (pdu_ptr >= pdu_length) {
		printf("Decode-Error: end of PDU before UDL\n");
		return(-1);
	}
	udl = pdu[pdu_ptr++];
	if (dcs_distilled == 7) {
		if (udl > 160) {
			printf("Decode-Error: UDL %u > 160\n", udl);
			return(-1);
		}
		udl_octets = (udl * 7 + 7) / 8;
	} else {
		if (udl > 140) {
			printf("Decode-Error: UDL %u > 140\n", udl);
			return(-1);
		}
		udl_octets = udl;
	}
	if (require_exact_length && pdu_length - pdu_ptr != udl_octets) {
		printf("Decode-Error: UD length in PDU %u != expected %u\n",
			pdu_length - pdu_ptr, udl_octets);
		return(-1);
	}
	if (first_octet & 0x40) {
		if (!udl) {
			printf("Decode-Error: UDHI set with UDL=0\n");
			return(-1);
		}
		udhl = pdu[pdu_ptr];
		udh_octets = udhl + 1;
		if (udh_octets > udl_octets) {
			printf("Decode-Error: UDHL exceeds UDL\n");
			return(-1);
		}
		printf("UDH-Length: %u\n", udhl);
		if (dcs_distilled == 7)
			udh_chars = (udh_octets * 8 + 6) / 7;
		else
			udh_chars = udh_octets;
	} else {
		udhl = 0;
		udh_octets = 0;
		udh_chars = 0;
	}
	if (udh_chars >= udl) {
		ud_chars = 0;
		printf("Length: 0\n");
	} else {
		ud_chars = udl - udh_chars;
		if (dcs_distilled == 7)
			gsm7_unpack(pdu + pdu_ptr, ud7, udl);
		if (global_hexdump_mode)
			do_hexdump = 1;
		else switch (dcs_distilled) {
		case 7:
			do_hexdump = 0;
			break;
		case 8:
		case 9:
			do_hexdump = 1;
			break;
		case 16:
			if (ud_chars & 1)
				do_hexdump = 1;
			else
				do_hexdump = 0;
			break;
		}
		if (do_hexdump)
			printf("Length: %u (raw)\n", ud_chars);
		else {
			switch (dcs_distilled) {
			case 7:
				gsm7_to_ascii_or_ext(ud7 + udh_chars, ud_chars,
						     decode_buf, &decoded_len,
						     ascii_ext_mode, 1);
				break;
			case 16:
				ucs2_to_ascii_or_ext(pdu + pdu_ptr + udh_chars,
						     ud_chars,
						     decode_buf, &decoded_len,
						     ascii_ext_mode, 1);
				break;
			}
			printf("Length: %u", ud_chars);
			if (decoded_len != ud_chars)
				printf("->%u", decoded_len);
			putchar('\n');
		}
	}

	if (udhl) {
		printf("\nUDH:\n");
		msg_bits_hexdump(pdu + pdu_ptr + 1, udhl);
	}
	if (!ud_chars)
		return(0);
	putchar('\n');
	if (do_hexdump) {
		if (dcs_distilled == 7)
			msg_bits_hexdump(ud7 + udh_chars, ud_chars);
		else
			msg_bits_hexdump(pdu + pdu_ptr + udh_chars, ud_chars);
	} else
		puts(decode_buf);
	return(0);
}