blob: 543775cc9396c5515a2c355dfbea939a2b91f800 [file] [log] [blame]
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +01001/* Point-to-Point (PP) Short Message Service (SMS)
2 * Support on Mobile Radio Interface
3 * 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>
6 * (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 *
13 * This program is free software; you can redistribute it and/or modify
Harald Welte388fb032014-10-26 20:42:49 +010014 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010016 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte388fb032014-10-26 20:42:49 +010021 * GNU General Public License for more details.
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010022 *
Harald Welte388fb032014-10-26 20:42:49 +010023 * You should have received a copy of the GNU General Public License
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010024 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 *
26 */
27
Harald Welte7c8e2cc2012-08-29 16:47:30 +020028#include "../../config.h"
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010029
30#include <time.h>
31#include <string.h>
32#include <osmocom/core/msgb.h>
33#include <osmocom/core/logging.h>
34
35#include <osmocom/gsm/gsm48.h>
Max2f0b0c92017-01-12 16:47:13 +010036#include <osmocom/gsm/gsm0480.h>
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +020037#include <osmocom/gsm/gsm_utils.h>
38#include <osmocom/gsm/protocol/gsm_03_40.h>
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010039#include <osmocom/gsm/protocol/gsm_04_11.h>
40
Harald Welte96e2a002017-06-12 21:44:18 +020041/*! \addtogroup sms
42 * @{
43 */
44
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010045#define GSM411_ALLOC_SIZE 1024
46#define GSM411_ALLOC_HEADROOM 128
47
Harald Welte96e2a002017-06-12 21:44:18 +020048/*! \brief Allocate a message buffer for use as TS 04.11 message
49 * \returns allocated message buffer */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010050struct msgb *gsm411_msgb_alloc(void)
51{
52 return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
53 "GSM 04.11");
54}
55
Harald Welte96e2a002017-06-12 21:44:18 +020056/*! \brief Turn int into semi-octet representation: 98 => 0x89
57 * \param[in] integer value representing decimal number 0..99
58 * \returns BSC encoded as nibbles, swapped */
Harald Weltead633b02011-12-01 21:08:19 +010059uint8_t gsm411_bcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010060{
61 uint8_t ret;
62
63 ret = value / 10;
64 ret |= (value % 10) << 4;
65
66 return ret;
67}
68
Harald Welte96e2a002017-06-12 21:44:18 +020069/*! \brief Turn semi-octet representation into int: 0x89 => 98
70 * \param[in] value byte containing two BCD nibbles in revere order
71 * \returns integer representing decoded, re-ordered nibbles */
Harald Weltead633b02011-12-01 21:08:19 +010072uint8_t gsm411_unbcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010073{
74 uint8_t ret;
75
76 if ((value & 0x0F) > 9 || (value >> 4) > 9)
77 LOGP(DLSMS, LOGL_ERROR,
Harald Weltead633b02011-12-01 21:08:19 +010078 "gsm411_unbcdify got too big nibble: 0x%02X\n", value);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010079
80 ret = (value&0x0F)*10;
81 ret += value>>4;
82
83 return ret;
84}
85
Harald Welte96e2a002017-06-12 21:44:18 +020086/*! \brief Generate 03.40 TP-SCTS
87 * \param[out] scts Caller-provided buffer to store SCTS (7 octets)
88 * \param[in] time to encode */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010089void gsm340_gen_scts(uint8_t *scts, time_t time)
90{
91 struct tm *tm = gmtime(&time);
92
Harald Weltead633b02011-12-01 21:08:19 +010093 *scts++ = gsm411_bcdify(tm->tm_year % 100);
94 *scts++ = gsm411_bcdify(tm->tm_mon + 1);
95 *scts++ = gsm411_bcdify(tm->tm_mday);
96 *scts++ = gsm411_bcdify(tm->tm_hour);
97 *scts++ = gsm411_bcdify(tm->tm_min);
98 *scts++ = gsm411_bcdify(tm->tm_sec);
Harald Welte7c8e2cc2012-08-29 16:47:30 +020099#ifdef HAVE_TM_GMTOFF_IN_TM
100 *scts++ = gsm411_bcdify(tm->tm_gmtoff/(60*15));
101#else
102#warning find a portable way to obtain timezone offset
103 *scts++ = 0;
104#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100105}
106
Harald Welte96e2a002017-06-12 21:44:18 +0200107/*! \brief Decode 03.40 TP-SCTS (into utc/gmt timestamp)
108 * \param[in] scts SMS Center Time Stamp
109 * \return time in UTC time_t format */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100110time_t gsm340_scts(uint8_t *scts)
111{
112 struct tm tm;
Harald Weltead633b02011-12-01 21:08:19 +0100113 uint8_t yr = gsm411_unbcdify(*scts++);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100114 int ofs;
115
116 memset(&tm, 0x00, sizeof(struct tm));
117
118 if (yr <= 80)
119 tm.tm_year = 100 + yr;
120 else
121 tm.tm_year = yr;
Harald Weltead633b02011-12-01 21:08:19 +0100122 tm.tm_mon = gsm411_unbcdify(*scts++) - 1;
123 tm.tm_mday = gsm411_unbcdify(*scts++);
124 tm.tm_hour = gsm411_unbcdify(*scts++);
125 tm.tm_min = gsm411_unbcdify(*scts++);
126 tm.tm_sec = gsm411_unbcdify(*scts++);
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200127#ifdef HAVE_TM_GMTOFF_IN_TM
128 tm.tm_gmtoff = gsm411_unbcdify(*scts++) * 15*60;
129#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100130
131 /* according to gsm 03.40 time zone is
132 "expressed in quarters of an hour" */
Harald Weltead633b02011-12-01 21:08:19 +0100133 ofs = gsm411_unbcdify(*scts++) * 15*60;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100134
135 return mktime(&tm) - ofs;
136}
137
138/* Return the default validity period in minutes */
139static unsigned long gsm340_vp_default(void)
140{
141 unsigned long minutes;
142 /* Default validity: two days */
143 minutes = 24 * 60 * 2;
144 return minutes;
145}
146
147/* Decode validity period format 'relative' */
148static unsigned long gsm340_vp_relative(uint8_t *sms_vp)
149{
150 /* Chapter 9.2.3.12.1 */
151 uint8_t vp;
152 unsigned long minutes;
153
154 vp = *(sms_vp);
155 if (vp <= 143)
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100156 minutes = (vp + 1) * 5;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100157 else if (vp <= 167)
158 minutes = 12*60 + (vp-143) * 30;
159 else if (vp <= 196)
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100160 minutes = (vp-166) * 60 * 24;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100161 else
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100162 minutes = (vp-192) * 60 * 24 * 7;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100163 return minutes;
164}
165
166/* Decode validity period format 'absolute' */
167static unsigned long gsm340_vp_absolute(uint8_t *sms_vp)
168{
169 /* Chapter 9.2.3.12.2 */
170 time_t expires, now;
171 unsigned long minutes;
172
173 expires = gsm340_scts(sms_vp);
174 now = time(NULL);
175 if (expires <= now)
176 minutes = 0;
177 else
178 minutes = (expires-now)/60;
179 return minutes;
180}
181
182/* Decode validity period format 'relative in integer representation' */
183static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp)
184{
185 uint8_t vp;
186 unsigned long minutes;
187 vp = *(sms_vp);
188 if (vp == 0) {
189 LOGP(DLSMS, LOGL_ERROR,
190 "reserved relative_integer validity period\n");
191 return gsm340_vp_default();
192 }
193 minutes = vp/60;
194 return minutes;
195}
196
197/* Decode validity period format 'relative in semi-octet representation' */
198static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp)
199{
200 unsigned long minutes;
Harald Weltead633b02011-12-01 21:08:19 +0100201 minutes = gsm411_unbcdify(*sms_vp++)*60; /* hours */
202 minutes += gsm411_unbcdify(*sms_vp++); /* minutes */
203 minutes += gsm411_unbcdify(*sms_vp++)/60; /* seconds */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100204 return minutes;
205}
206
Harald Welte96e2a002017-06-12 21:44:18 +0200207/*! \brief decode validity period. return minutes
208 * \param[in] sms_vpf Validity Period Format in 03.40 encoding
209 * \param[in] sms_vp Validity Period Information Element
210 * \returns validity period in minutes */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100211unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp)
212{
213 uint8_t fi; /* functionality indicator */
214
215 switch (sms_vpf) {
216 case GSM340_TP_VPF_RELATIVE:
217 return gsm340_vp_relative(sms_vp);
218 case GSM340_TP_VPF_ABSOLUTE:
219 return gsm340_vp_absolute(sms_vp);
220 case GSM340_TP_VPF_ENHANCED:
221 /* Chapter 9.2.3.12.3 */
222 fi = *sms_vp++;
223 /* ignore additional fi */
224 if (fi & (1<<7)) sms_vp++;
225 /* read validity period format */
226 switch (fi & 0x7) {
227 case 0x0:
228 return gsm340_vp_default(); /* no vpf specified */
229 case 0x1:
230 return gsm340_vp_relative(sms_vp);
231 case 0x2:
232 return gsm340_vp_relative_integer(sms_vp);
233 case 0x3:
234 return gsm340_vp_relative_semioctet(sms_vp);
235 default:
236 /* The GSM spec says that the SC should reject any
237 unsupported and/or undefined values. FIXME */
238 LOGP(DLSMS, LOGL_ERROR,
239 "Reserved enhanced validity period format\n");
240 return gsm340_vp_default();
241 }
242 case GSM340_TP_VPF_NONE:
243 default:
244 return gsm340_vp_default();
245 }
246}
247
Harald Welte96e2a002017-06-12 21:44:18 +0200248/*! \brief determine coding alphabet dependent on GSM 03.38 Section 4 DCS
249 * \param[in] dcs Data Coding Scheme in 03.38 encoding
250 * \returns libosmogsm internal enum \ref sms_alphabet */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100251enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
252{
253 uint8_t cgbits = dcs >> 4;
254 enum sms_alphabet alpha = DCS_NONE;
255
256 if ((cgbits & 0xc) == 0) {
257 if (cgbits & 2) {
258 LOGP(DLSMS, LOGL_NOTICE,
259 "Compressed SMS not supported yet\n");
260 return 0xffffffff;
261 }
262
263 switch ((dcs >> 2)&0x03) {
264 case 0:
265 alpha = DCS_7BIT_DEFAULT;
266 break;
267 case 1:
268 alpha = DCS_8BIT_DATA;
269 break;
270 case 2:
271 alpha = DCS_UCS2;
272 break;
273 }
274 } else if (cgbits == 0xc || cgbits == 0xd)
275 alpha = DCS_7BIT_DEFAULT;
276 else if (cgbits == 0xe)
277 alpha = DCS_UCS2;
278 else if (cgbits == 0xf) {
279 if (dcs & 4)
280 alpha = DCS_8BIT_DATA;
281 else
282 alpha = DCS_7BIT_DEFAULT;
283 }
284
285 return alpha;
286}
287
Harald Welte96e2a002017-06-12 21:44:18 +0200288/*! \brief generate a TPDU address field compliant with 03.40 sec. 9.1.2.5
289 * \param[out] oa caller-provided output buffer
290 * \param[in] oa_len caller-specified length of \a oa in bytes
291 * \param[in] type GSM340_TYPE_*
292 * \param[in] plan Numbering Plan
293 * \param[in] number string containing number
294 * \reurns number of bytes of \a oa that have been used */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100295int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
296 uint8_t plan, const char *number)
297{
298 int len_in_bytes;
299
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100300 oa[1] = 0x80 | (type << 4) | plan;
301
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +0200302 if (type == GSM340_TYPE_ALPHA_NUMERIC) {
303 /*
304 * TODO/FIXME: what is the 'useful semi-octets' excluding any
305 * semi octet containing only fill bits.
306 * The current code picks the number of bytes written by the
307 * 7bit encoding routines and multiplies it by two.
308 */
309 gsm_7bit_encode_n(&oa[2], oa_len - 2, number, &len_in_bytes);
310 oa[0] = len_in_bytes * 2;
311 len_in_bytes += 2;
312 } else {
313 /* prevent buffer overflows */
314 if (strlen(number) > 20)
315 number = "";
316 len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
317 /* GSM 03.40 tells us the length is in 'useful semi-octets' */
318 oa[0] = strlen(number) & 0xff;
319 }
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100320
321 return len_in_bytes;
322}
323
Harald Welte96e2a002017-06-12 21:44:18 +0200324/*! \brief Prefix \ref msgb with a RP header
325 * \param msg Message Buffer containing message
326 * \param[in] rp_msg_type RP Message Type
327 * \param[in] rp_msg_ref RP Message Reference
328 * \returns 0 */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100329int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
330 uint8_t rp_msg_ref)
331{
332 struct gsm411_rp_hdr *rp;
333 uint8_t len = msg->len;
334
335 /* GSM 04.11 RP-DATA header */
336 rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
337 rp->len = len + 2;
338 rp->msg_type = rp_msg_type;
339 rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
340
341 return 0;
342}
343
Harald Welte96e2a002017-06-12 21:44:18 +0200344/*! \brief Prefix \ref msgb with a 04.08/04.11 CP header
345 * \param msg Message Buffer containing message
346 * \param[in] proto Protocol
347 * \param[in] trans Transaction
348 * \param[in] msg_type Message Type
349 * \retrns 0 */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100350int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
351 uint8_t msg_type)
352{
Neels Hofmeyr25774b92016-11-26 15:21:05 +0100353 /* Outgoing proto_discr needs the highest bit set */
354 gsm0480_l3hdr_push(msg, proto | (trans << 4), msg_type);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100355 return 0;
356}
Harald Welte96e2a002017-06-12 21:44:18 +0200357
358/*! @} */