diff --git a/configs/pjsip.conf.sample b/configs/pjsip.conf.sample index cd6deae5bef3db7ca2e115fe9c127a5ac5845bc4..5eb5335dae531e262ef492eed374fe444b97682f 100644 --- a/configs/pjsip.conf.sample +++ b/configs/pjsip.conf.sample @@ -603,8 +603,9 @@ ;user_agent= ; Value used in User Agent header for SIP requests and Server ; header for SIP responses (default: Populated by Asterisk ; 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 diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index d34005d621dc38c835c3fbfa2418fcd1c534bda5..18d1aafff29c66d28a8615ee49baebed7b44576d 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -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); +/*! + * \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 * diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 52a949257567076d1232231b522d91d19af43d28..08c4552429dcce684feeb23871c2ea55925e6b1c 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -1025,6 +1025,10 @@ <configOption name="user_agent" default="Asterisk <Asterisk Version>"> <synopsis>Value used in User-Agent header for SIP requests and Server header for SIP responses.</synopsis> </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> </configFile> </configInfo> diff --git a/res/res_pjsip/config_global.c b/res/res_pjsip/config_global.c index d883e5d4724c4bb173403324462f7b7b0395b2fb..1ae3e15771216d92c6c410b8e6440519435fd169 100644 --- a/res/res_pjsip/config_global.c +++ b/res/res_pjsip/config_global.c @@ -22,11 +22,13 @@ #include <pjlib.h> #include "asterisk/res_pjsip.h" +#include "include/res_pjsip_private.h" #include "asterisk/sorcery.h" #include "asterisk/ast_version.h" #define DEFAULT_MAX_FORWARDS 70 #define DEFAULT_USERAGENT_PREFIX "Asterisk PBX" +#define DEFAULT_OUTBOUND_ENDPOINT "default_outbound_endpoint" static char default_useragent[128]; @@ -34,6 +36,7 @@ struct global_config { SORCERY_OBJECT(details); AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(useragent); + AST_STRING_FIELD(default_outbound_endpoint); ); /* Value to put in Max-Forwards header */ unsigned int max_forwards; @@ -70,6 +73,30 @@ static int global_apply(const struct ast_sorcery *sorcery, void *obj) 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) { 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)); ast_sorcery_object_field_register(sorcery, "global", "user_agent", default_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; } diff --git a/res/res_pjsip/include/res_pjsip_private.h b/res/res_pjsip/include/res_pjsip_private.h index 0ee62529f603ffed2d9fafff2cac08b1b1e99803..368bdd04a6fe9f353182864d85150fb4289fb22c 100644 --- a/res/res_pjsip/include/res_pjsip_private.h +++ b/res/res_pjsip/include/res_pjsip_private.h @@ -105,4 +105,15 @@ int ast_sip_for_each_channel_snapshot(const struct ast_endpoint_snapshot *endpoi on_channel_snapshot_t on_channel_snapshot, 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_ */ diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 3b9bfd6a7ae94d212517d7456d11a65435cf944d..010eeb6970a4ea09ba145f61051e0bcb9ad0864f 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -1528,6 +1528,13 @@ struct ao2_container *ast_sip_get_endpoints(void) 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 i; diff --git a/res/res_pjsip_messaging.c b/res/res_pjsip_messaging.c index b66ee0b113b1e2c77e24d0993a7f7e9e663c9c3e..00923b23bad5a6ae249ab919e1ef3625a6245da7 100644 --- a/res/res_pjsip_messaging.c +++ b/res/res_pjsip_messaging.c @@ -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) { - if (ast_sip_is_content_type(&rdata->msg_info.msg->body->content_type, - "text", - "plain")) { - return PJSIP_SC_OK; + int res; + if (rdata->msg_info.msg->body && rdata->msg_info.msg->body->len) { + res = ast_sip_is_content_type( + &rdata->msg_info.msg->body->content_type, "text", "plain"); } 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:' */ -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:' */ 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: * sip[s]:endpoint[/aor] * 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 * 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 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_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, '/'))) { - *(*uri)++ = '\0'; - } else if ((*uri = strchr(name, '@'))) { - *(*uri) = '\0'; - } - - /* endpoint is required */ - if (ast_strlen_zero(name)) { - return NULL; + name = skip_sip(fromto); + if ((aor_uri = strchr(name, '/'))) { + *aor_uri++ = '\0'; + } else if ((aor_uri = strchr(name, '@'))) { + /* format was endpoint@ */ + *aor_uri = '\0'; } - if (!(endpoint = ast_sorcery_retrieve_by_id( + if (ast_strlen_zero(name) || !(endpoint = ast_sorcery_retrieve_by_id( 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 = (char*)ast_sip_location_retrieve_first_aor_contact(aor)->uri; + *uri = 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; @@ -163,13 +175,12 @@ static struct ast_sip_endpoint* get_endpoint(const char *fromto, char **uri) * \param tdata The outgoing message data structure * \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_sip_uri *from_uri; pjsip_uri *parsed; - char *uri; - + RAII_VAR(char *, uri, NULL, ast_free); RAII_VAR(struct ast_sip_endpoint *, endpoint, NULL, ao2_cleanup); 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 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 */ @@ -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) { char *c; + char name[MAX_HDR_SIZE]; char buf[MAX_HDR_SIZE]; int res = 0; 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) { buf[res] = '\0'; 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; } } @@ -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) { - int res = rdata->msg_info.msg->body->print_body( - rdata->msg_info.msg->body, buf, len); + int res; + + 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; } @@ -500,30 +530,32 @@ static int msg_send(void *data) }; pjsip_tx_data *tdata; - char *uri; - + RAII_VAR(char *, uri, NULL, ast_free); RAII_VAR(struct ast_sip_endpoint *, endpoint, get_endpoint( mdata->to, &uri), ao2_cleanup); + 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; } 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; } if (ast_sip_add_body(tdata, &body)) { 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; } update_from(tdata, mdata->from); vars_to_headers(mdata->msg, tdata); + 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; }