res_pjsip_messaging: send message to a default outbound endpoint

In some cases messages need to be sent to a direct URI (sip:<ip address>). This
patch adds in that support by using a default outbound endpoint.  When sending
messages, if no endpoint can be found then the default one is used.

To facilitate this a new default_outbound_endpoint option was added to the
globals section for pjsip.conf.

Review: https://reviewboard.asterisk.org/r/2944/
........

Merged revisions 403680 from http://svn.asterisk.org/svn/asterisk/branches/12


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@403687 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/97/197/1
Kevin Harwell 12 years ago
parent 90108b15a0
commit c602b086ed

@ -603,8 +603,9 @@
;user_agent= ; Value used in User Agent header for SIP requests and Server ;user_agent= ; Value used in User Agent header for SIP requests and Server
; header for SIP responses (default: Populated by Asterisk ; header for SIP responses (default: Populated by Asterisk
; Version) ; Version)
;default_outbound_endpoint= ; Endpoint to use when sending an outbound request
; to a URI without a specified endpoint.
; (default: "default_outbound_endpoint")
; MODULE PROVIDING BELOW SECTION(S): res_pjsip_acl ; MODULE PROVIDING BELOW SECTION(S): res_pjsip_acl

@ -1368,6 +1368,13 @@ struct ast_sip_endpoint *ast_pjsip_rdata_get_endpoint(pjsip_rx_data *rdata);
*/ */
struct ao2_container *ast_sip_get_endpoints(void); struct ao2_container *ast_sip_get_endpoints(void);
/*!
* \brief Retrieve the default outbound endpoint.
*
* \retval The default outbound endpoint, NULL if not found.
*/
struct ast_sip_endpoint *ast_sip_default_outbound_endpoint(void);
/*! /*!
* \brief Retrieve relevant SIP auth structures from sorcery * \brief Retrieve relevant SIP auth structures from sorcery
* *

@ -1025,6 +1025,10 @@
<configOption name="user_agent" default="Asterisk &lt;Asterisk Version&gt;"> <configOption name="user_agent" default="Asterisk &lt;Asterisk Version&gt;">
<synopsis>Value used in User-Agent header for SIP requests and Server header for SIP responses.</synopsis> <synopsis>Value used in User-Agent header for SIP requests and Server header for SIP responses.</synopsis>
</configOption> </configOption>
<configOption name="default_outbound_endpoint" default="default_outbound_endpoint">
<synopsis>Endpoint to use when sending an outbound request to a URI without a specified endpoint.</synopsis>
</configOption>
</configObject> </configObject>
</configFile> </configFile>
</configInfo> </configInfo>

@ -22,11 +22,13 @@
#include <pjlib.h> #include <pjlib.h>
#include "asterisk/res_pjsip.h" #include "asterisk/res_pjsip.h"
#include "include/res_pjsip_private.h"
#include "asterisk/sorcery.h" #include "asterisk/sorcery.h"
#include "asterisk/ast_version.h" #include "asterisk/ast_version.h"
#define DEFAULT_MAX_FORWARDS 70 #define DEFAULT_MAX_FORWARDS 70
#define DEFAULT_USERAGENT_PREFIX "Asterisk PBX" #define DEFAULT_USERAGENT_PREFIX "Asterisk PBX"
#define DEFAULT_OUTBOUND_ENDPOINT "default_outbound_endpoint"
static char default_useragent[128]; static char default_useragent[128];
@ -34,6 +36,7 @@ struct global_config {
SORCERY_OBJECT(details); SORCERY_OBJECT(details);
AST_DECLARE_STRING_FIELDS( AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(useragent); AST_STRING_FIELD(useragent);
AST_STRING_FIELD(default_outbound_endpoint);
); );
/* Value to put in Max-Forwards header */ /* Value to put in Max-Forwards header */
unsigned int max_forwards; unsigned int max_forwards;
@ -70,6 +73,30 @@ static int global_apply(const struct ast_sorcery *sorcery, void *obj)
return 0; return 0;
} }
static struct global_config *get_global_cfg(void)
{
RAII_VAR(struct ao2_container *, globals, ast_sorcery_retrieve_by_fields(
ast_sip_get_sorcery(), "global", AST_RETRIEVE_FLAG_MULTIPLE,
NULL), ao2_cleanup);
if (!globals) {
return NULL;
}
return ao2_find(globals, NULL, 0);
}
char *ast_sip_global_default_outbound_endpoint(void)
{
RAII_VAR(struct global_config *, cfg, get_global_cfg(), ao2_cleanup);
if (!cfg) {
return NULL;
}
return ast_strdup(cfg->default_outbound_endpoint);
}
int ast_sip_initialize_sorcery_global(struct ast_sorcery *sorcery) int ast_sip_initialize_sorcery_global(struct ast_sorcery *sorcery)
{ {
snprintf(default_useragent, sizeof(default_useragent), "%s %s", DEFAULT_USERAGENT_PREFIX, ast_get_version()); snprintf(default_useragent, sizeof(default_useragent), "%s %s", DEFAULT_USERAGENT_PREFIX, ast_get_version());
@ -85,6 +112,8 @@ int ast_sip_initialize_sorcery_global(struct ast_sorcery *sorcery)
OPT_UINT_T, 0, FLDSET(struct global_config, max_forwards)); OPT_UINT_T, 0, FLDSET(struct global_config, max_forwards));
ast_sorcery_object_field_register(sorcery, "global", "user_agent", default_useragent, ast_sorcery_object_field_register(sorcery, "global", "user_agent", default_useragent,
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, useragent)); OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, useragent));
ast_sorcery_object_field_register(sorcery, "global", "default_outbound_endpoint", DEFAULT_OUTBOUND_ENDPOINT,
OPT_STRINGFIELD_T, 0, STRFLDSET(struct global_config, default_outbound_endpoint));
return 0; return 0;
} }

@ -105,4 +105,15 @@ int ast_sip_for_each_channel_snapshot(const struct ast_endpoint_snapshot *endpoi
on_channel_snapshot_t on_channel_snapshot, on_channel_snapshot_t on_channel_snapshot,
void *arg); void *arg);
/*!
* \brief Retrieve the name of the default outbound endpoint.
*
* \note This returns a memory allocated copy of the name that
* needs to be freed by the caller.
*
* \retval The name of the default outbound endpoint.
* \retval NULL if configuration not found.
*/
char *ast_sip_global_default_outbound_endpoint(void);
#endif /* RES_PJSIP_PRIVATE_H_ */ #endif /* RES_PJSIP_PRIVATE_H_ */

@ -1528,6 +1528,13 @@ struct ao2_container *ast_sip_get_endpoints(void)
return endpoints; return endpoints;
} }
struct ast_sip_endpoint *ast_sip_default_outbound_endpoint(void)
{
RAII_VAR(char *, name, ast_sip_global_default_outbound_endpoint(), ast_free);
return ast_strlen_zero(name) ? NULL : ast_sorcery_retrieve_by_id(
sip_sorcery, "endpoint", name);
}
int ast_sip_retrieve_auths(const struct ast_sip_auth_vector *auths, struct ast_sip_auth **out) int ast_sip_retrieve_auths(const struct ast_sip_auth_vector *auths, struct ast_sip_auth **out)
{ {
int i; int i;

@ -80,13 +80,17 @@ static enum pjsip_status_code get_destination(const pjsip_rx_data *rdata, const
*/ */
static enum pjsip_status_code check_content_type(const pjsip_rx_data *rdata) static enum pjsip_status_code check_content_type(const pjsip_rx_data *rdata)
{ {
if (ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type, int res;
"text", if (rdata->msg_info.msg->body && rdata->msg_info.msg->body->len) {
"plain")) { res = ast_sip_is_content_type(
return PJSIP_SC_OK; &rdata->msg_info.msg->body->content_type, "text", "plain");
} else { } else {
return PJSIP_SC_UNSUPPORTED_MEDIA_TYPE; res = rdata->msg_info.ctype &&
!pj_strcmp2(&rdata->msg_info.ctype->media.type, "text") &&
!pj_strcmp2(&rdata->msg_info.ctype->media.subtype, "plain");
} }
return res ? PJSIP_SC_OK : PJSIP_SC_UNSUPPORTED_MEDIA_TYPE;
} }
/*! /*!
@ -96,9 +100,9 @@ static enum pjsip_status_code check_content_type(const pjsip_rx_data *rdata)
* *
* \param fromto 'From' or 'To' field containing 'sip:' * \param fromto 'From' or 'To' field containing 'sip:'
*/ */
static const char* skip_sip(const char *fromto) static char* skip_sip(char *fromto)
{ {
const char *p; char *p;
/* need to be one past 'sip:' or 'sips:' */ /* need to be one past 'sip:' or 'sips:' */
if (!(p = strstr(fromto, "sip"))) { if (!(p = strstr(fromto, "sip"))) {
@ -119,6 +123,7 @@ static const char* skip_sip(const char *fromto)
* Expects the given 'fromto' to be in one of the following formats: * Expects the given 'fromto' to be in one of the following formats:
* sip[s]:endpoint[/aor] * sip[s]:endpoint[/aor]
* sip[s]:endpoint[/uri] * sip[s]:endpoint[/uri]
* sip[s]:uri <-- will use default outbound endpoint
* *
* If an optional aor is given it will try to find an associated uri * If an optional aor is given it will try to find an associated uri
* to return. If an optional uri is given then that will be returned, * to return. If an optional uri is given then that will be returned,
@ -127,30 +132,37 @@ static const char* skip_sip(const char *fromto)
* \param fromto 'From' or 'To' field with possible endpoint * \param fromto 'From' or 'To' field with possible endpoint
* \param uri Optional uri to return * \param uri Optional uri to return
*/ */
static struct ast_sip_endpoint* get_endpoint(const char *fromto, char **uri) static struct ast_sip_endpoint* get_endpoint(char *fromto, char **uri)
{ {
const char *name = skip_sip(fromto); char *name, *aor_uri;
struct ast_sip_endpoint* endpoint; struct ast_sip_endpoint* endpoint;
struct ast_sip_aor *aor; RAII_VAR(struct ast_sip_aor *, aor, NULL, ao2_cleanup);
RAII_VAR(struct ast_sip_contact *, contact, NULL, ao2_cleanup);
if ((*uri = strchr(name, '/'))) { name = skip_sip(fromto);
*(*uri)++ = '\0'; if ((aor_uri = strchr(name, '/'))) {
} else if ((*uri = strchr(name, '@'))) { *aor_uri++ = '\0';
*(*uri) = '\0'; } else if ((aor_uri = strchr(name, '@'))) {
} /* format was endpoint@ */
*aor_uri = '\0';
/* endpoint is required */
if (ast_strlen_zero(name)) {
return NULL;
} }
if (!(endpoint = ast_sorcery_retrieve_by_id( if (ast_strlen_zero(name) || !(endpoint = ast_sorcery_retrieve_by_id(
ast_sip_get_sorcery(), "endpoint", name))) { ast_sip_get_sorcery(), "endpoint", name))) {
return NULL; /* assume sending to direct uri -
use default outbound endpoint */
*uri = ast_strdup(fromto);
return ast_sip_default_outbound_endpoint();
} }
if (*uri && (aor = ast_sip_location_retrieve_aor(*uri))) { *uri = aor_uri;
*uri = (char*)ast_sip_location_retrieve_first_aor_contact(aor)->uri; if (*uri) {
if ((aor = ast_sip_location_retrieve_aor(*uri)) &&
(contact = ast_sip_location_retrieve_first_aor_contact(aor))) {
*uri = (char*)contact->uri;
}
/* need to copy because contact-uri might go away*/
*uri = ast_strdup(*uri);
} }
return endpoint; return endpoint;
@ -163,13 +175,12 @@ static struct ast_sip_endpoint* get_endpoint(const char *fromto, char **uri)
* \param tdata The outgoing message data structure * \param tdata The outgoing message data structure
* \param from Info to potentially copy into the 'From' header * \param from Info to potentially copy into the 'From' header
*/ */
static void update_from(pjsip_tx_data *tdata, const char *from) static void update_from(pjsip_tx_data *tdata, char *from)
{ {
pjsip_name_addr *from_name_addr; pjsip_name_addr *from_name_addr;
pjsip_sip_uri *from_uri; pjsip_sip_uri *from_uri;
pjsip_uri *parsed; pjsip_uri *parsed;
char *uri; RAII_VAR(char *, uri, NULL, ast_free);
RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup);
if (ast_strlen_zero(from)) { if (ast_strlen_zero(from)) {
@ -182,7 +193,20 @@ static void update_from(pjsip_tx_data *tdata, const char *from)
if (ast_strlen_zero(uri)) { if (ast_strlen_zero(uri)) {
/* if no aor/uri was specified get one from the endpoint */ /* if no aor/uri was specified get one from the endpoint */
uri = (char*)ast_sip_location_retrieve_contact_from_aor_list(endpoint->aors)->uri; RAII_VAR(struct ast_sip_contact *, contact,
ast_sip_location_retrieve_contact_from_aor_list(
endpoint->aors), ao2_cleanup);
if (!contact || ast_strlen_zero(contact->uri)) {
ast_log(LOG_WARNING, "No contact found for endpoint %s\n",
ast_sorcery_object_get_id(endpoint));
return;
}
if (uri) {
ast_free(uri);
}
uri = ast_strdup(contact->uri);
} }
/* get current 'from' hdr & uri - going to overwrite some fields */ /* get current 'from' hdr & uri - going to overwrite some fields */
@ -341,6 +365,7 @@ static enum pjsip_status_code vars_to_headers(const struct ast_msg *msg, pjsip_t
static int headers_to_vars(const pjsip_rx_data *rdata, struct ast_msg *msg) static int headers_to_vars(const pjsip_rx_data *rdata, struct ast_msg *msg)
{ {
char *c; char *c;
char name[MAX_HDR_SIZE];
char buf[MAX_HDR_SIZE]; char buf[MAX_HDR_SIZE];
int res = 0; int res = 0;
pjsip_hdr *h = rdata->msg_info.msg->hdr.next; pjsip_hdr *h = rdata->msg_info.msg->hdr.next;
@ -350,10 +375,11 @@ static int headers_to_vars(const pjsip_rx_data *rdata, struct ast_msg *msg)
if ((res = pjsip_hdr_print_on(h, buf, sizeof(buf)-1)) > 0) { if ((res = pjsip_hdr_print_on(h, buf, sizeof(buf)-1)) > 0) {
buf[res] = '\0'; buf[res] = '\0';
if ((c = strchr(buf, ':'))) { if ((c = strchr(buf, ':'))) {
ast_copy_string(buf, ast_skip_blanks(c + 1), sizeof(buf)-(c-buf)); ast_copy_string(buf, ast_skip_blanks(c + 1), sizeof(buf));
} }
if ((res = ast_msg_set_var(msg, pj_strbuf(&h->name), buf)) != 0) { ast_copy_pj_str(name, &h->name, sizeof(name));
if ((res = ast_msg_set_var(msg, name, buf)) != 0) {
break; break;
} }
} }
@ -375,10 +401,14 @@ static int headers_to_vars(const pjsip_rx_data *rdata, struct ast_msg *msg)
*/ */
static int print_body(pjsip_rx_data *rdata, char *buf, int len) static int print_body(pjsip_rx_data *rdata, char *buf, int len)
{ {
int res = rdata->msg_info.msg->body->print_body( int res;
rdata->msg_info.msg->body, buf, len);
if (!rdata->msg_info.msg->body || !rdata->msg_info.msg->body->len) {
return 0;
}
if (res < 0) { if ((res = rdata->msg_info.msg->body->print_body(
rdata->msg_info.msg->body, buf, len)) < 0) {
return res; return res;
} }
@ -500,30 +530,32 @@ static int msg_send(void *data)
}; };
pjsip_tx_data *tdata; pjsip_tx_data *tdata;
char *uri; RAII_VAR(char *, uri, NULL, ast_free);
RAII_VAR(struct ast_sip_endpoint *, endpoint, get_endpoint( RAII_VAR(struct ast_sip_endpoint *, endpoint, get_endpoint(
mdata->to, &uri), ao2_cleanup); mdata->to, &uri), ao2_cleanup);
if (!endpoint) { if (!endpoint) {
ast_log(LOG_ERROR, "SIP MESSAGE - Endpoint not found in %s\n", mdata->to); ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not find endpoint and "
"no default outbound endpoint configured\n");
return -1; return -1;
} }
if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, &tdata)) { if (ast_sip_create_request("MESSAGE", NULL, endpoint, uri, &tdata)) {
ast_log(LOG_ERROR, "SIP MESSAGE - Could not create request\n"); ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not create request\n");
return -1; return -1;
} }
if (ast_sip_add_body(tdata, &body)) { if (ast_sip_add_body(tdata, &body)) {
pjsip_tx_data_dec_ref(tdata); pjsip_tx_data_dec_ref(tdata);
ast_log(LOG_ERROR, "SIP MESSAGE - Could not add body to request\n"); ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not add body to request\n");
return -1; return -1;
} }
update_from(tdata, mdata->from); update_from(tdata, mdata->from);
vars_to_headers(mdata->msg, tdata); vars_to_headers(mdata->msg, tdata);
if (ast_sip_send_request(tdata, NULL, endpoint)) { if (ast_sip_send_request(tdata, NULL, endpoint)) {
ast_log(LOG_ERROR, "SIP MESSAGE - Could not send request\n"); ast_log(LOG_ERROR, "PJSIP MESSAGE - Could not send request\n");
return -1; return -1;
} }

Loading…
Cancel
Save