Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugins/janus_sip.c: MESSAGE Authentication and Deliver Status Report #2786

Merged
merged 8 commits into from
Nov 22, 2021
108 changes: 106 additions & 2 deletions plugins/janus_sip.c
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,7 @@ typedef struct janus_sip_session {
static GHashTable *sessions;
static GHashTable *identities;
static GHashTable *callids;
static GHashTable *messageids;
static GHashTable *masters;
static GHashTable *transfers;
static janus_mutex sessions_mutex = JANUS_MUTEX_INITIALIZER;
Expand Down Expand Up @@ -1936,6 +1937,7 @@ int janus_sip_init(janus_callbacks *callback, const char *config_path) {
sessions = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_sip_session_destroy);
identities = g_hash_table_new(g_str_hash, g_str_equal);
callids = g_hash_table_new(g_str_hash, g_str_equal);
messageids = g_hash_table_new(g_str_hash, g_str_equal);
masters = g_hash_table_new(NULL, NULL);
transfers = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)janus_sip_transfer_destroy);
messages = g_async_queue_new_full((GDestroyNotify) janus_sip_message_free);
Expand Down Expand Up @@ -1972,11 +1974,13 @@ void janus_sip_destroy(void) {
janus_mutex_lock(&sessions_mutex);
g_hash_table_destroy(sessions);
g_hash_table_destroy(callids);
g_hash_table_destroy(messageids);
g_hash_table_destroy(identities);
g_hash_table_destroy(masters);
g_hash_table_destroy(transfers);
sessions = NULL;
callids = NULL;
messageids = NULL:
identities = NULL;
masters = NULL;
transfers = NULL;
Expand Down Expand Up @@ -4637,7 +4641,10 @@ static void *janus_sip_handler(void *data) {
char custom_headers[2048];
janus_sip_parse_custom_headers(root, (char *)&custom_headers, sizeof(custom_headers));

char *message_callid = NULL;
if(in_dialog_message) {
/* Take Call-ID, later used to report delivery status */
message_callid = g_strdup(session->callid) ;
nua_message(session->stack->s_nh_i,
SIPTAG_CONTENT_TYPE_STR(content_type),
SIPTAG_PAYLOAD_STR(msg_content),
Expand All @@ -4656,18 +4663,34 @@ static void *janus_sip_handler(void *data) {
session->stack->s_nh_m = nua_handle(session->stack->s_nua, session, TAG_END());
}
janus_mutex_unlock(&session->stack->smutex);
json_t *request_callid = json_object_get(root, "call_id");
/* Use call-id from the request, if it exists */
if(request_callid) {
message_callid = g_strdup(json_string_value(request_callid));
} else {
/* If call-id does not exist in request, create a random one */
message_callid = g_malloc0(24);
janus_sip_random_string(24, message_callid);
}
nua_message(session->stack->s_nh_m,
SIPTAG_TO_STR(uri_text),
SIPTAG_CONTENT_TYPE_STR(content_type),
SIPTAG_PAYLOAD_STR(msg_content),
NUTAG_PROXY(session->helper && session->master ?
session->master->account.outbound_proxy : session->account.outbound_proxy),
TAG_IF(strlen(custom_headers) > 0, SIPTAG_HEADER_STR(custom_headers)),
SIPTAG_CALL_ID_STR(message_callid),
TAG_END());
}
/* Notify the operation */
/* Notify the application */
result = json_object();
json_object_set_new(result, "event", json_string("messagesent"));
json_object_set_new(result, "call_id", json_string(message_callid));
/* Store message id and session */
janus_mutex_lock(&sessions_mutex);
g_hash_table_insert(messageids, message_callid, session);
janus_mutex_unlock(&sessions_mutex);
g_free(message_callid);
} else if(!strcasecmp(request_text, "dtmf_info")) {
/* Send DMTF tones using SIP INFO
* (https://tools.ietf.org/html/draft-kaplan-dispatch-info-dtmf-package-00)
Expand Down Expand Up @@ -5419,7 +5442,88 @@ void janus_sip_sofia_callback(nua_event_t event, int status, char const *phrase,
break;
case nua_r_message:
JANUS_LOG(LOG_VERB, "[%s][%s]: %d %s\n", session->account.username, nua_event_name(event), status, phrase ? phrase : "??");
/* FIXME Should we notify the user, in case the SIP MESSAGE returned an error? */
/* Handle authetntication for SIP MESSAGE - eg. SippySoft Softswitch requires 401 authentication even if SIP user is registerered */
if(status == 401 || status == 407) {
const char *scheme = NULL;
const char *realm = NULL;
if(status == 401) {
/* Get scheme/realm from 401 error */
sip_www_authenticate_t const* www_auth = sip->sip_www_authenticate;
scheme = www_auth->au_scheme;
realm = msg_params_find(www_auth->au_params, "realm=");
} else {
/* Get scheme/realm from 407 error, proxy-auth */
sip_proxy_authenticate_t const* proxy_auth = sip->sip_proxy_authenticate;
scheme = proxy_auth->au_scheme;
realm = msg_params_find(proxy_auth->au_params, "realm=");
}
char authuser[100], secret[100];
memset(authuser, 0, sizeof(authuser));
memset(secret, 0, sizeof(secret));
if(session->helper) {
/* This is an helper session, we'll need the credentials from the master */
if(session->master == NULL) {
JANUS_LOG(LOG_WARN, "No master session for this helper, authentication will fail...\n");
} else {
session = session->master;
}
}
if(session->account.authuser && strchr(session->account.authuser, ':')) {
/* The authuser contains a colon: wrap it in quotes */
g_snprintf(authuser, sizeof(authuser), "\"%s\"", session->account.authuser);
} else {
g_snprintf(authuser, sizeof(authuser), "%s", session->account.authuser);
}
if(session->account.secret && strchr(session->account.secret, ':')) {
/* The secret contains a colon: wrap it in quotes */
g_snprintf(secret, sizeof(secret), "\"%s\"", session->account.secret);
} else {
g_snprintf(secret, sizeof(secret), "%s", session->account.secret);
}
char auth[256];
memset(auth, 0, sizeof(auth));
g_snprintf(auth, sizeof(auth), "%s%s:%s:%s:%s%s",
session->account.secret_type == janus_sip_secret_type_hashed ? "HA1+" : "",
scheme,
realm,
authuser,
session->account.secret_type == janus_sip_secret_type_hashed ? "HA1+" : "",
secret);
JANUS_LOG(LOG_VERB, "\t%s\n", auth);
/* Authenticate */
nua_authenticate(nh,
NUTAG_AUTH(auth),
TAG_END());
Comment on lines +5488 to +5537
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just noticed that this bloc of code repeated the third time. Could we move it to a separate function?

I am implementing publish command and will repeat it a fourth time. For sure I could do it in my PR, just curious is it the right way of refactoring, or is there some other sense in repeating the same code several times?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It could be a function or could be before the main switch(event) in janus_sip_sofia_callback. Auth and ProxyAuth are not used outside of this function, IMHO it is better to split code into functions only if used elsewhere.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do re-use the same auth code for different SIP responses, true. Anyway, this isn't something I'd do here: this is a change I'd do separately in another PR, devoted just to that, so I can look into this once we merge this effort.

} else {
/* Find session associated with the message */
janus_mutex_lock(&sessions_mutex);
janus_sip_session *message_session = g_hash_table_lookup(messageids, sip->sip_call_id->i_id);
janus_mutex_unlock(&sessions_mutex);
if (!message_session) {
message_session = session
JANUS_LOG(LOG_VERB, "Message (%s) not associated with any session, event will be reported to master\n", sip->sip_call_id->i_id);
} else {
/* Session was found in messageids, removes from hashtable */
janus_mutex_lock(&sessions_mutex);
g_hash_table_remove(messageids, sip->sip_call_id->i_id);
janus_mutex_unlock(&sessions_mutex);
}
/* MESSAGE response, notify the application */
json_t *result = json_object();
/* SIP code and reason */
json_object_set_new(result, "event", json_string("messagedelivery"));
json_object_set_new(result, "code", json_integer(status));
json_object_set_new(result, "reason", json_string(phrase));
/* Build the delivery receipt */
json_t *dr = json_object();
json_object_set_new(dr, "sip", json_string("event"));
json_object_set_new(dr, "result", result);
json_object_set_new(dr, "call_id", json_string(sip->sip_call_id->i_id));
/* Report delivery */
int ret = gateway->push_event(message_session->handle, &janus_sip_plugin, message_session->transaction, dr, NULL);
JANUS_LOG(LOG_VERB, " >> Pushing event to peer: %d (%s)\n", ret, janus_get_api_error(ret));
json_decref(dr);
}
break;
case nua_r_refer: {
JANUS_LOG(LOG_VERB, "[%s][%s]: %d %s\n", session->account.username, nua_event_name(event), status, phrase ? phrase : "??");
Expand Down