diff options
Diffstat (limited to 'res')
-rw-r--r-- | res/res_pjsip.c | 16 | ||||
-rw-r--r-- | res/res_pjsip/location.c | 59 | ||||
-rw-r--r-- | res/res_pjsip/pjsip_configuration.c | 2 | ||||
-rw-r--r-- | res/res_pjsip_registrar.c | 107 | ||||
-rw-r--r-- | res/res_pjsip_transport_websocket.c | 16 |
5 files changed, 183 insertions, 17 deletions
diff --git a/res/res_pjsip.c b/res/res_pjsip.c index 2917df3c5..ca0c30126 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -367,9 +367,12 @@ <configOption name="rewrite_contact"> <synopsis>Allow Contact header to be rewritten with the source IP address-port</synopsis> <description><para> - On inbound SIP messages from this endpoint, the Contact header or an appropriate Record-Route - header will be changed to have the source IP address and port. This option does not affect - outbound messages sent to this endpoint. + On inbound SIP messages from this endpoint, the Contact header or an + appropriate Record-Route header will be changed to have the source IP + address and port. This option does not affect outbound messages sent to + this endpoint. This option helps servers communicate with endpoints + that are behind NATs. This option also helps reuse reliable transport + connections such as TCP and TLS. </para></description> </configOption> <configOption name="rtp_ipv6" default="no"> @@ -1364,6 +1367,13 @@ in incoming SIP REGISTER requests and is not intended to be configured manually. </para></description> </configOption> + <configOption name="prune_on_boot"> + <synopsis>A contact that cannot survive a restart/boot.</synopsis> + <description><para> + The option is set if the incoming SIP REGISTER contact is rewritten + on a reliable transport and is not intended to be configured manually. + </para></description> + </configOption> </configObject> <configObject name="aor"> <synopsis>The configuration for a location of an endpoint</synopsis> diff --git a/res/res_pjsip/location.c b/res/res_pjsip/location.c index 6213046e3..557aeb6b9 100644 --- a/res/res_pjsip/location.c +++ b/res/res_pjsip/location.c @@ -356,13 +356,12 @@ struct ast_sip_contact *ast_sip_location_retrieve_contact(const char *contact_na return ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "contact", contact_name); } -int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri, - struct timeval expiration_time, const char *path_info, const char *user_agent, - const char *via_addr, int via_port, const char *call_id, - struct ast_sip_endpoint *endpoint) +struct ast_sip_contact *ast_sip_location_create_contact(struct ast_sip_aor *aor, + const char *uri, struct timeval expiration_time, const char *path_info, + const char *user_agent, const char *via_addr, int via_port, const char *call_id, + int prune_on_boot, struct ast_sip_endpoint *endpoint) { struct ast_sip_contact *contact; - int res; char name[MAX_OBJECT_FIELD * 2 + 3]; char hash[33]; @@ -371,7 +370,7 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", name); if (!contact) { - return -1; + return NULL; } ast_string_field_set(contact, uri, uri); @@ -405,14 +404,30 @@ int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri } contact->endpoint = ao2_bump(endpoint); - if (endpoint) { ast_string_field_set(contact, endpoint_name, ast_sorcery_object_get_id(endpoint)); } - res = ast_sorcery_create(ast_sip_get_sorcery(), contact); - ao2_ref(contact, -1); - return res; + contact->prune_on_boot = prune_on_boot; + + if (ast_sorcery_create(ast_sip_get_sorcery(), contact)) { + ao2_ref(contact, -1); + return NULL; + } + return contact; +} + +int ast_sip_location_add_contact_nolock(struct ast_sip_aor *aor, const char *uri, + struct timeval expiration_time, const char *path_info, const char *user_agent, + const char *via_addr, int via_port, const char *call_id, + struct ast_sip_endpoint *endpoint) +{ + struct ast_sip_contact *contact; + + contact = ast_sip_location_create_contact(aor, uri, expiration_time, path_info, + user_agent, via_addr, via_port, call_id, 0, endpoint); + ao2_cleanup(contact); + return contact ? 0 : -1; } int ast_sip_location_add_contact(struct ast_sip_aor *aor, const char *uri, @@ -441,6 +456,29 @@ int ast_sip_location_delete_contact(struct ast_sip_contact *contact) return ast_sorcery_delete(ast_sip_get_sorcery(), contact); } +static int prune_boot_contacts_cb(void *obj, void *arg, int flags) +{ + struct ast_sip_contact *contact = obj; + + if (contact->prune_on_boot) { + ast_sip_location_delete_contact(contact); + } + + return 0; +} + +void ast_sip_location_prune_boot_contacts(void) +{ + struct ao2_container *contacts; + + contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "contact", + AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); + if (contacts) { + ao2_callback(contacts, 0, prune_boot_contacts_cb, NULL); + ao2_ref(contacts, -1); + } +} + /*! \brief Custom handler for translating from a string timeval to actual structure */ static int expiration_str2struct(const struct aco_option *opt, struct ast_variable *var, void *obj) { @@ -1221,6 +1259,7 @@ int ast_sip_initialize_sorcery_location(void) ast_sorcery_object_field_register(sorcery, "contact", "via_addr", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, via_addr)); ast_sorcery_object_field_register(sorcery, "contact", "via_port", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_contact, via_port)); ast_sorcery_object_field_register(sorcery, "contact", "call_id", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_contact, call_id)); + ast_sorcery_object_field_register(sorcery, "contact", "prune_on_boot", "no", OPT_YESNO_T, 1, FLDSET(struct ast_sip_contact, prune_on_boot)); ast_sorcery_object_field_register(sorcery, "aor", "type", "", OPT_NOOP_T, 0, 0); ast_sorcery_object_field_register(sorcery, "aor", "minimum_expiration", "60", OPT_UINT_T, 0, FLDSET(struct ast_sip_aor, minimum_expiration)); diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index d3ff1f32b..715ffe8eb 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -2057,6 +2057,8 @@ int ast_res_pjsip_initialize_configuration(void) load_all_endpoints(); + ast_sip_location_prune_boot_contacts(); + return 0; } diff --git a/res/res_pjsip_registrar.c b/res/res_pjsip_registrar.c index a4ce54769..ba1c074b3 100644 --- a/res/res_pjsip_registrar.c +++ b/res/res_pjsip_registrar.c @@ -310,6 +310,47 @@ static int registrar_validate_path(pjsip_rx_data *rdata, struct ast_sip_aor *aor return -1; } +/*! Transport monitor for incoming REGISTER contacts */ +struct contact_transport_monitor { + /*! + * \brief Sorcery contact name to remove on transport shutdown + * \note Stored after aor_name in space reserved when struct allocated. + */ + char *contact_name; + /*! AOR name the contact is associated */ + char aor_name[0]; +}; + +static void register_contact_transport_shutdown_cb(void *data) +{ + struct contact_transport_monitor *monitor = data; + struct ast_sip_contact *contact; + struct ast_sip_aor *aor; + + aor = ast_sip_location_retrieve_aor(monitor->aor_name); + if (!aor) { + return; + } + + ao2_lock(aor); + contact = ast_sip_location_retrieve_contact(monitor->contact_name); + if (contact) { + ast_sip_location_delete_contact(contact); + ast_verb(3, "Removed contact '%s' from AOR '%s' due to transport shutdown\n", + contact->uri, monitor->aor_name); + ast_test_suite_event_notify("AOR_CONTACT_REMOVED", + "Contact: %s\r\n" + "AOR: %s\r\n" + "UserAgent: %s", + contact->uri, + monitor->aor_name, + contact->user_agent); + ao2_ref(contact, -1); + } + ao2_unlock(aor); + ao2_ref(aor, -1); +} + static int register_aor_core(pjsip_rx_data *rdata, struct ast_sip_endpoint *endpoint, struct ast_sip_aor *aor, @@ -419,6 +460,9 @@ static int register_aor_core(pjsip_rx_data *rdata, pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, details.uri, contact_uri, sizeof(contact_uri)); if (!(contact = ao2_callback(contacts, OBJ_UNLINK, registrar_find_contact, &details))) { + int prune_on_boot = 0; + pj_str_t host_name; + /* If they are actually trying to delete a contact that does not exist... be forgiving */ if (!expiration) { ast_verb(3, "Attempted to remove non-existent contact '%s' from AOR '%s' by request\n", @@ -426,14 +470,68 @@ static int register_aor_core(pjsip_rx_data *rdata, continue; } - if (ast_sip_location_add_contact_nolock(aor, contact_uri, ast_tvadd(ast_tvnow(), - ast_samp2tv(expiration, 1)), path_str ? ast_str_buffer(path_str) : NULL, - user_agent, via_addr, via_port, call_id, endpoint)) { + /* Determine if the contact cannot survive a restart/boot. */ + if (details.uri->port == rdata->pkt_info.src_port + && !pj_strcmp(&details.uri->host, + pj_cstr(&host_name, rdata->pkt_info.src_name)) + /* We have already checked if the URI scheme is sip: or sips: */ + && PJSIP_TRANSPORT_IS_RELIABLE(rdata->tp_info.transport)) { + pj_str_t type_name; + + /* Determine the transport parameter value */ + if (!strcasecmp("WSS", rdata->tp_info.transport->type_name)) { + /* WSS is special, as it needs to be ws. */ + pj_cstr(&type_name, "ws"); + } else { + pj_cstr(&type_name, rdata->tp_info.transport->type_name); + } + + if (!pj_stricmp(&details.uri->transport_param, &type_name) + && (endpoint->nat.rewrite_contact + /* Websockets are always rewritten */ + || !pj_stricmp(&details.uri->transport_param, + pj_cstr(&type_name, "ws")))) { + /* + * The contact was rewritten to the reliable transport's + * source address. Disconnecting the transport for any + * reason invalidates the contact. + */ + prune_on_boot = 1; + } + } + + contact = ast_sip_location_create_contact(aor, contact_uri, + ast_tvadd(ast_tvnow(), ast_samp2tv(expiration, 1)), + path_str ? ast_str_buffer(path_str) : NULL, + user_agent, via_addr, via_port, call_id, prune_on_boot, endpoint); + if (!contact) { ast_log(LOG_ERROR, "Unable to bind contact '%s' to AOR '%s'\n", - contact_uri, aor_name); + contact_uri, aor_name); continue; } + if (prune_on_boot) { + const char *contact_name; + struct contact_transport_monitor *monitor; + + /* + * Monitor the transport in case it gets disconnected because + * the contact won't be valid anymore if that happens. + */ + contact_name = ast_sorcery_object_get_id(contact); + monitor = ao2_alloc_options(sizeof(*monitor) + 2 + strlen(aor_name) + + strlen(contact_name), NULL, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (monitor) { + strcpy(monitor->aor_name, aor_name);/* Safe */ + monitor->contact_name = monitor->aor_name + strlen(aor_name) + 1; + strcpy(monitor->contact_name, contact_name);/* Safe */ + + ast_sip_transport_monitor_register(rdata->tp_info.transport, + register_contact_transport_shutdown_cb, monitor); + ao2_ref(monitor, -1); + } + } + ast_verb(3, "Added contact '%s' to AOR '%s' with expiration of %d seconds\n", contact_uri, aor_name, expiration); ast_test_suite_event_notify("AOR_CONTACT_ADDED", @@ -885,6 +983,7 @@ static int unload_module(void) ast_manager_unregister(AMI_SHOW_REGISTRATIONS); ast_manager_unregister(AMI_SHOW_REGISTRATION_CONTACT_STATUSES); ast_sip_unregister_service(®istrar_module); + ast_sip_transport_monitor_unregister_all(register_contact_transport_shutdown_cb); return 0; } diff --git a/res/res_pjsip_transport_websocket.c b/res/res_pjsip_transport_websocket.c index 1429cceed..22ec19540 100644 --- a/res/res_pjsip_transport_websocket.c +++ b/res/res_pjsip_transport_websocket.c @@ -145,6 +145,7 @@ static int transport_create(void *data) { struct transport_create_data *create_data = data; struct ws_transport *newtransport = NULL; + pjsip_tp_state_callback state_cb; pjsip_endpoint *endpt = ast_sip_get_pjsip_endpoint(); struct pjsip_tpmgr *tpmgr = pjsip_endpt_get_tpmgr(endpt); @@ -161,6 +162,10 @@ static int transport_create(void *data) goto on_error; } + /* Give websocket transport a unique name for its lifetime */ + snprintf(newtransport->transport.obj_name, PJ_MAX_OBJ_NAME, "ws%p", + &newtransport->transport); + newtransport->transport.endpt = endpt; if (!(pool = pjsip_endpt_create_pool(endpt, "ws", 512, 512))) { @@ -219,6 +224,7 @@ static int transport_create(void *data) newtransport->transport.flag = pjsip_transport_get_flag_from_type((pjsip_transport_type_e)newtransport->transport.key.type); newtransport->transport.info = (char *)pj_pool_alloc(newtransport->transport.pool, 64); + newtransport->transport.dir = PJSIP_TP_DIR_INCOMING; newtransport->transport.tpmgr = tpmgr; newtransport->transport.send_msg = &ws_send_msg; newtransport->transport.destroy = &ws_destroy; @@ -242,6 +248,16 @@ static int transport_create(void *data) } create_data->transport = newtransport; + + /* Notify application of transport state */ + state_cb = pjsip_tpmgr_get_state_cb(newtransport->transport.tpmgr); + if (state_cb) { + pjsip_transport_state_info state_info; + + memset(&state_info, 0, sizeof(state_info)); + state_cb(&newtransport->transport, PJSIP_TP_STATE_CONNECTED, &state_info); + } + return 0; on_error: |