From 82fff5d1446c7787b7f18c55c932f689cbda56db Mon Sep 17 00:00:00 2001 From: Okan Ergin Date: Sat, 29 Aug 2026 13:01:51 +0300 Subject: [PATCH 1/3] tom_modem: add Turkish character support for SMS --- application/tom_modem/src/extlib/pdu.c | 33 ++++++--- application/tom_modem/src/operations.c | 20 ++++-- application/tom_modem/src/utils.c | 99 ++++++++++++++++++++++++-- 3 files changed, 135 insertions(+), 17 deletions(-) diff --git a/application/tom_modem/src/extlib/pdu.c b/application/tom_modem/src/extlib/pdu.c index 7e140fc9..9fcbe023 100644 --- a/application/tom_modem/src/extlib/pdu.c +++ b/application/tom_modem/src/extlib/pdu.c @@ -136,10 +136,10 @@ static const unsigned char gsm7bits_extend_to_latin1[128] = { 0, 0, 0, 0, '^', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{', '}', 0, 0, 0, 0, 0,'\\', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '[', '~', ']', 0, - '|', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + '|', 0, 0, 0xC7, 0, 0, 0, 0xD0, 0, 0xDD, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0xDE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0xE7, 0, 0, 0, 0xF0, 0, 0xFD, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0xFE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; static int @@ -147,14 +147,21 @@ G7bitToAscii(char* buffer, int buffer_length) { int i; - for (i = 0; i= buffer_length) { + // Bozuk veri: ESCAPE tamponun son baytı, genişletilecek + // karakter yok. Bu baytı at ve döngüyü sonlandır. + buffer_length--; + break; + } + buffer[i] = gsm7bits_extend_to_latin1[(unsigned char)buffer[i + 1]]; + if (i + 2 < buffer_length) + memmove(&buffer[i + 1], &buffer[i + 2], buffer_length - i - 2); buffer_length--; } else { - buffer[i] = gsm7bits_to_latin1[buffer[i]]; + buffer[i] = gsm7bits_to_latin1[(unsigned char)buffer[i]]; } } } @@ -376,6 +383,11 @@ int pdu_decode(const unsigned char* buffer, int buffer_length, *ref_number = 0x000000FF&buffer[sms_start + tmp - 2]; *total_parts = 0x000000FF&buffer[sms_start + tmp - 1]; *part_number = 0x000000FF&buffer[sms_start + tmp]; + if (*total_parts <= 1) { + *ref_number = 0; + *total_parts = 0; + *part_number = 0; + } } else { tmp = 0; *skip_bytes = tmp; @@ -398,7 +410,10 @@ int pdu_decode(const unsigned char* buffer, int buffer_length, int decoded_sms_text_size = DecodePDUMessage_GSM_7bit(buffer + sms_start + 1, buffer_length - (sms_start + 1), output_sms_text, output_sms_text_length); if (decoded_sms_text_size != output_sms_text_length) return -1; // Decoder length is not as expected. - output_sms_text_length = G7bitToAscii(output_sms_text, output_sms_text_length); + int skip_septets = 0; + if (*skip_bytes > 0) + skip_septets = (*skip_bytes * 8 + 6) / 7; + output_sms_text_length = skip_septets + G7bitToAscii(output_sms_text + skip_septets, output_sms_text_length - skip_septets); break; } case 2: diff --git a/application/tom_modem/src/operations.c b/application/tom_modem/src/operations.c index d62c45d5..4cce4b27 100644 --- a/application/tom_modem/src/operations.c +++ b/application/tom_modem/src/operations.c @@ -114,12 +114,18 @@ int sms_read(PROFILE_T *profile, void *transport_ptr) dbg_msg("No PDU found for line: %s", line); pdu = strtok(NULL, "\n"); } - sms->sms_pdu = (char *)malloc(strlen(pdu)); + if (pdu == NULL) { + dbg_msg("PDU still NULL after fallback, skipping entry for line: %s", line); + free(sms); + line = strtok(NULL, "\n"); + continue; + } + sms->sms_pdu = (char *)malloc(strlen(pdu) + 1); sms->sender = (char *)malloc(PHONE_NUMBER_SIZE); sms->sms_text = (char *)malloc(SMS_TEXT_SIZE); memset(sms->sms_text, 0, SMS_TEXT_SIZE); sms->sms_index = get_sms_index(line); - memcpy(sms->sms_pdu, pdu, strlen(pdu)); + memcpy(sms->sms_pdu, pdu, strlen(pdu) + 1); int sms_len = decode_pdu(sms); if (sms_len > 0) { @@ -243,12 +249,18 @@ int sms_read_unread(PROFILE_T *profile, void *transport_ptr) dbg_msg("No PDU found for line: %s", line); pdu = strtok(NULL, "\n"); } - sms->sms_pdu = (char *)malloc(strlen(pdu)); + if (pdu == NULL) { + dbg_msg("PDU still NULL after fallback, skipping entry for line: %s", line); + free(sms); + line = strtok(NULL, "\n"); + continue; + } + sms->sms_pdu = (char *)malloc(strlen(pdu) + 1); sms->sender = (char *)malloc(PHONE_NUMBER_SIZE); sms->sms_text = (char *)malloc(SMS_TEXT_SIZE); memset(sms->sms_text, 0, SMS_TEXT_SIZE); sms->sms_index = get_sms_index(line); - memcpy(sms->sms_pdu, pdu, strlen(pdu)); + memcpy(sms->sms_pdu, pdu, strlen(pdu) + 1); int sms_len = decode_pdu(sms); if (sms_len > 0) { diff --git a/application/tom_modem/src/utils.c b/application/tom_modem/src/utils.c index 88db2265..f4324b59 100644 --- a/application/tom_modem/src/utils.c +++ b/application/tom_modem/src/utils.c @@ -94,6 +94,57 @@ int decode_pdu(SMS_T *sms) } sms->sms_lenght = sms_len; + // Convert sender to UTF-8 in place + { + char temp_sender[64]; + int s_offset = 0; + for (int i = 0; sms->sender[i] != '\0'; i++) + { + unsigned char c = (unsigned char)sms->sender[i]; + if (c < 128) + { + temp_sender[s_offset++] = c; + } + else if (c == 0xD0) { // Ğ + temp_sender[s_offset++] = 0xC4; + temp_sender[s_offset++] = 0x9E; + } + else if (c == 0xF0) { // ğ + temp_sender[s_offset++] = 0xC4; + temp_sender[s_offset++] = 0x9F; + } + else if (c == 0xDD) { // İ + temp_sender[s_offset++] = 0xC4; + temp_sender[s_offset++] = 0xB0; + } + else if (c == 0xFD) { // ı + temp_sender[s_offset++] = 0xC4; + temp_sender[s_offset++] = 0xB1; + } + else if (c == 0xDE) { // Ş + temp_sender[s_offset++] = 0xC5; + temp_sender[s_offset++] = 0x9E; + } + else if (c == 0xFE) { // ş + temp_sender[s_offset++] = 0xC5; + temp_sender[s_offset++] = 0x9F; + } + else + { + temp_sender[s_offset++] = 0xC0 | (c >> 6); + temp_sender[s_offset++] = 0x80 | (c & 0x3F); + } + if (s_offset >= 62) break; // prevent temp_sender overflow + } + temp_sender[s_offset] = '\0'; + + // sms->sender, operations.c'de PHONE_NUMBER_SIZE ile ayrilmistir. + // strcpy YERINE strncpy + garantili null-terminate kullaniliyor ki + // temp_sender (64 byte'a kadar) hedef tamponu tasirmasin. + strncpy(sms->sender, temp_sender, PHONE_NUMBER_SIZE - 1); + sms->sender[PHONE_NUMBER_SIZE - 1] = '\0'; + } + switch ((tp_dcs / 4) % 4) { case 0: @@ -101,15 +152,55 @@ int decode_pdu(SMS_T *sms) // GSM 7 bit sms->type = SMS_CHARSET_7BIT; int i; + int offset = 0; i = skip_bytes; if (skip_bytes > 0) i = (skip_bytes * 8 + 6) / 7; - for (; i < strlen(sms_text); i++) + for (; i < sms_len; i++) { - sprintf(sms->sms_text + i, "%c", sms_text[i]); + if (offset >= SMS_TEXT_SIZE - 2) + { + dbg_msg("SMS text truncated to fit buffer (SMS_TEXT_SIZE=%d)", SMS_TEXT_SIZE); + break; + } + unsigned char c = (unsigned char)sms_text[i]; + if (c < 128) + { + sms->sms_text[offset++] = c; + } + else if (c == 0xD0) { // Ğ + sms->sms_text[offset++] = 0xC4; + sms->sms_text[offset++] = 0x9E; + } + else if (c == 0xF0) { // ğ + sms->sms_text[offset++] = 0xC4; + sms->sms_text[offset++] = 0x9F; + } + else if (c == 0xDD) { // İ + sms->sms_text[offset++] = 0xC4; + sms->sms_text[offset++] = 0xB0; + } + else if (c == 0xFD) { // ı + sms->sms_text[offset++] = 0xC4; + sms->sms_text[offset++] = 0xB1; + } + else if (c == 0xDE) { // Ş + sms->sms_text[offset++] = 0xC5; + sms->sms_text[offset++] = 0x9E; + } + else if (c == 0xFE) { // ş + sms->sms_text[offset++] = 0xC5; + sms->sms_text[offset++] = 0x9F; + } + else + { + // Latin-1'den UTF-8'e Donusum + sms->sms_text[offset++] = 0xC0 | (c >> 6); + sms->sms_text[offset++] = 0x80 | (c & 0x3F); + } } - i++; - sprintf(sms->sms_text + i, "%c", '\0'); + if (offset >= SMS_TEXT_SIZE) offset = SMS_TEXT_SIZE - 1; + sms->sms_text[offset] = '\0'; break; } case 2: From 49e2f810ce31273a1b61fd0512a59c9924d69cf0 Mon Sep 17 00:00:00 2001 From: Okan Ergin Date: Mon, 31 Aug 2026 00:36:01 +0300 Subject: [PATCH 2/3] tom_modem: gate Turkish GSM-7 shift table behind UDH language IE Addresses review feedback on #257/#256: the Turkish national language extension mappings were previously written directly into the shared gsm7bits_extend_to_latin1 table, so every GSM 7-bit message decoded through it regardless of language -- reserved extension codes used by other languages/future 3GPP additions could have been misdecoded as Turkish characters. This moves the Turkish mappings into a separate turkish_extend_to_latin1 table and adds udh_has_turkish_shift(), which walks the PDU's User Data Header IEs and only selects that table when a National Language Locking Shift (IEI 0x24) or Single Shift (IEI 0x25) IE explicitly selects Turkish (language id 0x01, per 3GPP TS 23.038 table 6.2.1.2.5). Every other message continues to decode through the original, unmodified table. Also adds a bounds check in sms_send() (operations.c): profile->sms_pdu was hex-encoded into a fixed 512-byte pdu_hex buffer with no upper bound on the source PDU length, which could overflow the stack buffer for a long/malformed PDU. This rejects PDUs longer than 255 chars (the max that fits pdu_hex[512] as hex + terminator) up front. Tested on Arcadyan AW1000 (Quectel RG500Q-EA), IPQ807x: Turkish SMS with the language shift IE set decode correctly; a batch of non-Turkish GSM 7-bit messages (EN/DE test SMS) decode unchanged before and after this patch. --- application/tom_modem/src/extlib/pdu.c | 57 ++++++++++++++++++++++++-- application/tom_modem/src/operations.c | 4 ++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/application/tom_modem/src/extlib/pdu.c b/application/tom_modem/src/extlib/pdu.c index 9fcbe023..ebb035c5 100644 --- a/application/tom_modem/src/extlib/pdu.c +++ b/application/tom_modem/src/extlib/pdu.c @@ -136,16 +136,64 @@ static const unsigned char gsm7bits_extend_to_latin1[128] = { 0, 0, 0, 0, '^', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '{', '}', 0, 0, 0, 0, 0,'\\', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '[', '~', ']', 0, + '|', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* 3GPP TS 23.038 Turkish National Language Single Shift table. + * Only used when the PDU's UDH explicitly selects it (IEI 0x24/0x25, + * language identifier 0x01 = Turkish) -- see udh_has_turkish_shift(). + * Non-Turkish messages must never be decoded through this table. */ +static const unsigned char turkish_extend_to_latin1[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,'\f', 0, 0, 0, 0, 0, + 0, 0, 0, 0, '^', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, '{', '}', 0, 0, 0, 0, 0,'\\', + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '[', '~', ']', 0, '|', 0, 0, 0xC7, 0, 0, 0, 0xD0, 0, 0xDD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xDE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xE7, 0, 0, 0, 0xF0, 0, 0xFD, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; +/* Walks the PDU's User Data Header looking for a National Language + * Locking Shift (IEI 0x24) or Single Shift (IEI 0x25) IE whose data + * byte selects Turkish (0x01, per 3GPP TS 23.038 table 6.2.1.2.5). + * Returns 1 only if such an IE is present; every other message keeps + * using the standard extension table. */ +static int +udh_has_turkish_shift(const unsigned char* buffer, int buffer_length, + int sms_start, int user_data_header_length) +{ + if ((user_data_header_length & 0x04) != 0x04) + return 0; + if (sms_start + 1 >= buffer_length) + return 0; + + const int udhl = buffer[sms_start + 1]; + int pos = sms_start + 2; + const int ie_end = pos + udhl; + if (udhl <= 0 || ie_end > buffer_length) + return 0; + + while (pos + 1 < ie_end) { + const unsigned char iei = buffer[pos]; + const unsigned char iedl = buffer[pos + 1]; + if (pos + 2 + iedl > ie_end) + break; + if ((iei == 0x24 || iei == 0x25) && iedl >= 1 && buffer[pos + 2] == 0x01) + return 1; + pos += 2 + iedl; + } + return 0; +} + static int -G7bitToAscii(char* buffer, int buffer_length) +G7bitToAscii(char* buffer, int buffer_length, int use_turkish) { int i; + const unsigned char *ext_table = use_turkish ? turkish_extend_to_latin1 : gsm7bits_extend_to_latin1; for (i = 0; i < buffer_length; i++) { if (buffer[i] < 128) { @@ -156,7 +204,7 @@ G7bitToAscii(char* buffer, int buffer_length) buffer_length--; break; } - buffer[i] = gsm7bits_extend_to_latin1[(unsigned char)buffer[i + 1]]; + buffer[i] = ext_table[(unsigned char)buffer[i + 1]]; if (i + 2 < buffer_length) memmove(&buffer[i + 1], &buffer[i + 2], buffer_length - i - 2); buffer_length--; @@ -355,7 +403,7 @@ int pdu_decode(const unsigned char* buffer, int buffer_length, const int sender_type_of_address = buffer[sms_deliver_start + 2]; if (sender_type_of_address == TYPE_OF_ADDRESS_ALPHANUMERIC) { int sender_len1 = DecodePDUMessage_GSM_7bit(buffer + sms_deliver_start + 3, (sender_number_length + 1) / 2, output_sender_phone_number, sender_number_length); - int sender_len2 = G7bitToAscii(output_sender_phone_number, sender_len1 - 1); + int sender_len2 = G7bitToAscii(output_sender_phone_number, sender_len1 - 1, 0); output_sender_phone_number[sender_len2] = 0; } else { DecodePhoneNumber(buffer + sms_deliver_start + 3, sender_number_length, output_sender_phone_number); @@ -413,7 +461,8 @@ int pdu_decode(const unsigned char* buffer, int buffer_length, int skip_septets = 0; if (*skip_bytes > 0) skip_septets = (*skip_bytes * 8 + 6) / 7; - output_sms_text_length = skip_septets + G7bitToAscii(output_sms_text + skip_septets, output_sms_text_length - skip_septets); + const int use_turkish = udh_has_turkish_shift(buffer, buffer_length, sms_start, user_data_header_length); + output_sms_text_length = skip_septets + G7bitToAscii(output_sms_text + skip_septets, output_sms_text_length - skip_septets, use_turkish); break; } case 2: diff --git a/application/tom_modem/src/operations.c b/application/tom_modem/src/operations.c index 4cce4b27..a0316f45 100644 --- a/application/tom_modem/src/operations.c +++ b/application/tom_modem/src/operations.c @@ -160,6 +160,10 @@ int sms_send(PROFILE_T *profile, void *transport_ptr) } int pdu_len = strlen(profile->sms_pdu); + if (pdu_len <= 0 || pdu_len > 255) { + dbg_msg("SMS PDU length invalid or too large for buffer (len=%d)", pdu_len); + return INVALID_PARAM; + } int pdu_expected_len = (pdu_len) / 2 - 1; char send_sms_cmd[32]; char pdu_hex[512]; From 0a5b566f414efbf7ef2f8f38aae57c57f493eb21 Mon Sep 17 00:00:00 2001 From: fjr Date: Tue, 8 Sep 2026 10:43:20 +0000 Subject: [PATCH 3/3] tom_modem: fix Turkish UDH parsing and PDU validation --- application/tom_modem/src/extlib/pdu.c | 135 ++++++++++++------ application/tom_modem/src/utils.c | 16 ++- .../tom_modem/tests/test_turkish_udh.c | 20 +++ 3 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 application/tom_modem/tests/test_turkish_udh.c diff --git a/application/tom_modem/src/extlib/pdu.c b/application/tom_modem/src/extlib/pdu.c index ebb035c5..d6275f6f 100644 --- a/application/tom_modem/src/extlib/pdu.c +++ b/application/tom_modem/src/extlib/pdu.c @@ -162,41 +162,94 @@ static const unsigned char turkish_extend_to_latin1[128] = { * byte selects Turkish (0x01, per 3GPP TS 23.038 table 6.2.1.2.5). * Returns 1 only if such an IE is present; every other message keeps * using the standard extension table. */ +struct udh_info { + int present; + int bytes; + int turkish_locking; + int turkish_single; + int concat_ref; + int concat_total; + int concat_part; +}; + +/* Parse every IE in the UDH. Concatenation is not required to be the last + * IE; language shift IEs and application IEs may follow it. */ static int -udh_has_turkish_shift(const unsigned char* buffer, int buffer_length, - int sms_start, int user_data_header_length) +parse_udh(const unsigned char *buffer, int buffer_length, int sms_start, + struct udh_info *info) { - if ((user_data_header_length & 0x04) != 0x04) - return 0; - if (sms_start + 1 >= buffer_length) + memset(info, 0, sizeof(*info)); + if (sms_start < 0 || sms_start + 1 >= buffer_length || + !(buffer[sms_start] & 0x40)) return 0; - const int udhl = buffer[sms_start + 1]; - int pos = sms_start + 2; - const int ie_end = pos + udhl; - if (udhl <= 0 || ie_end > buffer_length) - return 0; - - while (pos + 1 < ie_end) { + const int first = sms_start + 2; + const int end = first + udhl; + if (end > buffer_length) + return -1; + info->present = 1; + info->bytes = udhl + 1; + for (int pos = first; pos < end;) { + if (pos + 2 > end) + return -1; const unsigned char iei = buffer[pos]; const unsigned char iedl = buffer[pos + 1]; - if (pos + 2 + iedl > ie_end) - break; - if ((iei == 0x24 || iei == 0x25) && iedl >= 1 && buffer[pos + 2] == 0x01) - return 1; + if (pos + 2 + iedl > end) + return -1; + if (iei == 0x24 && iedl >= 1 && buffer[pos + 2] == 0x01) + info->turkish_locking = 1; + if (iei == 0x25 && iedl >= 1 && buffer[pos + 2] == 0x01) + info->turkish_single = 1; + if (iei == 0x00 && iedl == 3) { + info->concat_ref = buffer[pos + 2]; + info->concat_total = buffer[pos + 3]; + info->concat_part = buffer[pos + 4]; + } else if (iei == 0x08 && iedl == 4) { + info->concat_ref = (buffer[pos + 2] << 8) | buffer[pos + 3]; + info->concat_total = buffer[pos + 4]; + info->concat_part = buffer[pos + 5]; + } pos += 2 + iedl; } + if (info->concat_total <= 1) { + info->concat_ref = info->concat_total = info->concat_part = 0; + } return 0; } +static unsigned char +gsm7_locking_to_latin1(unsigned char value) +{ + switch (value) { + case 0x07: return 0xFD; /* dotless i */ + case 0x0B: return 0xD0; /* G */ + case 0x0C: return 0xF0; /* g */ + case 0x1C: return 0xDE; /* S */ + case 0x1D: return 0xFE; /* s */ + case 0x40: return 0xDD; /* I with dot */ + case 0x5B: return 0xC4; + case 0x5C: return 0xD6; + case 0x5D: return 0xD1; + case 0x5E: return 0xDC; + case 0x60: return 0xE7; + case 0x7B: return 0xE4; + case 0x7C: return 0xF6; + case 0x7D: return 0xF1; + case 0x7E: return 0xFC; + case 0x7F: return 0xE0; + default: return gsm7bits_to_latin1[value]; + } +} + static int -G7bitToAscii(char* buffer, int buffer_length, int use_turkish) +G7bitToAscii(char* buffer, int buffer_length, int use_turkish_locking, + int use_turkish_single) { int i; - const unsigned char *ext_table = use_turkish ? turkish_extend_to_latin1 : gsm7bits_extend_to_latin1; + const unsigned char *ext_table = use_turkish_single ? turkish_extend_to_latin1 : gsm7bits_extend_to_latin1; for (i = 0; i < buffer_length; i++) { - if (buffer[i] < 128) { + if ((unsigned char)buffer[i] < 128) { if (buffer[i] == GSM_7BITS_ESCAPE) { if (i + 1 >= buffer_length) { // Bozuk veri: ESCAPE tamponun son baytı, genişletilecek @@ -209,7 +262,8 @@ G7bitToAscii(char* buffer, int buffer_length, int use_turkish) memmove(&buffer[i + 1], &buffer[i + 2], buffer_length - i - 2); buffer_length--; } else { - buffer[i] = gsm7bits_to_latin1[(unsigned char)buffer[i]]; + buffer[i] = use_turkish_locking ? gsm7_locking_to_latin1((unsigned char)buffer[i]) : + gsm7bits_to_latin1[(unsigned char)buffer[i]]; } } } @@ -394,8 +448,6 @@ int pdu_decode(const unsigned char* buffer, int buffer_length, if (sms_deliver_start + 1 > buffer_length) return -2; - const int user_data_header_length = (buffer[sms_deliver_start]>>4); - const int sender_number_length = buffer[sms_deliver_start + 1]; if (sender_number_length + 1 > sender_phone_number_size) return -3; // Buffer too small to hold decoded phone number. @@ -403,7 +455,7 @@ int pdu_decode(const unsigned char* buffer, int buffer_length, const int sender_type_of_address = buffer[sms_deliver_start + 2]; if (sender_type_of_address == TYPE_OF_ADDRESS_ALPHANUMERIC) { int sender_len1 = DecodePDUMessage_GSM_7bit(buffer + sms_deliver_start + 3, (sender_number_length + 1) / 2, output_sender_phone_number, sender_number_length); - int sender_len2 = G7bitToAscii(output_sender_phone_number, sender_len1 - 1, 0); + int sender_len2 = G7bitToAscii(output_sender_phone_number, sender_len1 - 1, 0, 0); output_sender_phone_number[sender_len2] = 0; } else { DecodePhoneNumber(buffer + sms_deliver_start + 3, sender_number_length, output_sender_phone_number); @@ -422,27 +474,15 @@ int pdu_decode(const unsigned char* buffer, int buffer_length, (*output_sms_time) = timegm(&sms_broken_time); const int sms_start = sms_pid_start + 2 + 7; - if (sms_start + 1 > buffer_length) return -1; // Invalid input buffer. - - int tmp; - if((user_data_header_length&0x04)==0x04) { - tmp = buffer[sms_start + 1] + 1; - *skip_bytes = tmp; - *ref_number = 0x000000FF&buffer[sms_start + tmp - 2]; - *total_parts = 0x000000FF&buffer[sms_start + tmp - 1]; - *part_number = 0x000000FF&buffer[sms_start + tmp]; - if (*total_parts <= 1) { - *ref_number = 0; - *total_parts = 0; - *part_number = 0; - } - } else { - tmp = 0; - *skip_bytes = tmp; - *ref_number = tmp; - *total_parts = tmp; - *part_number = tmp; - } + if (sms_start + 1 >= buffer_length) return -1; // Invalid input buffer. + + struct udh_info udh; + if (parse_udh(buffer, buffer_length, sms_start, &udh) < 0) + return -1; + *skip_bytes = udh.present ? udh.bytes : 0; + *ref_number = udh.concat_ref; + *total_parts = udh.concat_total; + *part_number = udh.concat_part; int output_sms_text_length = buffer[sms_start]; if (sms_text_size < output_sms_text_length) return -1; // Cannot hold decoded buffer. @@ -461,8 +501,11 @@ int pdu_decode(const unsigned char* buffer, int buffer_length, int skip_septets = 0; if (*skip_bytes > 0) skip_septets = (*skip_bytes * 8 + 6) / 7; - const int use_turkish = udh_has_turkish_shift(buffer, buffer_length, sms_start, user_data_header_length); - output_sms_text_length = skip_septets + G7bitToAscii(output_sms_text + skip_septets, output_sms_text_length - skip_septets, use_turkish); + if (skip_septets > output_sms_text_length) + return -1; + output_sms_text_length = skip_septets + G7bitToAscii(output_sms_text + skip_septets, + output_sms_text_length - skip_septets, udh.turkish_locking, + udh.turkish_single); break; } case 2: diff --git a/application/tom_modem/src/utils.c b/application/tom_modem/src/utils.c index f4324b59..c7146579 100644 --- a/application/tom_modem/src/utils.c +++ b/application/tom_modem/src/utils.c @@ -73,10 +73,22 @@ int decode_pdu(SMS_T *sms) int pdu_str_len; unsigned char hex_pdu[SMS_PDU_HEX_SIZE] = {0}; pdu_str_len = strlen(sms->sms_pdu); + if (pdu_str_len == 0 || (pdu_str_len & 1) != 0 || + pdu_str_len / 2 > (int)sizeof(hex_pdu)) + { + err_msg("Invalid PDU length"); + return -1; + } for (int i = 0; i < pdu_str_len; i += 2) { - hex_pdu[i / 2] = char_to_hex(sms->sms_pdu[i]) << 4; - hex_pdu[i / 2] |= char_to_hex(sms->sms_pdu[i + 1]); + int high = char_to_hex(sms->sms_pdu[i]); + int low = char_to_hex(sms->sms_pdu[i + 1]); + if (high < 0 || low < 0) + { + err_msg("Invalid hexadecimal digit in PDU"); + return -1; + } + hex_pdu[i / 2] = (unsigned char)((high << 4) | low); } int sms_len = pdu_decode(hex_pdu, pdu_str_len/2, &sms->timestamp, diff --git a/application/tom_modem/tests/test_turkish_udh.c b/application/tom_modem/tests/test_turkish_udh.c new file mode 100644 index 00000000..32082cbc --- /dev/null +++ b/application/tom_modem/tests/test_turkish_udh.c @@ -0,0 +1,20 @@ +#include +#include + +#include "../src/extlib/pdu.c" + +int main(void) +{ + struct udh_info info; + unsigned char udh[] = { + 0x40, 0x08, 0x00, 0x03, 0x7a, 0x02, 0x01, + 0x24, 0x01, 0x01 + }; + assert(parse_udh(udh, sizeof(udh), 0, &info) == 0); + assert(info.concat_ref == 0x7a && info.concat_total == 2 && info.concat_part == 1); + assert(info.turkish_locking && !info.turkish_single); + assert(gsm7_locking_to_latin1(0x0b) == 0xd0); + assert(gsm7bits_to_latin1[0x0b] != gsm7_locking_to_latin1(0x0b)); + assert(parse_udh(udh, 5, 0, &info) < 0); + return 0; +}