diff options
author | Sergey Kostanbaev <sergey.kostanbaev@gmail.com> | 2015-10-26 20:47:49 +0300 |
---|---|---|
committer | Ivan Kluchnikov <kluchnikovi@gmail.com> | 2017-02-07 18:59:53 +0300 |
commit | 1e4a954c73b264a13092fbd236eb63fbf752a67b (patch) | |
tree | 05c42d0acfbdf7cb247e833c4e9ef5f5b78a4b23 /openbsc/src/reg-proxy/ussd_proxy.c | |
parent | 2f4878a90f11fb4d10493478c9f63abada6ad3fe (diff) |
ussd_proxy: handle USSD dialogs into sip INFO messages
Diffstat (limited to 'openbsc/src/reg-proxy/ussd_proxy.c')
-rw-r--r-- | openbsc/src/reg-proxy/ussd_proxy.c | 209 |
1 files changed, 183 insertions, 26 deletions
diff --git a/openbsc/src/reg-proxy/ussd_proxy.c b/openbsc/src/reg-proxy/ussd_proxy.c index a19dd5cd3..994b9f0d2 100644 --- a/openbsc/src/reg-proxy/ussd_proxy.c +++ b/openbsc/src/reg-proxy/ussd_proxy.c @@ -37,6 +37,7 @@ typedef struct operation operation_t; #include <openbsc/gprs_gsup_messages.h> +typedef uint8_t sup_tcap_tid_t; /******************************************************************************/ /* Put this into separate file */ @@ -81,7 +82,7 @@ static int rx_uss_message_parse(struct ss_request *ss, uint8_t len; switch (*const_data) { case ASN1_OCTET_STRING_TAG: - len = *(++const_data); + ss->ussd_text_len = len = *(++const_data); strncpy((char*)ss->ussd_text, (const char*)++const_data, (len > MAX_LEN_USSD_STRING) ? MAX_LEN_USSD_STRING : len); @@ -216,6 +217,9 @@ int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_le static int ussd_send_data_ss(isup_connection_t *conn, struct ss_request* reply); +static +int ussd_send_reject(isup_connection_t *conn, uint8_t invoke_id, uint8_t opcode); + static const char* get_unknown_header(sip_t const *sip, const char *header) { sip_header_t *h = (sip_header_t *)sip->sip_unknown; @@ -305,7 +309,25 @@ static int ussd_parse_xml(const char *xml, return 1; } -static void operetion_destroy(operation_t* op) +// Operation APIs +static operation_t* operation_find_by_tid(context_t* ctx, sup_tcap_tid_t id) +{ + if (ctx->operations == NULL) + return NULL; + + if (ctx->operations->ussd.rigester_msg.invoke_id != id) + return NULL; + + return ctx->operations; +} + +static operation_t* operation_alloc(context_t* ctx) +{ + // TODO + return NULL; +} + +static void operation_destroy(operation_t* op) { /* release operation handle */ nua_handle_destroy(op->handle); @@ -332,8 +354,10 @@ void proxy_r_invite(int status, } else { printf("response to INVITE: %03d %s\n", status, phrase); - ussd_send_data(hmagic, 1, "en", 2, NULL, 0); - operetion_destroy(hmagic); + ussd_send_reject(hmagic->ussd.conn, + hmagic->ussd.rigester_msg.invoke_id, + hmagic->ussd.rigester_msg.opcode); + operation_destroy(hmagic); } } @@ -380,7 +404,77 @@ void proxy_r_bye(int status, } } - operetion_destroy(hmagic); + fprintf(stderr, "*** response BYE with %d satus is malformed, drop session\n", + status); + ussd_send_reject(hmagic->ussd.conn, + hmagic->ussd.rigester_msg.invoke_id, + hmagic->ussd.rigester_msg.opcode); + operation_destroy(hmagic); +} + + +void proxy_info(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[], + int response) +{ + const char* ri; + int rc; + + // Normal ACK is recieved + if (response == 1 && status == 200) + return; + + ri = get_unknown_header(sip, "Recv-Info"); + if (ri && (strcasecmp(ri, "g.3gpp.ussd") == 0)) { + /* Parse XML */ + const char *language; + const char *msg; + unsigned language_len; + unsigned msg_len; + + if (ussd_parse_xml(sip->sip_payload->pl_data, + sip->sip_payload->pl_len, + &language, &language_len, + &msg, &msg_len)) { + printf("%s USSD (%.*s): %.*s\n", + (response) ? ">>>" : "<<<", + language_len, language, + msg_len, msg); + + if (hmagic == 0) { + printf("*** unknown session, ignoring"); + + // FIXME this function works only with a dialog! + nua_respond(nh, 481, "INFO with no session", TAG_END()); + return; + } + + /* Send reply back to SUP */ + // TODO !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + rc = ussd_send_data(hmagic, 0, language, language_len, + msg, msg_len); + if (rc == 0) + return; + + fprintf(stderr, "*** unable to send to SUP in INFO\n"); + } else { + fprintf(stderr, "*** unable to parse XML in INFO\n"); + } + } + + fprintf(stderr, "*** %s INFO with %d satus is malformed, drop session\n", + response ? "response" : "request", + status); + ussd_send_reject(hmagic->ussd.conn, + hmagic->ussd.rigester_msg.invoke_id, + hmagic->ussd.rigester_msg.opcode); + operation_destroy(hmagic); } int ussd_create_xml_ascii(char *content, size_t max_len, const char* language, const char* msg, int msg_len) @@ -403,7 +497,7 @@ int ussd_create_xml_ascii(char *content, size_t max_len, const char* language, c /* URL_RESERVED_CHARS in sofia is not strict enough as in RFC3986 */ #define RFC3986_RESERVED_CHARS "!*'();:@&=+$,/?#[]" -operation_t *open_ussd_session_mo(context_t* ctx, +operation_t *ussd_session_open_mo(context_t* ctx, isup_connection_t *conn, struct ss_request* ss, const char* extention) @@ -507,7 +601,29 @@ failed_alloc: return NULL; } +int ussd_session_facility(operation_t *op, + isup_connection_t *conn, + struct ss_request* ss, + const char* extention) +{ + char content[1024]; + if (!ussd_create_xml_ascii(content, sizeof(content), + "en", + (const char* )ss->ussd_text, + ss->ussd_text_len)) { + 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_PAYLOAD_STR(content), + TAG_END()); + return 0; +} void context_callback(nua_event_t event, int status, @@ -519,7 +635,27 @@ void context_callback(nua_event_t event, sip_t const *sip, tagi_t tags[]) { + fprintf(stderr, "$$$ got event %d: status: %d : %p\n", event, status, hmagic); + switch (event) { +#if 0 + case nua_r_unpublish: + if (hmagic != NULL) { + ussd_send_reject(hmagic->ussd.conn, + hmagic->ussd.rigester_msg.invoke_id, + hmagic->ussd.rigester_msg.opcode); + operation_destroy(hmagic); + } + break; +#endif + case nua_i_info: + proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 0); + break; + + case nua_r_info: + proxy_info(status, phrase, nua, magic, nh, hmagic, sip, tags, 1); + break; + case nua_i_bye: proxy_r_bye(status, phrase, nua, magic, nh, hmagic, sip, tags); break; @@ -553,6 +689,8 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data, { char extention[32] = {0}; struct ss_request ss; + operation_t* op; + int rc; context_t *ctx = sup_conn->ctx; memset(&ss, 0, sizeof(ss)); @@ -567,38 +705,54 @@ static int rx_sup_uss_message(isup_connection_t *sup_conn, const uint8_t* data, switch (ss.message_type) { case GSM0480_MTYPE_REGISTER: /* Create new session */ - { if (ctx->operations) { LOGP(DLCTRL, LOGL_ERROR, "Doesn't support multiple sessions. Failing this for now\n"); - struct ss_request ess; - memset(&ess, 0, sizeof(ess)); - ess.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; - ess.component_type = GSM0480_CTYPE_REJECT; - ess.invoke_id = ss.invoke_id; - ess.opcode = ss.opcode; - - ussd_send_data_ss(sup_conn, &ess); - return -1; + goto err_send_reject; } - operation_t * newop = open_ussd_session_mo(ctx, - sup_conn, - &ss, - extention); - - ctx->operations = newop; + op = ussd_session_open_mo(ctx, + sup_conn, + &ss, + extention); - } + ctx->operations = op; break; case GSM0480_MTYPE_FACILITY: + op = operation_find_by_tid(ctx, ss.invoke_id); + + // TODO check result!! MO/MT error handling + rc = ussd_session_facility(op, sup_conn, &ss, extention); + if (rc < 0) + goto err_send_reject; break; case GSM0480_MTYPE_RELEASE_COMPLETE: break; + + default: + LOGP(DLCTRL, LOGL_ERROR, "Unknown message type 0x%02x\n", ss.message_type); + goto err_send_reject; } return 0; + +err_send_reject: + ussd_send_reject(sup_conn, ss.invoke_id, ss.opcode); + return -1; +} + +int ussd_send_reject(isup_connection_t *conn, uint8_t invoke_id, uint8_t opcode) +{ + struct ss_request error_ss; + + memset(&error_ss, 0, sizeof(error_ss)); + error_ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; + error_ss.component_type = GSM0480_CTYPE_REJECT; + error_ss.invoke_id = invoke_id; + error_ss.opcode = opcode; + + return ussd_send_data_ss(conn, &error_ss); } int ussd_send_data_ss(isup_connection_t *conn, struct ss_request* reply) @@ -624,16 +778,18 @@ int ussd_send_data(operation_t *op, int last, const char* lang, unsigned lang_le if (msg == NULL) { ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; ss.component_type = GSM0480_CTYPE_REJECT; + ss.opcode = op->ussd.rigester_msg.opcode; } else if (last) { ss.message_type = GSM0480_MTYPE_RELEASE_COMPLETE; ss.component_type = GSM0480_CTYPE_RETURN_RESULT; + ss.opcode = op->ussd.rigester_msg.opcode; } else { ss.message_type = GSM0480_MTYPE_FACILITY; - ss.component_type = (op->ussd.rigester_msg.component_type == GSM0480_CTYPE_INVOKE) ? - GSM0480_CTYPE_RETURN_RESULT : GSM0480_CTYPE_INVOKE; + ss.component_type = (op->ussd.ms_originated) ? GSM0480_CTYPE_INVOKE + : GSM0480_CTYPE_RETURN_RESULT; + ss.opcode = GSM0480_OP_CODE_USS_REQUEST; } - ss.opcode = op->ussd.rigester_msg.opcode; ss.invoke_id = op->ussd.rigester_msg.invoke_id; if (msg) { @@ -916,6 +1072,7 @@ int main(int argc, char *argv[]) NUTAG_SESSION_TIMER(0), NUTAG_AUTOANSWER(0), NUTAG_MEDIA_ENABLE(0), + NUTAG_ALLOW("INVITE, ACK, BYE, CANCEL, INFO"), TAG_NULL()); if (force_tcp) { |