blob: ebde8fca3697694929a311c076c8bf0c04c53400 [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>
7 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
8 * (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>
36#include <osmocom/gsm/protocol/gsm_04_11.h>
37
38#define GSM411_ALLOC_SIZE 1024
39#define GSM411_ALLOC_HEADROOM 128
40
41struct msgb *gsm411_msgb_alloc(void)
42{
43 return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
44 "GSM 04.11");
45}
46
47/* Turn int into semi-octet representation: 98 => 0x89 */
Harald Weltead633b02011-12-01 21:08:19 +010048uint8_t gsm411_bcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010049{
50 uint8_t ret;
51
52 ret = value / 10;
53 ret |= (value % 10) << 4;
54
55 return ret;
56}
57
58/* Turn semi-octet representation into int: 0x89 => 98 */
Harald Weltead633b02011-12-01 21:08:19 +010059uint8_t gsm411_unbcdify(uint8_t value)
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010060{
61 uint8_t ret;
62
63 if ((value & 0x0F) > 9 || (value >> 4) > 9)
64 LOGP(DLSMS, LOGL_ERROR,
Harald Weltead633b02011-12-01 21:08:19 +010065 "gsm411_unbcdify got too big nibble: 0x%02X\n", value);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010066
67 ret = (value&0x0F)*10;
68 ret += value>>4;
69
70 return ret;
71}
72
73/* Generate 03.40 TP-SCTS */
74void gsm340_gen_scts(uint8_t *scts, time_t time)
75{
76 struct tm *tm = gmtime(&time);
77
Harald Weltead633b02011-12-01 21:08:19 +010078 *scts++ = gsm411_bcdify(tm->tm_year % 100);
79 *scts++ = gsm411_bcdify(tm->tm_mon + 1);
80 *scts++ = gsm411_bcdify(tm->tm_mday);
81 *scts++ = gsm411_bcdify(tm->tm_hour);
82 *scts++ = gsm411_bcdify(tm->tm_min);
83 *scts++ = gsm411_bcdify(tm->tm_sec);
Harald Welte7c8e2cc2012-08-29 16:47:30 +020084#ifdef HAVE_TM_GMTOFF_IN_TM
85 *scts++ = gsm411_bcdify(tm->tm_gmtoff/(60*15));
86#else
87#warning find a portable way to obtain timezone offset
88 *scts++ = 0;
89#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010090}
91
92/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
93time_t gsm340_scts(uint8_t *scts)
94{
95 struct tm tm;
Harald Weltead633b02011-12-01 21:08:19 +010096 uint8_t yr = gsm411_unbcdify(*scts++);
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +010097 int ofs;
98
99 memset(&tm, 0x00, sizeof(struct tm));
100
101 if (yr <= 80)
102 tm.tm_year = 100 + yr;
103 else
104 tm.tm_year = yr;
Harald Weltead633b02011-12-01 21:08:19 +0100105 tm.tm_mon = gsm411_unbcdify(*scts++) - 1;
106 tm.tm_mday = gsm411_unbcdify(*scts++);
107 tm.tm_hour = gsm411_unbcdify(*scts++);
108 tm.tm_min = gsm411_unbcdify(*scts++);
109 tm.tm_sec = gsm411_unbcdify(*scts++);
Harald Welte7c8e2cc2012-08-29 16:47:30 +0200110#ifdef HAVE_TM_GMTOFF_IN_TM
111 tm.tm_gmtoff = gsm411_unbcdify(*scts++) * 15*60;
112#endif
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100113
114 /* according to gsm 03.40 time zone is
115 "expressed in quarters of an hour" */
Harald Weltead633b02011-12-01 21:08:19 +0100116 ofs = gsm411_unbcdify(*scts++) * 15*60;
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100117
118 return mktime(&tm) - ofs;
119}
120
121/* Return the default validity period in minutes */
122static unsigned long gsm340_vp_default(void)
123{
124 unsigned long minutes;
125 /* Default validity: two days */
126 minutes = 24 * 60 * 2;
127 return minutes;
128}
129
130/* Decode validity period format 'relative' */
131static unsigned long gsm340_vp_relative(uint8_t *sms_vp)
132{
133 /* Chapter 9.2.3.12.1 */
134 uint8_t vp;
135 unsigned long minutes;
136
137 vp = *(sms_vp);
138 if (vp <= 143)
139 minutes = vp + 1 * 5;
140 else if (vp <= 167)
141 minutes = 12*60 + (vp-143) * 30;
142 else if (vp <= 196)
143 minutes = vp-166 * 60 * 24;
144 else
145 minutes = vp-192 * 60 * 24 * 7;
146 return minutes;
147}
148
149/* Decode validity period format 'absolute' */
150static unsigned long gsm340_vp_absolute(uint8_t *sms_vp)
151{
152 /* Chapter 9.2.3.12.2 */
153 time_t expires, now;
154 unsigned long minutes;
155
156 expires = gsm340_scts(sms_vp);
157 now = time(NULL);
158 if (expires <= now)
159 minutes = 0;
160 else
161 minutes = (expires-now)/60;
162 return minutes;
163}
164
165/* Decode validity period format 'relative in integer representation' */
166static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp)
167{
168 uint8_t vp;
169 unsigned long minutes;
170 vp = *(sms_vp);
171 if (vp == 0) {
172 LOGP(DLSMS, LOGL_ERROR,
173 "reserved relative_integer validity period\n");
174 return gsm340_vp_default();
175 }
176 minutes = vp/60;
177 return minutes;
178}
179
180/* Decode validity period format 'relative in semi-octet representation' */
181static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp)
182{
183 unsigned long minutes;
Harald Weltead633b02011-12-01 21:08:19 +0100184 minutes = gsm411_unbcdify(*sms_vp++)*60; /* hours */
185 minutes += gsm411_unbcdify(*sms_vp++); /* minutes */
186 minutes += gsm411_unbcdify(*sms_vp++)/60; /* seconds */
Andreas.Eversbergd84f47a2011-11-06 20:22:12 +0100187 return minutes;
188}
189
190/* decode validity period. return minutes */
191unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp)
192{
193 uint8_t fi; /* functionality indicator */
194
195 switch (sms_vpf) {
196 case GSM340_TP_VPF_RELATIVE:
197 return gsm340_vp_relative(sms_vp);
198 case GSM340_TP_VPF_ABSOLUTE:
199 return gsm340_vp_absolute(sms_vp);
200 case GSM340_TP_VPF_ENHANCED:
201 /* Chapter 9.2.3.12.3 */
202 fi = *sms_vp++;
203 /* ignore additional fi */
204 if (fi & (1<<7)) sms_vp++;
205 /* read validity period format */
206 switch (fi & 0x7) {
207 case 0x0:
208 return gsm340_vp_default(); /* no vpf specified */
209 case 0x1:
210 return gsm340_vp_relative(sms_vp);
211 case 0x2:
212 return gsm340_vp_relative_integer(sms_vp);
213 case 0x3:
214 return gsm340_vp_relative_semioctet(sms_vp);
215 default:
216 /* The GSM spec says that the SC should reject any
217 unsupported and/or undefined values. FIXME */
218 LOGP(DLSMS, LOGL_ERROR,
219 "Reserved enhanced validity period format\n");
220 return gsm340_vp_default();
221 }
222 case GSM340_TP_VPF_NONE:
223 default:
224 return gsm340_vp_default();
225 }
226}
227
228/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
229enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
230{
231 uint8_t cgbits = dcs >> 4;
232 enum sms_alphabet alpha = DCS_NONE;
233
234 if ((cgbits & 0xc) == 0) {
235 if (cgbits & 2) {
236 LOGP(DLSMS, LOGL_NOTICE,
237 "Compressed SMS not supported yet\n");
238 return 0xffffffff;
239 }
240
241 switch ((dcs >> 2)&0x03) {
242 case 0:
243 alpha = DCS_7BIT_DEFAULT;
244 break;
245 case 1:
246 alpha = DCS_8BIT_DATA;
247 break;
248 case 2:
249 alpha = DCS_UCS2;
250 break;
251 }
252 } else if (cgbits == 0xc || cgbits == 0xd)
253 alpha = DCS_7BIT_DEFAULT;
254 else if (cgbits == 0xe)
255 alpha = DCS_UCS2;
256 else if (cgbits == 0xf) {
257 if (dcs & 4)
258 alpha = DCS_8BIT_DATA;
259 else
260 alpha = DCS_7BIT_DEFAULT;
261 }
262
263 return alpha;
264}
265
266/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
267int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
268 uint8_t plan, const char *number)
269{
270 int len_in_bytes;
271
272 /* prevent buffer overflows */
273 if (strlen(number) > 20)
274 number = "";
275
276// oa[1] = 0xb9; /* networks-specific number, private numbering plan */
277 oa[1] = 0x80 | (type << 4) | plan;
278
279 len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
280
281 /* GSM 03.40 tells us the length is in 'useful semi-octets' */
282 oa[0] = strlen(number) & 0xff;
283
284 return len_in_bytes;
285}
286
287/* Prefix msg with a RP header */
288int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
289 uint8_t rp_msg_ref)
290{
291 struct gsm411_rp_hdr *rp;
292 uint8_t len = msg->len;
293
294 /* GSM 04.11 RP-DATA header */
295 rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
296 rp->len = len + 2;
297 rp->msg_type = rp_msg_type;
298 rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
299
300 return 0;
301}
302
303/* Prefix msg with a 04.08/04.11 CP header */
304int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
305 uint8_t msg_type)
306{
307 struct gsm48_hdr *gh;
308
309 gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
310 /* Outgoing needs the highest bit set */
311 gh->proto_discr = proto | (trans << 4);
312 gh->msg_type = msg_type;
313
314 return 0;
315}