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