diff options
Diffstat (limited to 'src/pdu_decode.c')
-rw-r--r-- | src/pdu_decode.c | 340 |
1 files changed, 340 insertions, 0 deletions
diff --git a/src/pdu_decode.c b/src/pdu_decode.c new file mode 100644 index 0000000..643fa15 --- /dev/null +++ b/src/pdu_decode.c @@ -0,0 +1,340 @@ +/* + * PDU Decode + * + * Copyright (C) 2010 by James Maki + * + * Author: James Maki <jmaki@multitech.com> + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define _GNU_SOURCE + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <stdint.h> +#include <ctype.h> + +#include "config.h" +#include "log.h" +#include "utils.h" +#include "pdu_decode.h" + +#define DECODE_FAIL(cond, name, ret) \ +do { \ + if (cond) { \ + log_error("decode failed at %s", name); \ + return ret; \ + } \ +} while (0) + +int pdu_decode_timestamp(const char *pdu_str, struct tm *tm) +{ + char buf[PDU_TIMESTAMP_SIZE + 3]; + char *cp; + int off_upper; + int off_lower; + int off; + + STRLEN_CHECK(pdu_str, PDU_TIMESTAMP_LEN, -1); + + memset(tm, 0, sizeof(*tm)); + memset(buf, 0, sizeof(buf)); + + strncpy(buf, pdu_str, PDU_TIMESTAMP_LEN); + nibble_swap(buf, PDU_TIMESTAMP_LEN); + strunpad(buf, 'F'); + + off_upper = hex_nibble_scan(buf + GMT_OFFSET_IDX, 1); + off_lower = hex_nibble_scan(buf + GMT_OFFSET_IDX + 1, 1); + + if (off_upper & BIT(3)) { + off_upper &= ~BIT(3); + buf[GMT_OFFSET_IDX] = '-'; + } else { + buf[GMT_OFFSET_IDX] = '+'; + } + + off = (off_upper * 10 + off_lower) * 15; + + snprintf(buf + GMT_OFFSET_IDX + 1, 5, "%02d%02d", off / 60, off % 60); + + cp = (char *) strptime(buf, "%y%m%d%H%M%S%z", tm); + if (!cp || *cp) { + log_error("timestamp could not be converted to tm"); + return -1; + } + + return PDU_TIMESTAMP_LEN; +} + +int pdu_decode_addr(const char *pdu_str, struct pdu_addr *addr, int smsc) +{ + const char *begin = pdu_str; + int addr_len; + int tmp; + + memset(addr, 0, sizeof(*addr)); + + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "addr-len", -1); + pdu_str += HEX_BYTE_LEN; + addr->len = tmp; + + addr_len = addr->len; + if (smsc) { + if (!addr_len) { + goto done; + } + addr_len = addr_len * HEX_BYTE_LEN - HEX_BYTE_LEN; + } else { + if (addr_len & 1) { + addr_len++; + } + } + + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "addr-type", -1); + pdu_str += HEX_BYTE_LEN; + addr->type = tmp; + + if (addr_len < 0 || addr_len >= sizeof(addr->addr)) { + log_error("invalid length: 0x%02X", addr_len); + return -1; + } + + log_debug("addr-len [transformed]: 0x%02X", addr_len); + log_debug("addr-type: 0x%02X", addr->type); + + STRLEN_CHECK(pdu_str, addr_len, -1); + + strncpy(addr->addr, pdu_str, addr_len); + + nibble_swap(addr->addr, addr_len); + strunpad(addr->addr, 'F'); + pdu_str += addr_len; + +done: + + log_debug("addr: %s", addr->addr); + log_debug("addr-len: 0x%02X", addr->len); + + return pdu_str - begin; +} + +#define octet_idx(n) octet_align(n) + +#define decode_septet(buf, n) \ +shiftl(bit_maskr(cycledown(n, 8)) & buf[octet_idx(n)], cycleup(n, 8)) | \ +shiftr(bit_maskl(cycleup(n, 8), 8) & buf[octet_idx(n - 1)], cycledown(n - 1, 8)) + +int pdu_decode_user_data(const char *pdu_str, struct pdu_info *pdu) +{ + const char *begin = pdu_str; + int tmp; + uint8_t octets[PDU_OCTETS_MAX + 1]; + int nr_octets; + int i; + + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "user-data-len", -1); + pdu_str += HEX_BYTE_LEN; + pdu->user_data_len = tmp; + + if (pdu->data_coding.general.unused) { + log_error("data coding group 0x%02X not implemented", + pdu->data_coding.data_coding & 0xF0); + return -1; + } + + if (pdu->data_coding.general.alphabet == PDU_ALPHABET_DEFAULT) { + log_debug("data coding alphabet is default"); + + if (pdu->user_data_len > PDU_UD_7BIT_MAX) { + log_warning("pdu contains invalid user-data-len: 0x%02X", + pdu->user_data_len); + pdu->user_data_len = PDU_UD_7BIT_MAX; + } + + nr_octets = octet_align(pdu->user_data_len); + } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_EIGHT) { + log_debug("data coding alphabet is eight"); + + if (pdu->user_data_len > PDU_UD_8BIT_MAX) { + log_warning("pdu contains invalid user-data-len: 0x%02X", + pdu->user_data_len); + pdu->user_data_len = PDU_UD_8BIT_MAX; + } + + nr_octets = pdu->user_data_len; + } else { + log_debug("data coding alphabet 0x%02X not implemented", + pdu->data_coding.general.alphabet); + return -1; + } + + STRLEN_CHECK(pdu_str, nr_octets * 2, -1); + + log_debug("user-data-len: 0x%02X", pdu->user_data_len); + log_debug("nr_octets: 0x%02X", nr_octets); + + for (i = 0; i < nr_octets; i++) { + octets[i] = hex_byte_decode(pdu_str); + pdu_str += HEX_BYTE_LEN; + } + + if (pdu->data_coding.general.alphabet == PDU_ALPHABET_DEFAULT) { + for (i = 0; i < pdu->user_data_len; i++) { + pdu->user_data[i] = decode_septet(octets, i); + } + pdu->user_data[i] = '\0'; + } else { + for (i = 0; i < pdu->user_data_len; i++) { + pdu->user_data[i] = octets[i]; + } + pdu->user_data[i] = '\0'; + } + + return pdu_str - begin; +} + +int pdu_decode(const char *pdu_str, struct pdu_info *pdu) +{ + const char *begin = pdu_str; + const char *msg_begin; + int tmp; + + memset(pdu, 0, sizeof(*pdu)); + + tmp = pdu_decode_addr(pdu_str, &pdu->smsc_addr, 1); + DECODE_FAIL(tmp < 0, "smsc-addr", -1); + pdu_str += tmp; + + msg_begin = pdu_str; + + log_debug("smsc-addr: %s", pdu->smsc_addr.addr); + log_debug("smsc-addr-type: 0x%02X", pdu->smsc_addr.type); + log_debug("smsc-addr-len: 0x%02X", pdu->smsc_addr.len); + + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "type", -1); + pdu_str += HEX_BYTE_LEN; + pdu->type.type = tmp; + + log_debug("type: 0x%02X", pdu->type.type); + + if (pdu->type.msg_type == PDU_MTI_SUBMIT || + pdu->type.msg_type == PDU_MTI_REPORT) { + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "msg-reference", -1); + pdu_str += HEX_BYTE_LEN; + pdu->msg_reference = tmp; + + log_debug("msg-reference: 0x%02X", pdu->msg_reference); + } + + tmp = pdu_decode_addr(pdu_str, &pdu->addr, 0); + DECODE_FAIL(tmp < 0, "addr", -1); + pdu_str += tmp; + + log_debug("addr: %s", pdu->addr.addr); + log_debug("addr-type: 0x%02X", pdu->addr.type); + log_debug("addr-len: 0x%02X", pdu->addr.len); + + switch (pdu->type.msg_type) { + case PDU_MTI_REPORT: + tmp = pdu_decode_timestamp(pdu_str, &pdu->timestamp); + DECODE_FAIL(tmp < 0, "report-timestamp", -1); + pdu_str += tmp; + + break; + case PDU_MTI_DELIVER: + case PDU_MTI_SUBMIT: + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "protocol-id", -1); + pdu_str += HEX_BYTE_LEN; + pdu->protocol_id = tmp; + + log_debug("protocol-id: 0x%02X", pdu->protocol_id); + + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "data-coding-scheme", -1); + pdu_str += HEX_BYTE_LEN; + pdu->data_coding.data_coding = tmp; + + log_debug("data-coding-scheme: 0x%02X", pdu->data_coding.data_coding); + + if (pdu->type.msg_type == PDU_MTI_SUBMIT) { + log_debug("validity-period-format: 0x%02X", + pdu->type.validity_period_format); + switch (pdu->type.validity_period_format) { + case PDU_VPF_RELATIVE: + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "validity-period", -1); + pdu_str += HEX_BYTE_LEN; + pdu->validity_period = tmp; + + log_debug("validity-period: 0x%02X", pdu->validity_period); + + break; + + case PDU_VPF_ENHANCED: + log_warning("PDU_VPF_ENHANCED? Falling through to absolute"); + case PDU_VPF_ABSOUTE: + tmp = pdu_decode_timestamp(pdu_str, &pdu->timestamp); + DECODE_FAIL(tmp < 0, "validity-period-timestamp", -1); + pdu_str += tmp; + + return -1; + + case PDU_VPF_NOT_PRESENT: + default: + break; + } + } else if (pdu->type.msg_type == PDU_MTI_DELIVER) { + tmp = pdu_decode_timestamp(pdu_str, &pdu->timestamp); + DECODE_FAIL(tmp < 0, "delivery-timestamp", -1); + pdu_str += tmp; + } + + tmp = pdu_decode_user_data(pdu_str, pdu); + DECODE_FAIL(tmp < 0, "user-data", -1); + pdu_str += tmp; + } + + pdu->msg_len = (pdu_str - msg_begin) / 2; + + log_debug("msg_len: %d", pdu->msg_len); + + return pdu_str - begin; +} |