aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergey Kostanbaev <sergey.kostanbaev@gmail.com>2015-10-26 20:47:49 +0300
committerIvan Kluchnikov <kluchnikovi@gmail.com>2017-02-07 18:59:53 +0300
commit1e4a954c73b264a13092fbd236eb63fbf752a67b (patch)
tree05c42d0acfbdf7cb247e833c4e9ef5f5b78a4b23
parent2f4878a90f11fb4d10493478c9f63abada6ad3fe (diff)
ussd_proxy: handle USSD dialogs into sip INFO messages
-rwxr-xr-xopenbsc/src/reg-proxy/test_sip.py65
-rw-r--r--openbsc/src/reg-proxy/ussd_proxy.c209
2 files changed, 234 insertions, 40 deletions
diff --git a/openbsc/src/reg-proxy/test_sip.py b/openbsc/src/reg-proxy/test_sip.py
index e8bc296f6..d3c68d543 100755
--- a/openbsc/src/reg-proxy/test_sip.py
+++ b/openbsc/src/reg-proxy/test_sip.py
@@ -28,13 +28,15 @@ class RegistrationProxyServer(protocol.Protocol):
def sipClientDataReceived(self, data):
log.msg("\n[USSD:RX]\n%s" % data)
if data:
+ self.ussd_queue.get().addCallback(self.sipClientDataReceived)
+
msgType, firstLine, headers, body = sip_parser.parseSipMessage(data)
via = headers["via"][0].split(";")
via_branch = via[1].split("=")
from_hdr = headers["from"].split(";")
from_tag = from_hdr[1]
to_hdr = headers["to"].split(";")
- to_tag = from_hdr[1]
+ to_tag = "%s" % from_hdr[1]
call_id = headers["call-id"]
sip_url = re.split(r"[:@]", from_hdr[0])
ussd_url = sip_url[1]
@@ -46,7 +48,9 @@ class RegistrationProxyServer(protocol.Protocol):
via_dest_ip,via_dest_port=via[0].split(" ")[1].split(":")
if msgType=="INVITE":
- r = sip.Response(100, "Trying")
+ #sleep(5)
+
+ r = sip.Response(200, "OK")
r.addHeader('Via', headers["via"][0]) #sip.Via(via_dest_ip, via_dest_port, transport='TCP', ttl=None, hidden=False, received=None, rport=None, branch=via_branch[1], maddr=None).toString())
r.addHeader('From', "%s;%s" % (from_hdr[0], from_tag)) #"<sip:%s@%s>;%s" % (from_hdr[0], self.src_ip, from_tag))
r.addHeader('To', "%s;%s" % (to_hdr[0], to_tag)) #"<sip:%s@%s>;%s" % (to_hdr[0], self.src_ip, to_tag))
@@ -54,13 +58,13 @@ class RegistrationProxyServer(protocol.Protocol):
r.addHeader('Max-Forwards', 20)
r.addHeader('Cseq', cseq)
r.addHeader('Contact', '<sip:test@127.0.0.1:5060>')
+ r.addHeader('Recv-Info', 'g.3gpp.ussd')
r.addHeader('Content-Length', 0)
+ #r.addHeader("Authentication-Info", auth_info)
log.msg("\n[SIP:TX]\n%s" % r.toString())
self.transport.write(r.toString())
- #sleep(5)
-
- r = sip.Response(200, "OK")
+ r = sip.Response(100, "Trying")
r.addHeader('Via', headers["via"][0]) #sip.Via(via_dest_ip, via_dest_port, transport='TCP', ttl=None, hidden=False, received=None, rport=None, branch=via_branch[1], maddr=None).toString())
r.addHeader('From', "%s;%s" % (from_hdr[0], from_tag)) #"<sip:%s@%s>;%s" % (from_hdr[0], self.src_ip, from_tag))
r.addHeader('To', "%s;%s" % (to_hdr[0], to_tag)) #"<sip:%s@%s>;%s" % (to_hdr[0], self.src_ip, to_tag))
@@ -68,37 +72,70 @@ class RegistrationProxyServer(protocol.Protocol):
r.addHeader('Max-Forwards', 20)
r.addHeader('Cseq', cseq)
r.addHeader('Contact', '<sip:test@127.0.0.1:5060>')
- r.addHeader('Recv-Info', 'g.3gpp.ussd')
r.addHeader('Content-Length', 0)
- #r.addHeader("Authentication-Info", auth_info)
log.msg("\n[SIP:TX]\n%s" % r.toString())
self.transport.write(r.toString())
- elif msgType=="ACK":
+
+
+ elif msgType=="ACK" and to_hdr[0].startswith("<sip:*101"):
+ msg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ussd-data><language>en</language><ussd-string>%s</ussd-string></ussd-data>" % (
+ "Select Item from menu:\n1) Option 1\n2) Option 2");
+
+ r = sip.Request("INFO", to_hdr[0].replace('<', '').replace('>', ''))
+ r.addHeader('Via', headers["via"][0]) #sip.Via(via_dest_ip, via_dest_port, transport='TCP', ttl=None, hidden=False, received=None, rport=None, branch=via_branch[1], maddr=None).toString())
+ r.addHeader('From', "%s;%s" % (from_hdr[0], to_tag)) #"<sip:%s@%s>;%s" % (from_hdr[0], self.src_ip, from_tag))
+ r.addHeader('To', "%s;%s" % (to_hdr[0], from_tag)) #"<sip:%s@%s>;%s" % (to_hdr[0], self.src_ip, to_tag))
+ r.addHeader('Call-Id', call_id)
+ r.addHeader('Max-Forwards', 20)
+ r.addHeader('Cseq', "%d INFO" % (int(cseq.split(' ')[0]) + 1))
+ r.addHeader('Recv-Info', 'g.3gpp.ussd')
+ r.addHeader('Content-Type', 'application/vnd.3gpp.ussd+xml')
+ #r.addHeader('Content-Disposition', 'Info-Package')
+ r.addHeader('Content-Length', len(msg))
+ #r.addHeader("Authentication-Info", auth_info)
+ log.msg("\n[SIP:TX]]\n%s" % r.toString())
+ self.transport.write(r.toString() + msg)
+
+ elif msgType=="ACK" or msgType=="INFO":
msg = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><ussd-data><language>en</language><ussd-string>%s</ussd-string></ussd-data>" % (
"Test");
+ if msgType=="INFO":
+ r = sip.Response(200, "OK")
+ r.addHeader('Via', headers["via"][0]) #sip.Via(via_dest_ip, via_dest_port, transport='TCP', ttl=None, hidden=False, received=None, rport=None, branch=via_branch[1], maddr=None).toString())
+ r.addHeader('From', "%s;%s" % (from_hdr[0], from_tag)) #"<sip:%s@%s>;%s" % (from_hdr[0], self.src_ip, from_tag))
+ r.addHeader('To', "%s;%s" % (to_hdr[0], to_tag)) #"<sip:%s@%s>;%s" % (to_hdr[0], self.src_ip, to_tag))
+ r.addHeader('Call-Id', call_id)
+ r.addHeader('Max-Forwards', 20)
+ r.addHeader('Cseq', cseq)
+ r.addHeader('Recv-Info', 'g.3gpp.ussd')
+ r.addHeader('Content-Length', 0)
+ #r.addHeader("Authentication-Info", auth_info)
+ log.msg("\n[SIP:TX]\n%s" % r.toString())
+ self.transport.write(r.toString())
+
r = sip.Request("BYE", to_hdr[0].replace('<', '').replace('>', ''))
r.addHeader('Via', headers["via"][0]) #sip.Via(via_dest_ip, via_dest_port, transport='TCP', ttl=None, hidden=False, received=None, rport=None, branch=via_branch[1], maddr=None).toString())
- r.addHeader('From', "%s;%s" % (from_hdr[0], from_tag)) #"<sip:%s@%s>;%s" % (from_hdr[0], self.src_ip, from_tag))
- r.addHeader('To', "%s;%s" % (to_hdr[0], to_tag)) #"<sip:%s@%s>;%s" % (to_hdr[0], self.src_ip, to_tag))
+ r.addHeader('From', "%s;%s" % (from_hdr[0], to_tag)) #"<sip:%s@%s>;%s" % (from_hdr[0], self.src_ip, from_tag))
+ r.addHeader('To', "%s;%s" % (to_hdr[0], from_tag)) #"<sip:%s@%s>;%s" % (to_hdr[0], self.src_ip, to_tag))
r.addHeader('Call-Id', call_id)
r.addHeader('Max-Forwards', 20)
r.addHeader('Cseq', "%d BYE" % (int(cseq.split(' ')[0]) + 1))
r.addHeader('Recv-Info', 'g.3gpp.ussd')
r.addHeader('Content-Type', 'application/vnd.3gpp.ussd+xml')
- r.addHeader('Content-Disposition', 'Info-Package')
+ #r.addHeader('Content-Disposition', 'Info-Package')
r.addHeader('Content-Length', len(msg))
#r.addHeader("Authentication-Info", auth_info)
log.msg("\n[SIP:TX]]\n%s" % r.toString())
- self.transport.write(r.toString() + "\n" + msg)
+ self.transport.write(r.toString() + msg)
else:
print msgType
- self.ussd_queue.get().addCallback(self.sipClientDataReceived)
+ #self.ussd_queue.get().addCallback(self.sipClientDataReceived)
#self.ussd_queue.get().addCallback(self.sipClientDataReceived)
def dataReceived(self, data):
- log.msg("\n[IMSI:RX] [Proxy <=============== BSC]\n%s" % data)
+ #log.msg("\n[IMSI:RX] [Proxy <=============== BSC]\n%s" % data)
self.ussd_queue.put(data)
class RegistrationProxyServerFactory(protocol.ClientFactory):
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) {