blob: 197f2c3f1b4f00f2a87df5c240ec56130d01f193 [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>
Alexander Chemerise6475442013-11-26 16:43:10 -060010 * (C) 2014 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010011 *
12 * All Rights Reserved
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Affero General Public License as published by
16 * the Free Software Foundation; either version 3 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Affero General Public License for more details.
23 *
24 * You should have received a copy of the GNU Affero General Public License
25 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 *
27 */
28
Harald Welte7c8e2cc2012-08-29 16:47:30 +020029#include "../../config.h"
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010030
31#include <time.h>
32#include <string.h>
33#include <osmocom/core/msgb.h>
34#include <osmocom/core/logging.h>
35
36#include <osmocom/gsm/gsm48.h>
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +020037#include <osmocom/gsm/gsm_utils.h>
Alexander Chemerise6475442013-11-26 16:43:10 -060038#include <osmocom/gsm/gsm0411_utils.h>
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +020039#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
42#define GSM411_ALLOC_SIZE 1024
43#define GSM411_ALLOC_HEADROOM 128
44
45struct msgb *gsm411_msgb_alloc(void)
46{
47 return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
48 "GSM 04.11");
49}
50
51/* Turn int into semi-octet representation: 98 => 0x89 */
Harald Weltead633b02011-12-01 21:08:19 +010052uint8_t gsm411_bcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010053{
54 uint8_t ret;
55
56 ret = value / 10;
57 ret |= (value % 10) << 4;
58
59 return ret;
60}
61
62/* Turn semi-octet representation into int: 0x89 => 98 */
Harald Weltead633b02011-12-01 21:08:19 +010063uint8_t gsm411_unbcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010064{
65 uint8_t ret;
66
67 if ((value & 0x0F) > 9 || (value >> 4) > 9)
68 LOGP(DLSMS, LOGL_ERROR,
Harald Weltead633b02011-12-01 21:08:19 +010069 "gsm411_unbcdify got too big nibble: 0x%02X\n", value);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010070
71 ret = (value&0x0F)*10;
72 ret += value>>4;
73
74 return ret;
75}
76
77/* Generate 03.40 TP-SCTS */
78void gsm340_gen_scts(uint8_t *scts, time_t time)
79{
80 struct tm *tm = gmtime(&time);
81
Harald Weltead633b02011-12-01 21:08:19 +010082 *scts++ = gsm411_bcdify(tm->tm_year % 100);
83 *scts++ = gsm411_bcdify(tm->tm_mon + 1);
84 *scts++ = gsm411_bcdify(tm->tm_mday);
85 *scts++ = gsm411_bcdify(tm->tm_hour);
86 *scts++ = gsm411_bcdify(tm->tm_min);
87 *scts++ = gsm411_bcdify(tm->tm_sec);
Harald Welte7c8e2cc2012-08-29 16:47:30 +020088#ifdef HAVE_TM_GMTOFF_IN_TM
Alexander Chemeris7fb5e2d2014-03-07 21:02:46 +010089 if (tm->tm_gmtoff >= 0)
90 *scts++ = gsm411_bcdify(tm->tm_gmtoff/(60*15));
91 else
92 *scts++ = gsm411_bcdify(-tm->tm_gmtoff/(60*15)) | 0x80;
Harald Welte7c8e2cc2012-08-29 16:47:30 +020093#else
94#warning find a portable way to obtain timezone offset
95 *scts++ = 0;
96#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010097}
98
99/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
100time_t gsm340_scts(uint8_t *scts)
101{
102 struct tm tm;
Alexander Chemerisb9a94182014-03-07 21:03:44 +0100103 uint8_t yr, tz;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100104 int ofs;
Alexander Chemerisb9a94182014-03-07 21:03:44 +0100105 time_t timestamp;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100106
107 memset(&tm, 0x00, sizeof(struct tm));
108
Alexander Chemerisb9a94182014-03-07 21:03:44 +0100109 yr = gsm411_unbcdify(*scts++);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100110 if (yr <= 80)
111 tm.tm_year = 100 + yr;
112 else
113 tm.tm_year = yr;
Harald Weltead633b02011-12-01 21:08:19 +0100114 tm.tm_mon = gsm411_unbcdify(*scts++) - 1;
115 tm.tm_mday = gsm411_unbcdify(*scts++);
116 tm.tm_hour = gsm411_unbcdify(*scts++);
117 tm.tm_min = gsm411_unbcdify(*scts++);
118 tm.tm_sec = gsm411_unbcdify(*scts++);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100119
120 /* according to gsm 03.40 time zone is
121 "expressed in quarters of an hour" */
Alexander Chemerisb9a94182014-03-07 21:03:44 +0100122 tz = *scts++;
123 ofs = gsm411_unbcdify(tz&0x7f) * 15*60;
124 if (tz&0x80)
125 ofs = -ofs;
126 /* mktime() doesn't take tm.tm_gmtoff into account. Instead, it fills this
127 * field with the current timezone. Which means that the resulting time is
128 * off by several hours after that. So here we're setting tm.tm_isdt to -1
129 * to indicate that the tm time is local, but later we subtract the
130 * offset introduced by mktime. */
131 tm.tm_isdst = -1;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100132
Alexander Chemerisb9a94182014-03-07 21:03:44 +0100133 timestamp = mktime(&tm);
134 if (timestamp < 0)
135 return -1;
136
137 /* Take into account timezone offset */
138 timestamp -= ofs;
139#ifdef HAVE_TM_GMTOFF_IN_TM
140 /* Remove an artificial timezone offset, introduced by mktime() */
141 timestamp += tm.tm_gmtoff;
142#endif
143
144 return timestamp;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100145}
146
Alexander Chemerise6475442013-11-26 16:43:10 -0600147/* Decode validity period format 'relative'.
148 * Returns number of seconds relative to a current time. */
149static time_t gsm340_vp_relative(uint8_t *sms_vp)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100150{
151 /* Chapter 9.2.3.12.1 */
152 uint8_t vp;
Alexander Chemerise6475442013-11-26 16:43:10 -0600153 time_t minutes;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100154
155 vp = *(sms_vp);
156 if (vp <= 143)
Alexander Chemeris36d0fee2014-03-07 21:00:19 +0100157 minutes = (vp + 1) * 5;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100158 else if (vp <= 167)
159 minutes = 12*60 + (vp-143) * 30;
160 else if (vp <= 196)
Alexander Chemeris36d0fee2014-03-07 21:00:19 +0100161 minutes = (vp-166) * 60 * 24;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100162 else
Alexander Chemeris36d0fee2014-03-07 21:00:19 +0100163 minutes = (vp-192) * 60 * 24 * 7;
Alexander Chemerise6475442013-11-26 16:43:10 -0600164
165 /* Convert to seconds */
166 return minutes * 60;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100167}
168
Alexander Chemerise6475442013-11-26 16:43:10 -0600169/* Decode validity period format 'absolute'.
170 * Returns UNIX time. */
171static time_t gsm340_vp_absolute(uint8_t *sms_vp)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100172{
173 /* Chapter 9.2.3.12.2 */
Alexander Chemerise6475442013-11-26 16:43:10 -0600174 return gsm340_scts(sms_vp);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100175}
176
Alexander Chemerise6475442013-11-26 16:43:10 -0600177/* Decode validity period format 'relative in integer representation'.
178 * Returns number of seconds relative to a current time. */
179static time_t gsm340_vp_relative_integer(uint8_t *sms_vp)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100180{
181 uint8_t vp;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100182 vp = *(sms_vp);
183 if (vp == 0) {
184 LOGP(DLSMS, LOGL_ERROR,
185 "reserved relative_integer validity period\n");
Alexander Chemerise6475442013-11-26 16:43:10 -0600186#warning We should return an RP-Error here.
187 return SMS_DEFAULT_VALIDITY_PERIOD;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100188 }
Alexander Chemerise6475442013-11-26 16:43:10 -0600189 return vp;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100190}
191
Alexander Chemerise6475442013-11-26 16:43:10 -0600192/* Decode validity period format 'relative in semi-octet representation'.
193 * Returns number of seconds relative to a current time. */
194static time_t gsm340_vp_relative_semioctet(uint8_t *sms_vp)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100195{
Alexander Chemerise6475442013-11-26 16:43:10 -0600196 time_t hours, minutes, seconds;
197 hours = gsm411_unbcdify(*sms_vp++); /* hours */
198 minutes = gsm411_unbcdify(*sms_vp++); /* minutes */
199 seconds = gsm411_unbcdify(*sms_vp++); /* seconds */
200 return hours*60*60 + minutes*60 + seconds;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100201}
202
Alexander Chemerise6475442013-11-26 16:43:10 -0600203/* Decode validity period. Returns absolute UNIX time. */
204time_t gsm340_validity_time(time_t now, uint8_t sms_vpf, uint8_t *sms_vp)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100205{
206 uint8_t fi; /* functionality indicator */
207
208 switch (sms_vpf) {
209 case GSM340_TP_VPF_RELATIVE:
Alexander Chemerise6475442013-11-26 16:43:10 -0600210 return now + gsm340_vp_relative(sms_vp);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100211 case GSM340_TP_VPF_ABSOLUTE:
212 return gsm340_vp_absolute(sms_vp);
213 case GSM340_TP_VPF_ENHANCED:
214 /* Chapter 9.2.3.12.3 */
215 fi = *sms_vp++;
216 /* ignore additional fi */
217 if (fi & (1<<7)) sms_vp++;
218 /* read validity period format */
219 switch (fi & 0x7) {
220 case 0x0:
Alexander Chemerise6475442013-11-26 16:43:10 -0600221 return now + SMS_DEFAULT_VALIDITY_PERIOD; /* no vpf specified */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100222 case 0x1:
Alexander Chemerise6475442013-11-26 16:43:10 -0600223 return now + gsm340_vp_relative(sms_vp);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100224 case 0x2:
Alexander Chemerise6475442013-11-26 16:43:10 -0600225 return now + gsm340_vp_relative_integer(sms_vp);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100226 case 0x3:
Alexander Chemerise6475442013-11-26 16:43:10 -0600227 return now + gsm340_vp_relative_semioctet(sms_vp);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100228 default:
229 /* The GSM spec says that the SC should reject any
230 unsupported and/or undefined values. FIXME */
231 LOGP(DLSMS, LOGL_ERROR,
232 "Reserved enhanced validity period format\n");
Alexander Chemerise6475442013-11-26 16:43:10 -0600233 return now + SMS_DEFAULT_VALIDITY_PERIOD;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100234 }
235 case GSM340_TP_VPF_NONE:
236 default:
Alexander Chemerise6475442013-11-26 16:43:10 -0600237 return now + SMS_DEFAULT_VALIDITY_PERIOD;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100238 }
239}
240
Alexander Chemerise6475442013-11-26 16:43:10 -0600241/* Decode validity period. return relative minutes.
242 * This behaviour is broken, but we're mimicing to it for compatibility. */
243unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp)
244{
245 time_t now = time(NULL);
246 return (gsm340_validity_time(now, sms_vpf, sms_vp) - now) / 60;
247}
248
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100249/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
250enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
251{
252 uint8_t cgbits = dcs >> 4;
253 enum sms_alphabet alpha = DCS_NONE;
254
255 if ((cgbits & 0xc) == 0) {
256 if (cgbits & 2) {
257 LOGP(DLSMS, LOGL_NOTICE,
258 "Compressed SMS not supported yet\n");
259 return 0xffffffff;
260 }
261
262 switch ((dcs >> 2)&0x03) {
263 case 0:
264 alpha = DCS_7BIT_DEFAULT;
265 break;
266 case 1:
267 alpha = DCS_8BIT_DATA;
268 break;
269 case 2:
270 alpha = DCS_UCS2;
271 break;
272 }
273 } else if (cgbits == 0xc || cgbits == 0xd)
274 alpha = DCS_7BIT_DEFAULT;
275 else if (cgbits == 0xe)
276 alpha = DCS_UCS2;
277 else if (cgbits == 0xf) {
278 if (dcs & 4)
279 alpha = DCS_8BIT_DATA;
280 else
281 alpha = DCS_7BIT_DEFAULT;
282 }
283
284 return alpha;
285}
286
287/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
288int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
289 uint8_t plan, const char *number)
290{
291 int len_in_bytes;
292
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100293 oa[1] = 0x80 | (type << 4) | plan;
294
Holger Hans Peter Freyther4d7e49b2013-05-02 22:37:16 +0200295 if (type == GSM340_TYPE_ALPHA_NUMERIC) {
296 /*
297 * TODO/FIXME: what is the 'useful semi-octets' excluding any
298 * semi octet containing only fill bits.
299 * The current code picks the number of bytes written by the
300 * 7bit encoding routines and multiplies it by two.
301 */
302 gsm_7bit_encode_n(&oa[2], oa_len - 2, number, &len_in_bytes);
303 oa[0] = len_in_bytes * 2;
304 len_in_bytes += 2;
305 } else {
306 /* prevent buffer overflows */
307 if (strlen(number) > 20)
308 number = "";
309 len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
310 /* GSM 03.40 tells us the length is in 'useful semi-octets' */
311 oa[0] = strlen(number) & 0xff;
312 }
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100313
314 return len_in_bytes;
315}
316
317/* Prefix msg with a RP header */
318int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
319 uint8_t rp_msg_ref)
320{
321 struct gsm411_rp_hdr *rp;
322 uint8_t len = msg->len;
323
324 /* GSM 04.11 RP-DATA header */
325 rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
326 rp->len = len + 2;
327 rp->msg_type = rp_msg_type;
328 rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
329
330 return 0;
331}
332
333/* Prefix msg with a 04.08/04.11 CP header */
334int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
335 uint8_t msg_type)
336{
337 struct gsm48_hdr *gh;
338
339 gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
340 /* Outgoing needs the highest bit set */
341 gh->proto_discr = proto | (trans << 4);
342 gh->msg_type = msg_type;
343
344 return 0;
345}