aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergey Kostanbaev <sergey.kostanbaev@gmail.com>2015-10-30 15:14:49 +0300
committerIvan Kluchnikov <kluchnikovi@gmail.com>2017-02-07 18:59:54 +0300
commit012c9203e46a359f0178b45e0bc936bcd044ad00 (patch)
treeaa9d1e5e17c62d72c197da23e580fac762b6bc0b
parent5087f994fdf2457f6b20af90823cb2f668d8c5d5 (diff)
ussd: handle UCS-2 coding
-rw-r--r--openbsc/include/openbsc/gsm_04_80.h10
-rw-r--r--openbsc/src/libmsc/gsm_04_80.c35
-rw-r--r--openbsc/src/libmsc/gsm_sup.c12
-rw-r--r--openbsc/src/libmsc/ussd.c2
-rw-r--r--openbsc/src/reg-proxy/ussd_proxy.c120
5 files changed, 137 insertions, 42 deletions
diff --git a/openbsc/include/openbsc/gsm_04_80.h b/openbsc/include/openbsc/gsm_04_80.h
index 959eb7bff..68de65a0f 100644
--- a/openbsc/include/openbsc/gsm_04_80.h
+++ b/openbsc/include/openbsc/gsm_04_80.h
@@ -8,8 +8,14 @@
struct gsm_subscriber_connection;
int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
- const struct msgb *in_msg, const char* response_text,
- const struct ussd_request *req, uint8_t code, uint8_t ctype, uint8_t mtype);
+ const struct msgb *in_msg,
+ int response_text_len,
+ uint8_t response_lang,
+ const char* response_text,
+ const struct ussd_request *req,
+ uint8_t code,
+ uint8_t ctype,
+ uint8_t mtype);
int gsm0480_send_ussd_reject(struct gsm_subscriber_connection *conn,
const struct msgb *msg,
const struct ussd_request *request);
diff --git a/openbsc/src/libmsc/gsm_04_80.c b/openbsc/src/libmsc/gsm_04_80.c
index 4350557b4..a8cf3e9bf 100644
--- a/openbsc/src/libmsc/gsm_04_80.c
+++ b/openbsc/src/libmsc/gsm_04_80.c
@@ -78,7 +78,10 @@ static inline unsigned char *msgb_push_TLV1(struct msgb *msgb, uint8_t tag,
/* Send response to a mobile-originated ProcessUnstructuredSS-Request */
int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
- const struct msgb *in_msg, const char *response_text,
+ const struct msgb *in_msg,
+ int response_text_len,
+ uint8_t response_lang,
+ const char *response_text,
const struct ussd_request *req,
uint8_t code,
uint8_t ctype,
@@ -89,33 +92,23 @@ int gsm0480_send_ussd_response(struct gsm_subscriber_connection *conn,
uint8_t *ptr8;
int response_len;
-#if 1
- /* First put the payload text into the message */
ptr8 = msgb_put(msg, 0);
- gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len);
- msgb_put(msg, response_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, 0x0F);
-#else
- response_len = strlen(response_text);
- if (response_len > MAX_LEN_USSD_STRING)
- response_len = MAX_LEN_USSD_STRING;
-
- ptr8 = msgb_put(msg, 0);
- memcpy(ptr8, response_text, response_len);
- msgb_put(msg, response_len);
+ if (response_text_len < 0) {
+ /* First put the payload text into the message */
+ gsm_7bit_encode_n_ussd(ptr8, msgb_tailroom(msg), response_text, &response_len);
+ msgb_put(msg, response_len);
+ response_lang = 0x0F;
+ } else {
+ memcpy(ptr8, response_text, response_text_len);
+ msgb_put(msg, response_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, 0xf4);
-#endif
-
+ msgb_push_TLV1(msg, ASN1_OCTET_STRING_TAG, response_lang);
/* Then wrap these as a Sequence */
msgb_wrap_with_ASN1_TL(msg, GSM_0480_SEQUENCE_TAG);
diff --git a/openbsc/src/libmsc/gsm_sup.c b/openbsc/src/libmsc/gsm_sup.c
index da65977b3..e4dc7a9ca 100644
--- a/openbsc/src/libmsc/gsm_sup.c
+++ b/openbsc/src/libmsc/gsm_sup.c
@@ -58,8 +58,7 @@ static int subscr_uss_message(struct msgb *msg,
msgb_tlv_put(msg, GSM0480_OPERATION_CODE, 1, &req->opcode);
if (req->ussd_text_len > 0) {
- //msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, 1, &req->ussd_text_language);
- msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_len, req->ussd_text);
+ msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_len + 1, &req->ussd_text_language);
}
if (extention) {
@@ -70,7 +69,7 @@ static int subscr_uss_message(struct msgb *msg,
}
/* fill actual length */
- gsup_indicator[3] = 3 + 3 + (req->ussd_text_len + 2) + (bcd_len + 2);;
+ 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);
@@ -131,9 +130,10 @@ static int rx_uss_message_parse(struct ss_request *ss,
uint8_t len;
switch (*const_data) {
case ASN1_OCTET_STRING_TAG:
- len = *(++const_data);
- strncpy((char*)ss->ussd_text,
- (const char*)++const_data,
+ 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;
diff --git a/openbsc/src/libmsc/ussd.c b/openbsc/src/libmsc/ussd.c
index 97e22b86a..b31f3e1a0 100644
--- a/openbsc/src/libmsc/ussd.c
+++ b/openbsc/src/libmsc/ussd.c
@@ -173,6 +173,8 @@ int on_ussd_response(const struct ss_request *req, const char *extention)
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,
diff --git a/openbsc/src/reg-proxy/ussd_proxy.c b/openbsc/src/reg-proxy/ussd_proxy.c
index e8fcab0e6..3e8f14020 100644
--- a/openbsc/src/reg-proxy/ussd_proxy.c
+++ b/openbsc/src/reg-proxy/ussd_proxy.c
@@ -39,6 +39,8 @@ typedef struct operation operation_t;
#include <openbsc/gprs_gsup_messages.h>
+#include <iconv.h>
+
typedef uint8_t sup_tcap_tid_t;
/******************************************************************************/
@@ -84,9 +86,10 @@ static int rx_uss_message_parse(struct ss_request *ss,
uint8_t len;
switch (*const_data) {
case ASN1_OCTET_STRING_TAG:
- ss->ussd_text_len = len = *(++const_data);
- strncpy((char*)ss->ussd_text,
- (const char*)++const_data,
+ 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;
@@ -130,8 +133,7 @@ static int subscr_uss_message(struct msgb *msg,
msgb_tlv_put(msg, GSM0480_OPERATION_CODE, 1, &req->opcode);
if (req->ussd_text_len > 0) {
- //msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, 1, &req->ussd_text_language);
- msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_len, req->ussd_text);
+ msgb_tlv_put(msg, ASN1_OCTET_STRING_TAG, req->ussd_text_len + 1, &req->ussd_text_language);
}
if (extention) {
@@ -142,7 +144,7 @@ static int subscr_uss_message(struct msgb *msg,
}
/* fill actual length */
- gsup_indicator[3] = 3 + 3 + (req->ussd_text_len + 2) + (bcd_len + 2);
+ 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);
@@ -194,6 +196,15 @@ struct context_s {
su_timer_t *timer;
su_duration_t max_ussd_ses_duration;
+ /* iconv data */
+ iconv_t* utf8_to_ucs2;
+ iconv_t* ucs2_to_utf8;
+
+ iconv_t* utf8_to_latin1;
+ iconv_t* latin1_to_utf8;
+
+ int dont_encode_in_latin1;
+
/* Array of isup connections */
struct isup_connection isup[1];
@@ -656,7 +667,6 @@ failed_to_parse_xml:
}
int ussd_session_facility(operation_t *op,
- isup_connection_t *conn,
struct ss_request* ss,
const char* extention)
{
@@ -668,11 +678,10 @@ int ussd_session_facility(operation_t *op,
return -1;
}
- // TODO add to header
- // Recv-Info: g.3gpp.ussd
nua_info(op->handle,
/* other tags as needed ... */
SIPTAG_CONTENT_TYPE_STR("application/vnd.3gpp.ussd+xml"),
+ SIPTAG_UNKNOWN_STR("Recv-Info: g.3gpp.ussd"),
SIPTAG_PAYLOAD_STR(content),
TAG_END());
@@ -754,6 +763,14 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
switch (ss.message_type) {
case GSM0480_MTYPE_REGISTER:
+ if (ss.component_type != GSM0480_CTYPE_INVOKE) {
+ LOGP(DLCTRL, LOGL_ERROR, "Non-INVOKE component type in REGISTER: 0x%02x\n", ss.component_type);
+ goto err_send_reject;
+ }
+ if (ss.opcode != GSM0480_OP_CODE_PROCESS_USS_REQ) {
+ LOGP(DLCTRL, LOGL_ERROR, "Don't know hot to handle this SS opcode: 0x%02x\n", ss.opcode);
+ goto err_send_reject;
+ }
/* Create new session */
op = operation_alloc(ctx);
if (op == NULL) {
@@ -774,6 +791,23 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
break;
case GSM0480_MTYPE_FACILITY:
+ //Only MS-originated Menu session is supported, so we ignore INVOKE here
+ if (ss.component_type != GSM0480_CTYPE_RETURN_RESULT &&
+ ss.component_type != GSM0480_CTYPE_RETURN_ERROR &&
+ ss.component_type != GSM0480_CTYPE_REJECT) {
+ LOGP(DLCTRL, LOGL_ERROR, "Non-{RESULT/RETURN_ERROR/REJECT} component type in FACILITY: 0x%02x\n", ss.component_type);
+ goto err_send_reject;
+ }
+ // /////////////////////////////////////////////////
+ // TODO handle RETURN_ERROR/REJECT
+ if (ss.component_type != GSM0480_CTYPE_RETURN_RESULT) {
+ LOGP(DLCTRL, LOGL_ERROR, "Component type in FACILITY: 0x%02x is not implemented yet\n", ss.component_type);
+ goto err_send_reject;
+ }
+ if (ss.opcode != GSM0480_OP_CODE_USS_REQUEST) {
+ LOGP(DLCTRL, LOGL_ERROR, "Don't know hot to handle this SS opcode: 0x%02x\n", ss.opcode);
+ goto err_send_reject;
+ }
op = operation_find_by_tid(ctx, ss.invoke_id);
if (op == NULL) {
LOGP(DLCTRL, LOGL_ERROR, "No active session with tid=%d were found\n",
@@ -782,7 +816,7 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
}
// TODO check result!! MO/MT error handling
- rc = ussd_session_facility(op, sup_conn, &ss, extention);
+ rc = ussd_session_facility(op, &ss, extention);
if (rc < 0) {
operation_destroy(op);
goto err_send_reject;
@@ -794,7 +828,7 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data,
if (op == NULL) {
LOGP(DLCTRL, LOGL_ERROR, "No active session with tid=%d were found for RELEASE_COMPLETE\n",
ss.invoke_id);
- return;
+ return 0;
}
nua_bye(op->handle, TAG_END());
@@ -838,6 +872,16 @@ int ussd_send_data_ss(isup_connection_t *conn, struct ss_request* reply)
return sup_server_send(conn, outmsg);
}
+static int is_string_ascii(const char* msg, unsigned msg_len)
+{
+ unsigned i;
+ for (i = 0; i < msg_len; ++i) {
+ if (*((uint8_t*)(msg++)) >= 0x80)
+ return 0;
+ }
+ return 1;
+}
+
int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_len,
const char* msg, unsigned msg_len)
{
@@ -866,11 +910,44 @@ int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_le
if (msg_len > MAX_LEN_USSD_STRING) {
msg_len = MAX_LEN_USSD_STRING;
}
+ if (is_string_ascii(msg, msg_len)) {
+ // GSM 7-bit coding, done on the other end of SUP
+ ss.ussd_text_len = msg_len;
+ ss.ussd_text_language = 0x80;
+ strncpy((char*)ss.ussd_text, msg, msg_len);
+ } else {
+ char* inbuf = (char*)msg;
+ size_t inleft = msg_len;
+ char* outbuf = (char*)ss.ussd_text;
+ size_t outleft = MAX_LEN_USSD_STRING;
+ size_t s;
+ // First of all try latin1
+ if (op->ctx->dont_encode_in_latin1) {
+ s =(size_t)-1;
+ } else {
+ s = iconv(op->ctx->utf8_to_latin1,
+ &inbuf, &inleft,
+ &outbuf, &outleft);
+ }
+ if (s == (size_t)-1) {
+ s = iconv(op->ctx->utf8_to_ucs2,
+ &inbuf, &inleft,
+ &outbuf, &outleft);
+ if (s == (size_t)-1) {
+ perror("can't convert string from utf8");
+ }
+ // UCS-2 encoding
+ ss.ussd_text_language = 0x48;
+ } else {
+ // 8-bit DATA encoding
+ ss.ussd_text_language = 0x44;
+ }
+ ss.ussd_text_len = (uint8_t*)outbuf - ss.ussd_text;
- ss.ussd_text_len = msg_len;
- strncpy((char*)ss.ussd_text, msg, msg_len);
+ }
} else {
ss.ussd_text_len = 0;
+ ss.ussd_text_language = 0x80;
ss.ussd_text[0] = 0;
}
@@ -1065,6 +1142,7 @@ static void Usage(char* progname)
" -o <sessions> Maximum number of concurrent USSD sessions\n"
" (default: 200)\n"
" -l <0-9> sip sofia loglevel, 0 - none; 9 - max\n"
+ " -L Do not try to encode in 8-bit (use 7-bit or UCS-2)\n"
, progname);
}
@@ -1081,6 +1159,7 @@ int main(int argc, char *argv[])
int max_ussd_ses_secs = 90;
int max_op_limit = 200;
int sip_loglevel = 1;
+ int dont_try_latin1 = 0;
int c;
while ((c = getopt (argc, argv, "p:t:u:D:To:l:?")) != -1) {
@@ -1107,6 +1186,9 @@ int main(int argc, char *argv[])
case 'l':
sip_loglevel = atoi(optarg);
break;
+ case 'L':
+ dont_try_latin1 = 1;
+ break;
case '?':
default:
Usage(argv[0]);
@@ -1131,6 +1213,18 @@ int main(int argc, char *argv[])
return 1;
}
+ context->dont_encode_in_latin1 = dont_try_latin1;
+ context->utf8_to_latin1=iconv_open("iso8859-1", "utf-8");
+ context->latin1_to_utf8=iconv_open("utf-8", "iso8859-1");
+ context->utf8_to_ucs2=iconv_open("utf-16be", "utf-8");
+ context->ucs2_to_utf8=iconv_open("utf-8", "utf-16be");
+
+ if (context->utf8_to_ucs2 == NULL || context->ucs2_to_utf8 == NULL ||
+ context->utf8_to_latin1 == NULL || context->latin1_to_utf8 == NULL) {
+ fprintf(stderr, "Unable to initialize iconv\n");
+ return 1;
+ }
+
context->isup_acc_socket = su_socket(AF_INET, SOCK_STREAM, 0);
if (context->isup_acc_socket == INVALID_SOCKET) {
perror("unable to create socket\n");