blob: 470b017f533ba9ac1399e5ba6af8a96d9da925ca [file] [log] [blame]
Harald Welte55d724a2017-10-16 18:25:45 +02001/* Point-to-Point (PP) Short Message Service (SMS).
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +01002 * Support on Mobile Radio Interface
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02003 * 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0. */
4/*
5 * (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +01006 * (C) 2009 by Harald Welte <laforge@gnumonks.org>
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +02007 * (C) 2010-2013 by Holger Hans Peter Freyther <zecke@selfish.org>
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +01008 * (C) 2010 by On-Waves
9 * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
10 *
11 * All Rights Reserved
12 *
Harald Weltee08da972017-11-13 01:00:26 +090013 * SPDX-License-Identifier: GPL-2.0+
14 *
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010015 * This program is free software; you can redistribute it and/or modify
Harald Welte388fb032014-10-26 20:42:49 +010016 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010018 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte388fb032014-10-26 20:42:49 +010023 * GNU General Public License for more details.
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010024 *
Harald Welte388fb032014-10-26 20:42:49 +010025 * You should have received a copy of the GNU General Public License
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010026 * along with this program. If not, see <http://www.gnu.org/licenses/>.
27 *
28 */
29
Pau Espin Pedrol88955fb2023-01-18 18:54:00 +010030#include "config.h"
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010031
32#include <time.h>
33#include <string.h>
34#include <osmocom/core/msgb.h>
35#include <osmocom/core/logging.h>
36
37#include <osmocom/gsm/gsm48.h>
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +020038#include <osmocom/gsm/gsm_utils.h>
39#include <osmocom/gsm/protocol/gsm_03_40.h>
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010040#include <osmocom/gsm/protocol/gsm_04_11.h>
41
Harald Welte96e2a002017-06-12 21:44:18 +020042/*! \addtogroup sms
43 * @{
Harald Welte55d724a2017-10-16 18:25:45 +020044 * \file gsm0411_utils.c
Harald Welte96e2a002017-06-12 21:44:18 +020045 */
46
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010047#define GSM411_ALLOC_SIZE 1024
48#define GSM411_ALLOC_HEADROOM 128
49
Neels Hofmeyr87e45502017-06-20 00:17:59 +020050/*! Allocate a message buffer for use as TS 04.11 message
Harald Welte96e2a002017-06-12 21:44:18 +020051 * \returns allocated message buffer */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010052struct msgb *gsm411_msgb_alloc(void)
53{
54 return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
55 "GSM 04.11");
56}
57
Neels Hofmeyr87e45502017-06-20 00:17:59 +020058/*! Turn int into semi-octet representation: 98 => 0x89
Harald Welte96e2a002017-06-12 21:44:18 +020059 * \param[in] integer value representing decimal number 0..99
60 * \returns BSC encoded as nibbles, swapped */
Harald Weltead633b02011-12-01 21:08:19 +010061uint8_t gsm411_bcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010062{
63 uint8_t ret;
64
65 ret = value / 10;
66 ret |= (value % 10) << 4;
67
68 return ret;
69}
70
Neels Hofmeyr87e45502017-06-20 00:17:59 +020071/*! Turn semi-octet representation into int: 0x89 => 98
Harald Welte96e2a002017-06-12 21:44:18 +020072 * \param[in] value byte containing two BCD nibbles in revere order
73 * \returns integer representing decoded, re-ordered nibbles */
Harald Weltead633b02011-12-01 21:08:19 +010074uint8_t gsm411_unbcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010075{
76 uint8_t ret;
77
78 if ((value & 0x0F) > 9 || (value >> 4) > 9)
79 LOGP(DLSMS, LOGL_ERROR,
Harald Weltead633b02011-12-01 21:08:19 +010080 "gsm411_unbcdify got too big nibble: 0x%02X\n", value);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010081
82 ret = (value&0x0F)*10;
83 ret += value>>4;
84
85 return ret;
86}
87
Neels Hofmeyr87e45502017-06-20 00:17:59 +020088/*! Generate 03.40 TP-SCTS
Harald Welte96e2a002017-06-12 21:44:18 +020089 * \param[out] scts Caller-provided buffer to store SCTS (7 octets)
90 * \param[in] time to encode */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010091void gsm340_gen_scts(uint8_t *scts, time_t time)
92{
Keith733810c2017-08-17 21:37:47 +020093 struct tm *tm = localtime(&time);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010094
Harald Weltead633b02011-12-01 21:08:19 +010095 *scts++ = gsm411_bcdify(tm->tm_year % 100);
96 *scts++ = gsm411_bcdify(tm->tm_mon + 1);
97 *scts++ = gsm411_bcdify(tm->tm_mday);
98 *scts++ = gsm411_bcdify(tm->tm_hour);
99 *scts++ = gsm411_bcdify(tm->tm_min);
100 *scts++ = gsm411_bcdify(tm->tm_sec);
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200101#ifdef HAVE_TM_GMTOFF_IN_TM
102 *scts++ = gsm411_bcdify(tm->tm_gmtoff/(60*15));
103#else
Pau Espin Pedrol29b7d532017-06-18 14:15:16 +0200104#pragma message ("find a portable way to obtain timezone offset")
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200105 *scts++ = 0;
106#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100107}
108
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200109/*! Decode 03.40 TP-SCTS (into utc/gmt timestamp)
Harald Welte96e2a002017-06-12 21:44:18 +0200110 * \param[in] scts SMS Center Time Stamp
111 * \return time in UTC time_t format */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100112time_t gsm340_scts(uint8_t *scts)
113{
114 struct tm tm;
Harald Weltead633b02011-12-01 21:08:19 +0100115 uint8_t yr = gsm411_unbcdify(*scts++);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100116 int ofs;
117
118 memset(&tm, 0x00, sizeof(struct tm));
119
120 if (yr <= 80)
121 tm.tm_year = 100 + yr;
122 else
123 tm.tm_year = yr;
Harald Weltead633b02011-12-01 21:08:19 +0100124 tm.tm_mon = gsm411_unbcdify(*scts++) - 1;
125 tm.tm_mday = gsm411_unbcdify(*scts++);
126 tm.tm_hour = gsm411_unbcdify(*scts++);
127 tm.tm_min = gsm411_unbcdify(*scts++);
128 tm.tm_sec = gsm411_unbcdify(*scts++);
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200129#ifdef HAVE_TM_GMTOFF_IN_TM
130 tm.tm_gmtoff = gsm411_unbcdify(*scts++) * 15*60;
131#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100132
133 /* according to gsm 03.40 time zone is
134 "expressed in quarters of an hour" */
Harald Weltead633b02011-12-01 21:08:19 +0100135 ofs = gsm411_unbcdify(*scts++) * 15*60;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100136
137 return mktime(&tm) - ofs;
138}
139
140/* Return the default validity period in minutes */
141static unsigned long gsm340_vp_default(void)
142{
143 unsigned long minutes;
144 /* Default validity: two days */
145 minutes = 24 * 60 * 2;
146 return minutes;
147}
148
149/* Decode validity period format 'relative' */
150static unsigned long gsm340_vp_relative(uint8_t *sms_vp)
151{
152 /* Chapter 9.2.3.12.1 */
153 uint8_t vp;
154 unsigned long minutes;
155
156 vp = *(sms_vp);
157 if (vp <= 143)
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100158 minutes = (vp + 1) * 5;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100159 else if (vp <= 167)
160 minutes = 12*60 + (vp-143) * 30;
161 else if (vp <= 196)
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100162 minutes = (vp-166) * 60 * 24;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100163 else
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100164 minutes = (vp-192) * 60 * 24 * 7;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100165 return minutes;
166}
167
168/* Decode validity period format 'absolute' */
169static unsigned long gsm340_vp_absolute(uint8_t *sms_vp)
170{
171 /* Chapter 9.2.3.12.2 */
172 time_t expires, now;
173 unsigned long minutes;
174
175 expires = gsm340_scts(sms_vp);
176 now = time(NULL);
177 if (expires <= now)
178 minutes = 0;
179 else
180 minutes = (expires-now)/60;
181 return minutes;
182}
183
184/* Decode validity period format 'relative in integer representation' */
185static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp)
186{
187 uint8_t vp;
188 unsigned long minutes;
189 vp = *(sms_vp);
190 if (vp == 0) {
191 LOGP(DLSMS, LOGL_ERROR,
192 "reserved relative_integer validity period\n");
193 return gsm340_vp_default();
194 }
195 minutes = vp/60;
196 return minutes;
197}
198
199/* Decode validity period format 'relative in semi-octet representation' */
200static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp)
201{
202 unsigned long minutes;
Harald Weltead633b02011-12-01 21:08:19 +0100203 minutes = gsm411_unbcdify(*sms_vp++)*60; /* hours */
204 minutes += gsm411_unbcdify(*sms_vp++); /* minutes */
205 minutes += gsm411_unbcdify(*sms_vp++)/60; /* seconds */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100206 return minutes;
207}
208
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200209/*! decode validity period. return minutes
Harald Welte96e2a002017-06-12 21:44:18 +0200210 * \param[in] sms_vpf Validity Period Format in 03.40 encoding
211 * \param[in] sms_vp Validity Period Information Element
212 * \returns validity period in minutes */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100213unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp)
214{
215 uint8_t fi; /* functionality indicator */
216
217 switch (sms_vpf) {
218 case GSM340_TP_VPF_RELATIVE:
219 return gsm340_vp_relative(sms_vp);
220 case GSM340_TP_VPF_ABSOLUTE:
221 return gsm340_vp_absolute(sms_vp);
222 case GSM340_TP_VPF_ENHANCED:
223 /* Chapter 9.2.3.12.3 */
224 fi = *sms_vp++;
225 /* ignore additional fi */
226 if (fi & (1<<7)) sms_vp++;
227 /* read validity period format */
228 switch (fi & 0x7) {
229 case 0x0:
230 return gsm340_vp_default(); /* no vpf specified */
231 case 0x1:
232 return gsm340_vp_relative(sms_vp);
233 case 0x2:
234 return gsm340_vp_relative_integer(sms_vp);
235 case 0x3:
236 return gsm340_vp_relative_semioctet(sms_vp);
237 default:
238 /* The GSM spec says that the SC should reject any
239 unsupported and/or undefined values. FIXME */
240 LOGP(DLSMS, LOGL_ERROR,
241 "Reserved enhanced validity period format\n");
242 return gsm340_vp_default();
243 }
244 case GSM340_TP_VPF_NONE:
245 default:
246 return gsm340_vp_default();
247 }
248}
249
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200250/*! determine coding alphabet dependent on GSM 03.38 Section 4 DCS
Harald Welte96e2a002017-06-12 21:44:18 +0200251 * \param[in] dcs Data Coding Scheme in 03.38 encoding
252 * \returns libosmogsm internal enum \ref sms_alphabet */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100253enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
254{
255 uint8_t cgbits = dcs >> 4;
256 enum sms_alphabet alpha = DCS_NONE;
257
258 if ((cgbits & 0xc) == 0) {
259 if (cgbits & 2) {
260 LOGP(DLSMS, LOGL_NOTICE,
261 "Compressed SMS not supported yet\n");
Pau Espin Pedrol29b7d532017-06-18 14:15:16 +0200262 return -1;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100263 }
264
265 switch ((dcs >> 2)&0x03) {
266 case 0:
267 alpha = DCS_7BIT_DEFAULT;
268 break;
269 case 1:
270 alpha = DCS_8BIT_DATA;
271 break;
272 case 2:
273 alpha = DCS_UCS2;
274 break;
275 }
276 } else if (cgbits == 0xc || cgbits == 0xd)
277 alpha = DCS_7BIT_DEFAULT;
278 else if (cgbits == 0xe)
279 alpha = DCS_UCS2;
280 else if (cgbits == 0xf) {
281 if (dcs & 4)
282 alpha = DCS_8BIT_DATA;
283 else
284 alpha = DCS_7BIT_DEFAULT;
285 }
286
287 return alpha;
288}
289
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200290/*! generate a TPDU address field compliant with 03.40 sec. 9.1.2.5
Harald Welte96e2a002017-06-12 21:44:18 +0200291 * \param[out] oa caller-provided output buffer
292 * \param[in] oa_len caller-specified length of \a oa in bytes
293 * \param[in] type GSM340_TYPE_*
294 * \param[in] plan Numbering Plan
295 * \param[in] number string containing number
Vadim Yanitskiy64277a02023-02-28 03:30:27 +0700296 * \returns number of bytes of \a oa that have been used */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100297int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
298 uint8_t plan, const char *number)
299{
300 int len_in_bytes;
301
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100302 oa[1] = 0x80 | (type << 4) | plan;
303
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +0200304 if (type == GSM340_TYPE_ALPHA_NUMERIC) {
305 /*
306 * TODO/FIXME: what is the 'useful semi-octets' excluding any
307 * semi octet containing only fill bits.
308 * The current code picks the number of bytes written by the
309 * 7bit encoding routines and multiplies it by two.
310 */
311 gsm_7bit_encode_n(&oa[2], oa_len - 2, number, &len_in_bytes);
312 oa[0] = len_in_bytes * 2;
313 len_in_bytes += 2;
314 } else {
315 /* prevent buffer overflows */
316 if (strlen(number) > 20)
317 number = "";
318 len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
319 /* GSM 03.40 tells us the length is in 'useful semi-octets' */
320 oa[0] = strlen(number) & 0xff;
321 }
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100322
323 return len_in_bytes;
324}
325
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200326/*! Prefix \ref msgb with a RP header
Harald Welte96e2a002017-06-12 21:44:18 +0200327 * \param msg Message Buffer containing message
328 * \param[in] rp_msg_type RP Message Type
329 * \param[in] rp_msg_ref RP Message Reference
330 * \returns 0 */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100331int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
332 uint8_t rp_msg_ref)
333{
334 struct gsm411_rp_hdr *rp;
335 uint8_t len = msg->len;
336
337 /* GSM 04.11 RP-DATA header */
338 rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
339 rp->len = len + 2;
340 rp->msg_type = rp_msg_type;
341 rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
342
343 return 0;
344}
345
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200346/*! Prefix \ref msgb with a 04.08/04.11 CP header
Harald Welte96e2a002017-06-12 21:44:18 +0200347 * \param msg Message Buffer containing message
348 * \param[in] proto Protocol
349 * \param[in] trans Transaction
350 * \param[in] msg_type Message Type
Vadim Yanitskiy64277a02023-02-28 03:30:27 +0700351 * \returns 0 */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100352int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
353 uint8_t msg_type)
354{
Neels Hofmeyr25774b92016-11-26 15:21:05 +0100355 /* Outgoing proto_discr needs the highest bit set */
Vadim Yanitskiy30cfeeb2018-08-03 05:44:00 +0700356 gsm48_push_l3hdr_tid(msg, proto, trans, msg_type);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100357 return 0;
358}
Harald Welte96e2a002017-06-12 21:44:18 +0200359
360/*! @} */