mirror of https://gerrit.osmocom.org/libosmocore
gsm/sms: Moved utility functions of SMS processing to new gsm0411_utils.c
Written-by: Andreas Eversberg <jolly@eversberg.eu> Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
This commit is contained in:
parent
c626da9f87
commit
d84f47abb3
|
@ -1,6 +1,7 @@
|
|||
osmogsm_HEADERS = a5.h comp128.h gsm0808.h gsm48_ie.h mncc.h rxlev_stat.h \
|
||||
gsm0480.h gsm48.h gsm_utils.h rsl.h tlv.h abis_nm.h \
|
||||
sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h
|
||||
sysinfo.h prim.h gsm0502.h lapd_core.h lapdm.h \
|
||||
gsm0411_utils.h
|
||||
|
||||
SUBDIRS = protocol
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef _GSM0411_UTILS_H
|
||||
#define _GSM0411_UTILS_H
|
||||
|
||||
struct msgb *gsm411_msgb_alloc(void);
|
||||
|
||||
/* Generate 03.40 TP-SCTS */
|
||||
void gsm340_gen_scts(uint8_t *scts, time_t time);
|
||||
|
||||
/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
|
||||
time_t gsm340_scts(uint8_t *scts);
|
||||
|
||||
/* decode validity period. return minutes */
|
||||
unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp);
|
||||
|
||||
/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
|
||||
enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs);
|
||||
|
||||
/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
|
||||
int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
|
||||
uint8_t plan, const char *number);
|
||||
|
||||
/* Prefix msg with a RP header */
|
||||
int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
|
||||
uint8_t rp_msg_ref);
|
||||
|
||||
/* Prefix msg with a 04.08/04.11 CP header */
|
||||
int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
|
||||
uint8_t msg_type);
|
||||
|
||||
#endif /* _GSM0411_UTILS_H */
|
|
@ -10,6 +10,8 @@ lib_LTLIBRARIES = libosmogsm.la
|
|||
libosmogsm_la_SOURCES = a5.c rxlev_stat.c tlv_parser.c comp128.c gsm_utils.c \
|
||||
rsl.c gsm48.c gsm48_ie.c gsm0808.c sysinfo.c \
|
||||
gprs_cipher_core.c gsm0480.c abis_nm.c gsm0502.c \
|
||||
gsm0411_utils.c \
|
||||
lapd_core.c lapdm.c
|
||||
|
||||
libosmogsm_la_LDFLAGS = -version-info $(LIBVERSION)
|
||||
libosmogsm_la_LIBADD = $(top_builddir)/src/libosmocore.la
|
||||
|
|
|
@ -0,0 +1,306 @@
|
|||
/* Point-to-Point (PP) Short Message Service (SMS)
|
||||
* Support on Mobile Radio Interface
|
||||
* 3GPP TS 04.11 version 7.1.0 Release 1998 / ETSI TS 100 942 V7.1.0 */
|
||||
|
||||
/* (C) 2008 by Daniel Willmann <daniel@totalueberwachung.de>
|
||||
* (C) 2009 by Harald Welte <laforge@gnumonks.org>
|
||||
* (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
|
||||
* (C) 2010 by On-Waves
|
||||
* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
*
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
|
||||
#include <osmocom/gsm/gsm48.h>
|
||||
#include <osmocom/gsm/protocol/gsm_04_11.h>
|
||||
|
||||
#define GSM411_ALLOC_SIZE 1024
|
||||
#define GSM411_ALLOC_HEADROOM 128
|
||||
|
||||
struct msgb *gsm411_msgb_alloc(void)
|
||||
{
|
||||
return msgb_alloc_headroom(GSM411_ALLOC_SIZE, GSM411_ALLOC_HEADROOM,
|
||||
"GSM 04.11");
|
||||
}
|
||||
|
||||
/* Turn int into semi-octet representation: 98 => 0x89 */
|
||||
static uint8_t bcdify(uint8_t value)
|
||||
{
|
||||
uint8_t ret;
|
||||
|
||||
ret = value / 10;
|
||||
ret |= (value % 10) << 4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Turn semi-octet representation into int: 0x89 => 98 */
|
||||
static uint8_t unbcdify(uint8_t value)
|
||||
{
|
||||
uint8_t ret;
|
||||
|
||||
if ((value & 0x0F) > 9 || (value >> 4) > 9)
|
||||
LOGP(DLSMS, LOGL_ERROR,
|
||||
"unbcdify got too big nibble: 0x%02X\n", value);
|
||||
|
||||
ret = (value&0x0F)*10;
|
||||
ret += value>>4;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Generate 03.40 TP-SCTS */
|
||||
void gsm340_gen_scts(uint8_t *scts, time_t time)
|
||||
{
|
||||
struct tm *tm = gmtime(&time);
|
||||
|
||||
*scts++ = bcdify(tm->tm_year % 100);
|
||||
*scts++ = bcdify(tm->tm_mon + 1);
|
||||
*scts++ = bcdify(tm->tm_mday);
|
||||
*scts++ = bcdify(tm->tm_hour);
|
||||
*scts++ = bcdify(tm->tm_min);
|
||||
*scts++ = bcdify(tm->tm_sec);
|
||||
*scts++ = bcdify(0); /* GMT */
|
||||
}
|
||||
|
||||
/* Decode 03.40 TP-SCTS (into utc/gmt timestamp) */
|
||||
time_t gsm340_scts(uint8_t *scts)
|
||||
{
|
||||
struct tm tm;
|
||||
uint8_t yr = unbcdify(*scts++);
|
||||
int ofs;
|
||||
|
||||
memset(&tm, 0x00, sizeof(struct tm));
|
||||
|
||||
if (yr <= 80)
|
||||
tm.tm_year = 100 + yr;
|
||||
else
|
||||
tm.tm_year = yr;
|
||||
tm.tm_mon = unbcdify(*scts++) - 1;
|
||||
tm.tm_mday = unbcdify(*scts++);
|
||||
tm.tm_hour = unbcdify(*scts++);
|
||||
tm.tm_min = unbcdify(*scts++);
|
||||
tm.tm_sec = unbcdify(*scts++);
|
||||
|
||||
/* according to gsm 03.40 time zone is
|
||||
"expressed in quarters of an hour" */
|
||||
ofs = unbcdify(*scts++) * 15*60;
|
||||
|
||||
return mktime(&tm) - ofs;
|
||||
}
|
||||
|
||||
/* Return the default validity period in minutes */
|
||||
static unsigned long gsm340_vp_default(void)
|
||||
{
|
||||
unsigned long minutes;
|
||||
/* Default validity: two days */
|
||||
minutes = 24 * 60 * 2;
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* Decode validity period format 'relative' */
|
||||
static unsigned long gsm340_vp_relative(uint8_t *sms_vp)
|
||||
{
|
||||
/* Chapter 9.2.3.12.1 */
|
||||
uint8_t vp;
|
||||
unsigned long minutes;
|
||||
|
||||
vp = *(sms_vp);
|
||||
if (vp <= 143)
|
||||
minutes = vp + 1 * 5;
|
||||
else if (vp <= 167)
|
||||
minutes = 12*60 + (vp-143) * 30;
|
||||
else if (vp <= 196)
|
||||
minutes = vp-166 * 60 * 24;
|
||||
else
|
||||
minutes = vp-192 * 60 * 24 * 7;
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* Decode validity period format 'absolute' */
|
||||
static unsigned long gsm340_vp_absolute(uint8_t *sms_vp)
|
||||
{
|
||||
/* Chapter 9.2.3.12.2 */
|
||||
time_t expires, now;
|
||||
unsigned long minutes;
|
||||
|
||||
expires = gsm340_scts(sms_vp);
|
||||
now = time(NULL);
|
||||
if (expires <= now)
|
||||
minutes = 0;
|
||||
else
|
||||
minutes = (expires-now)/60;
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* Decode validity period format 'relative in integer representation' */
|
||||
static unsigned long gsm340_vp_relative_integer(uint8_t *sms_vp)
|
||||
{
|
||||
uint8_t vp;
|
||||
unsigned long minutes;
|
||||
vp = *(sms_vp);
|
||||
if (vp == 0) {
|
||||
LOGP(DLSMS, LOGL_ERROR,
|
||||
"reserved relative_integer validity period\n");
|
||||
return gsm340_vp_default();
|
||||
}
|
||||
minutes = vp/60;
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* Decode validity period format 'relative in semi-octet representation' */
|
||||
static unsigned long gsm340_vp_relative_semioctet(uint8_t *sms_vp)
|
||||
{
|
||||
unsigned long minutes;
|
||||
minutes = unbcdify(*sms_vp++)*60; /* hours */
|
||||
minutes += unbcdify(*sms_vp++); /* minutes */
|
||||
minutes += unbcdify(*sms_vp++)/60; /* seconds */
|
||||
return minutes;
|
||||
}
|
||||
|
||||
/* decode validity period. return minutes */
|
||||
unsigned long gsm340_validity_period(uint8_t sms_vpf, uint8_t *sms_vp)
|
||||
{
|
||||
uint8_t fi; /* functionality indicator */
|
||||
|
||||
switch (sms_vpf) {
|
||||
case GSM340_TP_VPF_RELATIVE:
|
||||
return gsm340_vp_relative(sms_vp);
|
||||
case GSM340_TP_VPF_ABSOLUTE:
|
||||
return gsm340_vp_absolute(sms_vp);
|
||||
case GSM340_TP_VPF_ENHANCED:
|
||||
/* Chapter 9.2.3.12.3 */
|
||||
fi = *sms_vp++;
|
||||
/* ignore additional fi */
|
||||
if (fi & (1<<7)) sms_vp++;
|
||||
/* read validity period format */
|
||||
switch (fi & 0x7) {
|
||||
case 0x0:
|
||||
return gsm340_vp_default(); /* no vpf specified */
|
||||
case 0x1:
|
||||
return gsm340_vp_relative(sms_vp);
|
||||
case 0x2:
|
||||
return gsm340_vp_relative_integer(sms_vp);
|
||||
case 0x3:
|
||||
return gsm340_vp_relative_semioctet(sms_vp);
|
||||
default:
|
||||
/* The GSM spec says that the SC should reject any
|
||||
unsupported and/or undefined values. FIXME */
|
||||
LOGP(DLSMS, LOGL_ERROR,
|
||||
"Reserved enhanced validity period format\n");
|
||||
return gsm340_vp_default();
|
||||
}
|
||||
case GSM340_TP_VPF_NONE:
|
||||
default:
|
||||
return gsm340_vp_default();
|
||||
}
|
||||
}
|
||||
|
||||
/* determine coding alphabet dependent on GSM 03.38 Section 4 DCS */
|
||||
enum sms_alphabet gsm338_get_sms_alphabet(uint8_t dcs)
|
||||
{
|
||||
uint8_t cgbits = dcs >> 4;
|
||||
enum sms_alphabet alpha = DCS_NONE;
|
||||
|
||||
if ((cgbits & 0xc) == 0) {
|
||||
if (cgbits & 2) {
|
||||
LOGP(DLSMS, LOGL_NOTICE,
|
||||
"Compressed SMS not supported yet\n");
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
switch ((dcs >> 2)&0x03) {
|
||||
case 0:
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
break;
|
||||
case 1:
|
||||
alpha = DCS_8BIT_DATA;
|
||||
break;
|
||||
case 2:
|
||||
alpha = DCS_UCS2;
|
||||
break;
|
||||
}
|
||||
} else if (cgbits == 0xc || cgbits == 0xd)
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
else if (cgbits == 0xe)
|
||||
alpha = DCS_UCS2;
|
||||
else if (cgbits == 0xf) {
|
||||
if (dcs & 4)
|
||||
alpha = DCS_8BIT_DATA;
|
||||
else
|
||||
alpha = DCS_7BIT_DEFAULT;
|
||||
}
|
||||
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/* generate a TPDU address field compliant with 03.40 sec. 9.1.2.5 */
|
||||
int gsm340_gen_oa(uint8_t *oa, unsigned int oa_len, uint8_t type,
|
||||
uint8_t plan, const char *number)
|
||||
{
|
||||
int len_in_bytes;
|
||||
|
||||
/* prevent buffer overflows */
|
||||
if (strlen(number) > 20)
|
||||
number = "";
|
||||
|
||||
// oa[1] = 0xb9; /* networks-specific number, private numbering plan */
|
||||
oa[1] = 0x80 | (type << 4) | plan;
|
||||
|
||||
len_in_bytes = gsm48_encode_bcd_number(oa, oa_len, 1, number);
|
||||
|
||||
/* GSM 03.40 tells us the length is in 'useful semi-octets' */
|
||||
oa[0] = strlen(number) & 0xff;
|
||||
|
||||
return len_in_bytes;
|
||||
}
|
||||
|
||||
/* Prefix msg with a RP header */
|
||||
int gsm411_push_rp_header(struct msgb *msg, uint8_t rp_msg_type,
|
||||
uint8_t rp_msg_ref)
|
||||
{
|
||||
struct gsm411_rp_hdr *rp;
|
||||
uint8_t len = msg->len;
|
||||
|
||||
/* GSM 04.11 RP-DATA header */
|
||||
rp = (struct gsm411_rp_hdr *)msgb_push(msg, sizeof(*rp));
|
||||
rp->len = len + 2;
|
||||
rp->msg_type = rp_msg_type;
|
||||
rp->msg_ref = rp_msg_ref; /* FIXME: Choose randomly */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Prefix msg with a 04.08/04.11 CP header */
|
||||
int gsm411_push_cp_header(struct msgb *msg, uint8_t proto, uint8_t trans,
|
||||
uint8_t msg_type)
|
||||
{
|
||||
struct gsm48_hdr *gh;
|
||||
|
||||
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
|
||||
/* Outgoing needs the highest bit set */
|
||||
gh->proto_discr = proto | (trans << 4);
|
||||
gh->msg_type = msg_type;
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue