summaryrefslogtreecommitdiff
path: root/src/pdu_decode.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pdu_decode.c')
-rw-r--r--src/pdu_decode.c340
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;
+}