Skip to content

Commit d5e3d2e

Browse files
authored
plugins/janus_sip.c: MESSAGE Authentication and Deliver Status Report (#2786)
CHANGE: sets MESSAGE Call-ID if not defined in the message event ADD: SIP Authentication for 401 and 407 reponse to Message ADD: messagedelivery event to report delivery status back to the peer
1 parent 1d580cd commit d5e3d2e

File tree

3 files changed

+177
-12
lines changed

3 files changed

+177
-12
lines changed

html/siptest.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ <h3>Demo details</h3>
111111
<button class="btn btn-success margin-bottom-sm" autocomplete="off" id="call">Call</button> <input autocomplete="off" id="dovideo" type="checkbox" />Use Video
112112
</div>
113113
</div>
114-
<div/>
114+
</div>
115115
<div id="videos" class="hide">
116116
<div class="col-md-6">
117117
<div class="panel panel-default">

html/siptest.js

+23-3
Original file line numberDiff line numberDiff line change
@@ -446,6 +446,16 @@ $(document).ready(function() {
446446
$('#call').removeAttr('disabled').html('Call')
447447
.removeClass("btn-danger").addClass("btn-success")
448448
.unbind('click').click(doCall);
449+
} else if(event === 'messagedelivery') {
450+
// message delivery status
451+
let reason = result["reason"];
452+
let code = result["code"];
453+
let callid = msg['call_id'];
454+
if (code == 200) {
455+
toastr.success(`${callid} Delivery Status: ${code} ${reason}`);
456+
} else {
457+
toastr.error(`${callid} Delivery Status: ${code} ${reason}`);
458+
}
449459
}
450460
}
451461
},
@@ -843,14 +853,14 @@ function doCall(ev) {
843853
bootbox.alert('Please insert a valid SIP address (e.g., sip:pluto@example.com)');
844854
$('#peer' + suffix).removeAttr('disabled');
845855
$('#dovideo' + suffix).removeAttr('disabled');
846-
$('#call' + suffix).removeAttr('disabled').click(function() { doCall(helperId); });
856+
$('#call' + suffix).removeAttr('disabled').click(function(ev) { doCall(ev); });
847857
return;
848858
}
849859
if(username.indexOf("sip:") != 0 || username.indexOf("@") < 0) {
850860
bootbox.alert('Please insert a valid SIP address (e.g., sip:pluto@example.com)');
851861
$('#peer' + suffix).removeAttr('disabled').val("");
852862
$('#dovideo' + suffix).removeAttr('disabled').val("");
853-
$('#call' + suffix).removeAttr('disabled').click(function() { doCall(helperId); });
863+
$('#call' + suffix).removeAttr('disabled').click(function(ev) { doCall(ev); });
854864
return;
855865
}
856866
// Call this URI
@@ -964,7 +974,7 @@ function addHelper(helperCreated) {
964974
' <button disabled class="btn btn-success margin-bottom-sm" autocomplete="off" id="call' + helperId + '">Call</button> <input autocomplete="off" id="dovideo' + helperId + '" type="checkbox">Use Video</input>' +
965975
' </div>' +
966976
' </div>' +
967-
' <div/>' +
977+
' </div>' +
968978
' <div id="videos' + helperId + '" class="hide">' +
969979
' <div class="col-md-6">' +
970980
' <div class="panel panel-default">' +
@@ -1313,6 +1323,16 @@ function addHelper(helperCreated) {
13131323
$('#call' + helperId).removeAttr('disabled').html('Call')
13141324
.removeClass("btn-danger").addClass("btn-success")
13151325
.unbind('click').click(doCall);
1326+
} else if(event === 'messagedelivery') {
1327+
// message delivery status
1328+
let reason = result["reason"];
1329+
let code = result["code"];
1330+
let callid = msg['call_id'];
1331+
if (code == 200) {
1332+
toastr.success(`${callid}/${helperId} Delivery Status: ${code} ${reason}`);
1333+
} else {
1334+
toastr.error(`${callid}/${helperId} Delivery Status: ${code} ${reason}`);
1335+
}
13161336
}
13171337
}
13181338
},

plugins/janus_sip.c

+153-8
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@
413413
\verbatim
414414
{
415415
"request" : "message",
416+
"call_id" : "<user-defined value of Call-ID SIP header used to send the message; optional>",
416417
"content_type" : "<content type; optional>"
417418
"content" : "<text to send>",
418419
"uri" : "<SIP URI of the peer; optional; if set, the message will be sent out of dialog>",
@@ -435,6 +436,21 @@
435436
"headers" : "<object with key/value strings; custom headers extracted from SIP event based on incoming_header_prefix defined in register request; optional>"
436437
}
437438
}
439+
\endverbatim
440+
*
441+
* After delivery a \c messagedelivery event will be sent back with the SIP server response.
442+
* Used to track the delivery status of the message.
443+
*
444+
\verbatim
445+
{
446+
"sip" : "event",
447+
"call_id" : "<value of SIP Call-ID header for related message>",
448+
"result" : {
449+
"event" : "messagedelivery",
450+
"code" : "<SIP error code>",
451+
"reason" : "<SIP error reason>",
452+
}
453+
}
438454
\endverbatim
439455
*
440456
* SIP INFO works pretty much the same way, except that you use an \c info
@@ -1049,6 +1065,7 @@ typedef struct janus_sip_session {
10491065
static GHashTable *sessions;
10501066
static GHashTable *identities;
10511067
static GHashTable *callids;
1068+
static GHashTable *messageids;
10521069
static GHashTable *masters;
10531070
static GHashTable *transfers;
10541071
static janus_mutex sessions_mutex = JANUS_MUTEX_INITIALIZER;
@@ -1076,6 +1093,11 @@ static void janus_sip_session_destroy(janus_sip_session *session) {
10761093
janus_refcount_decrease(&session->ref);
10771094
}
10781095

1096+
static void janus_sip_session_dereference(janus_sip_session *session) {
1097+
/* This is used to decrease the reference when removing to the messageids hashtable. janus_refcount_increase(&session->ref) must be called before inserting into messageids hashtable */
1098+
janus_refcount_decrease(&session->ref);
1099+
}
1100+
10791101
static void janus_sip_session_free(const janus_refcount *session_ref) {
10801102
janus_sip_session *session = janus_refcount_containerof(session_ref, janus_sip_session, ref);
10811103
/* Remove the reference to the core plugin session */
@@ -1936,6 +1958,7 @@ int janus_sip_init(janus_callbacks *callback, const char *config_path) {
19361958
sessions = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_sip_session_destroy);
19371959
identities = g_hash_table_new(g_str_hash, g_str_equal);
19381960
callids = g_hash_table_new(g_str_hash, g_str_equal);
1961+
messageids = g_hash_table_new_full(NULL, NULL, (GDestroyNotify)g_free, (GDestroyNotify)janus_sip_session_dereference);
19391962
masters = g_hash_table_new(NULL, NULL);
19401963
transfers = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_sip_transfer_destroy);
19411964
messages = g_async_queue_new_full((GDestroyNotify) janus_sip_message_free);
@@ -1972,11 +1995,13 @@ void janus_sip_destroy(void) {
19721995
janus_mutex_lock(&sessions_mutex);
19731996
g_hash_table_destroy(sessions);
19741997
g_hash_table_destroy(callids);
1998+
g_hash_table_destroy(messageids);
19751999
g_hash_table_destroy(identities);
19762000
g_hash_table_destroy(masters);
19772001
g_hash_table_destroy(transfers);
19782002
sessions = NULL;
19792003
callids = NULL;
2004+
messageids = NULL;
19802005
identities = NULL;
19812006
masters = NULL;
19822007
transfers = NULL;
@@ -4637,37 +4662,76 @@ static void *janus_sip_handler(void *data) {
46374662
char custom_headers[2048];
46384663
janus_sip_parse_custom_headers(root, (char *)&custom_headers, sizeof(custom_headers));
46394664

4665+
char *message_callid = NULL;
46404666
if(in_dialog_message) {
4667+
/* Take Call-ID, later used to report delivery status */
4668+
message_callid = g_strdup(session->callid) ;
46414669
nua_message(session->stack->s_nh_i,
46424670
SIPTAG_CONTENT_TYPE_STR(content_type),
46434671
SIPTAG_PAYLOAD_STR(msg_content),
46444672
TAG_IF(strlen(custom_headers) > 0, SIPTAG_HEADER_STR(custom_headers)),
46454673
TAG_END());
46464674
} else {
4647-
janus_mutex_lock(&session->stack->smutex);
4648-
if(session->stack->s_nh_m == NULL) {
4649-
if (session->stack->s_nua == NULL) {
4675+
/* Get appropriate handle */
4676+
nua_handle_t *nh = NULL;
4677+
if(!session->helper) {
4678+
janus_mutex_lock(&session->stack->smutex);
4679+
if(session->stack->s_nua == NULL) {
46504680
janus_mutex_unlock(&session->stack->smutex);
46514681
JANUS_LOG(LOG_ERR, "NUA destroyed while sending message?\n");
46524682
error_code = JANUS_SIP_ERROR_LIBSOFIA_ERROR;
46534683
g_snprintf(error_cause, 512, "Invalid NUA");
46544684
goto error;
46554685
}
4656-
session->stack->s_nh_m = nua_handle(session->stack->s_nua, session, TAG_END());
4686+
nh = nua_handle(session->stack->s_nua, session, TAG_END());
4687+
janus_mutex_unlock(&session->stack->smutex);
4688+
} else {
4689+
/* This is a helper, we need to use the master's SIP stack */
4690+
if(session->master == NULL || session->master->stack == NULL) {
4691+
error_code = JANUS_SIP_ERROR_HELPER_ERROR;
4692+
g_snprintf(error_cause, 512, "Invalid master SIP stack");
4693+
goto error;
4694+
}
4695+
janus_mutex_lock(&session->master->stack->smutex);
4696+
if(session->master->stack->s_nua == NULL) {
4697+
janus_mutex_unlock(&session->master->stack->smutex);
4698+
JANUS_LOG(LOG_ERR, "NUA destroyed while sending message?\n");
4699+
error_code = JANUS_SIP_ERROR_LIBSOFIA_ERROR;
4700+
g_snprintf(error_cause, 512, "Invalid NUA");
4701+
goto error;
4702+
}
4703+
nh = nua_handle(session->master->stack->s_nua, session, TAG_END());
4704+
janus_mutex_unlock(&session->master->stack->smutex);
46574705
}
4658-
janus_mutex_unlock(&session->stack->smutex);
4659-
nua_message(session->stack->s_nh_m,
4706+
json_t *request_callid = json_object_get(root, "call_id");
4707+
/* Use call-id from the request, if it exists */
4708+
if(request_callid) {
4709+
message_callid = g_strdup(json_string_value(request_callid));
4710+
} else {
4711+
/* If call-id does not exist in request, create a random one */
4712+
message_callid = g_malloc0(24);
4713+
janus_sip_random_string(24, message_callid);
4714+
}
4715+
nua_message(nh,
46604716
SIPTAG_TO_STR(uri_text),
46614717
SIPTAG_CONTENT_TYPE_STR(content_type),
46624718
SIPTAG_PAYLOAD_STR(msg_content),
46634719
NUTAG_PROXY(session->helper && session->master ?
46644720
session->master->account.outbound_proxy : session->account.outbound_proxy),
46654721
TAG_IF(strlen(custom_headers) > 0, SIPTAG_HEADER_STR(custom_headers)),
4722+
SIPTAG_CALL_ID_STR(message_callid),
46664723
TAG_END());
46674724
}
4668-
/* Notify the operation */
4725+
/* Notify the application */
46694726
result = json_object();
46704727
json_object_set_new(result, "event", json_string("messagesent"));
4728+
json_object_set_new(result, "call_id", json_string(message_callid));
4729+
/* Store message id and session */
4730+
janus_mutex_lock(&sessions_mutex);
4731+
janus_refcount_increase(&session->ref);
4732+
g_hash_table_insert(messageids, g_strdup(message_callid), session);
4733+
janus_mutex_unlock(&sessions_mutex);
4734+
g_free(message_callid);
46714735
} else if(!strcasecmp(request_text, "dtmf_info")) {
46724736
/* Send DMTF tones using SIP INFO
46734737
* (https://tools.ietf.org/html/draft-kaplan-dispatch-info-dtmf-package-00)
@@ -5419,7 +5483,88 @@ void janus_sip_sofia_callback(nua_event_t event, int status, char const *phrase,
54195483
break;
54205484
case nua_r_message:
54215485
JANUS_LOG(LOG_VERB, "[%s][%s]: %d %s\n", session->account.username, nua_event_name(event), status, phrase ? phrase : "??");
5422-
/* FIXME Should we notify the user, in case the SIP MESSAGE returned an error? */
5486+
/* Handle authetntication for SIP MESSAGE - eg. SippySoft Softswitch requires 401 authentication even if SIP user is registerered */
5487+
if(status == 401 || status == 407) {
5488+
const char *scheme = NULL;
5489+
const char *realm = NULL;
5490+
if(status == 401) {
5491+
/* Get scheme/realm from 401 error */
5492+
sip_www_authenticate_t const* www_auth = sip->sip_www_authenticate;
5493+
scheme = www_auth->au_scheme;
5494+
realm = msg_params_find(www_auth->au_params, "realm=");
5495+
} else {
5496+
/* Get scheme/realm from 407 error, proxy-auth */
5497+
sip_proxy_authenticate_t const* proxy_auth = sip->sip_proxy_authenticate;
5498+
scheme = proxy_auth->au_scheme;
5499+
realm = msg_params_find(proxy_auth->au_params, "realm=");
5500+
}
5501+
char authuser[100], secret[100];
5502+
memset(authuser, 0, sizeof(authuser));
5503+
memset(secret, 0, sizeof(secret));
5504+
if(session->helper) {
5505+
/* This is an helper session, we'll need the credentials from the master */
5506+
if(session->master == NULL) {
5507+
JANUS_LOG(LOG_WARN, "No master session for this helper, authentication will fail...\n");
5508+
} else {
5509+
session = session->master;
5510+
}
5511+
}
5512+
if(session->account.authuser && strchr(session->account.authuser, ':')) {
5513+
/* The authuser contains a colon: wrap it in quotes */
5514+
g_snprintf(authuser, sizeof(authuser), "\"%s\"", session->account.authuser);
5515+
} else {
5516+
g_snprintf(authuser, sizeof(authuser), "%s", session->account.authuser);
5517+
}
5518+
if(session->account.secret && strchr(session->account.secret, ':')) {
5519+
/* The secret contains a colon: wrap it in quotes */
5520+
g_snprintf(secret, sizeof(secret), "\"%s\"", session->account.secret);
5521+
} else {
5522+
g_snprintf(secret, sizeof(secret), "%s", session->account.secret);
5523+
}
5524+
char auth[256];
5525+
memset(auth, 0, sizeof(auth));
5526+
g_snprintf(auth, sizeof(auth), "%s%s:%s:%s:%s%s",
5527+
session->account.secret_type == janus_sip_secret_type_hashed ? "HA1+" : "",
5528+
scheme,
5529+
realm,
5530+
authuser,
5531+
session->account.secret_type == janus_sip_secret_type_hashed ? "HA1+" : "",
5532+
secret);
5533+
JANUS_LOG(LOG_VERB, "\t%s\n", auth);
5534+
/* Authenticate */
5535+
nua_authenticate(nh,
5536+
NUTAG_AUTH(auth),
5537+
TAG_END());
5538+
} else {
5539+
char *messageid = g_strdup(sip->sip_call_id->i_id);
5540+
/* Find session associated with the message */
5541+
janus_mutex_lock(&sessions_mutex);
5542+
janus_sip_session *message_session = g_hash_table_lookup(messageids, messageid);
5543+
if (!message_session) {
5544+
message_session = session;
5545+
JANUS_LOG(LOG_VERB, "Message (%s) not associated with any session, event will be reported to master\n", messageid);
5546+
}
5547+
janus_mutex_unlock(&sessions_mutex);
5548+
/* MESSAGE response, notify the application */
5549+
json_t *result = json_object();
5550+
/* SIP code and reason */
5551+
json_object_set_new(result, "event", json_string("messagedelivery"));
5552+
json_object_set_new(result, "code", json_integer(status));
5553+
json_object_set_new(result, "reason", json_string(phrase));
5554+
/* Build the delivery receipt */
5555+
json_t *dr = json_object();
5556+
json_object_set_new(dr, "sip", json_string("event"));
5557+
json_object_set_new(dr, "result", result);
5558+
json_object_set_new(dr, "call_id", json_string(messageid));
5559+
/* Report delivery */
5560+
int ret = gateway->push_event(message_session->handle, &janus_sip_plugin, message_session->transaction, dr, NULL);
5561+
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
5562+
json_decref(dr);
5563+
janus_mutex_lock(&sessions_mutex);
5564+
g_hash_table_remove(messageids, messageid);
5565+
janus_mutex_unlock(&sessions_mutex);
5566+
g_free(messageid);
5567+
}
54235568
break;
54245569
case nua_r_refer: {
54255570
JANUS_LOG(LOG_VERB, "[%s][%s]: %d %s\n", session->account.username, nua_event_name(event), status, phrase ? phrase : "??");

0 commit comments

Comments
 (0)