blob: 53d37a431fc97d8eca89a4054e8b12ba9c6a35e6 [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
Harald Welte7c8e2cc2012-08-29 16:47:30 +020030#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>
Max2f0b0c92017-01-12 16:47:13 +010038#include <osmocom/gsm/gsm0480.h>
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +020039#include <osmocom/gsm/gsm_utils.h>
40#include <osmocom/gsm/protocol/gsm_03_40.h>
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010041#include <osmocom/gsm/protocol/gsm_04_11.h>
42
Harald Welte96e2a002017-06-12 21:44:18 +020043/*! \addtogroup sms
44 * @{
Harald Welte55d724a2017-10-16 18:25:45 +020045 * \file gsm0411_utils.c
Harald Welte96e2a002017-06-12 21:44:18 +020046 */
47
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010048#define GSM411_ALLOC_SIZE 1024
49#define GSM411_ALLOC_HEADROOM 128
50
Neels Hofmeyr87e45502017-06-20 00:17:59 +020051/*! Allocate a message buffer for use as TS 04.11 message
Harald Welte96e2a002017-06-12 21:44:18 +020052 * \returns allocated message buffer */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010053struct msgb *gsm411_msgb_alloc(void)
54{
55 return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
56 "GSM 04.11");
57}
58
Neels Hofmeyr87e45502017-06-20 00:17:59 +020059/*! Turn int into semi-octet representation: 98 => 0x89
Harald Welte96e2a002017-06-12 21:44:18 +020060 * \param[in] integer value representing decimal number 0..99
61 * \returns BSC encoded as nibbles, swapped */
Harald Weltead633b02011-12-01 21:08:19 +010062uint8_t gsm411_bcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010063{
64 uint8_t ret;
65
66 ret = value / 10;
67 ret |= (value % 10) << 4;
68
69 return ret;
70}
71
Neels Hofmeyr87e45502017-06-20 00:17:59 +020072/*! Turn semi-octet representation into int: 0x89 => 98
Harald Welte96e2a002017-06-12 21:44:18 +020073 * \param[in] value byte containing two BCD nibbles in revere order
74 * \returns integer representing decoded, re-ordered nibbles */
Harald Weltead633b02011-12-01 21:08:19 +010075uint8_t gsm411_unbcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010076{
77 uint8_t ret;
78
79 if ((value & 0x0F) > 9 || (value >> 4) > 9)
80 LOGP(DLSMS, LOGL_ERROR,
Harald Weltead633b02011-12-01 21:08:19 +010081 "gsm411_unbcdify got too big nibble: 0x%02X\n", value);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010082
83 ret = (value&0x0F)*10;
84 ret += value>>4;
85
86 return ret;
87}
88
Neels Hofmeyr87e45502017-06-20 00:17:59 +020089/*! Generate 03.40 TP-SCTS
Harald Welte96e2a002017-06-12 21:44:18 +020090 * \param[out] scts Caller-provided buffer to store SCTS (7 octets)
91 * \param[in] time to encode */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010092void gsm340_gen_scts(uint8_t *scts, time_t time)
93{
Keith733810c2017-08-17 21:37:47 +020094 struct tm *tm = localtime(&time);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010095
Harald Weltead633b02011-12-01 21:08:19 +010096 *scts++ = gsm411_bcdify(tm->tm_year % 100);
97 *scts++ = gsm411_bcdify(tm->tm_mon + 1);
98 *scts++ = gsm411_bcdify(tm->tm_mday);
99 *scts++ = gsm411_bcdify(tm->tm_hour);
100 *scts++ = gsm411_bcdify(tm->tm_min);
101 *scts++ = gsm411_bcdify(tm->tm_sec);
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200102#ifdef HAVE_TM_GMTOFF_IN_TM
103 *scts++ = gsm411_bcdify(tm->tm_gmtoff/(60*15));
104#else
Pau Espin Pedrol29b7d532017-06-18 14:15:16 +0200105#pragma message ("find a portable way to obtain timezone offset")
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200106 *scts++ = 0;
107#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100108}
109
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200110/*! Decode 03.40 TP-SCTS (into utc/gmt timestamp)
Harald Welte96e2a002017-06-12 21:44:18 +0200111 * \param[in] scts SMS Center Time Stamp
112 * \return time in UTC time_t format */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100113time_t gsm340_scts(uint8_t *scts)
114{
115 struct tm tm;
Harald Weltead633b02011-12-01 21:08:19 +0100116 uint8_t yr = gsm411_unbcdify(*scts++);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100117 int ofs;
118
119 memset(&tm, 0x00, sizeof(struct tm));
120
121 if (yr <= 80)
122 tm.tm_year = 100 + yr;
123 else
124 tm.tm_year = yr;
Harald Weltead633b02011-12-01 21:08:19 +0100125 tm.tm_mon = gsm411_unbcdify(*scts++) - 1;
126 tm.tm_mday = gsm411_unbcdify(*scts++);
127 tm.tm_hour = gsm411_unbcdify(*scts++);
128 tm.tm_min = gsm411_unbcdify(*scts++);
129 tm.tm_sec = gsm411_unbcdify(*scts++);
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200130#ifdef HAVE_TM_GMTOFF_IN_TM
131 tm.tm_gmtoff = gsm411_unbcdify(*scts++) * 15*60;
132#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100133
134 /* according to gsm 03.40 time zone is
135 "expressed in quarters of an hour" */
Harald Weltead633b02011-12-01 21:08:19 +0100136 ofs = gsm411_unbcdify(*scts++) * 15*60;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100137
138 return mktime(&tm) - ofs;
139}
140
141/* Return the default validity period in minutes */
142static unsigned long gsm340_vp_default(void)
143{
144 unsigned long minutes;
145 /* Default validity: two days */
146 minutes = 24 * 60 * 2;
147 return minutes;
148}
149
150/* Decode validity period format 'relative' */
151static unsigned long gsm340_vp_relative(uint8_t *sms_vp)
152{
153 /* Chapter 9.2.3.12.1 */
154 uint8_t vp;
155 unsigned long minutes;
156
157 vp = *(sms_vp);
158 if (vp <= 143)
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100159 minutes = (vp + 1) * 5;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100160 else if (vp <= 167)
161 minutes = 12*60 + (vp-143) * 30;
162 else if (vp <= 196)
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100163 minutes = (vp-166) * 60 * 24;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100164 else
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100165 minutes = (vp-192) * 60 * 24 * 7;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100166 return minutes;
167}
168
169/* Decode validity period format 'absolute' */
170static unsigned long gsm340_vp_absolute(uint8_t *sms_vp)
171{
172 /* Chapter 9.2.3.12.2 */
173 time_t expires, now;
174 unsigned long minutes;
175
176 expires = gsm340_scts(sms_vp);
177 now = time(NULL);
178 if (expires <= now)
179 minutes = 0;
180 else
181 minutes = (expires-now)/60;
182 return minutes;
183}
184
185/* Decode validity period format 'relative in integer representation' */
186static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp)
187{
188 uint8_t vp;
189 unsigned long minutes;
190 vp = *(sms_vp);
191 if (vp == 0) {
192 LOGP(DLSMS, LOGL_ERROR,
193 "reserved relative_integer validity period\n");
194 return gsm340_vp_default();
195 }
196 minutes = vp/60;
197 return minutes;
198}
199
200/* Decode validity period format 'relative in semi-octet representation' */
201static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp)
202{
203 unsigned long minutes;
Harald Weltead633b02011-12-01 21:08:19 +0100204 minutes = gsm411_unbcdify(*sms_vp++)*60; /* hours */
205 minutes += gsm411_unbcdify(*sms_vp++); /* minutes */
206 minutes += gsm411_unbcdify(*sms_vp++)/60; /* seconds */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100207 return minutes;
208}
209
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200210/*! decode validity period. return minutes
Harald Welte96e2a002017-06-12 21:44:18 +0200211 * \param[in] sms_vpf Validity Period Format in 03.40 encoding
212 * \param[in] sms_vp Validity Period Information Element
213 * \returns validity period in minutes */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100214unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp)
215{
216 uint8_t fi; /* functionality indicator */
217
218 switch (sms_vpf) {
219 case GSM340_TP_VPF_RELATIVE:
220 return gsm340_vp_relative(sms_vp);
221 case GSM340_TP_VPF_ABSOLUTE:
222 return gsm340_vp_absolute(sms_vp);
223 case GSM340_TP_VPF_ENHANCED:
224 /* Chapter 9.2.3.12.3 */
225 fi = *sms_vp++;
226 /* ignore additional fi */
227 if (fi & (1<<7)) sms_vp++;
228 /* read validity period format */
229 switch (fi & 0x7) {
230 case 0x0:
231 return gsm340_vp_default(); /* no vpf specified */
232 case 0x1:
233 return gsm340_vp_relative(sms_vp);
234 case 0x2:
235 return gsm340_vp_relative_integer(sms_vp);
236 case 0x3:
237 return gsm340_vp_relative_semioctet(sms_vp);
238 default:
239 /* The GSM spec says that the SC should reject any
240 unsupported and/or undefined values. FIXME */
241 LOGP(DLSMS, LOGL_ERROR,
242 "Reserved enhanced validity period format\n");
243 return gsm340_vp_default();
244 }
245 case GSM340_TP_VPF_NONE:
246 default:
247 return gsm340_vp_default();
248 }
249}
250
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200251/*! determine coding alphabet dependent on GSM 03.38 Section 4 DCS
Harald Welte96e2a002017-06-12 21:44:18 +0200252 * \param[in] dcs Data Coding Scheme in 03.38 encoding
253 * \returns libosmogsm internal enum \ref sms_alphabet */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100254enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
255{
256 uint8_t cgbits = dcs >> 4;
257 enum sms_alphabet alpha = DCS_NONE;
258
259 if ((cgbits & 0xc) == 0) {
260 if (cgbits & 2) {
261 LOGP(DLSMS, LOGL_NOTICE,
262 "Compressed SMS not supported yet\n");
Pau Espin Pedrol29b7d532017-06-18 14:15:16 +0200263 return -1;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100264 }
265
266 switch ((dcs >> 2)&0x03) {
267 case 0:
268 alpha = DCS_7BIT_DEFAULT;
269 break;
270 case 1:
271 alpha = DCS_8BIT_DATA;
272 break;
273 case 2:
274 alpha = DCS_UCS2;
275 break;
276 }
277 } else if (cgbits == 0xc || cgbits == 0xd)
278 alpha = DCS_7BIT_DEFAULT;
279 else if (cgbits == 0xe)
280 alpha = DCS_UCS2;
281 else if (cgbits == 0xf) {
282 if (dcs & 4)
283 alpha = DCS_8BIT_DATA;
284 else
285 alpha = DCS_7BIT_DEFAULT;
286 }
287
288 return alpha;
289}
290
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200291/*! generate a TPDU address field compliant with 03.40 sec. 9.1.2.5
Harald Welte96e2a002017-06-12 21:44:18 +0200292 * \param[out] oa caller-provided output buffer
293 * \param[in] oa_len caller-specified length of \a oa in bytes
294 * \param[in] type GSM340_TYPE_*
295 * \param[in] plan Numbering Plan
296 * \param[in] number string containing number
297 * \reurns number of bytes of \a oa that have been used */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100298int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
299 uint8_t plan, const char *number)
300{
301 int len_in_bytes;
302
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100303 oa[1] = 0x80 | (type << 4) | plan;
304
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +0200305 if (type == GSM340_TYPE_ALPHA_NUMERIC) {
306 /*
307 * TODO/FIXME: what is the 'useful semi-octets' excluding any
308 * semi octet containing only fill bits.
309 * The current code picks the number of bytes written by the
310 * 7bit encoding routines and multiplies it by two.
311 */
312 gsm_7bit_encode_n(&oa[2], oa_len - 2, number, &len_in_bytes);
313 oa[0] = len_in_bytes * 2;
314 len_in_bytes += 2;
315 } else {
316 /* prevent buffer overflows */
317 if (strlen(number) > 20)
318 number = "";
319 len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
320 /* GSM 03.40 tells us the length is in 'useful semi-octets' */
321 oa[0] = strlen(number) & 0xff;
322 }
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100323
324 return len_in_bytes;
325}
326
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200327/*! Prefix \ref msgb with a RP header
Harald Welte96e2a002017-06-12 21:44:18 +0200328 * \param msg Message Buffer containing message
329 * \param[in] rp_msg_type RP Message Type
330 * \param[in] rp_msg_ref RP Message Reference
331 * \returns 0 */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100332int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
333 uint8_t rp_msg_ref)
334{
335 struct gsm411_rp_hdr *rp;
336 uint8_t len = msg->len;
337
338 /* GSM 04.11 RP-DATA header */
339 rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
340 rp->len = len + 2;
341 rp->msg_type = rp_msg_type;
342 rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
343
344 return 0;
345}
346
Neels Hofmeyr87e45502017-06-20 00:17:59 +0200347/*! Prefix \ref msgb with a 04.08/04.11 CP header
Harald Welte96e2a002017-06-12 21:44:18 +0200348 * \param msg Message Buffer containing message
349 * \param[in] proto Protocol
350 * \param[in] trans Transaction
351 * \param[in] msg_type Message Type
352 * \retrns 0 */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100353int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
354 uint8_t msg_type)
355{
Neels Hofmeyr25774b92016-11-26 15:21:05 +0100356 /* Outgoing proto_discr needs the highest bit set */
357 gsm0480_l3hdr_push(msg, proto | (trans << 4), msg_type);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100358 return 0;
359}
Harald Welte96e2a002017-06-12 21:44:18 +0200360
361/*! @} */