Skip to content

Commit b959175

Browse files
committed
app: sms: More robust handling of concat SMS receiving
Fixes receiving of concatenated SMS messages in mixed order with normal short messages. Two concatenated message receiving in mixed order is still blocked as it's not important use case now. Dynamic memory is allocated to concatenated SMS messages. Earlier limit of 3 messages is now made to 10. This could have been unlimited but a limit is for security reasons. Jira: SM-36 Signed-off-by: Tommi Rantanen <tommi.rantanen@nordicsemi.no>
1 parent dea9eb0 commit b959175

1 file changed

Lines changed: 157 additions & 93 deletions

File tree

app/src/sm_at_sms.c

Lines changed: 157 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@
66
#include <zephyr/logging/log.h>
77
#include <zephyr/kernel.h>
88
#include <stdio.h>
9+
#include <stdlib.h>
910
#include <string.h>
1011
#include <modem/sms.h>
1112
#include "sm_util.h"
1213
#include "sm_at_host.h"
1314

1415
LOG_MODULE_REGISTER(sm_sms, CONFIG_SM_LOG_LEVEL);
1516

16-
#define MAX_CONCATENATED_MESSAGE 3
17+
#define MAX_CONCATENATED_MESSAGE 10
18+
#define SM_SMS_AT_HEADER_INFO_MAX_LEN 64
19+
/* Maximum time to wait for the next part of the concatenated message to be received in minutes */
20+
#define MAX_CONCATENATED_MESSAGE_AGE K_MINUTES(3)
1721

1822
/**@brief SMS operations. */
1923
enum sm_sms_operation {
@@ -22,102 +26,161 @@ enum sm_sms_operation {
2226
AT_SMS_SEND
2327
};
2428

25-
static int sms_handle = -1;
26-
static struct modem_pipe *sms_pipe;
29+
static void sms_concat_cleanup_work_fn(struct k_work *work);
2730

28-
static void sms_callback(struct sms_data *const data, void *context)
31+
struct sm_sms_context {
32+
int sms_handle;
33+
struct modem_pipe *pipe; /* Pipe requesting SMS service */
34+
uint16_t ref_number; /* Reference number in the concatenated message */
35+
uint8_t total_msgs; /* Total number of messages in the concatenated message */
36+
uint8_t count; /* Current number of messages received in the concatenated message */
37+
char *concat_rsp_buf; /* Buffer for the concatenated message */
38+
struct k_work_delayable cleanup_work;
39+
};
40+
41+
static struct sm_sms_context sms_ctx = {
42+
.sms_handle = -1,
43+
.concat_rsp_buf = NULL,
44+
.cleanup_work = Z_WORK_DELAYABLE_INITIALIZER(sms_concat_cleanup_work_fn),
45+
};
46+
47+
static void sms_concat_clear(struct sm_sms_context *ctx)
48+
{
49+
k_work_cancel_delayable(&ctx->cleanup_work);
50+
if (ctx->concat_rsp_buf != NULL) {
51+
free(ctx->concat_rsp_buf);
52+
ctx->concat_rsp_buf = NULL;
53+
}
54+
ctx->ref_number = 0;
55+
ctx->total_msgs = 0;
56+
ctx->count = 0;
57+
}
58+
59+
static void sms_concat_cleanup_work_fn(struct k_work *work)
60+
{
61+
struct k_work_delayable *dwork = CONTAINER_OF(work, struct k_work_delayable, work);
62+
struct sm_sms_context *ctx = CONTAINER_OF(dwork, struct sm_sms_context, cleanup_work);
63+
64+
sms_concat_clear(ctx);
65+
LOG_INF("Concat msg timed out, ref_number %u", ctx->ref_number);
66+
}
67+
68+
static void sms_concat_handle(struct sms_data *const data)
2969
{
30-
static uint16_t ref_number;
31-
static uint8_t total_msgs;
32-
static uint8_t count;
33-
static char messages[MAX_CONCATENATED_MESSAGE - 1][SMS_MAX_PAYLOAD_LEN_CHARS + 1];
34-
static char rsp_buf[MAX_CONCATENATED_MESSAGE * SMS_MAX_PAYLOAD_LEN_CHARS + 64] = {0};
70+
struct sms_deliver_header *header = &data->header.deliver;
71+
72+
LOG_DBG("Concat msg %d, %d, %d",
73+
header->concatenated.ref_number,
74+
header->concatenated.total_msgs,
75+
header->concatenated.seq_number);
3576

77+
/* ref_number and total_msgs should remain unchanged.
78+
* If they are different, this is a different concatenated message
79+
* and we discard the current and try with the new message.
80+
* We may end up in changing from one message to another one if two
81+
* concatenated messages are received at the same time in mixed order.
82+
*/
83+
if (sms_ctx.ref_number != 0 && sms_ctx.ref_number != header->concatenated.ref_number) {
84+
LOG_ERR("Concat msg ref_number error: %d, %d",
85+
sms_ctx.ref_number, header->concatenated.ref_number);
86+
sms_concat_clear(&sms_ctx);
87+
}
88+
if (sms_ctx.ref_number == 0) {
89+
sms_ctx.ref_number = header->concatenated.ref_number;
90+
91+
if (header->concatenated.total_msgs > MAX_CONCATENATED_MESSAGE) {
92+
LOG_WRN("Ignoring concat msg with %d messages (max: %d)",
93+
header->concatenated.total_msgs, MAX_CONCATENATED_MESSAGE);
94+
goto done;
95+
}
96+
97+
/* Allocate buffer for concatenated message. The allocation
98+
* size is an upper boundary as headers and last message part
99+
* are slightly less in practice.
100+
*/
101+
uint16_t concat_msg_len =
102+
SM_SMS_AT_HEADER_INFO_MAX_LEN +
103+
SMS_MAX_PAYLOAD_LEN_CHARS * header->concatenated.total_msgs;
104+
sms_ctx.concat_rsp_buf = calloc(1, concat_msg_len);
105+
if (sms_ctx.concat_rsp_buf == NULL) {
106+
LOG_ERR("Concat msg no memory for %d bytes, %d messages",
107+
concat_msg_len, header->concatenated.total_msgs);
108+
goto done;
109+
}
110+
}
111+
if (sms_ctx.total_msgs == 0) {
112+
sms_ctx.total_msgs = header->concatenated.total_msgs;
113+
}
114+
if (sms_ctx.total_msgs != header->concatenated.total_msgs) {
115+
LOG_ERR("Concat msg total_msgs error: %d, %d",
116+
sms_ctx.total_msgs, header->concatenated.total_msgs);
117+
goto done;
118+
}
119+
/* seq_number should start with 1 but could arrive in random order */
120+
if (header->concatenated.seq_number == 0 ||
121+
header->concatenated.seq_number > sms_ctx.total_msgs) {
122+
LOG_ERR("Concat msg seq_number error: %d, %d",
123+
header->concatenated.seq_number, sms_ctx.total_msgs);
124+
goto done;
125+
}
126+
if (header->concatenated.seq_number == 1) {
127+
sprintf(sms_ctx.concat_rsp_buf,
128+
"\r\n#XSMS: \"%02d-%02d-%02d %02d:%02d:%02d "
129+
"UTC%+03d:%02d\",\"%s\",\"%s\"",
130+
header->time.year, header->time.month, header->time.day,
131+
header->time.hour, header->time.minute, header->time.second,
132+
header->time.timezone * 15 / 60,
133+
abs(header->time.timezone) * 15 % 60,
134+
header->originating_address.address_str,
135+
data->payload);
136+
} else {
137+
strcpy(sms_ctx.concat_rsp_buf + SM_SMS_AT_HEADER_INFO_MAX_LEN +
138+
(header->concatenated.seq_number - 1) * SMS_MAX_PAYLOAD_LEN_CHARS,
139+
data->payload);
140+
}
141+
sms_ctx.count++;
142+
if (sms_ctx.count == sms_ctx.total_msgs) {
143+
for (int i = 1; i < (sms_ctx.total_msgs); i++) {
144+
strncat(sms_ctx.concat_rsp_buf,
145+
sms_ctx.concat_rsp_buf + SM_SMS_AT_HEADER_INFO_MAX_LEN +
146+
i * SMS_MAX_PAYLOAD_LEN_CHARS,
147+
SMS_MAX_PAYLOAD_LEN_CHARS);
148+
}
149+
strcat(sms_ctx.concat_rsp_buf, "\"\r\n");
150+
urc_send_to(sms_ctx.pipe, "%s", sms_ctx.concat_rsp_buf);
151+
} else {
152+
/* If new messages for the concatenated message are not received
153+
* within 3 minutes, discard the concatenated message.
154+
*/
155+
(void)k_work_reschedule(&sms_ctx.cleanup_work, MAX_CONCATENATED_MESSAGE_AGE);
156+
return;
157+
}
158+
done:
159+
sms_concat_clear(&sms_ctx);
160+
}
161+
162+
static void sms_callback(struct sms_data *const data, void *context)
163+
{
36164
ARG_UNUSED(context);
37165

38166
if (data == NULL) {
39-
LOG_WRN("NULL data");
40167
return;
41168
}
42169

43170
if (data->type == SMS_TYPE_DELIVER) {
44171
struct sms_deliver_header *header = &data->header.deliver;
45172

46173
if (!header->concatenated.present) {
47-
sprintf(rsp_buf,
48-
"\r\n#XSMS: \"%02d-%02d-%02d %02d:%02d:%02d UTC%+03d:%02d\",\"",
174+
urc_send_to(sms_ctx.pipe,
175+
"\r\n#XSMS: \"%02d-%02d-%02d %02d:%02d:%02d UTC%+03d:%02d\",\""
176+
"%s\",\"%s\"\r\n",
49177
header->time.year, header->time.month, header->time.day,
50178
header->time.hour, header->time.minute, header->time.second,
51179
header->time.timezone * 15 / 60,
52-
abs(header->time.timezone) * 15 % 60);
53-
strcat(rsp_buf, header->originating_address.address_str);
54-
strcat(rsp_buf, "\",\"");
55-
strcat(rsp_buf, data->payload);
56-
strcat(rsp_buf, "\"\r\n");
57-
urc_send_to(sms_pipe, "%s", rsp_buf);
180+
abs(header->time.timezone) * 15 % 60,
181+
header->originating_address.address_str, data->payload);
58182
} else {
59-
LOG_DBG("concatenated message %d, %d, %d",
60-
header->concatenated.ref_number,
61-
total_msgs = header->concatenated.total_msgs,
62-
header->concatenated.seq_number);
63-
/* ref_number and total_msgs should remain unchanged */
64-
if (ref_number == 0) {
65-
ref_number = header->concatenated.ref_number;
66-
}
67-
if (ref_number != header->concatenated.ref_number) {
68-
LOG_ERR("SMS concatenated message ref_number error: %d, %d",
69-
ref_number, header->concatenated.ref_number);
70-
goto done;
71-
}
72-
if (total_msgs == 0) {
73-
total_msgs = header->concatenated.total_msgs;
74-
}
75-
if (total_msgs != header->concatenated.total_msgs) {
76-
LOG_ERR("SMS concatenated message total_msgs error: %d, %d",
77-
total_msgs, header->concatenated.total_msgs);
78-
goto done;
79-
}
80-
if (total_msgs > MAX_CONCATENATED_MESSAGE) {
81-
LOG_ERR("SMS concatenated message no memory: %d", total_msgs);
82-
goto done;
83-
}
84-
/* seq_number should start with 1 but could arrive in random order */
85-
if (header->concatenated.seq_number == 0 ||
86-
header->concatenated.seq_number > total_msgs) {
87-
LOG_ERR("SMS concatenated message seq_number error: %d, %d",
88-
header->concatenated.seq_number, total_msgs);
89-
goto done;
90-
}
91-
if (header->concatenated.seq_number == 1) {
92-
sprintf(rsp_buf,
93-
"\r\n#XSMS: \"%02d-%02d-%02d %02d:%02d:%02d "
94-
"UTC%+03d:%02d\",\"",
95-
header->time.year, header->time.month, header->time.day,
96-
header->time.hour, header->time.minute, header->time.second,
97-
header->time.timezone * 15 / 60,
98-
abs(header->time.timezone) * 15 % 60);
99-
strcat(rsp_buf, header->originating_address.address_str);
100-
strcat(rsp_buf, "\",\"");
101-
strcat(rsp_buf, data->payload);
102-
count++;
103-
} else {
104-
strcpy(messages[header->concatenated.seq_number - 2],
105-
data->payload);
106-
count++;
107-
}
108-
if (count == total_msgs) {
109-
for (int i = 0; i < (total_msgs - 1); i++) {
110-
strcat(rsp_buf, messages[i]);
111-
}
112-
strcat(rsp_buf, "\"\r\n");
113-
urc_send_to(sms_pipe, "%s", rsp_buf);
114-
} else {
115-
return;
116-
}
117-
done:
118-
ref_number = 0;
119-
total_msgs = 0;
120-
count = 0;
183+
sms_concat_handle(data);
121184
}
122185
} else {
123186
LOG_WRN("Unknown type: %d", data->type);
@@ -128,25 +191,26 @@ static int do_sms_start(void)
128191
{
129192
int err = 0;
130193

131-
if (sms_handle >= 0) {
194+
if (sms_ctx.sms_handle >= 0) {
132195
/* already registered */
133196
return -EBUSY;
134197
}
135198

136-
sms_handle = sms_register_listener(sms_callback, NULL);
137-
if (sms_handle < 0) {
138-
err = sms_handle;
139-
LOG_ERR("SMS start error: %d", err);
140-
sms_handle = -1;
199+
sms_ctx.sms_handle = sms_register_listener(sms_callback, NULL);
200+
if (sms_ctx.sms_handle < 0) {
201+
err = sms_ctx.sms_handle;
202+
LOG_ERR("Start error: %d", err);
203+
sms_ctx.sms_handle = -1;
141204
}
142205

143206
return err;
144207
}
145208

146209
static int do_sms_stop(void)
147210
{
148-
sms_unregister_listener(sms_handle);
149-
sms_handle = -1;
211+
sms_unregister_listener(sms_ctx.sms_handle);
212+
sms_ctx.sms_handle = -1;
213+
sms_concat_clear(&sms_ctx);
150214

151215
return 0;
152216
}
@@ -155,14 +219,14 @@ static int do_sms_send(const char *number, const char *message, uint16_t message
155219
{
156220
int err;
157221

158-
if (sms_handle < 0) {
159-
LOG_ERR("SMS not registered");
222+
if (sms_ctx.sms_handle < 0) {
223+
LOG_ERR("Not registered");
160224
return -EPERM;
161225
}
162226

163227
err = sms_send(number, message, message_len, SMS_DATA_TYPE_ASCII);
164228
if (err) {
165-
LOG_ERR("SMS send error: %d", err);
229+
LOG_ERR("Send error: %d", err);
166230
}
167231

168232
return err;
@@ -183,7 +247,7 @@ static int handle_at_sms(enum at_parser_cmd_type cmd_type, struct at_parser *par
183247
if (op == AT_SMS_STOP) {
184248
err = do_sms_stop();
185249
} else if (op == AT_SMS_START) {
186-
sms_pipe = sm_at_host_get_current_pipe();
250+
sms_ctx.pipe = sm_at_host_get_current_pipe();
187251
err = do_sms_start();
188252
} else if (op == AT_SMS_SEND) {
189253
char number[SMS_MAX_ADDRESS_LEN_CHARS + 1];
@@ -201,7 +265,7 @@ static int handle_at_sms(enum at_parser_cmd_type cmd_type, struct at_parser *par
201265
}
202266
err = do_sms_send(number, msg_ptr, size);
203267
} else {
204-
LOG_WRN("Unknown SMS operation: %d", op);
268+
LOG_WRN("Unknown operation: %d", op);
205269
err = -EINVAL;
206270
}
207271
break;

0 commit comments

Comments
 (0)