manual merge SS from sup-ussd-on-master-ss-wip

This commit is contained in:
Sergey.Kostanbaev
2016-02-09 20:21:08 +03:00
committed by Ivan Kluchnikov
parent db0e216845
commit d4839fe14a
20 changed files with 1330 additions and 702 deletions

View File

@@ -211,6 +211,18 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
AC_MSG_RESULT([$enable_ext_tests]) AC_MSG_RESULT([$enable_ext_tests])
AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes") AM_CONDITIONAL(ENABLE_EXT_TESTS, test "x$enable_ext_tests" = "xyes")
# Enable/disable ussd_proxy utility
AC_ARG_ENABLE([ussd_proxy], [AS_HELP_STRING([--enable-ussd-proxy], [Build the USSD MAP SUP to SIP proxy])],
[osmo_ac_build_ussd_proxy="$enableval"],[osmo_ac_build_ussd_proxy="no"])
if test "$osmo_ac_build_ussd_proxy" = "yes" ; then
PKG_CHECK_MODULES(LIBSOFIA_SIP_UA, sofia-sip-ua >= 1.10)
AC_DEFINE(BUILD_USSD_PROXY, 1, [Define if we want to build ussd_proxy])
fi
AM_CONDITIONAL(BUILD_USSD_PROXY, test "x$osmo_ac_build_ussd_proxy" = "xyes")
AC_SUBST(osmo_ac_build_smpp)
dnl Generate the output dnl Generate the output
AM_CONFIG_HEADER(bscconfig.h) AM_CONFIG_HEADER(bscconfig.h)
@@ -259,6 +271,7 @@ AC_OUTPUT(
tests/slhc/Makefile tests/slhc/Makefile
tests/v42bis/Makefile tests/v42bis/Makefile
tests/nanobts_omlattr/Makefile tests/nanobts_omlattr/Makefile
tests/ussd/Makefile
doc/Makefile doc/Makefile
doc/examples/Makefile doc/examples/Makefile
Makefile) Makefile)

View File

@@ -39,6 +39,7 @@ enum {
DSUA, DSUA,
DV42BIS, DV42BIS,
DSUP, DSUP,
DSS,
Debug_LastEntry, Debug_LastEntry,
}; };

View File

@@ -7,18 +7,17 @@
struct gsm_subscriber_connection; struct gsm_subscriber_connection;
int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, int gsm0480_send_component(struct gsm_subscriber_connection *conn,
const struct msgb *in_msg, struct msgb *msg,
int response_text_len, struct ss_header* reqhdr);
uint8_t response_lang,
const char* response_text,
const struct ussd_request *req,
uint8_t code,
uint8_t ctype,
uint8_t mtype);
int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
const struct msgb *msg, uint8_t invoke_id,
const struct ussd_request *request); uint8_t transaction_id);
struct msgb *gsm0480_compose_ussd_component(struct ss_request* req);
int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level,
const char *text); const char *text);

View File

@@ -0,0 +1,14 @@
#ifndef _GSM_USSD_MAP_H
#define _GSM_USSD_MAP_H
#include <openbsc/gprs_gsup_client.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/gsm_ussd_map_proto.h>
int ussd_map_read_cb(struct gprs_gsup_client *sup_client,
struct msgb *msg);
int ussd_map_tx_message(struct gsm_network *net, struct ss_header *req,
const char *extension, uint32_t ref, const uint8_t *component_data);
#endif /* _GSM_USSD_MAP_H */

View File

@@ -0,0 +1,25 @@
#ifndef _GSM_USSD_MAP_PROTO_H
#define _GSM_USSD_MAP_PROTO_H
#include <osmocom/gsm/gsm0480.h>
enum {
FMAP_MSISDN = 0x80
};
int subscr_uss_message(struct msgb *msg,
struct ss_header *req,
const char* extension,
uint32_t ref,
const uint8_t *component_data);
int rx_uss_message_parse(const uint8_t* data,
size_t len,
struct ss_header *ss,
uint32_t *ref,
char* extention,
size_t extention_len);
#endif /* _GSM_USSD_MAP_PROTO_H */

View File

@@ -56,6 +56,11 @@ struct gsm_trans {
struct gsm_sms *sms; struct gsm_sms *sms;
} sms; } sms;
struct {
uint8_t invoke_id;
uint8_t mo;
uint8_t dirty;
} ss;
}; };
}; };

View File

@@ -11,6 +11,13 @@
int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg); int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg);
int on_ussd_response(const struct ss_request* req, const char* extention); int on_ussd_response(struct gsm_network *net,
uint32_t ref,
struct ss_header *reqhdr,
const uint8_t *component,
const char* extention);
void _ussd_trans_free(struct gsm_trans *trans);
#endif #endif

View File

@@ -45,6 +45,7 @@ SUBDIRS += \
ipaccess \ ipaccess \
gprs \ gprs \
reg-proxy \ reg-proxy \
ussd-proxy \
$(NULL) $(NULL)
# Conditional Programs # Conditional Programs

View File

@@ -47,6 +47,8 @@ libmsc_a_SOURCES = \
osmo_msc.c \ osmo_msc.c \
ctrl_commands.c \ ctrl_commands.c \
meas_feed.c \ meas_feed.c \
gsm_ussd_map_proto.c \
gsm_ussd_map.c \
$(NULL) $(NULL)
if BUILD_SMPP if BUILD_SMPP

View File

@@ -39,7 +39,7 @@
#include <osmocom/core/msgb.h> #include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h> #include <osmocom/gsm/tlv.h>
/* This function can handle ASN1 length up to 255 which is enough for USSD */
static inline unsigned char *msgb_wrap_with_ASN1_TL(struct msgb *msgb, uint8_t tag) static inline unsigned char *msgb_wrap_with_ASN1_TL(struct msgb *msgb, uint8_t tag)
{ {
uint16_t origlen = msgb->len; uint16_t origlen = msgb->len;
@@ -75,56 +75,47 @@ static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag,
return data; return data;
} }
static inline unsigned char *msgb_wrap_with_L(struct msgb *msgb)
{
uint8_t *data = msgb_push(msgb, 1);
/* Send response to a mobile-originated ProcessUnstructuredSS-Request */ data[0] = msgb->len - 1;
int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn, return data;
const struct msgb *in_msg, }
int response_text_len,
uint8_t response_lang, /* Compose universial USSD packet invoke/return_result payload */
const char *response_text, struct msgb *gsm0480_compose_ussd_component(struct ss_request* req)
const struct ussd_request *req,
uint8_t code,
uint8_t ctype,
uint8_t mtype)
{ {
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP"); struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP");
struct gsm48_hdr *gh;
uint8_t *ptr8; uint8_t *ptr8;
int response_len;
/* First put the payload text into the message */
ptr8 = msgb_put(msg, 0); ptr8 = msgb_put(msg, 0);
if (response_text_len < 0) { memcpy(ptr8, req->ussd_text, req->ussd_text_len);
/* First put the payload text into the message */ msgb_put(msg, req->ussd_text_len);
gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len);
msgb_put(msg, response_len);
response_lang = 0x0F;
} else {
memcpy(ptr8, response_text, response_text_len);
msgb_put(msg, response_text_len);
}
/* Then wrap it as an Octet String */ /* Then wrap it as an Octet String */
msgb_wrap_with_ASN1_TL(msg, ASN1_OCTET_STRING_TAG); msgb_wrap_with_ASN1_TL(msg, ASN1_OCTET_STRING_TAG);
/* Pre-pend the DCS octet string */ /* Pre-pend the DCS octet string */
msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, response_lang); msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_language);
/* Then wrap these as a Sequence */ /* Then wrap these as a Sequence */
msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG); msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG);
if (ctype == GSM0480_CTYPE_RETURN_RESULT) { if (req->component_type == GSM0480_CTYPE_RETURN_RESULT) {
/* Pre-pend the operation code */ /* Pre-pend the operation code */
msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, code); msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, req->opcode);
/* Wrap the operation code and IA5 string as a sequence */ /* Wrap the operation code and IA5 string as a sequence */
msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG); msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG);
/* Pre-pend the invoke ID */ /* Pre-pend the invoke ID */
msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
} else if (ctype == GSM0480_CTYPE_INVOKE) { } else if (req->component_type == GSM0480_CTYPE_INVOKE) {
/* Pre-pend the operation code */ /* Pre-pend the operation code */
msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, code); msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, req->opcode);
/* Pre-pend the invoke ID */ /* Pre-pend the invoke ID */
msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
@@ -132,16 +123,104 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
abort(); abort();
} }
/* Wrap this up as a Return Result component */ /* Wrap this up as an Invoke or a Return Result component */
msgb_wrap_with_ASN1_TL(msg, ctype); msgb_wrap_with_ASN1_TL(msg, req->component_type);
return msg;
}
if (mtype == GSM0480_MTYPE_REGISTER || #ifndef NO_GSM0480_SEND_FUNC
mtype == GSM0480_MTYPE_RELEASE_COMPLETE) {
int gsm0480_send_component(struct gsm_subscriber_connection *conn,
struct msgb *msg,
struct ss_header* reqhdr)
{
#if 0
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
uint8_t *ptr8;
ptr8 = msgb_put(msg, 0);
memcpy(ptr8, component, reqhdr->component_length);
msgb_put(msg, reqhdr->component_length);
#endif
struct gsm48_hdr *gh;
if (reqhdr->message_type == GSM0480_MTYPE_REGISTER ||
reqhdr->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) {
/* Wrap the component in a Facility message, it's not ASN1 */ /* Wrap the component in a Facility message, it's not ASN1 */
msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
} else if (mtype == GSM0480_MTYPE_FACILITY) { } else if (reqhdr->message_type == GSM0480_MTYPE_FACILITY) {
uint8_t *data = msgb_push(msg, 1); /* For GSM0480_MTYPE_FACILITY it's LV not TLV */
data[0] = msg->len - 1; msgb_wrap_with_L(msg);
} else {
abort();
}
/* And finally pre-pend the L3 header */
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_NC_SS | reqhdr->transaction_id
| (1<<7); /* TI direction = 1 */
gh->msg_type = reqhdr->message_type;
DEBUGP(DSS, "Sending SS to mobile: %s\n", msgb_hexdump(msg));
return gsm0808_submit_dtap(conn, msg, 0, 0);
}
#if 0
/* Compose universial SS packet except Reject opcodes */
int gsm0480_send_ussd(struct gsm_subscriber_connection *conn,
struct ss_request* req)
{
struct msgb *msg = gsm48_msgb_alloc();
struct gsm48_hdr *gh;
uint8_t *ptr8;
/* First put the payload text into the message */
ptr8 = msgb_put(msg, 0);
memcpy(ptr8, req->ussd_text, req->ussd_text_len);
msgb_put(msg, req->ussd_text_len);
/* Then wrap it as an Octet String */
msgb_wrap_with_ASN1_TL(msg, ASN1_OCTET_STRING_TAG);
/* Pre-pend the DCS octet string */
msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_language);
/* Then wrap these as a Sequence */
msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG);
if (req->component_type == GSM0480_CTYPE_RETURN_RESULT) {
/* Pre-pend the operation code */
msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, req->opcode);
/* Wrap the operation code and IA5 string as a sequence */
msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG);
/* Pre-pend the invoke ID */
msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
} else if (req->component_type == GSM0480_CTYPE_INVOKE) {
/* Pre-pend the operation code */
msgb_push_TLV1(msg, GSM0480_OPERATION_CODE, req->opcode);
/* Pre-pend the invoke ID */
msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
} else {
abort();
}
/* Wrap this up as an Invoke or a Return Result component */
msgb_wrap_with_ASN1_TL(msg, req->component_type);
if (req->message_type == GSM0480_MTYPE_REGISTER ||
req->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) {
/* Wrap the component in a Facility message, it's not ASN1 */
msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
} else if (req->message_type == GSM0480_MTYPE_FACILITY) {
/* For GSM0480_MTYPE_FACILITY it's LV not TLV */
msgb_wrap_with_L(msg);
} else { } else {
abort(); abort();
} }
@@ -150,41 +229,47 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id gh->proto_discr = GSM48_PDISC_NC_SS | req->transaction_id
| (1<<7); /* TI direction = 1 */ | (1<<7); /* TI direction = 1 */
gh->msg_type = req->message_type;
gh->msg_type = mtype; DEBUGP(DSS, "Sending USSD to mobile: %s\n", msgb_hexdump(msg));
DEBUGP(DSUP, "Sending USSD to mobile: %s\n", msgb_hexdump(msg));
return gsm0808_submit_dtap(conn, msg, 0, 0); return gsm0808_submit_dtap(conn, msg, 0, 0);
} }
#endif
int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn, int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
const struct msgb *in_msg, uint8_t invoke_id,
const struct ussd_request *req) uint8_t transaction_id)
{ {
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD REJ"); struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD REJ");
struct gsm48_hdr *gh; struct ss_header ssh;
/* First insert the problem code */ /* First insert the problem code */
msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL, msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL,
GSM_0480_GEN_PROB_CODE_UNRECOGNISED); GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
/* Before it insert the invoke ID */ /* Before it insert the invoke ID */
msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id); msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, invoke_id);
/* Wrap this up as a Reject component */ /* Wrap this up as a Reject component */
msgb_wrap_with_ASN1_TL(msg, GSM0480_CTYPE_REJECT); msgb_wrap_with_ASN1_TL(msg, GSM0480_CTYPE_REJECT);
/* Prepare data for L3 header */
ssh.transaction_id = transaction_id;
ssh.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
return gsm0480_send_component(conn, msg, &ssh);
#if 0
/* Wrap the component in a Facility message */ /* Wrap the component in a Facility message */
msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY); msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
/* And finally pre-pend the L3 header */ /* And finally pre-pend the L3 header */
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh)); gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_NC_SS; gh->proto_discr = GSM48_PDISC_NC_SS;
gh->proto_discr |= req->transaction_id | (1<<7); /* TI direction = 1 */ gh->proto_discr |= transaction_id | (1<<7); /* TI direction = 1 */
gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE; gh->msg_type = GSM0480_MTYPE_RELEASE_COMPLETE;
return gsm0808_submit_dtap(conn, msg, 0, 0); return gsm0808_submit_dtap(conn, msg, 0, 0);
#endif
} }
int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text) int msc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, const char *text)
@@ -202,3 +287,5 @@ int msc_send_ussd_release_complete(struct gsm_subscriber_connection *conn)
return -1; return -1;
return gsm0808_submit_dtap(conn, msg, 0, 0); return gsm0808_submit_dtap(conn, msg, 0, 0);
} }
#endif

View File

@@ -32,6 +32,7 @@
#include <openbsc/gprs_utils.h> #include <openbsc/gprs_utils.h>
#include <openbsc/ussd.h> #include <openbsc/ussd.h>
#if 0
enum { enum {
FMAP_MSISDN = 0x80 FMAP_MSISDN = 0x80
}; };
@@ -171,7 +172,7 @@ static int rx_uss_message(const uint8_t* data, size_t len)
return on_ussd_response(&ss, extention); return on_ussd_response(&ss, extention);
} }
#endif
static int subscr_tx_sup_message(struct gprs_gsup_client *sup_client, static int subscr_tx_sup_message(struct gprs_gsup_client *sup_client,
struct gsm_subscriber *subscr, struct gsm_subscriber *subscr,
@@ -438,11 +439,11 @@ static int subscr_rx_sup_message(struct gprs_gsup_client *sup_client, struct msg
struct gprs_gsup_message gsup_msg = {0}; struct gprs_gsup_message gsup_msg = {0};
struct gsm_subscriber *subscr; struct gsm_subscriber *subscr;
#if 0
if (*data == GPRS_GSUP_MSGT_MAP) { if (*data == GPRS_GSUP_MSGT_MAP) {
return rx_uss_message(data, data_len); return rx_uss_message(data, data_len);
} }
#endif
rc = gprs_gsup_decode(data, data_len, &gsup_msg); rc = gprs_gsup_decode(data, data_len, &gsup_msg);
if (rc < 0) { if (rc < 0) {
LOGP(DSUP, LOGL_ERROR, LOGP(DSUP, LOGL_ERROR,

View File

@@ -0,0 +1,93 @@
/* GSM USSD external MAP interface */
/* (C) 2015 by Sergey Kostanbaev <sergey.kostanbaev@gmail.com>
*
* 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 <openbsc/gsm_ussd_map.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/debug.h>
#include <openbsc/db.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/gsm_04_08_gprs.h>
#include <openbsc/gprs_gsup_messages.h>
#include <openbsc/gprs_gsup_client.h>
#include <openbsc/osmo_msc.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/ussd.h>
int ussd_map_tx_message(struct gsm_network* net,
struct ss_header *req,
const char* extension,
uint32_t ref,
const uint8_t* component_data)
{
struct msgb *msg = gprs_gsup_msgb_alloc();
if (!msg)
return -ENOMEM;
subscr_uss_message(msg, req, extension, ref, component_data);
return gprs_gsup_client_send(net->ussd_sup_client, msg);
}
static int ussd_map_rx_message_int(struct gsm_network *net, const uint8_t* data, size_t len)
{
char extension[32] = {0};
uint32_t ref;
struct ss_header ss;
memset(&ss, 0, sizeof(ss));
if (rx_uss_message_parse(data, len, &ss, &ref, extension, sizeof(extension))) {
LOGP(DSS, LOGL_ERROR, "Can't parse SUP MAP SS message\n");
return -1;
}
LOGP(DSS, LOGL_ERROR, "Got type=0x%02x len=%d\n",
ss.message_type, ss.component_length);
return on_ussd_response(net, ref, &ss, data + ss.component_offset, extension);
}
static int ussd_map_rx_message(struct gprs_gsup_client *sup_client, struct msgb *msg)
{
uint8_t *data = msgb_l2(msg);
size_t data_len = msgb_l2len(msg);
struct gsm_network *gsmnet = (struct gsm_network *)sup_client->data;
if (*data != GPRS_GSUP_MSGT_USSD_MAP) {
return -1;
}
return ussd_map_rx_message_int(gsmnet, data, data_len);
}
int ussd_map_read_cb(struct gprs_gsup_client *sup_client, struct msgb *msg)
{
int rc;
rc = ussd_map_rx_message(sup_client, msg);
msgb_free(msg);
if (rc < 0)
return -1;
return rc;
}

View File

@@ -0,0 +1,212 @@
/* GSM USSD external MAP protocol on pseudo TCAP */
/* (C) 2015 by Sergey Kostanbaev <sergey.kostanbaev@gmail.com>
*
* 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 <openbsc/gsm_ussd_map.h>
#include <openbsc/gsm_ussd_map_proto.h>
#include <openbsc/gsm_subscriber.h>
#include <openbsc/gsm_04_08.h>
#include <openbsc/debug.h>
#include <openbsc/db.h>
#include <openbsc/chan_alloc.h>
#include <openbsc/gsm_04_08_gprs.h>
#include <openbsc/gprs_gsup_messages.h>
#include <openbsc/gprs_gsup_client.h>
#include <openbsc/osmo_msc.h>
#include <openbsc/gprs_utils.h>
#include <openbsc/ussd.h>
/*
* 0 - GPRS_GSUP_MSGT_USSD_MAP constant
* 1 - LEN
* 2 - message_type [ REGISTER / FACILITY / RELEASE COMPLETE ]
* 3,4,5,6 - tid ID associated with the session
* 7 - FMAP_MSISDN constant
* 8 - extention_len
* 9..x - extention
* x+1 .. original MAP message
*/
int subscr_uss_message(struct msgb *msg,
struct ss_header *req,
const char* extension,
uint32_t ref,
const uint8_t* component_data)
{
uint8_t bcd_lvlen;
uint8_t offset = 0;
uint8_t *gsup_indicator;
gsup_indicator = msgb_put(msg, 7);
/* First byte should always be GPRS_GSUP_MSGT_USSD_MAP */
gsup_indicator[offset++] = GPRS_GSUP_MSGT_USSD_MAP;
gsup_indicator[offset++] = 0; // Total length
gsup_indicator[offset++] = req->message_type;
gsup_indicator[offset++] = ref >> 24;
gsup_indicator[offset++] = ref >> 16;
gsup_indicator[offset++] = ref >> 8;
gsup_indicator[offset++] = ref;
if (extension) {
gsup_indicator[offset++] = FMAP_MSISDN;
bcd_lvlen = gsm48_encode_bcd_number(gsup_indicator + offset,
32, 0, extension);
offset += bcd_lvlen;
msgb_put(msg, bcd_lvlen + 1);
}
if (component_data) {
msgb_put(msg, req->component_length);
memcpy(gsup_indicator + offset, component_data, req->component_length);
}
gsup_indicator[1] = offset + req->component_length - 2; //except GPRS_GSUP_MSGT_USSD_MAP and length field
return 0;
#if 0
gsup_indicator[6] = req->component_type;
/* invokeId */
msgb_tlv_put(msg, GSM0480_COMPIDTAG_INVOKE_ID, 1, &req->invoke_id);
/* opCode */
msgb_tlv_put(msg, GSM0480_OPERATION_CODE, 1, &req->opcode);
if (req->ussd_text_len > 0) {
msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_len + 1, &req->ussd_text_language);
}
if (extension) {
uint8_t bcd_buf[32];
bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0,
extension);
msgb_tlv_put(msg, FMAP_MSISDN, bcd_len - 1, &bcd_buf[1]);
}
/* fill actual length */
gsup_indicator[7] = 3 + 3 + (req->ussd_text_len + 1 + 2) + (bcd_len + 2);;
/* wrap with GSM0480_CTYPE_INVOKE */
// gsm0480_wrap_invoke(msg, req->opcode, invoke_id);
// gsup_indicator = msgb_push(msgb, 1);
// gsup_indicator[0] = GPRS_GSUP_MSGT_MAP;
return 0;
#endif
}
int rx_uss_message_parse(const uint8_t* data,
size_t len,
struct ss_header *ss,
uint32_t *pref,
char* extention,
size_t extention_len)
{
uint8_t ext_len;
const uint8_t* const_data = data + 1; // Skip constant
uint32_t ref;
int total_len;
if (len < 7)
return -1;
/* skip GPRS_GSUP_MSGT_MAP */
total_len = *(const_data++);
ss->message_type = *(const_data++);
ref = ((uint32_t)(*(const_data++))) << 24;
ref |= ((uint32_t)(*(const_data++))) << 16;
ref |= ((uint32_t)(*(const_data++))) << 8;
ref |= ((uint32_t)(*(const_data++)));
if (pref)
*pref = ref;
total_len -= 4 + 1; // ref + sizeof(len)
if (*const_data == FMAP_MSISDN) {
ext_len = *(++const_data);
if (extention) {
gsm48_decode_bcd_number(extention,
extention_len,
const_data,
0);
}
const_data += ext_len + 1;
total_len -= ext_len + 2; // tag FMAP_MSISDN + sizeof(len)
}
ss->component_offset = const_data - data;
ss->component_length = total_len; //data[ss->component_offset + 1];
return 0;
#if 0
ss->component_type = *(++const_data);
/* skip full len and move to component id */
const_data += 2;
if (*const_data != GSM0480_COMPIDTAG_INVOKE_ID) {
return -1;
}
const_data += 2;
ss->invoke_id = *const_data;
const_data++;
//
if (*const_data != GSM0480_OPERATION_CODE) {
return -1;
}
const_data += 2;
ss->opcode = *const_data;
const_data++;
while (const_data - data < len) {
uint8_t len;
switch (*const_data) {
case ASN1_OCTET_STRING_TAG:
ss->ussd_text_len = len = (*(++const_data) - 1);
ss->ussd_text_language = *(++const_data);
memcpy(ss->ussd_text,
++const_data,
(len > MAX_LEN_USSD_STRING) ? MAX_LEN_USSD_STRING : len);
const_data += len;
break;
case FMAP_MSISDN:
len = *(++const_data);
gsm48_decode_bcd_number(extention,
extention_len,
const_data,
0);
const_data += len + 1;
break;
default:
DEBUGP(DSS, "Unknown code: %d\n", *const_data);
return -1;
}
}
return 0;
#endif
}

View File

@@ -33,378 +33,341 @@
#include <openbsc/gsm_subscriber.h> #include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h> #include <openbsc/debug.h>
#include <openbsc/osmo_msc.h> #include <openbsc/osmo_msc.h>
#include <openbsc/gsm_sup.h> #include <openbsc/gsm_ussd_map.h>
#include <openbsc/ussd.h> #include <openbsc/ussd.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsm0480.h> #include <osmocom/gsm/gsm0480.h>
#include <osmocom/gsm/protocol/gsm_04_08.h>
#include <openbsc/transaction.h>
struct gsm_ussd { /* Last uniq generated session id */
struct llist_head ussqueue; static uint32_t s_uniq_ussd_sessiod_id = 0;
uint8_t uniq_id; /**< System wide uniq ID */ /* Forward declaration of USSD handler for USSD MAP interface */
static int handle_rcv_ussd_sup(struct gsm_subscriber_connection *conn, struct msgb *msg);
uint8_t invoke_id;
uint8_t transaction_id;
uint8_t mobile_originated;
struct gsm_subscriber_connection *conn;
};
static unsigned s_ussd_open_sessions = 0;
static uint64_t s_uniq_ussd_sessiod_id = 0;
static LLIST_HEAD(s_active_ussd_sessions);
static struct llist_head *get_active_ussd_sessions(void)
{
return &s_active_ussd_sessions;
}
static struct gsm_ussd* ussd_session_alloc(struct gsm_subscriber_connection* conn,
uint8_t tid,
uint8_t mo)
{
struct gsm_network* net = conn->bts->network;
struct gsm_ussd* m = talloc_zero(net, struct gsm_ussd);
if (!m)
return NULL;
m->conn = conn;
m->uniq_id = s_uniq_ussd_sessiod_id++;
m->transaction_id = tid;
m->mobile_originated = mo;
++s_ussd_open_sessions;
INIT_LLIST_HEAD(&m->ussqueue);
llist_add_tail(&m->ussqueue, &s_active_ussd_sessions);
DEBUGP(DMM, "Alloc USSD session: %d (open: %d)\n", m->uniq_id, s_ussd_open_sessions);
return m;
}
static void ussd_session_free(struct gsm_ussd* s)
{
--s_ussd_open_sessions;
DEBUGP(DMM, "Free USSD session: %d (open: %d)\n", s->uniq_id, s_ussd_open_sessions);
llist_del(&s->ussqueue);
talloc_free(s);
}
static struct gsm_ussd* get_by_uniq_id(uint8_t uniq_id)
{
struct gsm_ussd* c;
llist_for_each_entry(c, get_active_ussd_sessions(), ussqueue) {
if (c->uniq_id == uniq_id) {
DEBUGP(DMM, "uniq_id %d has %s extention\n",
uniq_id, c->conn->subscr->extension);
return c;
}
}
DEBUGP(DMM, "uniq_id %d hasn't been found\n", uniq_id);
return NULL;
}
static struct gsm_ussd* get_by_iid(struct gsm_subscriber_connection *conn, uint8_t invoke_id)
{
struct gsm_ussd* c;
llist_for_each_entry(c, get_active_ussd_sessions(), ussqueue) {
if (c->conn == conn && c->invoke_id == invoke_id) {
DEBUGP(DMM, "invoke_id %d has %s extention\n",
invoke_id, c->conn->subscr->extension);
return c;
}
}
DEBUGP(DMM, "invoke_id %d hasn't been found\n", invoke_id);
return NULL;
}
static struct gsm_ussd* get_by_tid(struct gsm_subscriber_connection *conn, uint8_t transaction_id)
{
struct gsm_ussd* c;
llist_for_each_entry(c, get_active_ussd_sessions(), ussqueue) {
if (c->conn == conn && c->transaction_id == transaction_id) {
DEBUGP(DMM, "transaction_id %d has %s extention\n",
transaction_id, c->conn->subscr->extension);
return c;
}
}
DEBUGP(DMM, "transaction_id %d hasn't been found\n", transaction_id);
return NULL;
}
// From SUP
int on_ussd_response(const struct ss_request *req, const char *extention)
{
struct ussd_request ussd_req;
struct gsm_ussd* ussdq;
memset(&ussd_req, 0, sizeof(ussd_req));
int rc = 0;
switch (req->message_type) {
case GSM0480_MTYPE_REGISTER:
DEBUGP(DMM, "Network originated USSD messages isn't supported yet!\n");
//TODO Send to sup rejection
return 0;
case GSM0480_MTYPE_FACILITY:
case GSM0480_MTYPE_RELEASE_COMPLETE:
// FIXME add uinq_id field
ussdq = get_by_uniq_id(req->invoke_id);
if (!ussdq) {
DEBUGP(DMM, "No session was found for uniq_id: %d!\n",
req->invoke_id);
// TODO SUP Reject
return 0;
}
break;
default:
DEBUGP(DMM, "Unknown message type 0x%02x\n", req->message_type);
// TODO SUP Reject
return 0;
}
ussd_req.transaction_id = ussdq->transaction_id;
ussd_req.invoke_id = ussdq->invoke_id;
if (req->component_type != GSM0480_CTYPE_REJECT) {
rc = gsm0480_send_ussd_response(ussdq->conn,
NULL,
(req->ussd_text_language == 0x80) ? -1 : req->ussd_text_len,
req->ussd_text_language,
(const char *)req->ussd_text,
&ussd_req,
req->opcode,
req->component_type,
req->message_type);
} else {
rc = gsm0480_send_ussd_reject(ussdq->conn, NULL, &ussd_req);
}
if (req->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) {
msc_release_connection(ussdq->conn);
ussd_session_free(ussdq);
}
return rc;
}
static int ussd_sup_send_reject(struct gsm_subscriber_connection *conn,
uint8_t uniq_id, uint8_t opcode)
{
struct ss_request rej;
rej.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
rej.component_type = GSM0480_CTYPE_REJECT;
rej.invoke_id = uniq_id;
rej.opcode = opcode;
rej.ussd_text_len = 0;
return subscr_tx_uss_message(&rej, conn->subscr);
}
/* Entrypoint - handler function common to all mobile-originated USSDs */
int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
int rc = 0;
struct gsm48_hdr *gh;
struct ss_request req;
struct gsm_ussd* ussdq = NULL;
struct ussd_request ussd_req;
memset(&req, 0, sizeof(req));
memset(&ussd_req, 0, sizeof(ussd_req));
DEBUGP(DMM, "handle ussd: %s\n", msgb_hexdump(msg));
gh = msgb_l3(msg);
rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req);
if (!rc) {
DEBUGP(DMM, "Unhandled SS\n");
ussdq = get_by_tid(conn, req.transaction_id);
if (ussdq) {
ussd_sup_send_reject(conn, ussdq->uniq_id, 0);
goto failed_transaction;
}
goto transaction_not_found;
}
switch (req.message_type) {
case GSM0480_MTYPE_REGISTER:
ussdq = ussd_session_alloc(conn, req.transaction_id, USSD_MO);
if (!ussdq) {
DEBUGP(DMM, "Failed to create new session\n");
goto transaction_not_found;
}
ussdq->invoke_id = req.invoke_id;
break;
case GSM0480_MTYPE_FACILITY:
ussdq = get_by_tid(conn, req.transaction_id);
if (!ussdq) {
ussdq = get_by_iid(conn, req.invoke_id);
if (!ussdq) {
DEBUGP(DMM, "no session found invoke_id=%d tid=%d\n",
req.invoke_id, req.transaction_id);
goto transaction_not_found;
}
}
break;
case GSM0480_MTYPE_RELEASE_COMPLETE:
// FIXME handle parsing in libosmocore
ussdq = get_by_tid(conn, req.transaction_id);
if (!ussdq) {
DEBUGP(DMM, "RELEASE_COMPLETE to non-existing transaction!\n");
goto release_conn;
}
ussd_session_free(ussdq);
ussd_sup_send_reject(conn, ussdq->uniq_id, req.opcode);
goto release_conn;
}
req.invoke_id = ussdq->uniq_id;
rc = subscr_tx_uss_message(&req, conn->subscr);
if (rc) {
DEBUGP(DMM, "Unable tp send uss over sup reason: %d\n", rc);
goto failed_transaction;
}
return 0;
failed_transaction:
ussd_session_free(ussdq);
transaction_not_found:
ussd_req.invoke_id = req.invoke_id;
ussd_req.transaction_id = req.transaction_id;
gsm0480_send_ussd_reject(conn, msg, &ussd_req);
release_conn:
msc_release_connection(conn);
return rc;
#if 0
ussdq = get_by_iid(conn, req.invoke_id);
// TODO FIXME !!!! Replace by message_type
switch (req.opcode) {
case GSM0480_OP_CODE_PROCESS_USS_REQ:
if (ussdq) {
/* new session with the same id as an open session, destroy both */
DEBUGP(DMM, "Duplicate session? invoke_id: %d\n", req.invoke_id);
goto failed;
}
if (req.component_type != GSM0480_CTYPE_INVOKE) {
DEBUGP(DMM, "processUSS with component_type 0x%02x\n", req.component_type);
goto failed;
}
ussdq = ussd_session_alloc(conn);
if (!ussdq) {
DEBUGP(DMM, "Failed to create new session\n");
goto failed;
}
ussdq->conn = conn;
ussdq->invoke_id = req.invoke_id;
ussdq->transaction_id = req.transaction_id;
break;
case GSM0480_OP_CODE_USS_REQUEST:
if (!ussdq) {
DEBUGP(DMM, "no session found for USS_REQUEST with invoke_id=%d\n", req.invoke_id);
goto failed;
}
if (req.component_type != GSM0480_CTYPE_RETURN_RESULT) {
DEBUGP(DMM, "USS with component_type 0x%02x\n", req.component_type);
goto failed;
}
ussdq->current_transaction_id = req.transaction_id;
break;
default:
DEBUGP(DMM, "Unhandled opcode: 0x%02x, component_type: 0x%02x, text: %s\n",
req.opcode, req.component_type, req.ussd_text);
goto failed;
}
// ACHTUNG! FIXME!! FIXME!! Introduce transaction ID instead
// Override Invoke ID
req.invoke_id = ussdq->uniq_id;
rc = subscr_tx_uss_message(&req, conn->subscr);
if (rc) {
DEBUGP(DMM, "Unable tp send uss over sup reason: %d\n", rc);
goto failed;
}
return 0;
#endif
#if 0
struct ussd_request req;
struct gsm48_hdr *gh;
memset(&req, 0, sizeof(req));
gh = msgb_l3(msg);
rc = gsm0480_decode_ussd_request(gh, msgb_l3len(msg), &req);
if (!rc) {
DEBUGP(DMM, "Unhandled SS\n");
rc = gsm0480_send_ussd_reject(conn, msg, &req);
msc_release_connection(conn);
return rc;
}
/* Release-Complete */
if (req.text[0] == '\0')
return 0;
if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)req.text)) {
DEBUGP(DMM, "USSD: Own number requested\n");
rc = send_own_number(conn, msg, &req);
} else {
rc = subscr_tx_uss_message(req, conn->subscr);
//TODO:
}
#endif
#if 0
failed:
// TODO handle error on SUP end
if (ussdq) {
ussd_session_free(ussdq);
}
ussd_req.invoke_id = req.invoke_id;
ussd_req.transaction_id = req.transaction_id;
gsm0480_send_ussd_reject(conn, msg, &ussd_req);
/* check if we can release it */
msc_release_connection(conn);
return rc;
#endif
}
#if 0
/* Declarations of USSD strings to be recognised */ /* Declarations of USSD strings to be recognised */
const char USSD_TEXT_OWN_NUMBER[] = "*#100#"; const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
/* Forward declarations of network-specific handler functions */ /* Forward declarations of network-specific handler functions */
static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req); static int send_own_number(struct gsm_subscriber_connection *conn,
const struct ss_header *reqhdr,
const struct ss_request *req);
/* Entrypoint - handler function common to all mobile-originated USSDs */
int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
int rc;
struct ss_header reqhdr;
struct ss_request req;
char request_string[MAX_LEN_USSD_STRING + 1];
struct gsm48_hdr *gh;
if (conn->subscr->group->net->ussd_sup_client)
return handle_rcv_ussd_sup(conn, msg);
memset(&req, 0, sizeof(req));
memset(&reqhdr, 0, sizeof(reqhdr));
gh = msgb_l3(msg);
rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &reqhdr);
if (!rc) {
DEBUGP(DSS, "Incorrect SS header\n");
msc_release_connection(conn);
return rc;
}
rc = gsm0480_parse_ss_facility(gh->data + reqhdr.component_offset,
reqhdr.component_length,
&req);
if (!rc) {
DEBUGP(DSS, "Unhandled SS\n");
// TODO req.invoke_id may not be set!!!
rc = gsm0480_send_ussd_reject(conn, req.invoke_id, reqhdr.transaction_id);
msc_release_connection(conn);
return rc;
}
if (reqhdr.message_type == GSM0480_MTYPE_RELEASE_COMPLETE)
return 0;
if (reqhdr.message_type != GSM0480_MTYPE_REGISTER ||
req.component_type != GSM0480_CTYPE_INVOKE ||
req.opcode != GSM0480_OP_CODE_PROCESS_USS_REQ ||
req.ussd_text_language != 0x0f)
{
DEBUGP(DSS, "Unexpected SS\n");
rc = gsm0480_send_ussd_reject(conn, req.invoke_id, reqhdr.transaction_id);
msc_release_connection(conn);
return rc;
}
gsm_7bit_decode_n_ussd(request_string, MAX_LEN_USSD_STRING, req.ussd_text, req.ussd_text_len);
if (!strcmp(USSD_TEXT_OWN_NUMBER, (const char *)request_string)) {
DEBUGP(DSS, "USSD: Own number requested\n");
rc = send_own_number(conn, &reqhdr, &req);
} else {
DEBUGP(DSS, "Unhandled USSD %s\n", request_string);
rc = gsm0480_send_ussd_reject(conn, req.invoke_id, reqhdr.transaction_id);
}
/* check if we can release it */
msc_release_connection(conn);
return rc;
}
/* A network-specific handler function */ /* A network-specific handler function */
static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req) static int send_own_number(struct gsm_subscriber_connection *conn,
const struct ss_header *reqhdr,
const struct ss_request *req)
{ {
struct ss_request rss;
struct ss_header rssh;
char *own_number = conn->subscr->extension; char *own_number = conn->subscr->extension;
char response_string[GSM_EXTENSION_LENGTH + 20]; char response_string[GSM_EXTENSION_LENGTH + 20];
int response_len;
/* Need trailing CR as EOT character */ /* Need trailing CR as EOT character */
snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number); snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
return gsm0480_send_ussd_response(conn, msg, response_string, req);
memset(&rss, 0, sizeof(rss));
gsm_7bit_encode_n_ussd(rss.ussd_text, MAX_LEN_USSD_STRING, response_string, &response_len);
rss.ussd_text_len = response_len;
rss.ussd_text_language = 0x0f;
rss.component_type = GSM0480_CTYPE_RETURN_RESULT;
rss.invoke_id = req->invoke_id;
rss.opcode = GSM0480_OP_CODE_PROCESS_USS_REQ;
rssh.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
rssh.transaction_id = reqhdr->transaction_id;
return gsm0480_send_component(conn,
gsm0480_compose_ussd_component(&rss),
&rssh);
} }
static int ussd_sup_send_reject(struct gsm_network *conn, uint32_t ref)
{
struct ss_header rej;
rej.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
rej.component_length = 0;
#if 0
rej.component_type = GSM0480_CTYPE_REJECT;
rej.invoke_id = invokeid;
rej.opcode = opcode;
rej.ussd_text_len = 0;
#endif #endif
return ussd_map_tx_message(conn, &rej, NULL, ref, NULL);
}
/* Callback from USSD MAP interface */
int on_ussd_response(struct gsm_network *net,
uint32_t ref,
struct ss_header *reqhdr,
const uint8_t* component,
const char *extention)
{
struct gsm_trans *trans = trans_find_by_callref(net, ref);
int rc = 0;
struct msgb *msg;
uint8_t *ptr8;
switch (reqhdr->message_type) {
case GSM0480_MTYPE_REGISTER:
DEBUGP(DSS, "Network originated USSD messages isn't supported yet!\n");
ussd_sup_send_reject(net, ref);
return 0;
case GSM0480_MTYPE_FACILITY:
case GSM0480_MTYPE_RELEASE_COMPLETE:
if (!trans) {
DEBUGP(DSS, "No session was found for ref: %d!\n",
ref);
ussd_sup_send_reject(net, ref);
return 0;
}
break;
default:
DEBUGP(DSS, "Unknown message type 0x%02x\n", reqhdr->message_type);
ussd_sup_send_reject(net, ref);
return 0;
}
#if 0
req->invoke_id = trans->ss.invoke_id;
req->transaction_id = (trans->transaction_id << 4) ^ 0x80;
if (req->component_type != GSM0480_CTYPE_REJECT) {
rc = gsm0480_send_ussd(trans->conn, req);
} else {
rc = gsm0480_send_ussd_reject(trans->conn, req);
}
#endif
msg = gsm48_msgb_alloc();
ptr8 = msgb_put(msg, 0);
memcpy(ptr8, component, reqhdr->component_length);
msgb_put(msg, reqhdr->component_length);
rc = gsm0480_send_component(trans->conn, msg, reqhdr);
if (reqhdr->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) {
struct gsm_subscriber_connection* conn = trans->conn;
trans_free(trans);
msc_release_connection(conn);
}
return rc;
}
static int get_invoke_id(const uint8_t* data, uint8_t len, uint8_t* pinvoke_id)
{
/* 0: CTYPE tag
* 1..x: CTYPE len
* x: INVOKE_ID tag
* x+1: INVOKE_ID len
* x+2: INVOKE_ID value
*/
if (len < 5)
return 0;
unsigned inv_offset = 2;
switch (data[0]) {
case GSM0480_CTYPE_INVOKE:
case GSM0480_CTYPE_RETURN_RESULT:
if (data[1] > 0x80)
inv_offset += data[1] & 0x7f;
if (inv_offset + 2 >= len)
return 0;
if (data[inv_offset] != GSM0480_COMPIDTAG_INVOKE_ID)
return 0;
*pinvoke_id = data[inv_offset + 2];
return 1;
}
return 0;
}
/* Handler function common to all mobile-originated USSDs in case if USSD MAP enabled */
static int handle_rcv_ussd_sup(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
int rc = 0;
struct gsm48_hdr *gh = msgb_l3(msg);
struct ss_header reqhdr;
struct gsm_trans *trans = NULL;
uint8_t transaction_id = ((gh->proto_discr >> 4) ^ 0x8); /* flip */
uint8_t invoke_id = 0;
if (!conn->subscr)
return -EIO;
memset(&reqhdr, 0, sizeof(reqhdr));
DEBUGP(DSS, "handle ussd tid=%d: %s\n", transaction_id, msgb_hexdump(msg));
trans = trans_find_by_id(conn, GSM48_PDISC_NC_SS, transaction_id);
rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &reqhdr);
if (!rc) {
DEBUGP(DSS, "Incorrect SS header\n");
if (!trans) {
goto release_conn;
}
/* don't know how to process */
goto failed_transaction;
}
switch (reqhdr.message_type) {
case GSM0480_MTYPE_REGISTER:
if (trans) {
/* we already have a transaction, ignore this message */
goto release_conn;
}
if (!get_invoke_id(gh->data + reqhdr.component_offset,
reqhdr.component_length,
&invoke_id)) {
DEBUGP(DSS, "Incorrect InvokeID in transaction\n");
goto release_conn;
}
trans = trans_alloc(conn->bts->network, conn->subscr,
GSM48_PDISC_NC_SS,
transaction_id, s_uniq_ussd_sessiod_id++);
if (!trans) {
DEBUGP(DSS, "Failed to create new ussd transaction\n");
goto transaction_not_found;
}
trans->conn = conn;
trans->ss.invoke_id = invoke_id;
trans->ss.mo = 1;
trans->ss.dirty = 1;
break;
case GSM0480_MTYPE_FACILITY:
if (!trans) {
DEBUGP(DSS, "No session found tid=%d\n",
transaction_id);
if (!get_invoke_id(gh->data + reqhdr.component_offset,
reqhdr.component_length,
&invoke_id)) {
DEBUGP(DSS, "Incorrect InvokeID in transaction\n");
goto release_conn;
}
goto transaction_not_found;
}
break;
case GSM0480_MTYPE_RELEASE_COMPLETE:
if (!trans) {
DEBUGP(DSS, "RELEASE_COMPLETE to non-existing transaction!\n");
goto release_conn;
}
trans_free(trans);
goto release_conn;
}
rc = ussd_map_tx_message(conn->subscr->group->net, &reqhdr,
conn->subscr->extension, trans->callref,
gh->data + reqhdr.component_offset);
if (rc) {
/* do not send reject if we failed with the message */
trans->ss.dirty = 0;
DEBUGP(DSS, "Unable tp send uss over sup reason: %d\n", rc);
goto failed_transaction;
}
return 0;
failed_transaction:
trans_free(trans);
transaction_not_found:
gsm0480_send_ussd_reject(conn, invoke_id, transaction_id);
release_conn:
msc_release_connection(conn);
return rc;
}
void _ussd_trans_free(struct gsm_trans *trans)
{
if (trans->ss.dirty) {
trans->ss.dirty = 0;
//ussd_sup_send_reject(trans->net, trans->callref, trans->ss.invoke_id, 0);
ussd_sup_send_reject(trans->net, trans->callref);
}
}

View File

@@ -50,7 +50,8 @@
#include <openbsc/sms_queue.h> #include <openbsc/sms_queue.h>
#include <openbsc/mncc_int.h> #include <openbsc/mncc_int.h>
#include <openbsc/handover.h> #include <openbsc/handover.h>
#include <openbsc/gsm_sup.h> #include <openbsc/gprs_gsup_client.h>
#include <openbsc/gsm_ussd_map.h>
#include <osmocom/vty/logging.h> #include <osmocom/vty/logging.h>
@@ -1035,19 +1036,20 @@ DEFUN(sup_ussd_destination, sup_ussd_destination_cmd,
struct gsm_network *gsmnet = gsmnet_from_vty(vty); struct gsm_network *gsmnet = gsmnet_from_vty(vty);
if (gsmnet->ussd_sup_client) { if (gsmnet->ussd_sup_client) {
LOGP(DSUP, LOGL_FATAL, "Can't create two USSD SUP clients\n"); LOGP(DSS, LOGL_FATAL, "Can't create two USSD SUP clients\n");
vty_out(vty, "%%USSD SUP client already configured%s", VTY_NEWLINE); vty_out(vty, "%%USSD SUP client already configured%s", VTY_NEWLINE);
return CMD_WARNING; return CMD_WARNING;
} }
gsmnet->ussd_sup_client = gprs_gsup_client_create( gsmnet->ussd_sup_client = gprs_gsup_client_create(
argv[0], atoi(argv[1]), &sup_read_cb); argv[0], atoi(argv[1]), &ussd_map_read_cb);
if (!gsmnet->ussd_sup_client) { if (!gsmnet->ussd_sup_client) {
LOGP(DSUP, LOGL_FATAL, "Cannot set up USSD SUP socket\n"); LOGP(DSS, LOGL_FATAL, "Cannot set up USSD SUP socket\n");
vty_out(vty, "%%Cannot set up USSD SUP socket%s", VTY_NEWLINE); vty_out(vty, "%%Cannot set up USSD SUP socket%s", VTY_NEWLINE);
return CMD_WARNING; return CMD_WARNING;
} }
gsmnet->ussd_sup_client->data = gsmnet;
return CMD_SUCCESS; return CMD_SUCCESS;
} }

View File

@@ -1,12 +1,10 @@
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir) AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(COVERAGE_CFLAGS) \ AM_CFLAGS=-Wall $(COVERAGE_CFLAGS) \
$(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) $(LIBOSMOABIS_CFLAGS) \ $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOABIS_CFLAGS)
-I/usr/include/sofia-sip-1.12
AM_LDFLAGS = $(COVERAGE_LDFLAGS) AM_LDFLAGS = $(COVERAGE_LDFLAGS)
bin_PROGRAMS = reg-proxy ussd-proxy bin_PROGRAMS = reg-proxy
reg_proxy_SOURCES = \ reg_proxy_SOURCES = \
../gprs/gsm_04_08_gprs.c \ ../gprs/gsm_04_08_gprs.c \
@@ -22,10 +20,3 @@ reg_proxy_LDADD = \
$(LIBOSMOCTRL_LIBS) $(LIBOSMOABIS_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOABIS_LIBS)
ussd_proxy_SOURCES = \
ussd_proxy.c
ussd_proxy_LDADD = \
-lsofia-sip-ua \
$(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) $(LIBOSMOABIS_LIBS)

View File

@@ -23,6 +23,7 @@ static int handle_sup_upd_loc_req(struct gsm_sup_server *sup_server,
return rc; return rc;
} }
#if 0
static int handle_sup_ss(struct gsm_sup_server *sup_server, static int handle_sup_ss(struct gsm_sup_server *sup_server,
struct ss_request *ss, struct ss_request *ss,
const char* extention) const char* extention)
@@ -40,7 +41,6 @@ static int handle_sup_ss(struct gsm_sup_server *sup_server,
return rc; return rc;
} }
enum { enum {
FMAP_MSISDN = 0x80 FMAP_MSISDN = 0x80
}; };
@@ -223,6 +223,8 @@ static int rx_sup_uss_message(struct gsm_sup_server *sup_server, const uint8_t*
#endif #endif
} }
#endif
int rx_sup_message(struct gsm_sup_server *sup_server, struct msgb *msg) int rx_sup_message(struct gsm_sup_server *sup_server, struct msgb *msg)
{ {
uint8_t *data = msgb_l2(msg); uint8_t *data = msgb_l2(msg);
@@ -231,14 +233,14 @@ int rx_sup_message(struct gsm_sup_server *sup_server, struct msgb *msg)
struct gprs_gsup_message sup_msg = {0}; struct gprs_gsup_message sup_msg = {0};
//struct gsm_subscriber *subscr; //struct gsm_subscriber *subscr;
#if 0
if (*data == GPRS_GSUP_MSGT_MAP) { if (*data == GPRS_GSUP_MSGT_MAP) {
LOGP(DSUP, LOGL_INFO, LOGP(DSUP, LOGL_INFO,
"Receive USS: %s\n", msgb_hexdump(msg)); "Receive USS: %s\n", msgb_hexdump(msg));
return rx_sup_uss_message(sup_server, data, data_len); return rx_sup_uss_message(sup_server, data, data_len);
} }
#endif
rc = gprs_gsup_decode(data, data_len, &sup_msg); rc = gprs_gsup_decode(data, data_len, &sup_msg);
if (rc < 0) { if (rc < 0) {
LOGP(DSUP, LOGL_ERROR, LOGP(DSUP, LOGL_ERROR,

View File

@@ -0,0 +1,19 @@
if BUILD_USSD_PROXY
AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include -I$(top_builddir)
AM_CFLAGS=-Wall $(COVERAGE_CFLAGS) \
$(LIBOSMOCORE_CFLAGS) $(LIBOSMOGSM_CFLAGS) $(LIBOSMOVTY_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) $(LIBOSMOABIS_CFLAGS) \
-I/usr/include/sofia-sip-1.12 -DNO_GSM0480_SEND_FUNC
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
bin_PROGRAMS = ussd-proxy
ussd_proxy_SOURCES = \
ussd_proxy.c ../libmsc/gsm_ussd_map_proto.c ../libmsc/gsm_04_80.c
ussd_proxy_LDADD = \
-lsofia-sip-ua \
$(LIBOSMOGSM_LIBS) $(LIBOSMOVTY_LIBS) $(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) $(LIBOSMOABIS_LIBS)
endif

View File

@@ -8,6 +8,7 @@
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <unistd.h> #include <unistd.h>
#include <ctype.h>
typedef struct context_s context_t; typedef struct context_s context_t;
#define NTA_OUTGOING_MAGIC_T context_t #define NTA_OUTGOING_MAGIC_T context_t
@@ -38,122 +39,12 @@ typedef struct operation operation_t;
#include <osmocom/core/linuxlist.h> #include <osmocom/core/linuxlist.h>
#include <openbsc/gprs_gsup_messages.h> #include <openbsc/gprs_gsup_messages.h>
#include <openbsc/gsm_ussd_map_proto.h>
#include <openbsc/gsm_04_80.h>
#include <iconv.h> #include <iconv.h>
typedef uint8_t sup_tcap_tid_t; typedef uint32_t sup_tcap_tid_t;
/******************************************************************************/
/* Put this into separate file */
enum {
FMAP_MSISDN = 0x80
};
static int rx_uss_message_parse(struct ss_request *ss,
const uint8_t* data,
size_t len,
char* extention,
size_t extention_len)
{
const uint8_t* const_data = data;
if (len < 1 + 2 + 3 + 3)
return -1;
/* skip GPRS_GSUP_MSGT_MAP */
ss->message_type = *(++const_data);
ss->component_type = *(++const_data);
const_data += 2;
//
if (*const_data != GSM0480_COMPIDTAG_INVOKE_ID) {
return -1;
}
const_data += 2;
ss->invoke_id = *const_data;
const_data++;
//
if (*const_data != GSM0480_OPERATION_CODE) {
return -1;
}
const_data += 2;
ss->opcode = *const_data;
const_data++;
while (const_data - data < len) {
uint8_t len;
switch (*const_data) {
case ASN1_OCTET_STRING_TAG:
ss->ussd_text_len = len = (*(++const_data) - 1);
ss->ussd_text_language = *(++const_data);
memcpy(ss->ussd_text,
++const_data,
(len > MAX_LEN_USSD_STRING) ? MAX_LEN_USSD_STRING : len);
const_data += len;
break;
case FMAP_MSISDN:
len = *(++const_data);
gsm48_decode_bcd_number(extention,
extention_len,
const_data,
0);
const_data += len + 1;
break;
default:
DEBUGP(DLCTRL, "Unknown code: %d\n", *const_data);
return -1;
}
}
return 0;
}
static int subscr_uss_message(struct msgb *msg,
struct ss_request *req,
const char* extention)
{
size_t bcd_len = 0;
uint8_t *gsup_indicator;
gsup_indicator = msgb_put(msg, 4);
/* First byte should always be GPRS_GSUP_MSGT_MAP */
gsup_indicator[0] = GPRS_GSUP_MSGT_MAP;
gsup_indicator[1] = req->message_type;
/* TODO ADD tid */
gsup_indicator[2] = req->component_type;
/* invokeId */
msgb_tlv_put(msg, GSM0480_COMPIDTAG_INVOKE_ID, 1, &req->invoke_id);
/* opCode */
msgb_tlv_put(msg, GSM0480_OPERATION_CODE, 1, &req->opcode);
if (req->ussd_text_len > 0) {
msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_len + 1, &req->ussd_text_language);
}
if (extention) {
uint8_t bcd_buf[32];
bcd_len = gsm48_encode_bcd_number(bcd_buf, sizeof(bcd_buf), 0,
extention);
msgb_tlv_put(msg, FMAP_MSISDN, bcd_len - 1, &bcd_buf[1]);
}
/* fill actual length */
gsup_indicator[3] = 3 + 3 + (req->ussd_text_len + 1 + 2) + (bcd_len + 2);
/* wrap with GSM0480_CTYPE_INVOKE */
// gsm0480_wrap_invoke(msg, req->opcode, invoke_id);
// gsup_indicator = msgb_push(msgb, 1);
// gsup_indicator[0] = GPRS_GSUP_MSGT_MAP;
return 0;
}
/******************************************************************************/
typedef struct isup_connection isup_connection_t; typedef struct isup_connection isup_connection_t;
@@ -170,15 +61,22 @@ struct isup_connection {
struct msgb *pending_msg; struct msgb *pending_msg;
}; };
typedef enum ss_type {
TYPE_USSD,
TYPE_SS_OTHER
} ss_type_t;
struct ussd_session { struct ussd_session {
isup_connection_t *conn; isup_connection_t *conn;
sup_tcap_tid_t ref;
// int32_t transaction_id;
int ms_originated; int ms_originated;
struct ss_request rigester_msg;
char extention[32]; char extention[32];
ss_type_t type;
uint8_t ss_code;
struct ss_request rigester_msg;
}; };
struct context_s { struct context_s {
@@ -203,9 +101,6 @@ struct context_s {
iconv_t* utf8_to_latin1; iconv_t* utf8_to_latin1;
iconv_t* latin1_to_utf8; iconv_t* latin1_to_utf8;
int dont_encode_in_latin1;
int force_7bit;
/* Array of isup connections */ /* Array of isup connections */
struct isup_connection isup[1]; struct isup_connection isup[1];
@@ -233,10 +128,14 @@ static
int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_len, int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_len,
const char* msg, unsigned msg_len); const char* msg, unsigned msg_len);
static static
int ussd_send_data_ss(isup_connection_t *conn, struct ss_request* reply); int ussd_send_data_ss(isup_connection_t *conn,
uint8_t message_type,
const uint8_t *component,
uint8_t component_len,
uint32_t ref);
static static
int ussd_send_reject(isup_connection_t *conn, uint8_t invoke_id, uint8_t opcode); int ussd_send_reject(isup_connection_t *conn, uint32_t ref, uint8_t invoke_id);
static const char* get_unknown_header(sip_t const *sip, const char *header) static const char* get_unknown_header(sip_t const *sip, const char *header)
{ {
@@ -328,11 +227,11 @@ static int ussd_parse_xml(const char *xml,
} }
// Operation APIs // Operation APIs
static operation_t* operation_find_by_tid(context_t* ctx, sup_tcap_tid_t id) static operation_t* operation_find_by_tid(context_t* ctx, sup_tcap_tid_t ref)
{ {
operation_t* op; operation_t* op;
llist_for_each_entry(op, &ctx->operation_list, list) { llist_for_each_entry(op, &ctx->operation_list, list) {
if (op->ussd.rigester_msg.invoke_id == id) if (op->ussd.ref == ref)
return op; return op;
} }
return NULL; return NULL;
@@ -372,11 +271,18 @@ static void operation_destroy(operation_t* op)
llist_del(&op->list); llist_del(&op->list);
op->ctx->operation_count--; op->ctx->operation_count--;
if (op->ussd.type == TYPE_USSD) {
fprintf(stderr, "--- operation %*.s from %s destroyed (sessions: %d)\n", fprintf(stderr, "--- operation %*.s from %s destroyed (sessions: %d)\n",
op->ussd.rigester_msg.ussd_text_len, op->ussd.rigester_msg.ussd_text_len,
op->ussd.rigester_msg.ussd_text, op->ussd.rigester_msg.ussd_text,
op->ussd.extention, op->ussd.extention,
op->ctx->operation_count); op->ctx->operation_count);
} else {
fprintf(stderr, "--- operation 0x%02x from %s destroyed (sessions: %d)\n",
op->ussd.ss_code,
op->ussd.extention,
op->ctx->operation_count);
}
/* release operation context information */ /* release operation context information */
su_free(op->ctx->home, op); su_free(op->ctx->home, op);
@@ -394,12 +300,21 @@ void proxy_r_invite(int status,
fprintf(stderr, "*** Got reply %d for INVITE\n", status); fprintf(stderr, "*** Got reply %d for INVITE\n", status);
if (status == 200) { if (status == 200) {
nua_ack(nh, TAG_END()); nua_ack(nh, TAG_END());
} else { } else if (hmagic->ussd.type == TYPE_USSD) {
printf("response to INVITE: %03d %s\n", status, phrase); printf("response to USSD INVITE: %03d %s\n", status, phrase);
ussd_send_reject(hmagic->ussd.conn, ussd_send_reject(hmagic->ussd.conn,
hmagic->ussd.rigester_msg.invoke_id, hmagic->ussd.ref,
hmagic->ussd.rigester_msg.opcode); hmagic->ussd.rigester_msg.invoke_id);
operation_destroy(hmagic);
} else {
printf("response to SS INVITE: %03d %s\n", status, phrase);
ussd_send_data_ss(hmagic->ussd.conn,
GSM0480_MTYPE_RELEASE_COMPLETE,
NULL,
0,
hmagic->ussd.ref);
operation_destroy(hmagic); operation_destroy(hmagic);
} }
} }
@@ -452,11 +367,69 @@ void proxy_i_bye(int status,
fprintf(stderr, "*** response BYE with %d satus is malformed, drop session\n", fprintf(stderr, "*** response BYE with %d satus is malformed, drop session\n",
status); status);
ussd_send_reject(hmagic->ussd.conn, ussd_send_reject(hmagic->ussd.conn,
hmagic->ussd.rigester_msg.invoke_id, hmagic->ussd.ref,
hmagic->ussd.rigester_msg.opcode); hmagic->ussd.rigester_msg.invoke_id);
operation_destroy(hmagic); operation_destroy(hmagic);
} }
static
uint8_t get_nibble(uint8_t a)
{
if (a >= '0' && a <= '9')
return a-'0';
else if (a >= 'A' && a <= 'F')
return a-'A';
else if (a >= 'a' && a <= 'f')
return a-'a';
fprintf(stderr, "*** Incorrect nibble deteced: %02x\n", a);
return 0;
}
void proxy_i_bye_ss(int status,
char const *phrase,
nua_t *nua,
nua_magic_t *magic,
nua_handle_t *nh,
nua_hmagic_t *hmagic,
sip_t const *sip,
tagi_t tags[])
{
const char* pl_txt = sip->sip_payload->pl_data;
unsigned pl_txt_len = sip->sip_payload->pl_len;
uint8_t buffer[256];
uint8_t buflen = 0;
int i;
for (i = 0; i < pl_txt_len && buflen < sizeof(buflen) - 1; ) {
uint8_t hi_nibble = pl_txt[i++];
if (hi_nibble == 0)
break;
if (isspace(hi_nibble))
continue;
uint8_t lo_nibble = pl_txt[i++];
if (lo_nibble == 0)
break;
buffer[buflen++] = (get_nibble(hi_nibble) << 4) |
get_nibble(lo_nibble);
}
if (buflen > 1) {
if (buffer[1] != buflen - 2) {
fprintf(stderr, "*** parsed %d len, but should be %d (%s)",
buflen, buffer[1] + 2, pl_txt);
}
}
ussd_send_data_ss(hmagic->ussd.conn,
GSM0480_MTYPE_RELEASE_COMPLETE,
buffer,
buflen,
hmagic->ussd.ref);
}
void proxy_r_bye(int status, void proxy_r_bye(int status,
char const *phrase, char const *phrase,
nua_t *nua, nua_t *nua,
@@ -552,13 +525,33 @@ void proxy_info(int status,
response ? "response" : "request", response ? "response" : "request",
status); status);
ussd_send_reject(hmagic->ussd.conn, ussd_send_reject(hmagic->ussd.conn,
hmagic->ussd.rigester_msg.invoke_id, hmagic->ussd.ref,
hmagic->ussd.rigester_msg.opcode); hmagic->ussd.rigester_msg.invoke_id);
operation_destroy(hmagic); operation_destroy(hmagic);
} }
int ussd_create_xml_ascii(char *content, size_t max_len, const char* language, const char* msg, int msg_len) int ussd_create_xml_latin1(context_t* ctx,
char *content, size_t max_len,
const char* inbuf_latin1, int buf_len)
{ {
const char *language = "en";
char tmpbuf_utf8[2*MAX_LEN_USSD_STRING];
unsigned tmpbuf_utf8_len;
char* inbuf = (char*)inbuf_latin1;
size_t inleft = buf_len;
char* outbuf = tmpbuf_utf8;
size_t outleft = sizeof(tmpbuf_utf8);
size_t s;
s = iconv(ctx->latin1_to_utf8, &inbuf, &inleft, &outbuf, &outleft);
if (s == (size_t)-1) {
LOGP(DLCTRL, LOGL_ERROR, "Unable to encode latin1 into utf8\n");
return 0;
}
tmpbuf_utf8_len = outbuf - tmpbuf_utf8;
int content_len = snprintf(content, max_len, int content_len = snprintf(content, max_len,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<ussd-data>\n" "<ussd-data>\n"
@@ -566,7 +559,7 @@ int ussd_create_xml_ascii(char *content, size_t max_len, const char* language, c
"<ussd-string>%.*s</ussd-string>\n" "<ussd-string>%.*s</ussd-string>\n"
"</ussd-data>", "</ussd-data>",
language, language,
msg_len, msg); tmpbuf_utf8_len, tmpbuf_utf8);
if (content_len > max_len) { if (content_len > max_len) {
content[max_len - 1] = 0; content[max_len - 1] = 0;
return 0; return 0;
@@ -574,15 +567,118 @@ int ussd_create_xml_ascii(char *content, size_t max_len, const char* language, c
return 1; return 1;
} }
static int decode_to_latin1(char* outbuf, unsigned size,
const uint8_t* msg, unsigned msg_len, uint8_t lang)
{
if (lang == 0x0f) {
return gsm_7bit_decode_n_ussd(outbuf, size, msg, msg_len);
} else {
LOGP(DLCTRL, LOGL_ERROR, "Unknown language: 0x%02x\n", lang);
return 0;
}
}
/* URL_RESERVED_CHARS in sofia is not strict enough as in RFC3986 */ /* URL_RESERVED_CHARS in sofia is not strict enough as in RFC3986 */
#define RFC3986_RESERVED_CHARS "!*'();:@&=+$,/?#[]" #define RFC3986_RESERVED_CHARS "!*'();:@&=+$,/?#[]"
int ss_session_open_mo(operation_t *op,
isup_connection_t *conn,
const uint8_t* component,
uint8_t component_len,
uint32_t ref,
const char* extention)
{
char buffer[512+1];
int i;
context_t* ctx = op->ctx;
sip_to_t *to = NULL;
sip_to_t *from = NULL;
url_t to_url, from_url;
char* to_url_str;
char* from_url_str;
op->ussd.ref = ref;
op->ussd.conn = conn;
op->ussd.ms_originated = 1;
op->ussd.type = TYPE_SS_OTHER;
strncpy(op->ussd.extention, extention, sizeof(op->ussd.extention));
for (i = 0; i < component_len; ++i) {
uint8_t nibble_h = component[i] >> 4;
uint8_t nibble_l = component[i] & 0xf;
buffer[2*i ] = (nibble_h < 10) ? '0' + nibble_h : 'a' + nibble_h - 10;
buffer[2*i + 1] = (nibble_l < 10) ? '0' + nibble_l : 'a' + nibble_l - 10;
}
buffer[2*i] = 0;
/* Destination address */
to_url = *ctx->to_url;
to_url.url_user = "mapss";
to_url_str = url_as_string(ctx->home, &to_url);
if (to_url_str == NULL) {
goto failed_create_handle;
}
to = sip_to_create(ctx->home, (url_string_t *)to_url_str);
su_free(ctx->home, to_url_str);
if (!to) {
goto failed_create_handle;
}
/* Source address */
from_url = *ctx->self_url;
from_url.url_user = extention;
from_url_str = url_as_string(ctx->home, &from_url);
if (from_url_str == NULL) {
goto failed_create_handle;
}
from = sip_from_create(ctx->home, (url_string_t *)from_url_str);
su_free(ctx->home, from_url_str);
if (!to) {
goto failed_create_handle;
}
/* create operation handle */
op->handle = nua_handle(ctx->nua,
op,
SIPTAG_TO(to),
SIPTAG_FROM(from),
NUTAG_M_USERNAME(extention),
TAG_END());
su_free(ctx->home, from);
su_free(ctx->home, to);
from = NULL;
to = NULL;
if (op->handle == NULL) {
goto failed_create_handle;
}
nua_invite(op->handle,
SIPTAG_CONTENT_TYPE_STR("application/map-ss-binary"),
SIPTAG_PAYLOAD_STR(buffer),
TAG_END());
return 0;
failed_create_handle:
if (from != NULL)
su_free(ctx->home, from);
if (to != NULL)
su_free(ctx->home, to);
return -1;
}
int ussd_session_open_mo(operation_t *op, int ussd_session_open_mo(operation_t *op,
isup_connection_t *conn, isup_connection_t *conn,
struct ss_request* ss, struct ss_request* ss,
uint32_t ref,
const char* extention) const char* extention)
{ {
char content[1024]; char content[1024];
char decoded[MAX_LEN_USSD_STRING + 1];
char escaped_to[512]; char escaped_to[512];
context_t* ctx = op->ctx; context_t* ctx = op->ctx;
sip_to_t *to = NULL; sip_to_t *to = NULL;
@@ -591,23 +687,32 @@ int ussd_session_open_mo(operation_t *op,
char* to_url_str; char* to_url_str;
char* from_url_str; char* from_url_str;
int decoded_len;
op->ussd.ref = ref;
op->ussd.conn = conn; op->ussd.conn = conn;
op->ussd.ms_originated = 1; op->ussd.ms_originated = 1;
op->ussd.type = TYPE_USSD;
op->ussd.rigester_msg = *ss; op->ussd.rigester_msg = *ss;
strncpy(op->ussd.extention, extention, sizeof(op->ussd.extention)); strncpy(op->ussd.extention, extention, sizeof(op->ussd.extention));
/* TODO add language support !!! */ decoded_len = decode_to_latin1(decoded, MAX_LEN_USSD_STRING,
op->ussd.rigester_msg.ussd_text,
op->ussd.rigester_msg.ussd_text_len,
op->ussd.rigester_msg.ussd_text_language);
if (decoded_len <= 0) {
goto failed_to_parse_xml;
}
decoded[decoded_len] = 0;
if (!ussd_create_xml_ascii(content, sizeof(content), if (!ussd_create_xml_latin1(ctx, content, sizeof(content),
"en", decoded, decoded_len)) {
(const char* )op->ussd.rigester_msg.ussd_text,
op->ussd.rigester_msg.ussd_text_len)) {
goto failed_to_parse_xml; goto failed_to_parse_xml;
} }
/* Destination address */ /* Destination address */
url_escape(escaped_to, (const char*)ss->ussd_text, RFC3986_RESERVED_CHARS); url_escape(escaped_to, decoded, RFC3986_RESERVED_CHARS);
to_url = *ctx->to_url; to_url = *ctx->to_url;
to_url.url_user = escaped_to; to_url.url_user = escaped_to;
to_url_str = url_as_string(ctx->home, &to_url); to_url_str = url_as_string(ctx->home, &to_url);
@@ -653,6 +758,7 @@ int ussd_session_open_mo(operation_t *op,
} }
nua_invite(op->handle, nua_invite(op->handle,
SIPTAG_UNKNOWN_STR("Recv-Info: g.3gpp.ussd"),
SIPTAG_CONTENT_TYPE_STR("application/vnd.3gpp.ussd+xml"), SIPTAG_CONTENT_TYPE_STR("application/vnd.3gpp.ussd+xml"),
SIPTAG_PAYLOAD_STR(content), SIPTAG_PAYLOAD_STR(content),
TAG_END()); TAG_END());
@@ -673,10 +779,20 @@ int ussd_session_facility(operation_t *op,
const char* extention) const char* extention)
{ {
char content[1024]; char content[1024];
if (!ussd_create_xml_ascii(content, sizeof(content), char decoded[MAX_LEN_USSD_STRING + 1];
"en", int decoded_len;
(const char* )ss->ussd_text,
ss->ussd_text_len)) { decoded_len = decode_to_latin1(decoded, MAX_LEN_USSD_STRING,
op->ussd.rigester_msg.ussd_text,
op->ussd.rigester_msg.ussd_text_len,
op->ussd.rigester_msg.ussd_text_language);
if (decoded_len <= 0) {
return -1;
}
decoded[decoded_len] = 0;
if (!ussd_create_xml_latin1(op->ctx, content, sizeof(content),
decoded, decoded_len)) {
return -1; return -1;
} }
@@ -708,15 +824,20 @@ void context_callback(nua_event_t event,
break; break;
case nua_i_info: case nua_i_info:
if (hmagic->ussd.type == TYPE_USSD)
proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 0); proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 0);
break; break;
case nua_r_info: case nua_r_info:
if (hmagic->ussd.type == TYPE_USSD)
proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 1); proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 1);
break; break;
case nua_i_bye: case nua_i_bye:
if (hmagic->ussd.type == TYPE_USSD)
proxy_i_bye(status, phrase, nua, magic, nh, hmagic, sip, tags); proxy_i_bye(status, phrase, nua, magic, nh, hmagic, sip, tags);
else
proxy_i_bye_ss(status, phrase, nua, magic, nh, hmagic, sip, tags);
break; break;
case nua_i_invite: case nua_i_invite:
@@ -749,28 +870,42 @@ void context_callback(nua_event_t event,
static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data, size_t len) static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data, size_t len)
{ {
char extention[32] = {0}; char extention[32] = {0};
struct ss_request ss; struct ss_header ss;
struct ss_request ssreq;
uint32_t ref;
operation_t* op; operation_t* op;
int rc; int rc;
context_t *ctx = sup_conn->ctx; context_t *ctx = sup_conn->ctx;
memset(&ss, 0, sizeof(ss)); memset(&ss, 0, sizeof(ss));
if (rx_uss_message_parse(&ss, data, len, extention, sizeof(extention))) { if (rx_uss_message_parse(data, len, &ss, &ref, extention, sizeof(extention))) {
LOGP(DLCTRL, LOGL_ERROR, "Can't parse uss message\n"); LOGP(DLCTRL, LOGL_ERROR, "Can't parse uss message\n");
goto err_send_reject; goto err_bad_packet;
} }
LOGP(DLCTRL, LOGL_ERROR, "Got mtype=0x%02x invoke_id=0x%02x opcode=0x%02x component_type=0x%02x text=%s\n", memset(&ssreq, 0, sizeof(ssreq));
ss.message_type, ss.invoke_id, ss.opcode, ss.component_type, ss.ussd_text); rc = gsm0480_parse_ss_facility(data + ss.component_offset,
ss.component_length,
&ssreq);
if (!rc) {
LOGP(DLCTRL, LOGL_ERROR, "Can't parse facility message\n");
goto err_bad_component;
}
LOGP(DLCTRL, LOGL_ERROR, "Got ref=%d mtype=0x%02x invoke_id=0x%02x opcode=0x%02x ss_code=0x%02x component_type=0x%02x text=%s\n", ref,
ss.message_type, ssreq.invoke_id, ssreq.opcode, ssreq.ss_code, ssreq.component_type, ssreq.ussd_text);
switch (ss.message_type) { switch (ss.message_type) {
case GSM0480_MTYPE_REGISTER: case GSM0480_MTYPE_REGISTER:
if (ss.component_type != GSM0480_CTYPE_INVOKE) { if (ssreq.component_type != GSM0480_CTYPE_INVOKE) {
LOGP(DLCTRL, LOGL_ERROR, "Non-INVOKE component type in REGISTER: 0x%02x\n", ss.component_type); LOGP(DLCTRL, LOGL_ERROR, "Non-INVOKE component type in REGISTER: 0x%02x\n", ssreq.component_type);
goto err_send_reject; goto err_send_reject;
} }
if (ss.opcode != GSM0480_OP_CODE_PROCESS_USS_REQ) { if (ssreq.opcode == GSM0480_OP_CODE_PROCESS_USS_DATA ||
LOGP(DLCTRL, LOGL_ERROR, "Don't know hot to handle this SS opcode: 0x%02x\n", ss.opcode); ssreq.opcode == GSM0480_OP_CODE_USS_NOTIFY ||
ssreq.opcode == GSM0480_OP_CODE_USS_REQUEST) {
LOGP(DLCTRL, LOGL_ERROR, "Don't know hot to handle this SS opcode: 0x%02x\n", ssreq.opcode);
goto err_send_reject; goto err_send_reject;
} }
/* Create new session */ /* Create new session */
@@ -779,46 +914,68 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
LOGP(DLCTRL, LOGL_ERROR, "Unable to allocate new session\n"); LOGP(DLCTRL, LOGL_ERROR, "Unable to allocate new session\n");
goto err_send_reject; goto err_send_reject;
} }
if (ssreq.opcode == GSM0480_OP_CODE_PROCESS_USS_REQ) {
LOGP(DLCTRL, LOGL_ERROR, "New session %.*s from %s, active: %d\n", LOGP(DLCTRL, LOGL_ERROR, "New session %.*s from %s, active: %d\n",
ss.ussd_text_len, ssreq.ussd_text_len,
ss.ussd_text, ssreq.ussd_text,
extention, extention,
ctx->operation_count); ctx->operation_count);
rc = ussd_session_open_mo(op, sup_conn, &ss, extention); op->ussd.ss_code = 0;
rc = ussd_session_open_mo(op, sup_conn, &ssreq, ref, extention);
if (rc < 0) { if (rc < 0) {
operation_destroy(op); operation_destroy(op);
goto err_send_reject; goto err_send_reject;
} }
} else {
LOGP(DLCTRL, LOGL_ERROR, "New session SS 0x%02x from %s, active: %d\n",
ssreq.opcode,
extention,
ctx->operation_count);
op->ussd.ss_code = ssreq.ss_code;
op->ussd.rigester_msg = ssreq;
rc = ss_session_open_mo(op,
sup_conn,
data + ss.component_offset,
ss.component_length,
ref,
extention);
if (rc < 0) {
operation_destroy(op);
goto err_send_reject;
}
}
break; break;
case GSM0480_MTYPE_FACILITY: case GSM0480_MTYPE_FACILITY:
//Only MS-originated Menu session is supported, so we ignore INVOKE here //Only MS-originated Menu session is supported, so we ignore INVOKE here
if (ss.component_type != GSM0480_CTYPE_RETURN_RESULT && if (ssreq.component_type != GSM0480_CTYPE_RETURN_RESULT &&
ss.component_type != GSM0480_CTYPE_RETURN_ERROR && ssreq.component_type != GSM0480_CTYPE_RETURN_ERROR &&
ss.component_type != GSM0480_CTYPE_REJECT) { ssreq.component_type != GSM0480_CTYPE_REJECT) {
LOGP(DLCTRL, LOGL_ERROR, "Non-{RESULT/RETURN_ERROR/REJECT} component type in FACILITY: 0x%02x\n", ss.component_type); LOGP(DLCTRL, LOGL_ERROR, "Non-{RESULT/RETURN_ERROR/REJECT} component type in FACILITY: 0x%02x\n", ssreq.component_type);
goto err_send_reject; goto err_send_reject;
} }
// ///////////////////////////////////////////////// // /////////////////////////////////////////////////
// TODO handle RETURN_ERROR/REJECT // TODO handle RETURN_ERROR/REJECT
if (ss.component_type != GSM0480_CTYPE_RETURN_RESULT) { if (ssreq.component_type != GSM0480_CTYPE_RETURN_RESULT) {
LOGP(DLCTRL, LOGL_ERROR, "Component type in FACILITY: 0x%02x is not implemented yet\n", ss.component_type); LOGP(DLCTRL, LOGL_ERROR, "Component type in FACILITY: 0x%02x is not implemented yet\n", ssreq.component_type);
goto err_send_reject; goto err_send_reject;
} }
if (ss.opcode != GSM0480_OP_CODE_USS_REQUEST) { if (ssreq.opcode != GSM0480_OP_CODE_USS_REQUEST) {
LOGP(DLCTRL, LOGL_ERROR, "Don't know hot to handle this SS opcode: 0x%02x\n", ss.opcode); LOGP(DLCTRL, LOGL_ERROR, "Don't know hot to handle this SS opcode: 0x%02x\n", ssreq.opcode);
goto err_send_reject; goto err_send_reject;
} }
op = operation_find_by_tid(ctx, ss.invoke_id); op = operation_find_by_tid(ctx, ref);
if (op == NULL) { if (op == NULL) {
LOGP(DLCTRL, LOGL_ERROR, "No active session with tid=%d were found\n", LOGP(DLCTRL, LOGL_ERROR, "No active session with tid=%d were found\n",
ss.invoke_id); ssreq.invoke_id);
goto err_send_reject; goto err_send_reject;
} }
// TODO check result!! MO/MT error handling // TODO check result!! MO/MT error handling
rc = ussd_session_facility(op, &ss, extention); rc = ussd_session_facility(op, &ssreq, extention);
if (rc < 0) { if (rc < 0) {
operation_destroy(op); operation_destroy(op);
goto err_send_reject; goto err_send_reject;
@@ -826,17 +983,14 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
break; break;
case GSM0480_MTYPE_RELEASE_COMPLETE: case GSM0480_MTYPE_RELEASE_COMPLETE:
op = operation_find_by_tid(ctx, ss.invoke_id); op = operation_find_by_tid(ctx, ref);
if (op == NULL) { if (op == NULL) {
LOGP(DLCTRL, LOGL_ERROR, "No active session with tid=%d were found for RELEASE_COMPLETE\n", LOGP(DLCTRL, LOGL_ERROR, "No active session with tid=%d were found for RELEASE_COMPLETE\n",
ss.invoke_id); ssreq.invoke_id);
return 0; return 0;
} }
// NOTE: Add ContentType for 3rd party software workaround, it's not needed by standard nua_bye(op->handle, TAG_END());
nua_bye(op->handle,
SIPTAG_CONTENT_TYPE_STR("application/vnd.3gpp.ussd+xml"),
TAG_END());
break; break;
default: default:
@@ -847,29 +1001,63 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
return 0; return 0;
err_send_reject: err_send_reject:
ussd_send_reject(sup_conn, ss.invoke_id, ss.opcode); ussd_send_reject(sup_conn, ref, ssreq.invoke_id);
return -1;
err_bad_component:
return ussd_send_data_ss(sup_conn,
GSM0480_MTYPE_RELEASE_COMPLETE,
NULL,
0,
ref);
return -1;
err_bad_packet:
// Disconnect ?
return -1; return -1;
} }
int ussd_send_reject(isup_connection_t *conn, uint8_t invoke_id, uint8_t opcode) int ussd_send_reject(isup_connection_t *conn, uint32_t ref, uint8_t invoke_id)
{ {
struct ss_request error_ss; uint8_t buffer[2+3+3];
memset(&error_ss, 0, sizeof(error_ss)); buffer[0] = GSM0480_CTYPE_REJECT;
error_ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; buffer[1] = 3+3;
error_ss.component_type = GSM0480_CTYPE_REJECT;
error_ss.invoke_id = invoke_id;
error_ss.opcode = opcode;
return ussd_send_data_ss(conn, &error_ss); buffer[2] = GSM0480_COMPIDTAG_INVOKE_ID;
buffer[3] = 1;
buffer[4] = invoke_id;
buffer[5] = GSM_0480_PROBLEM_CODE_TAG_GENERAL;
buffer[6] = 1;
buffer[7] = GSM_0480_GEN_PROB_CODE_UNRECOGNISED;
return ussd_send_data_ss(conn,
GSM0480_MTYPE_RELEASE_COMPLETE,
buffer,
sizeof(buffer),
ref);
} }
int ussd_send_data_ss(isup_connection_t *conn, struct ss_request* reply) int ussd_send_data_ss(isup_connection_t *conn,
uint8_t message_type,
const uint8_t* component,
uint8_t component_len,
uint32_t ref)
{ {
struct msgb *outmsg = msgb_alloc_headroom(4000, 64, __func__); struct msgb *outmsg = msgb_alloc_headroom(4000, 64, __func__);
struct ss_header hdr;
hdr.transaction_id = 0;
hdr.message_type = message_type;
hdr.component_length = component_len;
hdr.component_offset = 0;
subscr_uss_message(outmsg, subscr_uss_message(outmsg,
reply, &hdr,
NULL); NULL,
ref,
component);
LOGP(DLCTRL, LOGL_ERROR, LOGP(DLCTRL, LOGL_ERROR,
"Sending USS, will send: %s\n", msgb_hexdump(outmsg)); "Sending USS, will send: %s\n", msgb_hexdump(outmsg));
@@ -877,33 +1065,27 @@ int ussd_send_data_ss(isup_connection_t *conn, struct ss_request* reply)
return sup_server_send(conn, outmsg); return sup_server_send(conn, outmsg);
} }
static int is_string_ascii(const char* msg, unsigned msg_len)
{
unsigned i;
for (i = 0; i < msg_len; ++i) {
if (*((uint8_t*)(msg++)) >= 0x80)
return 0;
}
return 1;
}
int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_len, int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_len,
const char* msg, unsigned msg_len) const char* msg, unsigned msg_len)
{ {
struct msgb *buf;
struct ss_request ss; struct ss_request ss;
int rc;
uint8_t message_type;
memset(&ss, 0, sizeof(ss)); memset(&ss, 0, sizeof(ss));
// TODO handle language // TODO handle language
if (msg == NULL) { if (msg == NULL) {
ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
ss.component_type = GSM0480_CTYPE_REJECT; ss.component_type = GSM0480_CTYPE_REJECT;
ss.opcode = op->ussd.rigester_msg.opcode; ss.opcode = op->ussd.rigester_msg.opcode;
} else if (last) { } else if (last) {
ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
ss.component_type = GSM0480_CTYPE_RETURN_RESULT; ss.component_type = GSM0480_CTYPE_RETURN_RESULT;
ss.opcode = op->ussd.rigester_msg.opcode; ss.opcode = op->ussd.rigester_msg.opcode;
} else { } else {
ss.message_type = GSM0480_MTYPE_FACILITY; message_type = GSM0480_MTYPE_FACILITY;
ss.component_type = (op->ussd.ms_originated) ? GSM0480_CTYPE_INVOKE ss.component_type = (op->ussd.ms_originated) ? GSM0480_CTYPE_INVOKE
: GSM0480_CTYPE_RETURN_RESULT; : GSM0480_CTYPE_RETURN_RESULT;
ss.opcode = GSM0480_OP_CODE_USS_REQUEST; ss.opcode = GSM0480_OP_CODE_USS_REQUEST;
@@ -912,30 +1094,22 @@ int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_le
ss.invoke_id = op->ussd.rigester_msg.invoke_id; ss.invoke_id = op->ussd.rigester_msg.invoke_id;
if (msg) { if (msg) {
if (msg_len > MAX_LEN_USSD_STRING) { char tmpbuf[MAX_LEN_USSD_STRING + 1];
msg_len = MAX_LEN_USSD_STRING;
}
if (is_string_ascii(msg, msg_len)) {
// Only ASCII characters, no need extra convertion to
// GSM 7-bit, coding will be done on the other end of SUP
ss.ussd_text_len = msg_len;
ss.ussd_text_language = 0x80;
strncpy((char*)ss.ussd_text, msg, msg_len);
} else {
char* inbuf = (char*)msg; char* inbuf = (char*)msg;
size_t inleft = msg_len; size_t inleft = msg_len;
char* outbuf = (char*)ss.ussd_text; char* outbuf = (char*)tmpbuf;
size_t outleft = MAX_LEN_USSD_STRING; size_t outleft = sizeof(tmpbuf);
size_t s; size_t s;
// First of all try latin1 // First of all try latin1
if (!op->ctx->force_7bit && op->ctx->dont_encode_in_latin1) {
s =(size_t)-1;
} else {
s = iconv(op->ctx->utf8_to_latin1, s = iconv(op->ctx->utf8_to_latin1,
&inbuf, &inleft, &inbuf, &inleft,
&outbuf, &outleft); &outbuf, &outleft);
}
if (s == (size_t)-1) { if (s == (size_t)-1) {
outbuf = (char*)ss.ussd_text;
outleft = MAX_LEN_USSD_STRING;
s = iconv(op->ctx->utf8_to_ucs2, s = iconv(op->ctx->utf8_to_ucs2,
&inbuf, &inleft, &inbuf, &inleft,
&outbuf, &outleft); &outbuf, &outleft);
@@ -944,25 +1118,33 @@ int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_le
} }
// UCS-2 encoding // UCS-2 encoding
ss.ussd_text_language = 0x48; ss.ussd_text_language = 0x48;
} else {
if (op->ctx->force_7bit) {
// Decode in 7-bit on SUP side
ss.ussd_text_language = 0x80;
} else {
// 8-bit DATA encoding
ss.ussd_text_language = 0x44;
}
}
ss.ussd_text_len = (uint8_t*)outbuf - ss.ussd_text; ss.ussd_text_len = (uint8_t*)outbuf - ss.ussd_text;
} else {
int outlen;
// Set null-termination
outbuf[0] = 0;
gsm_7bit_encode_n_ussd(ss.ussd_text,
MAX_LEN_USSD_STRING, outbuf, &outlen);
ss.ussd_text_len = outlen;
ss.ussd_text_language = 0x0f;
} }
} else { } else {
ss.ussd_text_len = 0; ss.ussd_text_len = 0;
ss.ussd_text_language = 0x80; ss.ussd_text_language = 0x0f;
ss.ussd_text[0] = 0; ss.ussd_text[0] = 0;
} }
return ussd_send_data_ss(op->ussd.conn, &ss); buf = gsm0480_compose_ussd_component(&ss);
if (!buf) {
return -1;
}
rc = ussd_send_data_ss(op->ussd.conn, message_type,
buf->data, msgb_length(buf), op->ussd.ref);
msgb_free(buf);
return rc;
} }
static void timer_function(su_root_magic_t *magic, static void timer_function(su_root_magic_t *magic,
@@ -976,6 +1158,7 @@ static void timer_function(su_root_magic_t *magic,
llist_for_each_entry_safe(op, tmp, &cli->operation_list, list) { llist_for_each_entry_safe(op, tmp, &cli->operation_list, list) {
su_duration_t lasts = su_duration(n, op->tm_initiated); su_duration_t lasts = su_duration(n, op->tm_initiated);
if (lasts > cli->max_ussd_ses_duration) { if (lasts > cli->max_ussd_ses_duration) {
if (op->ussd.type == TYPE_USSD) {
fprintf(stderr, "!!! session %.*s from %s lasted %ld ms, more than thresold %ld ms, destroying\n", fprintf(stderr, "!!! session %.*s from %s lasted %ld ms, more than thresold %ld ms, destroying\n",
op->ussd.rigester_msg.ussd_text_len, op->ussd.rigester_msg.ussd_text_len,
op->ussd.rigester_msg.ussd_text, op->ussd.rigester_msg.ussd_text,
@@ -985,8 +1168,21 @@ static void timer_function(su_root_magic_t *magic,
ussd_send_reject(op->ussd.conn, ussd_send_reject(op->ussd.conn,
op->ussd.rigester_msg.invoke_id, op->ussd.ref,
op->ussd.rigester_msg.opcode); op->ussd.rigester_msg.invoke_id);
} else {
fprintf(stderr, "!!! session 0x%02x from %s lasted %ld ms, more than thresold %ld ms, destroying\n",
op->ussd.ss_code,
op->ussd.extention,
lasts,
cli->max_ussd_ses_duration);
ussd_send_data_ss(op->ussd.conn,
GSM0480_MTYPE_RELEASE_COMPLETE,
NULL,
0,
op->ussd.ref);
}
operation_destroy(op); operation_destroy(op);
} }
} }
@@ -1037,7 +1233,7 @@ static int isup_handle_connection(context_t *cli, su_wait_t *w, void *p)
goto err; goto err;
case IPAC_PROTO_OSMO: case IPAC_PROTO_OSMO:
// TODO callback // TODO callback
if (msg->l2h[1] == GPRS_GSUP_MSGT_MAP) { if (msg->l2h[1] == GPRS_GSUP_MSGT_USSD_MAP) {
LOGP(DLCTRL, LOGL_ERROR, LOGP(DLCTRL, LOGL_ERROR,
"Receive USS: %s\n", msgb_hexdump(msg)); "Receive USS: %s\n", msgb_hexdump(msg));
@@ -1154,8 +1350,6 @@ static void Usage(char* progname)
" -o <sessions> Maximum number of concurrent USSD sessions\n" " -o <sessions> Maximum number of concurrent USSD sessions\n"
" (default: 200)\n" " (default: 200)\n"
" -l <0-9> sip sofia loglevel, 0 - none; 9 - max\n" " -l <0-9> sip sofia loglevel, 0 - none; 9 - max\n"
" -L Do not try to encode in 8-bit (use 7-bit or UCS-2)\n"
" -7 Encode Latin1 in GSM 7-bit not in 8-bit (don't mix with -L)\n"
, progname); , progname);
} }
@@ -1173,8 +1367,6 @@ int main(int argc, char *argv[])
int max_ussd_ses_secs = 90; int max_ussd_ses_secs = 90;
int max_op_limit = 200; int max_op_limit = 200;
int sip_loglevel = 1; int sip_loglevel = 1;
int dont_try_latin1 = 0;
int force_7bit = 0;
int c; int c;
while ((c = getopt (argc, argv, "x:p:t:u:D:To:l:L7?")) != -1) { while ((c = getopt (argc, argv, "x:p:t:u:D:To:l:L7?")) != -1) {
@@ -1205,10 +1397,10 @@ int main(int argc, char *argv[])
sip_loglevel = atoi(optarg); sip_loglevel = atoi(optarg);
break; break;
case 'L': case 'L':
dont_try_latin1 = 1; fprintf(stderr, " -L is now obsolete, ignored\n");
break; break;
case '7': case '7':
force_7bit = 1; fprintf(stderr, " -7 is now obsolete, ignored\n");
break; break;
case '?': case '?':
default: default:
@@ -1234,8 +1426,6 @@ int main(int argc, char *argv[])
return 1; return 1;
} }
context->dont_encode_in_latin1 = dont_try_latin1;
context->force_7bit = force_7bit;
context->utf8_to_latin1=iconv_open("iso8859-1", "utf-8"); context->utf8_to_latin1=iconv_open("iso8859-1", "utf-8");
context->latin1_to_utf8=iconv_open("utf-8", "iso8859-1"); context->latin1_to_utf8=iconv_open("utf-8", "iso8859-1");
context->utf8_to_ucs2=iconv_open("utf-16be", "utf-8"); context->utf8_to_ucs2=iconv_open("utf-16be", "utf-8");

View File

@@ -10,6 +10,7 @@ SUBDIRS = \
subscr \ subscr \
mm_auth \ mm_auth \
nanobts_omlattr \ nanobts_omlattr \
ussd \
$(NULL) $(NULL)
if BUILD_NAT if BUILD_NAT