aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergey.Kostanbaev <Sergey.Kostanbaev@gmail.com>2016-02-09 20:21:08 +0300
committerIvan Kluchnikov <kluchnikovi@gmail.com>2017-02-07 18:59:54 +0300
commitd4839fe14a0d060933f0006d10dc932375a7c7d6 (patch)
treea640aa5741fa5f75646e6b3e779957639eb8294e
parentdb0e216845a7859bf878a891e2a210dbef6395df (diff)
manual merge SS from sup-ussd-on-master-ss-wip
-rw-r--r--openbsc/configure.ac13
-rw-r--r--openbsc/include/openbsc/debug.h1
-rw-r--r--openbsc/include/openbsc/gsm_04_80.h21
-rw-r--r--openbsc/include/openbsc/gsm_ussd_map.h14
-rw-r--r--openbsc/include/openbsc/gsm_ussd_map_proto.h25
-rw-r--r--openbsc/include/openbsc/transaction.h5
-rw-r--r--openbsc/include/openbsc/ussd.h9
-rw-r--r--openbsc/src/Makefile.am1
-rw-r--r--openbsc/src/libmsc/Makefile.am2
-rw-r--r--openbsc/src/libmsc/gsm_04_80.c167
-rw-r--r--openbsc/src/libmsc/gsm_sup.c7
-rw-r--r--openbsc/src/libmsc/gsm_ussd_map.c93
-rw-r--r--openbsc/src/libmsc/gsm_ussd_map_proto.c212
-rw-r--r--openbsc/src/libmsc/ussd.c517
-rw-r--r--openbsc/src/libmsc/vty_interface_layer3.c10
-rw-r--r--openbsc/src/reg-proxy/Makefile.am13
-rw-r--r--openbsc/src/reg-proxy/sup.c8
-rw-r--r--openbsc/src/ussd-proxy/Makefile.am19
-rw-r--r--openbsc/src/ussd-proxy/ussd_proxy.c (renamed from openbsc/src/reg-proxy/ussd_proxy.c)736
-rw-r--r--openbsc/tests/Makefile.am1
20 files changed, 1251 insertions, 623 deletions
diff --git a/openbsc/configure.ac b/openbsc/configure.ac
index c31d36eaf..179e56f97 100644
--- a/openbsc/configure.ac
+++ b/openbsc/configure.ac
@@ -211,6 +211,18 @@ AC_MSG_CHECKING([whether to enable VTY/CTRL tests])
AC_MSG_RESULT([$enable_ext_tests])
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
AM_CONFIG_HEADER(bscconfig.h)
@@ -259,6 +271,7 @@ AC_OUTPUT(
tests/slhc/Makefile
tests/v42bis/Makefile
tests/nanobts_omlattr/Makefile
+ tests/ussd/Makefile
doc/Makefile
doc/examples/Makefile
Makefile)
diff --git a/openbsc/include/openbsc/debug.h b/openbsc/include/openbsc/debug.h
index d255d29e9..45842dcd7 100644
--- a/openbsc/include/openbsc/debug.h
+++ b/openbsc/include/openbsc/debug.h
@@ -39,6 +39,7 @@ enum {
DSUA,
DV42BIS,
DSUP,
+ DSS,
Debug_LastEntry,
};
diff --git a/openbsc/include/openbsc/gsm_04_80.h b/openbsc/include/openbsc/gsm_04_80.h
index 68de65a0f..a092938ca 100644
--- a/openbsc/include/openbsc/gsm_04_80.h
+++ b/openbsc/include/openbsc/gsm_04_80.h
@@ -7,18 +7,17 @@
struct gsm_subscriber_connection;
-int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
- const struct msgb *in_msg,
- int response_text_len,
- 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_component(struct gsm_subscriber_connection *conn,
+ struct msgb *msg,
+ struct ss_header* reqhdr);
+
int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
- const struct msgb *msg,
- const struct ussd_request *request);
+ uint8_t invoke_id,
+ 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,
const char *text);
diff --git a/openbsc/include/openbsc/gsm_ussd_map.h b/openbsc/include/openbsc/gsm_ussd_map.h
new file mode 100644
index 000000000..72798b24f
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_ussd_map.h
@@ -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 */
diff --git a/openbsc/include/openbsc/gsm_ussd_map_proto.h b/openbsc/include/openbsc/gsm_ussd_map_proto.h
new file mode 100644
index 000000000..7faa5b85f
--- /dev/null
+++ b/openbsc/include/openbsc/gsm_ussd_map_proto.h
@@ -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 */
diff --git a/openbsc/include/openbsc/transaction.h b/openbsc/include/openbsc/transaction.h
index 9a87d04e4..6890f14ae 100644
--- a/openbsc/include/openbsc/transaction.h
+++ b/openbsc/include/openbsc/transaction.h
@@ -56,6 +56,11 @@ struct gsm_trans {
struct gsm_sms *sms;
} sms;
+ struct {
+ uint8_t invoke_id;
+ uint8_t mo;
+ uint8_t dirty;
+ } ss;
};
};
diff --git a/openbsc/include/openbsc/ussd.h b/openbsc/include/openbsc/ussd.h
index eee0b9a0c..b5b073ae9 100644
--- a/openbsc/include/openbsc/ussd.h
+++ b/openbsc/include/openbsc/ussd.h
@@ -11,6 +11,13 @@
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
diff --git a/openbsc/src/Makefile.am b/openbsc/src/Makefile.am
index cb6041cc2..0f50d7d97 100644
--- a/openbsc/src/Makefile.am
+++ b/openbsc/src/Makefile.am
@@ -45,6 +45,7 @@ SUBDIRS += \
ipaccess \
gprs \
reg-proxy \
+ ussd-proxy \
$(NULL)
# Conditional Programs
diff --git a/openbsc/src/libmsc/Makefile.am b/openbsc/src/libmsc/Makefile.am
index f9bcf2f37..c06b2960d 100644
--- a/openbsc/src/libmsc/Makefile.am
+++ b/openbsc/src/libmsc/Makefile.am
@@ -47,6 +47,8 @@ libmsc_a_SOURCES = \
osmo_msc.c \
ctrl_commands.c \
meas_feed.c \
+ gsm_ussd_map_proto.c \
+ gsm_ussd_map.c \
$(NULL)
if BUILD_SMPP
diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c
index a8cf3e9bf..716fe751f 100644
--- a/openbsc/src/libmsc/gsm_04_80.c
+++ b/openbsc/src/libmsc/gsm_04_80.c
@@ -39,7 +39,7 @@
#include <osmocom/core/msgb.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)
{
uint16_t origlen = msgb->len;
@@ -75,56 +75,135 @@ static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag,
return data;
}
+static inline unsigned char *msgb_wrap_with_L(struct msgb *msgb)
+{
+ uint8_t *data = msgb_push(msgb, 1);
+
+ data[0] = msgb->len - 1;
+ return data;
+}
-/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
-int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
- const struct msgb *in_msg,
- int response_text_len,
- uint8_t response_lang,
- const char *response_text,
- const struct ussd_request *req,
- uint8_t code,
- uint8_t ctype,
- uint8_t mtype)
+/* Compose universial USSD packet invoke/return_result payload */
+struct msgb *gsm0480_compose_ussd_component(struct ss_request* req)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 USSD RSP");
+ 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);
+ return msg;
+}
+
+#ifndef NO_GSM0480_SEND_FUNC
+
+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;
- int response_len;
ptr8 = msgb_put(msg, 0);
- if (response_text_len < 0) {
- /* First put the payload text into the message */
- gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len);
- msgb_put(msg, response_len);
- response_lang = 0x0F;
+ 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 */
+ msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
+ } else if (reqhdr->message_type == GSM0480_MTYPE_FACILITY) {
+ /* For GSM0480_MTYPE_FACILITY it's LV not TLV */
+ msgb_wrap_with_L(msg);
} else {
- memcpy(ptr8, response_text, response_text_len);
- msgb_put(msg, response_text_len);
+ 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, response_lang);
+ 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 (ctype == GSM0480_CTYPE_RETURN_RESULT) {
+ if (req->component_type == GSM0480_CTYPE_RETURN_RESULT) {
/* 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 */
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 (ctype == GSM0480_CTYPE_INVOKE) {
+ } else if (req->component_type == GSM0480_CTYPE_INVOKE) {
/* 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 */
msgb_push_TLV1(msg, GSM0480_COMPIDTAG_INVOKE_ID, req->invoke_id);
@@ -132,16 +211,16 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
abort();
}
- /* Wrap this up as a Return Result component */
- msgb_wrap_with_ASN1_TL(msg, ctype);
+ /* Wrap this up as an Invoke or a Return Result component */
+ msgb_wrap_with_ASN1_TL(msg, req->component_type);
- if (mtype == GSM0480_MTYPE_REGISTER ||
- mtype == GSM0480_MTYPE_RELEASE_COMPLETE) {
+ 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 (mtype == GSM0480_MTYPE_FACILITY) {
- uint8_t *data = msgb_push(msg, 1);
- data[0] = msg->len - 1;
+ } else if (req->message_type == GSM0480_MTYPE_FACILITY) {
+ /* For GSM0480_MTYPE_FACILITY it's LV not TLV */
+ msgb_wrap_with_L(msg);
} else {
abort();
}
@@ -149,42 +228,48 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
/* And finally pre-pend the L3 header */
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
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(DSUP, "Sending USSD to mobile: %s\n", msgb_hexdump(msg));
+ DEBUGP(DSS, "Sending USSD to mobile: %s\n", msgb_hexdump(msg));
return gsm0808_submit_dtap(conn, msg, 0, 0);
}
+#endif
int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
- const struct msgb *in_msg,
- const struct ussd_request *req)
+ uint8_t invoke_id,
+ uint8_t transaction_id)
{
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 */
msgb_push_TLV1(msg, GSM_0480_PROBLEM_CODE_TAG_GENERAL,
GSM_0480_GEN_PROB_CODE_UNRECOGNISED);
/* 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 */
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 */
msgb_wrap_with_TL(msg, GSM0480_IE_FACILITY);
/* And finally pre-pend the L3 header */
gh = (struct gsm48_hdr *) msgb_push(msg, sizeof(*gh));
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;
return gsm0808_submit_dtap(conn, msg, 0, 0);
+#endif
}
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 gsm0808_submit_dtap(conn, msg, 0, 0);
}
+
+#endif
diff --git a/openbsc/src/libmsc/gsm_sup.c b/openbsc/src/libmsc/gsm_sup.c
index f9edec352..9dc8f191e 100644
--- a/openbsc/src/libmsc/gsm_sup.c
+++ b/openbsc/src/libmsc/gsm_sup.c
@@ -32,6 +32,7 @@
#include <openbsc/gprs_utils.h>
#include <openbsc/ussd.h>
+#if 0
enum {
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);
}
-
+#endif
static int subscr_tx_sup_message(struct gprs_gsup_client *sup_client,
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 gsm_subscriber *subscr;
-
+#if 0
if (*data == GPRS_GSUP_MSGT_MAP) {
return rx_uss_message(data, data_len);
}
-
+#endif
rc = gprs_gsup_decode(data, data_len, &gsup_msg);
if (rc < 0) {
LOGP(DSUP, LOGL_ERROR,
diff --git a/openbsc/src/libmsc/gsm_ussd_map.c b/openbsc/src/libmsc/gsm_ussd_map.c
new file mode 100644
index 000000000..7ca84b133
--- /dev/null
+++ b/openbsc/src/libmsc/gsm_ussd_map.c
@@ -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;
+}
diff --git a/openbsc/src/libmsc/gsm_ussd_map_proto.c b/openbsc/src/libmsc/gsm_ussd_map_proto.c
new file mode 100644
index 000000000..1d48efbd5
--- /dev/null
+++ b/openbsc/src/libmsc/gsm_ussd_map_proto.c
@@ -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
+}
diff --git a/openbsc/src/libmsc/ussd.c b/openbsc/src/libmsc/ussd.c
index b31f3e1a0..ac032439e 100644
--- a/openbsc/src/libmsc/ussd.c
+++ b/openbsc/src/libmsc/ussd.c
@@ -33,378 +33,341 @@
#include <openbsc/gsm_subscriber.h>
#include <openbsc/debug.h>
#include <openbsc/osmo_msc.h>
-#include <openbsc/gsm_sup.h>
+#include <openbsc/gsm_ussd_map.h>
#include <openbsc/ussd.h>
+#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/gsm0480.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <openbsc/transaction.h>
-struct gsm_ussd {
- struct llist_head ussqueue;
+/* Last uniq generated session id */
+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;
+/* Declarations of USSD strings to be recognised */
+const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
- struct gsm_subscriber_connection *conn;
-};
+/* Forward declarations of network-specific handler functions */
+static int send_own_number(struct gsm_subscriber_connection *conn,
+ const struct ss_header *reqhdr,
+ const struct ss_request *req);
-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)
+/* Entrypoint - handler function common to all mobile-originated USSDs */
+int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
- return &s_active_ussd_sessions;
-}
+ 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);
-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;
-}
+ 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;
+ }
-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);
-}
+ 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;
+ }
-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;
- }
+ 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;
}
- DEBUGP(DMM, "uniq_id %d hasn't been found\n", uniq_id);
- return NULL;
+ 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;
}
-static struct gsm_ussd* get_by_iid(struct gsm_subscriber_connection *conn, uint8_t invoke_id)
+/* A network-specific handler function */
+static int send_own_number(struct gsm_subscriber_connection *conn,
+ const struct ss_header *reqhdr,
+ const struct ss_request *req)
{
- 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;
- }
- }
+ struct ss_request rss;
+ struct ss_header rssh;
- DEBUGP(DMM, "invoke_id %d hasn't been found\n", invoke_id);
- return NULL;
+ char *own_number = conn->subscr->extension;
+ char response_string[GSM_EXTENSION_LENGTH + 20];
+ int response_len;
+
+ /* Need trailing CR as EOT character */
+ snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
+
+ 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 struct gsm_ussd* get_by_tid(struct gsm_subscriber_connection *conn, uint8_t transaction_id)
+
+static int ussd_sup_send_reject(struct gsm_network *conn, uint32_t ref)
{
- 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;
- }
- }
+ struct ss_header rej;
+ rej.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+ rej.component_length = 0;
- DEBUGP(DMM, "transaction_id %d hasn't been found\n", transaction_id);
- return NULL;
+#if 0
+ rej.component_type = GSM0480_CTYPE_REJECT;
+ rej.invoke_id = invokeid;
+ rej.opcode = opcode;
+ rej.ussd_text_len = 0;
+#endif
+ return ussd_map_tx_message(conn, &rej, NULL, ref, NULL);
}
-// From SUP
-int on_ussd_response(const struct ss_request *req, const char *extention)
+/* 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 ussd_request ussd_req;
- struct gsm_ussd* ussdq;
- memset(&ussd_req, 0, sizeof(ussd_req));
+ struct gsm_trans *trans = trans_find_by_callref(net, ref);
int rc = 0;
+ struct msgb *msg;
+ uint8_t *ptr8;
- switch (req->message_type) {
+ switch (reqhdr->message_type) {
case GSM0480_MTYPE_REGISTER:
- DEBUGP(DMM, "Network originated USSD messages isn't supported yet!\n");
+ DEBUGP(DSS, "Network originated USSD messages isn't supported yet!\n");
- //TODO Send to sup rejection
+ ussd_sup_send_reject(net, ref);
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
+ if (!trans) {
+ DEBUGP(DSS, "No session was found for ref: %d!\n",
+ ref);
+
+ ussd_sup_send_reject(net, ref);
return 0;
}
break;
default:
- DEBUGP(DMM, "Unknown message type 0x%02x\n", req->message_type);
- // TODO SUP Reject
+ DEBUGP(DSS, "Unknown message type 0x%02x\n", reqhdr->message_type);
+ ussd_sup_send_reject(net, ref);
return 0;
}
- ussd_req.transaction_id = ussdq->transaction_id;
- ussd_req.invoke_id = ussdq->invoke_id;
+#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_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);
+ rc = gsm0480_send_ussd(trans->conn, req);
} else {
- rc = gsm0480_send_ussd_reject(ussdq->conn, NULL, &ussd_req);
+ 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 (req->message_type == GSM0480_MTYPE_RELEASE_COMPLETE) {
- msc_release_connection(ussdq->conn);
- ussd_session_free(ussdq);
+ 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 ussd_sup_send_reject(struct gsm_subscriber_connection *conn,
- uint8_t uniq_id, uint8_t opcode)
+static int get_invoke_id(const uint8_t* data, uint8_t len, uint8_t* pinvoke_id)
{
- 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;
+ /* 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;
- return subscr_tx_uss_message(&rej, conn->subscr);
+ 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;
}
-/* Entrypoint - handler function common to all mobile-originated USSDs */
-int handle_rcv_ussd(struct gsm_subscriber_connection *conn, struct msgb *msg)
+/* 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;
- struct ss_request req;
- struct gsm_ussd* ussdq = NULL;
- struct ussd_request ussd_req;
+ 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;
- memset(&req, 0, sizeof(req));
- memset(&ussd_req, 0, sizeof(ussd_req));
+ if (!conn->subscr)
+ return -EIO;
- DEBUGP(DMM, "handle ussd: %s\n", msgb_hexdump(msg));
+ memset(&reqhdr, 0, sizeof(reqhdr));
- gh = msgb_l3(msg);
- rc = gsm0480_decode_ss_request(gh, msgb_l3len(msg), &req);
+ 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(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;
+ DEBUGP(DSS, "Incorrect SS header\n");
+ if (!trans) {
+ goto release_conn;
}
- goto transaction_not_found;
+ /* don't know how to process */
+ goto failed_transaction;
}
- switch (req.message_type) {
+
+ switch (reqhdr.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");
+ 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;
}
- ussdq->invoke_id = req.invoke_id;
+
+ trans->conn = conn;
+ trans->ss.invoke_id = invoke_id;
+ trans->ss.mo = 1;
+ trans->ss.dirty = 1;
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;
+ 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:
- // FIXME handle parsing in libosmocore
- ussdq = get_by_tid(conn, req.transaction_id);
- if (!ussdq) {
- DEBUGP(DMM, "RELEASE_COMPLETE to non-existing transaction!\n");
+ if (!trans) {
+ DEBUGP(DSS, "RELEASE_COMPLETE to non-existing transaction!\n");
goto release_conn;
}
- ussd_session_free(ussdq);
- ussd_sup_send_reject(conn, ussdq->uniq_id, req.opcode);
+ trans_free(trans);
goto release_conn;
}
- req.invoke_id = ussdq->uniq_id;
- rc = subscr_tx_uss_message(&req, conn->subscr);
+ rc = ussd_map_tx_message(conn->subscr->group->net, &reqhdr,
+ conn->subscr->extension, trans->callref,
+ gh->data + reqhdr.component_offset);
if (rc) {
- DEBUGP(DMM, "Unable tp send uss over sup reason: %d\n", 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:
- ussd_session_free(ussdq);
+ trans_free(trans);
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);
+ gsm0480_send_ussd_reject(conn, invoke_id, transaction_id);
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 */
-const char USSD_TEXT_OWN_NUMBER[] = "*#100#";
-
-/* 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);
-
-
-/* A network-specific handler function */
-static int send_own_number(struct gsm_subscriber_connection *conn, const struct msgb *msg, const struct ussd_request *req)
+void _ussd_trans_free(struct gsm_trans *trans)
{
- char *own_number = conn->subscr->extension;
- char response_string[GSM_EXTENSION_LENGTH + 20];
+ if (trans->ss.dirty) {
+ trans->ss.dirty = 0;
- /* Need trailing CR as EOT character */
- snprintf(response_string, sizeof(response_string), "Your extension is %s\r", own_number);
- return gsm0480_send_ussd_response(conn, msg, response_string, req);
+ //ussd_sup_send_reject(trans->net, trans->callref, trans->ss.invoke_id, 0);
+ ussd_sup_send_reject(trans->net, trans->callref);
+ }
}
-#endif
+
diff --git a/openbsc/src/libmsc/vty_interface_layer3.c b/openbsc/src/libmsc/vty_interface_layer3.c
index 3b5778eac..9583a5b29 100644
--- a/openbsc/src/libmsc/vty_interface_layer3.c
+++ b/openbsc/src/libmsc/vty_interface_layer3.c
@@ -50,7 +50,8 @@
#include <openbsc/sms_queue.h>
#include <openbsc/mncc_int.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>
@@ -1035,19 +1036,20 @@ DEFUN(sup_ussd_destination, sup_ussd_destination_cmd,
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
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);
return CMD_WARNING;
}
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) {
- 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);
return CMD_WARNING;
}
+ gsmnet->ussd_sup_client->data = gsmnet;
return CMD_SUCCESS;
}
diff --git a/openbsc/src/reg-proxy/Makefile.am b/openbsc/src/reg-proxy/Makefile.am
index 0bb6c528f..b57af47ff 100644
--- a/openbsc/src/reg-proxy/Makefile.am
+++ b/openbsc/src/reg-proxy/Makefile.am
@@ -1,12 +1,10 @@
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
-
+ $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOABIS_CFLAGS)
AM_LDFLAGS = $(COVERAGE_LDFLAGS)
-bin_PROGRAMS = reg-proxy ussd-proxy
+bin_PROGRAMS = reg-proxy
reg_proxy_SOURCES = \
../gprs/gsm_04_08_gprs.c \
@@ -22,10 +20,3 @@ reg_proxy_LDADD = \
$(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)
diff --git a/openbsc/src/reg-proxy/sup.c b/openbsc/src/reg-proxy/sup.c
index a3f1263f2..4c2f8cbd6 100644
--- a/openbsc/src/reg-proxy/sup.c
+++ b/openbsc/src/reg-proxy/sup.c
@@ -23,6 +23,7 @@ static int handle_sup_upd_loc_req(struct gsm_sup_server *sup_server,
return rc;
}
+#if 0
static int handle_sup_ss(struct gsm_sup_server *sup_server,
struct ss_request *ss,
const char* extention)
@@ -40,7 +41,6 @@ static int handle_sup_ss(struct gsm_sup_server *sup_server,
return rc;
}
-
enum {
FMAP_MSISDN = 0x80
};
@@ -223,6 +223,8 @@ static int rx_sup_uss_message(struct gsm_sup_server *sup_server, const uint8_t*
#endif
}
+#endif
+
int rx_sup_message(struct gsm_sup_server *sup_server, struct msgb *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 gsm_subscriber *subscr;
-
+#if 0
if (*data == GPRS_GSUP_MSGT_MAP) {
LOGP(DSUP, LOGL_INFO,
"Receive USS: %s\n", msgb_hexdump(msg));
return rx_sup_uss_message(sup_server, data, data_len);
}
-
+#endif
rc = gprs_gsup_decode(data, data_len, &sup_msg);
if (rc < 0) {
LOGP(DSUP, LOGL_ERROR,
diff --git a/openbsc/src/ussd-proxy/Makefile.am b/openbsc/src/ussd-proxy/Makefile.am
new file mode 100644
index 000000000..3f308dd1b
--- /dev/null
+++ b/openbsc/src/ussd-proxy/Makefile.am
@@ -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
diff --git a/openbsc/src/reg-proxy/ussd_proxy.c b/openbsc/src/ussd-proxy/ussd_proxy.c
index 5488f8055..572989b10 100644
--- a/openbsc/src/reg-proxy/ussd_proxy.c
+++ b/openbsc/src/ussd-proxy/ussd_proxy.c
@@ -8,6 +8,7 @@
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
+#include <ctype.h>
typedef struct context_s context_t;
#define NTA_OUTGOING_MAGIC_T context_t
@@ -38,122 +39,12 @@ typedef struct operation operation_t;
#include <osmocom/core/linuxlist.h>
#include <openbsc/gprs_gsup_messages.h>
+#include <openbsc/gsm_ussd_map_proto.h>
+#include <openbsc/gsm_04_80.h>
#include <iconv.h>
-typedef uint8_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 uint32_t sup_tcap_tid_t;
typedef struct isup_connection isup_connection_t;
@@ -170,15 +61,22 @@ struct isup_connection {
struct msgb *pending_msg;
};
+typedef enum ss_type {
+ TYPE_USSD,
+ TYPE_SS_OTHER
+} ss_type_t;
+
struct ussd_session {
isup_connection_t *conn;
-
- // int32_t transaction_id;
+ sup_tcap_tid_t ref;
int ms_originated;
- struct ss_request rigester_msg;
-
char extention[32];
+
+ ss_type_t type;
+
+ uint8_t ss_code;
+ struct ss_request rigester_msg;
};
struct context_s {
@@ -203,9 +101,6 @@ struct context_s {
iconv_t* utf8_to_latin1;
iconv_t* latin1_to_utf8;
- int dont_encode_in_latin1;
- int force_7bit;
-
/* Array of isup connections */
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,
const char* msg, unsigned msg_len);
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
-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)
{
@@ -328,11 +227,11 @@ static int ussd_parse_xml(const char *xml,
}
// 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;
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 NULL;
@@ -372,11 +271,18 @@ static void operation_destroy(operation_t* op)
llist_del(&op->list);
op->ctx->operation_count--;
- fprintf(stderr, "--- operation %*.s from %s destroyed (sessions: %d)\n",
- op->ussd.rigester_msg.ussd_text_len,
- op->ussd.rigester_msg.ussd_text,
- op->ussd.extention,
- op->ctx->operation_count);
+ if (op->ussd.type == TYPE_USSD) {
+ fprintf(stderr, "--- operation %*.s from %s destroyed (sessions: %d)\n",
+ op->ussd.rigester_msg.ussd_text_len,
+ op->ussd.rigester_msg.ussd_text,
+ op->ussd.extention,
+ 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 */
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);
if (status == 200) {
nua_ack(nh, TAG_END());
- } else {
- printf("response to INVITE: %03d %s\n", status, phrase);
+ } else if (hmagic->ussd.type == TYPE_USSD) {
+ printf("response to USSD INVITE: %03d %s\n", status, phrase);
ussd_send_reject(hmagic->ussd.conn,
- hmagic->ussd.rigester_msg.invoke_id,
- hmagic->ussd.rigester_msg.opcode);
+ hmagic->ussd.ref,
+ 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);
}
}
@@ -452,11 +367,69 @@ void proxy_i_bye(int status,
fprintf(stderr, "*** response BYE with %d satus is malformed, drop session\n",
status);
ussd_send_reject(hmagic->ussd.conn,
- hmagic->ussd.rigester_msg.invoke_id,
- hmagic->ussd.rigester_msg.opcode);
+ hmagic->ussd.ref,
+ hmagic->ussd.rigester_msg.invoke_id);
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,
char const *phrase,
nua_t *nua,
@@ -552,13 +525,33 @@ void proxy_info(int status,
response ? "response" : "request",
status);
ussd_send_reject(hmagic->ussd.conn,
- hmagic->ussd.rigester_msg.invoke_id,
- hmagic->ussd.rigester_msg.opcode);
+ hmagic->ussd.ref,
+ hmagic->ussd.rigester_msg.invoke_id);
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,
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\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-data>",
language,
- msg_len, msg);
+ tmpbuf_utf8_len, tmpbuf_utf8);
if (content_len > max_len) {
content[max_len - 1] = 0;
return 0;
@@ -574,15 +567,118 @@ int ussd_create_xml_ascii(char *content, size_t max_len, const char* language, c
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 */
#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,
isup_connection_t *conn,
struct ss_request* ss,
+ uint32_t ref,
const char* extention)
{
char content[1024];
+ char decoded[MAX_LEN_USSD_STRING + 1];
char escaped_to[512];
context_t* ctx = op->ctx;
sip_to_t *to = NULL;
@@ -591,23 +687,32 @@ int ussd_session_open_mo(operation_t *op,
char* to_url_str;
char* from_url_str;
+ int decoded_len;
+
+ op->ussd.ref = ref;
op->ussd.conn = conn;
op->ussd.ms_originated = 1;
+ op->ussd.type = TYPE_USSD;
op->ussd.rigester_msg = *ss;
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),
- "en",
- (const char* )op->ussd.rigester_msg.ussd_text,
- op->ussd.rigester_msg.ussd_text_len)) {
+ if (!ussd_create_xml_latin1(ctx, content, sizeof(content),
+ decoded, decoded_len)) {
goto failed_to_parse_xml;
}
/* 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.url_user = escaped_to;
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,
+ SIPTAG_UNKNOWN_STR("Recv-Info: g.3gpp.ussd"),
SIPTAG_CONTENT_TYPE_STR("application/vnd.3gpp.ussd+xml"),
SIPTAG_PAYLOAD_STR(content),
TAG_END());
@@ -673,10 +779,20 @@ int ussd_session_facility(operation_t *op,
const char* extention)
{
char content[1024];
- if (!ussd_create_xml_ascii(content, sizeof(content),
- "en",
- (const char* )ss->ussd_text,
- ss->ussd_text_len)) {
+ char decoded[MAX_LEN_USSD_STRING + 1];
+ int decoded_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;
}
@@ -708,15 +824,20 @@ void context_callback(nua_event_t event,
break;
case nua_i_info:
- proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 0);
+ if (hmagic->ussd.type == TYPE_USSD)
+ proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 0);
break;
case nua_r_info:
- proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 1);
+ if (hmagic->ussd.type == TYPE_USSD)
+ proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 1);
break;
case nua_i_bye:
- proxy_i_bye(status, phrase, nua, magic, nh, hmagic, sip, tags);
+ if (hmagic->ussd.type == TYPE_USSD)
+ 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;
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)
{
char extention[32] = {0};
- struct ss_request ss;
+ struct ss_header ss;
+ struct ss_request ssreq;
+ uint32_t ref;
operation_t* op;
int rc;
context_t *ctx = sup_conn->ctx;
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");
- goto err_send_reject;
+ goto err_bad_packet;
+ }
+
+ memset(&ssreq, 0, sizeof(ssreq));
+ 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 mtype=0x%02x invoke_id=0x%02x opcode=0x%02x component_type=0x%02x text=%s\n",
- ss.message_type, ss.invoke_id, ss.opcode, ss.component_type, ss.ussd_text);
+ 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) {
case GSM0480_MTYPE_REGISTER:
- if (ss.component_type != GSM0480_CTYPE_INVOKE) {
- LOGP(DLCTRL, LOGL_ERROR, "Non-INVOKE component type in REGISTER: 0x%02x\n", ss.component_type);
+ if (ssreq.component_type != GSM0480_CTYPE_INVOKE) {
+ LOGP(DLCTRL, LOGL_ERROR, "Non-INVOKE component type in REGISTER: 0x%02x\n", ssreq.component_type);
goto err_send_reject;
}
- if (ss.opcode != GSM0480_OP_CODE_PROCESS_USS_REQ) {
- LOGP(DLCTRL, LOGL_ERROR, "Don't know hot to handle this SS opcode: 0x%02x\n", ss.opcode);
+ if (ssreq.opcode == GSM0480_OP_CODE_PROCESS_USS_DATA ||
+ 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;
}
/* 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");
goto err_send_reject;
}
- LOGP(DLCTRL, LOGL_ERROR, "New session %.*s from %s, active: %d\n",
- ss.ussd_text_len,
- ss.ussd_text,
- extention,
- ctx->operation_count);
- rc = ussd_session_open_mo(op, sup_conn, &ss, extention);
- if (rc < 0) {
- operation_destroy(op);
- 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",
+ ssreq.ussd_text_len,
+ ssreq.ussd_text,
+ extention,
+ ctx->operation_count);
+
+ op->ussd.ss_code = 0;
+ rc = ussd_session_open_mo(op, sup_conn, &ssreq, ref, extention);
+ if (rc < 0) {
+ operation_destroy(op);
+ 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;
case GSM0480_MTYPE_FACILITY:
//Only MS-originated Menu session is supported, so we ignore INVOKE here
- if (ss.component_type != GSM0480_CTYPE_RETURN_RESULT &&
- ss.component_type != GSM0480_CTYPE_RETURN_ERROR &&
- ss.component_type != GSM0480_CTYPE_REJECT) {
- LOGP(DLCTRL, LOGL_ERROR, "Non-{RESULT/RETURN_ERROR/REJECT} component type in FACILITY: 0x%02x\n", ss.component_type);
+ if (ssreq.component_type != GSM0480_CTYPE_RETURN_RESULT &&
+ ssreq.component_type != GSM0480_CTYPE_RETURN_ERROR &&
+ ssreq.component_type != GSM0480_CTYPE_REJECT) {
+ LOGP(DLCTRL, LOGL_ERROR, "Non-{RESULT/RETURN_ERROR/REJECT} component type in FACILITY: 0x%02x\n", ssreq.component_type);
goto err_send_reject;
}
// /////////////////////////////////////////////////
// TODO handle RETURN_ERROR/REJECT
- if (ss.component_type != GSM0480_CTYPE_RETURN_RESULT) {
- LOGP(DLCTRL, LOGL_ERROR, "Component type in FACILITY: 0x%02x is not implemented yet\n", ss.component_type);
+ if (ssreq.component_type != GSM0480_CTYPE_RETURN_RESULT) {
+ LOGP(DLCTRL, LOGL_ERROR, "Component type in FACILITY: 0x%02x is not implemented yet\n", ssreq.component_type);
goto err_send_reject;
}
- if (ss.opcode != GSM0480_OP_CODE_USS_REQUEST) {
- LOGP(DLCTRL, LOGL_ERROR, "Don't know hot to handle this SS opcode: 0x%02x\n", ss.opcode);
+ if (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;
}
- op = operation_find_by_tid(ctx, ss.invoke_id);
+ op = operation_find_by_tid(ctx, ref);
if (op == NULL) {
LOGP(DLCTRL, LOGL_ERROR, "No active session with tid=%d were found\n",
- ss.invoke_id);
+ ssreq.invoke_id);
goto err_send_reject;
}
// TODO check result!! MO/MT error handling
- rc = ussd_session_facility(op, &ss, extention);
+ rc = ussd_session_facility(op, &ssreq, extention);
if (rc < 0) {
operation_destroy(op);
goto err_send_reject;
@@ -826,17 +983,14 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
break;
case GSM0480_MTYPE_RELEASE_COMPLETE:
- op = operation_find_by_tid(ctx, ss.invoke_id);
+ op = operation_find_by_tid(ctx, ref);
if (op == NULL) {
LOGP(DLCTRL, LOGL_ERROR, "No active session with tid=%d were found for RELEASE_COMPLETE\n",
- ss.invoke_id);
+ ssreq.invoke_id);
return 0;
}
- // NOTE: Add ContentType for 3rd party software workaround, it's not needed by standard
- nua_bye(op->handle,
- SIPTAG_CONTENT_TYPE_STR("application/vnd.3gpp.ussd+xml"),
- TAG_END());
+ nua_bye(op->handle, TAG_END());
break;
default:
@@ -847,29 +1001,63 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
return 0;
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;
}
-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];
+
+ buffer[0] = GSM0480_CTYPE_REJECT;
+ buffer[1] = 3+3;
- memset(&error_ss, 0, sizeof(error_ss));
- error_ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
- error_ss.component_type = GSM0480_CTYPE_REJECT;
- error_ss.invoke_id = invoke_id;
- error_ss.opcode = opcode;
+ buffer[2] = GSM0480_COMPIDTAG_INVOKE_ID;
+ buffer[3] = 1;
+ buffer[4] = invoke_id;
- return ussd_send_data_ss(conn, &error_ss);
+ 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 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,
- reply,
- NULL);
+ &hdr,
+ NULL,
+ ref,
+ component);
LOGP(DLCTRL, LOGL_ERROR,
"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);
}
-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,
const char* msg, unsigned msg_len)
{
+ struct msgb *buf;
struct ss_request ss;
+ int rc;
+ uint8_t message_type;
+
memset(&ss, 0, sizeof(ss));
// TODO handle language
if (msg == NULL) {
- ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+ message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
ss.component_type = GSM0480_CTYPE_REJECT;
ss.opcode = op->ussd.rigester_msg.opcode;
} else if (last) {
- ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
+ message_type = GSM0480_MTYPE_RELEASE_COMPLETE;
ss.component_type = GSM0480_CTYPE_RETURN_RESULT;
ss.opcode = op->ussd.rigester_msg.opcode;
} else {
- ss.message_type = GSM0480_MTYPE_FACILITY;
+ message_type = GSM0480_MTYPE_FACILITY;
ss.component_type = (op->ussd.ms_originated) ? GSM0480_CTYPE_INVOKE
: GSM0480_CTYPE_RETURN_RESULT;
ss.opcode = GSM0480_OP_CODE_USS_REQUEST;
@@ -912,57 +1094,57 @@ int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_le
ss.invoke_id = op->ussd.rigester_msg.invoke_id;
if (msg) {
- if (msg_len > MAX_LEN_USSD_STRING) {
- 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;
- size_t inleft = msg_len;
- char* outbuf = (char*)ss.ussd_text;
- size_t outleft = MAX_LEN_USSD_STRING;
- size_t s;
- // 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,
- &inbuf, &inleft,
- &outbuf, &outleft);
- }
+ char tmpbuf[MAX_LEN_USSD_STRING + 1];
+
+ char* inbuf = (char*)msg;
+ size_t inleft = msg_len;
+ char* outbuf = (char*)tmpbuf;
+ size_t outleft = sizeof(tmpbuf);
+ size_t s;
+
+ // First of all try latin1
+ s = iconv(op->ctx->utf8_to_latin1,
+ &inbuf, &inleft,
+ &outbuf, &outleft);
+ if (s == (size_t)-1) {
+ outbuf = (char*)ss.ussd_text;
+ outleft = MAX_LEN_USSD_STRING;
+
+ s = iconv(op->ctx->utf8_to_ucs2,
+ &inbuf, &inleft,
+ &outbuf, &outleft);
if (s == (size_t)-1) {
- s = iconv(op->ctx->utf8_to_ucs2,
- &inbuf, &inleft,
- &outbuf, &outleft);
- if (s == (size_t)-1) {
- perror("can't convert string from utf8");
- }
- // UCS-2 encoding
- 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;
- }
+ perror("can't convert string from utf8");
}
+ // UCS-2 encoding
+ ss.ussd_text_language = 0x48;
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 {
ss.ussd_text_len = 0;
- ss.ussd_text_language = 0x80;
+ ss.ussd_text_language = 0x0f;
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,
@@ -976,17 +1158,31 @@ static void timer_function(su_root_magic_t *magic,
llist_for_each_entry_safe(op, tmp, &cli->operation_list, list) {
su_duration_t lasts = su_duration(n, op->tm_initiated);
if (lasts > cli->max_ussd_ses_duration) {
- 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,
- op->ussd.extention,
- lasts,
- cli->max_ussd_ses_duration);
-
-
- ussd_send_reject(op->ussd.conn,
- op->ussd.rigester_msg.invoke_id,
- op->ussd.rigester_msg.opcode);
+ if (op->ussd.type == TYPE_USSD) {
+ 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,
+ op->ussd.extention,
+ lasts,
+ cli->max_ussd_ses_duration);
+
+
+ ussd_send_reject(op->ussd.conn,
+ op->ussd.ref,
+ 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);
}
}
@@ -1037,7 +1233,7 @@ static int isup_handle_connection(context_t *cli, su_wait_t *w, void *p)
goto err;
case IPAC_PROTO_OSMO:
// TODO callback
- if (msg->l2h[1] == GPRS_GSUP_MSGT_MAP) {
+ if (msg->l2h[1] == GPRS_GSUP_MSGT_USSD_MAP) {
LOGP(DLCTRL, LOGL_ERROR,
"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"
" (default: 200)\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);
}
@@ -1173,8 +1367,6 @@ int main(int argc, char *argv[])
int max_ussd_ses_secs = 90;
int max_op_limit = 200;
int sip_loglevel = 1;
- int dont_try_latin1 = 0;
- int force_7bit = 0;
int c;
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);
break;
case 'L':
- dont_try_latin1 = 1;
+ fprintf(stderr, " -L is now obsolete, ignored\n");
break;
case '7':
- force_7bit = 1;
+ fprintf(stderr, " -7 is now obsolete, ignored\n");
break;
case '?':
default:
@@ -1234,8 +1426,6 @@ int main(int argc, char *argv[])
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->latin1_to_utf8=iconv_open("utf-8", "iso8859-1");
context->utf8_to_ucs2=iconv_open("utf-16be", "utf-8");
diff --git a/openbsc/tests/Makefile.am b/openbsc/tests/Makefile.am
index 9cbc1c172..633bb8638 100644
--- a/openbsc/tests/Makefile.am
+++ b/openbsc/tests/Makefile.am
@@ -10,6 +10,7 @@ SUBDIRS = \
subscr \
mm_auth \
nanobts_omlattr \
+ ussd \
$(NULL)
if BUILD_NAT