Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 133 additions & 26 deletions application/tom_modem/src/extlib/pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -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; i++) {
if (buffer[i] < 128) {
for (i = 0; i < buffer_length; i++) {
if ((unsigned char)buffer[i] < 128) {
if (buffer[i] == GSM_7BITS_ESCAPE) {
buffer[i] = gsm7bits_extend_to_latin1[buffer[i + 1]];
memmove(&buffer[i + 1], &buffer[i + 2], buffer_length - i - 1);
if (i + 1 >= 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]];
}
}
}
Expand Down Expand Up @@ -339,16 +448,14 @@ 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.

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);
Expand All @@ -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.
Expand All @@ -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:
Expand Down
24 changes: 20 additions & 4 deletions application/tom_modem/src/operations.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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)
{
Expand Down
115 changes: 109 additions & 6 deletions application/tom_modem/src/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -94,22 +106,113 @@ 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:
{
// 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:
Expand Down
Loading