-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This module implements a pinging mechanism using SIP OPTIONS to qualify the peer while a call is in the INCOMING state to ensure that the peer is reachable and we will be able to successfully answer the call. Configuration: qual_freq [seconds] qualify frequency qual_to [ms] qualify timeout The OPTIONS are only sent if both options are present, both are not zero, qualify_freq is greater than qual_to, and the call is incoming. As soon as the call is established or closed, we stop sending OPTIONS. If we run don’t receive a response to an OPTIONS request within the specified qual_to, the call is terminated and UA_EVENT_CALL_CLOSED is triggered. Extra account address parameters: The module can be activated by adding both the `qual_freq` and the `qual_to` parameters to the accounts parameter `extra`. Example: <sip:[email protected]>;extra=qual_freq=5,qual_to=2000
- Loading branch information
1 parent
9ce5fe6
commit 6b6ebb3
Showing
3 changed files
with
311 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ set(MODULES | |
kaoptions | ||
vidloop | ||
parcall | ||
qualify | ||
) | ||
|
||
if(DEFINED EXTRA_MODULES) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
project(qualify) | ||
|
||
set(SRCS qualify.c) | ||
|
||
if(STATIC) | ||
add_library(${PROJECT_NAME} OBJECT ${SRCS}) | ||
else() | ||
add_library(${PROJECT_NAME} MODULE ${SRCS}) | ||
endif() | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,300 @@ | ||
/** | ||
* @file qualify.c Pinging of peer in CALL_STATE_INCOMING via SIP OPTIONS | ||
* | ||
* Copyright (C) 2023 Commend.com - [email protected] | ||
*/ | ||
|
||
#include <string.h> | ||
#include <stdlib.h> | ||
#include <re.h> | ||
#include <baresip.h> | ||
|
||
|
||
/** | ||
* @defgroup qualify module | ||
* | ||
* This module implements a pinging mechanism using SIP OPTIONS to qualify the | ||
* peer while a call is in the INCOMING state to ensure that the peer is | ||
* reachable and we will be able to successfully answer the call. | ||
* | ||
* Configuration: | ||
* qual_freq [seconds] qualify frequency | ||
* qual_to [ms] qualify timeout | ||
* | ||
* The OPTIONS are only sent if both options are present, both are not zero, | ||
* qualify_freq is greater than qual_to, and the call is incoming. As soon as | ||
* the call is established or closed, we stop sending OPTIONS. | ||
* If we run don’t receive a response to an OPTIONS request within the | ||
* specified qual_to, the call is terminated and UA_EVENT_CALL_CLOSED is | ||
* triggered. | ||
* | ||
* Extra account address parameters: | ||
* The module can be activated by adding both the `qual_freq` and the `qual_to` | ||
* parameters to the accounts parameter `extra`. | ||
* | ||
* Example: | ||
* <sip:[email protected]>;extra=qual_freq=5,qual_to=2000 | ||
* | ||
*/ | ||
|
||
|
||
#define DEBUG_MODULE "qualify" | ||
#define DEBUG_LEVEL 5 | ||
#include <re_dbg.h> | ||
|
||
|
||
struct qualle { | ||
struct le he; | ||
struct call *call; | ||
struct tmr freq_tmr; | ||
struct tmr to_tmr; | ||
}; | ||
|
||
|
||
struct qualify { | ||
struct hash *qual_map; | ||
}; | ||
|
||
|
||
static struct qualify q = { NULL }; | ||
|
||
|
||
/* Forward declaration */ | ||
static int call_start_qualify(struct call *call, | ||
const struct account *acc, | ||
struct qualle *qualle); | ||
|
||
|
||
static void qualle_destructor(void *arg) | ||
{ | ||
struct qualle *qualle = arg; | ||
|
||
hash_unlink(&qualle->he); | ||
tmr_cancel(&qualle->to_tmr); | ||
tmr_cancel(&qualle->freq_tmr); | ||
} | ||
|
||
|
||
/** | ||
* Get specified field in the accounts extra parameter list | ||
* | ||
* @param acc Accounts object | ||
* @param n Specified field | ||
* @param v uint32_t ptr for the specified value | ||
* | ||
* @return 0 if success, otherwise errorcode | ||
*/ | ||
static int account_extra_uint(const struct account *acc, const char *n, | ||
uint32_t *v) | ||
{ | ||
struct pl pl; | ||
struct pl val; | ||
const char *extra = NULL; | ||
|
||
if (!acc || !n || !v) | ||
return EINVAL; | ||
|
||
extra = account_extra(acc); | ||
if (!str_isset(extra)) | ||
return EINVAL; | ||
|
||
pl_set_str(&pl, extra); | ||
if (!fmt_param_sep_get(&pl, n, ',', &val)) | ||
return EINVAL; | ||
|
||
*v = pl_u32(&val); | ||
|
||
return 0; | ||
} | ||
|
||
|
||
static void options_resp_handler(int err, const struct sip_msg *msg, void *arg) | ||
{ | ||
(void)msg; | ||
struct qualle *qualle = arg; | ||
|
||
if (err) { | ||
warning("OPTIONS reply error: %m\n", err); | ||
return; | ||
} | ||
|
||
tmr_cancel(&qualle->to_tmr); | ||
} | ||
|
||
|
||
static void to_handler(void *arg) | ||
{ | ||
struct qualle *qualle = arg; | ||
struct call *call = qualle->call; | ||
uint32_t qual_to = 0; | ||
conf_get_u32(conf_cur(), "qual_to", &qual_to); | ||
|
||
tmr_cancel(&qualle->freq_tmr); | ||
|
||
call_hangup(call, 408, "No response to OPTIONS received"); | ||
|
||
ua_event(call_get_ua(call), UA_EVENT_CALL_CLOSED, call, | ||
"No response recevied to OPTIONS in %u ms.", qual_to); | ||
} | ||
|
||
|
||
static void freq_handler(void *arg) | ||
{ | ||
struct qualle *qualle = arg; | ||
(void)call_start_qualify(qualle->call, call_account(qualle->call), | ||
qualle); | ||
} | ||
|
||
|
||
/** | ||
* Returns -1 if qual_freq or qual_to are zero | ||
* -2 if qual_to is greater than or equal to qual_freq | ||
* 0 on success | ||
* else error code | ||
*/ | ||
static int call_start_qualify(struct call *call, | ||
const struct account *acc, | ||
struct qualle *qualle) | ||
{ | ||
int err; | ||
struct sa peer_addr; | ||
char peer_uri[128]; | ||
uint32_t qual_to = 0; | ||
uint32_t qual_freq = 0; | ||
int newle = qualle == NULL; | ||
|
||
account_extra_uint(acc, "qual_freq", &qual_freq); | ||
account_extra_uint(acc, "qual_to", &qual_to); | ||
|
||
if (!call || !qual_freq || !qual_to) { | ||
return -1; | ||
} | ||
|
||
if ((qual_to / 1000) >= qual_freq) { | ||
warning("Will not send OPTIONS because qualify timeout is " | ||
"greater than or equal to qualify frequency.\n" | ||
"qual_to: %u, qual_freq: %u\n", qual_to, qual_freq); | ||
return -2; | ||
} | ||
|
||
if (newle) { | ||
qualle = mem_zalloc(sizeof(*qualle), qualle_destructor); | ||
if (!qualle) | ||
return ENOMEM; | ||
|
||
qualle->call = call; | ||
tmr_init(&qualle->to_tmr); | ||
tmr_init(&qualle->freq_tmr); | ||
hash_append(q.qual_map, hash_fast_str(account_aor(acc)), | ||
&qualle->he, qualle); | ||
} | ||
|
||
(void)call_msg_src(call, &peer_addr); | ||
err = re_snprintf(peer_uri, sizeof(peer_uri), "sip:%H:%d", | ||
sa_print_addr, &peer_addr, sa_port(&peer_addr)); | ||
|
||
if (err == -1 || err == 0) { | ||
warning("Failed to get peer URI for sending OPTIONS ping. " | ||
"Trying again in %u seconds.\n", err, qual_freq); | ||
tmr_start(&qualle->freq_tmr, qual_freq * 1000, freq_handler, | ||
qualle); | ||
return err; | ||
} | ||
|
||
err = ua_options_send(call_get_ua(call), peer_uri, | ||
options_resp_handler, qualle); | ||
if (err) { | ||
warning("Sending OPTIONS failed with err %d. " | ||
"Trying again in %u seconds.\n", err, qual_freq); | ||
tmr_start(&qualle->freq_tmr, qual_freq * 1000, freq_handler, | ||
qualle); | ||
return err; | ||
} | ||
|
||
tmr_start(&qualle->to_tmr, qual_to, to_handler, qualle); | ||
tmr_start(&qualle->freq_tmr, qual_freq * 1000, freq_handler, qualle); | ||
|
||
return 0; | ||
} | ||
|
||
|
||
static bool qualle_get_applyh(struct le *le, void *arg) | ||
{ | ||
(void)le; | ||
(void)arg; | ||
|
||
return true; | ||
} | ||
|
||
|
||
static void call_stop_qualify(struct account *acc) | ||
{ | ||
struct qualle *qualle; | ||
struct le *le = hash_lookup(q.qual_map, | ||
hash_fast_str(account_aor(acc)), | ||
qualle_get_applyh, NULL); | ||
|
||
if (!le || !le->data) | ||
return; | ||
|
||
qualle = le->data; | ||
mem_deref(qualle); | ||
} | ||
|
||
|
||
static void ua_event_handler(struct ua *ua, enum ua_event ev, | ||
struct call *call, const char *prm, void *arg) | ||
{ | ||
struct account *acc = ua_account(ua); | ||
(void) call; | ||
(void) prm; | ||
(void) arg; | ||
|
||
switch (ev) { | ||
case UA_EVENT_CALL_INCOMING: | ||
(void)call_start_qualify(call, acc, NULL); | ||
break; | ||
case UA_EVENT_CALL_ANSWERED: | ||
if (call_is_outgoing(call)) | ||
break; | ||
|
||
call_stop_qualify(acc); | ||
break; | ||
case UA_EVENT_CALL_CLOSED: | ||
call_stop_qualify(acc); | ||
break; | ||
default: | ||
break; | ||
} | ||
} | ||
|
||
|
||
static int module_init(void) | ||
{ | ||
int err; | ||
|
||
info("qualify: init\n"); | ||
|
||
err = uag_event_register(ua_event_handler, NULL); | ||
err |= hash_alloc(&q.qual_map, 32); | ||
|
||
return err; | ||
} | ||
|
||
|
||
static int module_close(void) | ||
{ | ||
uag_event_unregister(ua_event_handler); | ||
hash_flush(q.qual_map); | ||
mem_deref(q.qual_map); | ||
|
||
return 0; | ||
} | ||
|
||
|
||
const struct mod_export DECL_EXPORTS(qualify) = { | ||
"qualify", | ||
"application", | ||
module_init, | ||
module_close | ||
}; |