mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-mgw.git
synced 2025-11-03 13:33:29 +00:00
manual merge SS from sup-ussd-on-master-ss-wip
This commit is contained in:
committed by
Ivan Kluchnikov
parent
db0e216845
commit
d4839fe14a
@@ -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)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ enum {
|
|||||||
DSUA,
|
DSUA,
|
||||||
DV42BIS,
|
DV42BIS,
|
||||||
DSUP,
|
DSUP,
|
||||||
|
DSS,
|
||||||
Debug_LastEntry,
|
Debug_LastEntry,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
14
openbsc/include/openbsc/gsm_ussd_map.h
Normal file
14
openbsc/include/openbsc/gsm_ussd_map.h
Normal 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 */
|
||||||
25
openbsc/include/openbsc/gsm_ussd_map_proto.h
Normal file
25
openbsc/include/openbsc/gsm_ussd_map_proto.h
Normal 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 */
|
||||||
@@ -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;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ SUBDIRS += \
|
|||||||
ipaccess \
|
ipaccess \
|
||||||
gprs \
|
gprs \
|
||||||
reg-proxy \
|
reg-proxy \
|
||||||
|
ussd-proxy \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
# Conditional Programs
|
# Conditional Programs
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
93
openbsc/src/libmsc/gsm_ussd_map.c
Normal file
93
openbsc/src/libmsc/gsm_ussd_map.c
Normal 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;
|
||||||
|
}
|
||||||
212
openbsc/src/libmsc/gsm_ussd_map_proto.c
Normal file
212
openbsc/src/libmsc/gsm_ussd_map_proto.c
Normal 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
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
19
openbsc/src/ussd-proxy/Makefile.am
Normal file
19
openbsc/src/ussd-proxy/Makefile.am
Normal 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
|
||||||
@@ -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");
|
||||||
@@ -10,6 +10,7 @@ SUBDIRS = \
|
|||||||
subscr \
|
subscr \
|
||||||
mm_auth \
|
mm_auth \
|
||||||
nanobts_omlattr \
|
nanobts_omlattr \
|
||||||
|
ussd \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
if BUILD_NAT
|
if BUILD_NAT
|
||||||
|
|||||||
Reference in New Issue
Block a user