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