|
|
|
@ -215,17 +215,41 @@ enum sip_outbound_registration_status {
|
|
|
|
|
SIP_REGISTRATION_REJECTED_TEMPORARY,
|
|
|
|
|
/*! \brief Registration was rejected, permanently */
|
|
|
|
|
SIP_REGISTRATION_REJECTED_PERMANENT,
|
|
|
|
|
/*! \brief Registration is stopping. */
|
|
|
|
|
SIP_REGISTRATION_STOPPING,
|
|
|
|
|
/*! \brief Registration has been stopped */
|
|
|
|
|
SIP_REGISTRATION_STOPPED,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static const char *sip_outbound_registration_status_str[] = {
|
|
|
|
|
[SIP_REGISTRATION_UNREGISTERED] = "Unregistered",
|
|
|
|
|
[SIP_REGISTRATION_REGISTERED] = "Registered",
|
|
|
|
|
[SIP_REGISTRATION_REJECTED_TEMPORARY] = "Rejected",
|
|
|
|
|
[SIP_REGISTRATION_REJECTED_PERMANENT] = "Rejected",
|
|
|
|
|
[SIP_REGISTRATION_STOPPED] = "Stopped",
|
|
|
|
|
};
|
|
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
* \brief Convert the internal registration state to an external status string.
|
|
|
|
|
* \since 13.5.0
|
|
|
|
|
*
|
|
|
|
|
* \param state Current outbound registration state.
|
|
|
|
|
*
|
|
|
|
|
* \return External registration status string.
|
|
|
|
|
*/
|
|
|
|
|
static const char *sip_outbound_registration_status_str(enum sip_outbound_registration_status state)
|
|
|
|
|
{
|
|
|
|
|
const char *str;
|
|
|
|
|
|
|
|
|
|
str = "Unregistered";
|
|
|
|
|
switch (state) {
|
|
|
|
|
case SIP_REGISTRATION_STOPPING:
|
|
|
|
|
case SIP_REGISTRATION_STOPPED:
|
|
|
|
|
case SIP_REGISTRATION_UNREGISTERED:
|
|
|
|
|
break;
|
|
|
|
|
case SIP_REGISTRATION_REGISTERED:
|
|
|
|
|
str = "Registered";
|
|
|
|
|
break;
|
|
|
|
|
case SIP_REGISTRATION_REJECTED_TEMPORARY:
|
|
|
|
|
case SIP_REGISTRATION_REJECTED_PERMANENT:
|
|
|
|
|
str = "Rejected";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Outbound registration information */
|
|
|
|
|
struct sip_outbound_registration {
|
|
|
|
@ -266,9 +290,14 @@ struct sip_outbound_registration {
|
|
|
|
|
|
|
|
|
|
/*! \brief Outbound registration client state information (persists for lifetime of regc) */
|
|
|
|
|
struct sip_outbound_registration_client_state {
|
|
|
|
|
/*! \brief Current status of this registration */
|
|
|
|
|
/*! \brief Current state of this registration */
|
|
|
|
|
enum sip_outbound_registration_status status;
|
|
|
|
|
/*! \brief Outbound registration client */
|
|
|
|
|
/*!
|
|
|
|
|
* \brief Outbound registration client
|
|
|
|
|
* \note May only be accessed within the serializer thread
|
|
|
|
|
* because it might get destroyed and set to NULL for
|
|
|
|
|
* module unload.
|
|
|
|
|
*/
|
|
|
|
|
pjsip_regc *client;
|
|
|
|
|
/*! \brief Timer entry for retrying on temporal responses */
|
|
|
|
|
pj_timer_entry timer;
|
|
|
|
@ -488,8 +517,8 @@ static int handle_client_registration(void *data)
|
|
|
|
|
pjsip_regc_get_info(client_state->client, &info);
|
|
|
|
|
ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
|
|
|
|
|
ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
|
|
|
|
|
ast_debug(3, "REGISTER attempt %u to '%s' with client '%s'\n",
|
|
|
|
|
client_state->retries + 1, server_uri, client_uri);
|
|
|
|
|
ast_debug(1, "Outbound REGISTER attempt %u to '%s' with client '%s'\n",
|
|
|
|
|
client_state->retries + 1, server_uri, client_uri);
|
|
|
|
|
|
|
|
|
|
if (client_state->support_path) {
|
|
|
|
|
pjsip_supported_hdr *hdr;
|
|
|
|
@ -518,23 +547,15 @@ static int handle_client_registration(void *data)
|
|
|
|
|
static void sip_outbound_registration_timer_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
|
|
|
|
|
{
|
|
|
|
|
struct sip_outbound_registration_client_state *client_state = entry->user_data;
|
|
|
|
|
pjsip_regc_info info;
|
|
|
|
|
|
|
|
|
|
pjsip_regc_get_info(client_state->client, &info);
|
|
|
|
|
ast_debug(1, "Attempting scheduled outbound registration attempt to server '%.*s' from client '%.*s'\n",
|
|
|
|
|
(int) info.server_uri.slen, info.server_uri.ptr,
|
|
|
|
|
(int) info.client_uri.slen, info.client_uri.ptr);
|
|
|
|
|
entry->id = 0;
|
|
|
|
|
|
|
|
|
|
ao2_ref(client_state, +1);
|
|
|
|
|
if (ast_sip_push_task(client_state->serializer, handle_client_registration, client_state)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Scheduled outbound registration to server '%.*s' from client '%.*s' could not be executed\n",
|
|
|
|
|
(int) info.server_uri.slen, info.server_uri.ptr,
|
|
|
|
|
(int) info.client_uri.slen, info.client_uri.ptr);
|
|
|
|
|
ast_log(LOG_WARNING, "Scheduled outbound registration could not be executed.\n");
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
}
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
|
|
|
|
|
entry->id = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Helper function which sets up the timer to re-register in a specific amount of time */
|
|
|
|
@ -563,35 +584,58 @@ static void schedule_registration(struct sip_outbound_registration_client_state
|
|
|
|
|
/*! \brief Callback function for unregistering (potentially) and destroying state */
|
|
|
|
|
static int handle_client_state_destruction(void *data)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct sip_outbound_registration_client_state *, client_state, data, ao2_cleanup);
|
|
|
|
|
struct sip_outbound_registration_client_state *client_state = data;
|
|
|
|
|
|
|
|
|
|
cancel_registration(client_state);
|
|
|
|
|
|
|
|
|
|
if (client_state->client) {
|
|
|
|
|
pjsip_regc_info info;
|
|
|
|
|
pjsip_tx_data *tdata;
|
|
|
|
|
|
|
|
|
|
pjsip_regc_get_info(client_state->client, &info);
|
|
|
|
|
|
|
|
|
|
if (info.is_busy == PJ_TRUE) {
|
|
|
|
|
/* If a client transaction is in progress we defer until it is complete */
|
|
|
|
|
ast_debug(1,
|
|
|
|
|
"Registration transaction is busy with server '%.*s' from client '%.*s'.\n",
|
|
|
|
|
(int) info.server_uri.slen, info.server_uri.ptr,
|
|
|
|
|
(int) info.client_uri.slen, info.client_uri.ptr);
|
|
|
|
|
client_state->destroy = 1;
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (client_state->status != SIP_REGISTRATION_UNREGISTERED
|
|
|
|
|
&& client_state->status != SIP_REGISTRATION_REJECTED_PERMANENT) {
|
|
|
|
|
pjsip_tx_data *tdata;
|
|
|
|
|
switch (client_state->status) {
|
|
|
|
|
case SIP_REGISTRATION_UNREGISTERED:
|
|
|
|
|
break;
|
|
|
|
|
case SIP_REGISTRATION_REGISTERED:
|
|
|
|
|
ast_debug(1,
|
|
|
|
|
"Trying to unregister with server '%.*s' from client '%.*s' before destruction.\n",
|
|
|
|
|
(int) info.server_uri.slen, info.server_uri.ptr,
|
|
|
|
|
(int) info.client_uri.slen, info.client_uri.ptr);
|
|
|
|
|
|
|
|
|
|
if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS) {
|
|
|
|
|
pjsip_regc_send(client_state->client, tdata);
|
|
|
|
|
client_state->status = SIP_REGISTRATION_STOPPING;
|
|
|
|
|
client_state->destroy = 1;
|
|
|
|
|
if (pjsip_regc_unregister(client_state->client, &tdata) == PJ_SUCCESS
|
|
|
|
|
&& registration_client_send(client_state, tdata) == PJ_SUCCESS) {
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SIP_REGISTRATION_REJECTED_TEMPORARY:
|
|
|
|
|
case SIP_REGISTRATION_REJECTED_PERMANENT:
|
|
|
|
|
case SIP_REGISTRATION_STOPPING:
|
|
|
|
|
case SIP_REGISTRATION_STOPPED:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pjsip_regc_destroy(client_state->client);
|
|
|
|
|
client_state->client = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client_state->status = SIP_REGISTRATION_STOPPED;
|
|
|
|
|
ast_sip_auth_vector_destroy(&client_state->outbound_auths);
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
@ -668,21 +712,20 @@ static void schedule_retry(struct registration_response *response, unsigned int
|
|
|
|
|
/*! \brief Callback function for handling a response to a registration attempt */
|
|
|
|
|
static int handle_registration_response(void *data)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct registration_response *, response, data, ao2_cleanup);
|
|
|
|
|
struct registration_response *response = data;
|
|
|
|
|
pjsip_regc_info info;
|
|
|
|
|
char server_uri[PJSIP_MAX_URL_SIZE];
|
|
|
|
|
char client_uri[PJSIP_MAX_URL_SIZE];
|
|
|
|
|
|
|
|
|
|
pjsip_regc_get_info(response->client_state->client, &info);
|
|
|
|
|
ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
|
|
|
|
|
ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
|
|
|
|
|
|
|
|
|
|
if (response->client_state->status == SIP_REGISTRATION_STOPPED) {
|
|
|
|
|
ast_debug(1, "Not handling registration response from server '%s' for client '%s'. Registration already stopped\n",
|
|
|
|
|
server_uri, client_uri);
|
|
|
|
|
ao2_ref(response, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pjsip_regc_get_info(response->client_state->client, &info);
|
|
|
|
|
ast_copy_pj_str(server_uri, &info.server_uri, sizeof(server_uri));
|
|
|
|
|
ast_copy_pj_str(client_uri, &info.client_uri, sizeof(client_uri));
|
|
|
|
|
|
|
|
|
|
ast_debug(1, "Processing REGISTER response %d from server '%s' for client '%s'\n",
|
|
|
|
|
response->code, server_uri, client_uri);
|
|
|
|
|
|
|
|
|
@ -694,10 +737,10 @@ static int handle_registration_response(void *data)
|
|
|
|
|
response->client_state->auth_attempted = 1;
|
|
|
|
|
ast_debug(1, "Sending authenticated REGISTER to server '%s' from client '%s'\n",
|
|
|
|
|
server_uri, client_uri);
|
|
|
|
|
if (registration_client_send(response->client_state, tdata) != PJ_SUCCESS) {
|
|
|
|
|
response->client_state->auth_attempted = 0;
|
|
|
|
|
if (registration_client_send(response->client_state, tdata) == PJ_SUCCESS) {
|
|
|
|
|
ao2_ref(response, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_WARNING, "Failed to create authenticated REGISTER request to server '%s' from client '%s'\n",
|
|
|
|
|
server_uri, client_uri);
|
|
|
|
@ -719,6 +762,8 @@ static int handle_registration_response(void *data)
|
|
|
|
|
ast_debug(1, "Outbound unregistration to '%s' with client '%s' successful\n", server_uri, client_uri);
|
|
|
|
|
response->client_state->status = SIP_REGISTRATION_UNREGISTERED;
|
|
|
|
|
}
|
|
|
|
|
} else if (response->client_state->destroy) {
|
|
|
|
|
/* We need to deal with the pending destruction instead. */
|
|
|
|
|
} else if (response->retry_after) {
|
|
|
|
|
/* If we have been instructed to retry after a period of time, schedule it as such */
|
|
|
|
|
schedule_retry(response, response->retry_after, server_uri, client_uri);
|
|
|
|
@ -757,22 +802,23 @@ static int handle_registration_response(void *data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_system_publish_registry("PJSIP", client_uri, server_uri,
|
|
|
|
|
sip_outbound_registration_status_str[response->client_state->status], NULL);
|
|
|
|
|
sip_outbound_registration_status_str(response->client_state->status), NULL);
|
|
|
|
|
|
|
|
|
|
/* If deferred destruction is in use see if we need to destroy now */
|
|
|
|
|
if (response->client_state->destroy) {
|
|
|
|
|
/* We have a pending deferred destruction to complete now. */
|
|
|
|
|
ao2_ref(response->client_state, +1);
|
|
|
|
|
handle_client_state_destruction(response->client_state);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ao2_ref(response, -1);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*! \brief Callback function for outbound registration client */
|
|
|
|
|
static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *param)
|
|
|
|
|
{
|
|
|
|
|
RAII_VAR(struct sip_outbound_registration_client_state *, client_state, param->token, ao2_cleanup);
|
|
|
|
|
struct sip_outbound_registration_client_state *client_state = param->token;
|
|
|
|
|
struct registration_response *response;
|
|
|
|
|
pjsip_regc_info info;
|
|
|
|
|
int *callback_invoked;
|
|
|
|
|
|
|
|
|
|
callback_invoked = ast_threadstorage_get(®ister_callback_invoked, sizeof(int));
|
|
|
|
@ -784,18 +830,16 @@ static void sip_outbound_registration_response_cb(struct pjsip_regc_cbparam *par
|
|
|
|
|
|
|
|
|
|
response = ao2_alloc(sizeof(*response), registration_response_destroy);
|
|
|
|
|
if (!response) {
|
|
|
|
|
ao2_ref(client_state, -1);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
response->code = param->code;
|
|
|
|
|
response->expiration = param->expiration;
|
|
|
|
|
/* Transfer client_state reference to response. */
|
|
|
|
|
response->client_state = client_state;
|
|
|
|
|
ao2_ref(response->client_state, +1);
|
|
|
|
|
|
|
|
|
|
pjsip_regc_get_info(client_state->client, &info);
|
|
|
|
|
ast_debug(1, "Received REGISTER response %d(%.*s) from server '%.*s' for client '%.*s\n",
|
|
|
|
|
param->code, (int) param->reason.slen, param->reason.ptr,
|
|
|
|
|
(int) info.server_uri.slen, info.server_uri.ptr,
|
|
|
|
|
(int) info.client_uri.slen, info.client_uri.ptr);
|
|
|
|
|
ast_debug(1, "Received REGISTER response %d(%.*s)\n",
|
|
|
|
|
param->code, (int) param->reason.slen, param->reason.ptr);
|
|
|
|
|
|
|
|
|
|
if (param->rdata) {
|
|
|
|
|
struct pjsip_retry_after_hdr *retry_after;
|
|
|
|
@ -1053,8 +1097,8 @@ static int sip_outbound_registration_regc_alloc(void *data)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!state->client_state->client
|
|
|
|
|
&& pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state,
|
|
|
|
|
ast_assert(state->client_state->client == NULL);
|
|
|
|
|
if (pjsip_regc_create(ast_sip_get_pjsip_endpoint(), state->client_state,
|
|
|
|
|
sip_outbound_registration_response_cb,
|
|
|
|
|
&state->client_state->client) != PJ_SUCCESS) {
|
|
|
|
|
return -1;
|
|
|
|
@ -1493,7 +1537,7 @@ static int ami_outbound_registration_task(void *obj)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ast_str_append(&buf, 0, "Status: %s\r\n",
|
|
|
|
|
sip_outbound_registration_status_str[state->client_state->status]);
|
|
|
|
|
sip_outbound_registration_status_str(state->client_state->status));
|
|
|
|
|
|
|
|
|
|
pjsip_regc_get_info(state->client_state->client, &info);
|
|
|
|
|
ast_str_append(&buf, 0, "NextReg: %d\r\n", info.next_reg);
|
|
|
|
@ -1628,7 +1672,7 @@ static int cli_print_body(void *obj, void *arg, int flags)
|
|
|
|
|
AST_VECTOR_SIZE(®istration->outbound_auths)
|
|
|
|
|
? AST_VECTOR_GET(®istration->outbound_auths, 0)
|
|
|
|
|
: "n/a",
|
|
|
|
|
sip_outbound_registration_status_str[state->client_state->status]);
|
|
|
|
|
sip_outbound_registration_status_str(state->client_state->status));
|
|
|
|
|
ao2_ref(state, -1);
|
|
|
|
|
|
|
|
|
|
if (context->show_details
|
|
|
|
|