ussd: handle UCS-2 coding

This commit is contained in:
Sergey Kostanbaev 2015-10-30 15:14:49 +03:00 committed by Ivan Kluchnikov
parent 5087f994fd
commit 012c9203e4
5 changed files with 138 additions and 43 deletions

View File

@ -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);

View File

@ -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);
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, 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);
/* 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);

View File

@ -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;

View File

@ -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,

View File

@ -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");