blob: b84c9f2512c648a29136f2ab99383085453f830c [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
41#define GSM411_ALLOC_SIZE 1024
42#define GSM411_ALLOC_HEADROOM 128
43
44struct msgb *gsm411_msgb_alloc(void)
45{
46 return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
47 "GSM 04.11");
48}
49
50/* Turn int into semi-octet representation: 98 => 0x89 */
Harald Weltead633b02011-12-01 21:08:19 +010051uint8_t gsm411_bcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010052{
53 uint8_t ret;
54
55 ret = value / 10;
56 ret |= (value % 10) << 4;
57
58 return ret;
59}
60
61/* Turn semi-octet representation into int: 0x89 => 98 */
Harald Weltead633b02011-12-01 21:08:19 +010062uint8_t gsm411_unbcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010063{
64 uint8_t ret;
65
66 if ((value & 0x0F) > 9 || (value >> 4) > 9)
67 LOGP(DLSMS, LOGL_ERROR,
Harald Weltead633b02011-12-01 21:08:19 +010068 "gsm411_unbcdify got too big nibble: 0x%02X\n", value);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010069
70 ret = (value&0x0F)*10;
71 ret += value>>4;
72
73 return ret;
74}
75
76/* Generate 03.40 TP-SCTS */
77void gsm340_gen_scts(uint8_t *scts, time_t time)
78{
79 struct tm *tm = gmtime(&time);
80
Harald Weltead633b02011-12-01 21:08:19 +010081 *scts++ = gsm411_bcdify(tm->tm_year % 100);
82 *scts++ = gsm411_bcdify(tm->tm_mon + 1);
83 *scts++ = gsm411_bcdify(tm->tm_mday);
84 *scts++ = gsm411_bcdify(tm->tm_hour);
85 *scts++ = gsm411_bcdify(tm->tm_min);
86 *scts++ = gsm411_bcdify(tm->tm_sec);
Harald Welte7c8e2cc2012-08-29 16:47:30 +020087#ifdef HAVE_TM_GMTOFF_IN_TM
88 *scts++ = gsm411_bcdify(tm->tm_gmtoff/(60*15));
89#else
90#warning find a portable way to obtain timezone offset
91 *scts++ = 0;
92#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010093}
94
95/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
96time_t gsm340_scts(uint8_t *scts)
97{
98 struct tm tm;
Harald Weltead633b02011-12-01 21:08:19 +010099 uint8_t yr = gsm411_unbcdify(*scts++);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100100 int ofs;
101
102 memset(&tm, 0x00, sizeof(struct tm));
103
104 if (yr <= 80)
105 tm.tm_year = 100 + yr;
106 else
107 tm.tm_year = yr;
Harald Weltead633b02011-12-01 21:08:19 +0100108 tm.tm_mon = gsm411_unbcdify(*scts++) - 1;
109 tm.tm_mday = gsm411_unbcdify(*scts++);
110 tm.tm_hour = gsm411_unbcdify(*scts++);
111 tm.tm_min = gsm411_unbcdify(*scts++);
112 tm.tm_sec = gsm411_unbcdify(*scts++);
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200113#ifdef HAVE_TM_GMTOFF_IN_TM
114 tm.tm_gmtoff = gsm411_unbcdify(*scts++) * 15*60;
115#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100116
117 /* according to gsm 03.40 time zone is
118 "expressed in quarters of an hour" */
Harald Weltead633b02011-12-01 21:08:19 +0100119 ofs = gsm411_unbcdify(*scts++) * 15*60;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100120
121 return mktime(&tm) - ofs;
122}
123
124/* Return the default validity period in minutes */
125static unsigned long gsm340_vp_default(void)
126{
127 unsigned long minutes;
128 /* Default validity: two days */
129 minutes = 24 * 60 * 2;
130 return minutes;
131}
132
133/* Decode validity period format 'relative' */
134static unsigned long gsm340_vp_relative(uint8_t *sms_vp)
135{
136 /* Chapter 9.2.3.12.1 */
137 uint8_t vp;
138 unsigned long minutes;
139
140 vp = *(sms_vp);
141 if (vp <= 143)
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100142 minutes = (vp + 1) * 5;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100143 else if (vp <= 167)
144 minutes = 12*60 + (vp-143) * 30;
145 else if (vp <= 196)
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100146 minutes = (vp-166) * 60 * 24;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100147 else
Alexander Chemeriscc0645b2014-03-07 21:00:19 +0100148 minutes = (vp-192) * 60 * 24 * 7;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100149 return minutes;
150}
151
152/* Decode validity period format 'absolute' */
153static unsigned long gsm340_vp_absolute(uint8_t *sms_vp)
154{
155 /* Chapter 9.2.3.12.2 */
156 time_t expires, now;
157 unsigned long minutes;
158
159 expires = gsm340_scts(sms_vp);
160 now = time(NULL);
161 if (expires <= now)
162 minutes = 0;
163 else
164 minutes = (expires-now)/60;
165 return minutes;
166}
167
168/* Decode validity period format 'relative in integer representation' */
169static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp)
170{
171 uint8_t vp;
172 unsigned long minutes;
173 vp = *(sms_vp);
174 if (vp == 0) {
175 LOGP(DLSMS, LOGL_ERROR,
176 "reserved relative_integer validity period\n");
177 return gsm340_vp_default();
178 }
179 minutes = vp/60;
180 return minutes;
181}
182
183/* Decode validity period format 'relative in semi-octet representation' */
184static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp)
185{
186 unsigned long minutes;
Harald Weltead633b02011-12-01 21:08:19 +0100187 minutes = gsm411_unbcdify(*sms_vp++)*60; /* hours */
188 minutes += gsm411_unbcdify(*sms_vp++); /* minutes */
189 minutes += gsm411_unbcdify(*sms_vp++)/60; /* seconds */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100190 return minutes;
191}
192
193/* decode validity period. return minutes */
194unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp)
195{
196 uint8_t fi; /* functionality indicator */
197
198 switch (sms_vpf) {
199 case GSM340_TP_VPF_RELATIVE:
200 return gsm340_vp_relative(sms_vp);
201 case GSM340_TP_VPF_ABSOLUTE:
202 return gsm340_vp_absolute(sms_vp);
203 case GSM340_TP_VPF_ENHANCED:
204 /* Chapter 9.2.3.12.3 */
205 fi = *sms_vp++;
206 /* ignore additional fi */
207 if (fi & (1<<7)) sms_vp++;
208 /* read validity period format */
209 switch (fi & 0x7) {
210 case 0x0:
211 return gsm340_vp_default(); /* no vpf specified */
212 case 0x1:
213 return gsm340_vp_relative(sms_vp);
214 case 0x2:
215 return gsm340_vp_relative_integer(sms_vp);
216 case 0x3:
217 return gsm340_vp_relative_semioctet(sms_vp);
218 default:
219 /* The GSM spec says that the SC should reject any
220 unsupported and/or undefined values. FIXME */
221 LOGP(DLSMS, LOGL_ERROR,
222 "Reserved enhanced validity period format\n");
223 return gsm340_vp_default();
224 }
225 case GSM340_TP_VPF_NONE:
226 default:
227 return gsm340_vp_default();
228 }
229}
230
231/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
232enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
233{
234 uint8_t cgbits = dcs >> 4;
235 enum sms_alphabet alpha = DCS_NONE;
236
237 if ((cgbits & 0xc) == 0) {
238 if (cgbits & 2) {
239 LOGP(DLSMS, LOGL_NOTICE,
240 "Compressed SMS not supported yet\n");
241 return 0xffffffff;
242 }
243
244 switch ((dcs >> 2)&0x03) {
245 case 0:
246 alpha = DCS_7BIT_DEFAULT;
247 break;
248 case 1:
249 alpha = DCS_8BIT_DATA;
250 break;
251 case 2:
252 alpha = DCS_UCS2;
253 break;
254 }
255 } else if (cgbits == 0xc || cgbits == 0xd)
256 alpha = DCS_7BIT_DEFAULT;
257 else if (cgbits == 0xe)
258 alpha = DCS_UCS2;
259 else if (cgbits == 0xf) {
260 if (dcs & 4)
261 alpha = DCS_8BIT_DATA;
262 else
263 alpha = DCS_7BIT_DEFAULT;
264 }
265
266 return alpha;
267}
268
269/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
270int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
271 uint8_t plan, const char *number)
272{
273 int len_in_bytes;
274
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100275 oa[1] = 0x80 | (type << 4) | plan;
276
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +0200277 if (type == GSM340_TYPE_ALPHA_NUMERIC) {
278 /*
279 * TODO/FIXME: what is the 'useful semi-octets' excluding any
280 * semi octet containing only fill bits.
281 * The current code picks the number of bytes written by the
282 * 7bit encoding routines and multiplies it by two.
283 */
284 gsm_7bit_encode_n(&oa[2], oa_len - 2, number, &len_in_bytes);
285 oa[0] = len_in_bytes * 2;
286 len_in_bytes += 2;
287 } else {
288 /* prevent buffer overflows */
289 if (strlen(number) > 20)
290 number = "";
291 len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
292 /* GSM 03.40 tells us the length is in 'useful semi-octets' */
293 oa[0] = strlen(number) & 0xff;
294 }
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100295
296 return len_in_bytes;
297}
298
299/* Prefix msg with a RP header */
300int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
301 uint8_t rp_msg_ref)
302{
303 struct gsm411_rp_hdr *rp;
304 uint8_t len = msg->len;
305
306 /* GSM 04.11 RP-DATA header */
307 rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
308 rp->len = len + 2;
309 rp->msg_type = rp_msg_type;
310 rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
311
312 return 0;
313}
314
315/* Prefix msg with a 04.08/04.11 CP header */
316int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
317 uint8_t msg_type)
318{
Neels Hofmeyr25774b92016-11-26 15:21:05 +0100319 /* Outgoing proto_discr needs the highest bit set */
320 gsm0480_l3hdr_push(msg, proto | (trans << 4), msg_type);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100321 return 0;
322}