diff --git a/application/tom_modem/src/extlib/pdu.c b/application/tom_modem/src/extlib/pdu.c index 7e140fc9..d6275f6f 100644 --- a/application/tom_modem/src/extlib/pdu.c +++ b/application/tom_modem/src/extlib/pdu.c @@ -142,19 +142,128 @@ 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, }; +/* 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. */ +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 +parse_udh(const unsigned char *buffer, int buffer_length, int sms_start, + struct udh_info *info) +{ + 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]; + 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 > 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) +G7bitToAscii(char* buffer, int buffer_length, int use_turkish_locking, + int use_turkish_single) { int i; + const unsigned char *ext_table = use_turkish_single ? turkish_extend_to_latin1 : gsm7bits_extend_to_latin1; - 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] = 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--; } else { - buffer[i] = gsm7bits_to_latin1[buffer[i]]; + buffer[i] = use_turkish_locking ? gsm7_locking_to_latin1((unsigned char)buffer[i]) : + gsm7bits_to_latin1[(unsigned char)buffer[i]]; } } } @@ -339,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. @@ -348,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); + 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); @@ -367,22 +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]; - } 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. @@ -398,7 +498,14 @@ 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; + 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/operations.c b/application/tom_modem/src/operations.c index d62c45d5..a0316f45 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) { @@ -154,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]; @@ -243,12 +253,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..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, @@ -94,6 +106,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 +164,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: 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; +}