diff options
Diffstat (limited to 'res/res_pjsip/pjsip_options.c')
-rw-r--r-- | res/res_pjsip/pjsip_options.c | 209 |
1 files changed, 182 insertions, 27 deletions
diff --git a/res/res_pjsip/pjsip_options.c b/res/res_pjsip/pjsip_options.c index 70bbaf908..6f7455d50 100644 --- a/res/res_pjsip/pjsip_options.c +++ b/res/res_pjsip/pjsip_options.c @@ -43,7 +43,6 @@ static const char *status_map [] = { [UNKNOWN] = "Unknown", [CREATED] = "Created", [REMOVED] = "Removed", - [UPDATED] = "Updated", }; static const char *short_status_map [] = { @@ -52,7 +51,6 @@ static const char *short_status_map [] = { [UNKNOWN] = "Unknown", [CREATED] = "Created", [REMOVED] = "Removed", - [UPDATED] = "Updated", }; const char *ast_sip_get_contact_status_label(const enum ast_sip_contact_status_type status) @@ -160,7 +158,7 @@ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const * \brief Update an ast_sip_contact_status's elements. */ static void update_contact_status(const struct ast_sip_contact *contact, - enum ast_sip_contact_status_type value) + enum ast_sip_contact_status_type value, int is_contact_refresh) { RAII_VAR(struct ast_sip_contact_status *, status, NULL, ao2_cleanup); RAII_VAR(struct ast_sip_contact_status *, update, NULL, ao2_cleanup); @@ -172,6 +170,26 @@ static void update_contact_status(const struct ast_sip_contact *contact, return; } + if (is_contact_refresh + && status->status == CREATED) { + /* + * The contact status hasn't been updated since creation + * and we don't want to re-send a created status. + */ + if (contact->qualify_frequency + || status->rtt_start.tv_sec > 0) { + /* Ignore, the status will change soon. */ + return; + } + + /* + * Convert to a regular contact status update + * because the status may never change. + */ + is_contact_refresh = 0; + value = UNKNOWN; + } + update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(status)); if (!update) { @@ -185,22 +203,34 @@ static void update_contact_status(const struct ast_sip_contact *contact, return; } - update->last_status = status->status; - update->status = value; + if (is_contact_refresh) { + /* Copy everything just to set the refresh flag. */ + update->status = status->status; + update->last_status = status->last_status; + update->rtt = status->rtt; + update->rtt_start = status->rtt_start; + update->refresh = 1; + } else { + update->last_status = status->status; + update->status = value; - /* if the contact is available calculate the rtt as - the diff between the last start time and "now" */ - update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0 ? - ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0; - update->rtt_start = ast_tv(0, 0); + /* + * if the contact is available calculate the rtt as + * the diff between the last start time and "now" + */ + update->rtt = update->status == AVAILABLE && status->rtt_start.tv_sec > 0 + ? ast_tvdiff_us(ast_tvnow(), status->rtt_start) + : 0; + update->rtt_start = ast_tv(0, 0); - ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT", - "Contact: %s\r\n" - "Status: %s\r\n" - "RTT: %" PRId64, - ast_sorcery_object_get_id(update), - ast_sip_get_contact_status_label(update->status), - update->rtt); + ast_test_suite_event_notify("AOR_CONTACT_QUALIFY_RESULT", + "Contact: %s\r\n" + "Status: %s\r\n" + "RTT: %" PRId64, + ast_sorcery_object_get_id(update), + ast_sip_get_contact_status_label(update->status), + update->rtt); + } if (ast_sorcery_update(ast_sip_get_sorcery(), update)) { ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n", @@ -317,10 +347,10 @@ static void qualify_contact_cb(void *token, pjsip_event *e) /* Fall through */ case PJSIP_EVENT_TRANSPORT_ERROR: case PJSIP_EVENT_TIMER: - update_contact_status(contact, UNAVAILABLE); + update_contact_status(contact, UNAVAILABLE, 0); break; case PJSIP_EVENT_RX_MSG: - update_contact_status(contact, AVAILABLE); + update_contact_status(contact, AVAILABLE, 0); break; } ao2_cleanup(contact); @@ -341,7 +371,12 @@ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_con if (endpoint) { endpoint_local = ao2_bump(endpoint); } else { - endpoint_local = find_an_endpoint(contact); + if (!ast_strlen_zero(contact->endpoint_name)) { + endpoint_local = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", contact->endpoint_name); + } + if (!endpoint_local) { + endpoint_local = find_an_endpoint(contact); + } if (!endpoint_local) { ast_log(LOG_ERROR, "Unable to find an endpoint to qualify contact %s\n", contact->uri); @@ -371,7 +406,7 @@ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_con != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n", contact->uri); - update_contact_status(contact, UNAVAILABLE); + update_contact_status(contact, UNAVAILABLE, 0); ao2_ref(contact, -1); return -1; } @@ -531,7 +566,7 @@ static void qualify_and_schedule(struct ast_sip_contact *contact) schedule_qualify(contact, contact->qualify_frequency * 1000); } else { - update_contact_status(contact, UNKNOWN); + update_contact_status(contact, UNKNOWN, 0); } } @@ -550,8 +585,7 @@ static void contact_created(const void *obj) */ static void contact_updated(const void *obj) { - update_contact_status((struct ast_sip_contact *) obj, UPDATED); - qualify_and_schedule((struct ast_sip_contact *) obj); + update_contact_status(obj, AVAILABLE, 1); } /*! @@ -580,8 +614,8 @@ static void contact_deleted(const void *obj) static const struct ast_sorcery_observer contact_observer = { .created = contact_created, + .updated = contact_updated, .deleted = contact_deleted, - .updated = contact_updated }; static pj_bool_t options_start(void) @@ -1057,7 +1091,7 @@ static void qualify_and_schedule_contact(struct ast_sip_contact *contact) if (contact->qualify_frequency) { schedule_qualify(contact, initial_interval); } else { - update_contact_status(contact, UNKNOWN); + update_contact_status(contact, UNKNOWN, 0); } } @@ -1240,7 +1274,7 @@ static void aor_observer_deleted(const void *obj) contacts = ast_sip_location_retrieve_aor_contacts(aor); if (contacts) { - ao2_callback(contacts, OBJ_NODATA, unschedule_contact_cb, NULL); + ao2_callback(contacts, OBJ_NODATA | OBJ_MULTIPLE, unschedule_contact_cb, NULL); ao2_ref(contacts, -1); } } @@ -1251,6 +1285,126 @@ static const struct ast_sorcery_observer observer_callbacks_options = { .deleted = aor_observer_deleted }; +static int aor_update_endpoint_state(void *obj, void *arg, int flags) +{ + struct ast_sip_endpoint *endpoint = obj; + const char *endpoint_name = ast_sorcery_object_get_id(endpoint); + char *aor = arg; + char *endpoint_aor; + char *endpoint_aors; + + if (ast_strlen_zero(aor) || ast_strlen_zero(endpoint->aors)) { + return 0; + } + + endpoint_aors = ast_strdupa(endpoint->aors); + while ((endpoint_aor = ast_strip(strsep(&endpoint_aors, ",")))) { + if (!strcmp(aor, endpoint_aor)) { + if (ast_sip_persistent_endpoint_update_state(endpoint_name, AST_ENDPOINT_ONLINE) == -1) { + ast_log(LOG_WARNING, "Unable to find persistent endpoint '%s' for aor '%s'\n", + endpoint_name, aor); + } + } + } + + return 0; +} + +static int on_aor_update_endpoint_state(void *obj, void *arg, int flags) +{ + struct ast_sip_aor *aor = obj; + struct ao2_container *endpoints; + RAII_VAR(struct ast_variable *, var, NULL, ast_variables_destroy); + const char *aor_name = ast_sorcery_object_get_id(aor); + char *aor_like; + + if (ast_strlen_zero(aor_name)) { + return -1; + } + + if (aor->permanent_contacts && ((int)(aor->qualify_frequency * 1000)) <= 0) { + aor_like = ast_alloca(strlen(aor_name) + 3); + sprintf(aor_like, "%%%s%%", aor_name); + var = ast_variable_new("aors LIKE", aor_like, ""); + if (!var) { + return -1; + } + endpoints = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), + "endpoint", AST_RETRIEVE_FLAG_MULTIPLE, var); + + if (endpoints) { + /* + * Because aors are a string list, we have to use a pattern match but since a simple + * pattern match could return an endpoint that has an aor of "aaabccc" when searching + * for "abc", we still have to iterate over them to find an exact aor match. + */ + ao2_callback(endpoints, 0, aor_update_endpoint_state, (char *)aor_name); + ao2_ref(endpoints, -1); + } + } + + return 0; +} + +static int contact_update_endpoint_state(void *obj, void *arg, int flags) +{ + const struct ast_sip_contact *contact = obj; + struct timeval tv = ast_tvnow(); + + if (!ast_strlen_zero(contact->endpoint_name) && ((int)(contact->qualify_frequency * 1000)) <= 0 && + contact->expiration_time.tv_sec > tv.tv_sec) { + + if (ast_sip_persistent_endpoint_update_state(contact->endpoint_name, AST_ENDPOINT_ONLINE) == -1) { + ast_log(LOG_WARNING, "Unable to find persistent endpoint '%s' for contact '%s/%s'\n", + contact->endpoint_name, contact->aor, contact->uri); + return -1; + } + } + + return 0; +} + +static void update_all_unqualified_endpoints(void) +{ + struct ao2_container *aors; + struct ao2_container *contacts; + RAII_VAR(struct ast_variable *, var_aor, NULL, ast_variables_destroy); + RAII_VAR(struct ast_variable *, var_contact, NULL, ast_variables_destroy); + RAII_VAR(char *, time_now, NULL, ast_free); + struct timeval tv = ast_tvnow(); + + if (!(var_aor = ast_variable_new("contact !=", "", ""))) { + return; + } + if (!(var_aor->next = ast_variable_new("qualify_frequency <=", "0", ""))) { + return; + } + + if (ast_asprintf(&time_now, "%ld", tv.tv_sec) == -1) { + return; + } + if (!(var_contact = ast_variable_new("expiration_time >", time_now, ""))) { + return; + } + if (!(var_contact->next = ast_variable_new("qualify_frequency <=", "0", ""))) { + return; + } + + aors = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), + "aor", AST_RETRIEVE_FLAG_MULTIPLE, var_aor); + if (aors) { + ao2_callback(aors, OBJ_NODATA, on_aor_update_endpoint_state, NULL); + ao2_ref(aors, -1); + } + + contacts = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), + "contact", AST_RETRIEVE_FLAG_MULTIPLE, var_contact); + if (contacts) { + ao2_callback(contacts, OBJ_NODATA, contact_update_endpoint_state, NULL); + ao2_ref(contacts, -1); + } +} + int ast_res_pjsip_init_options_handling(int reload) { static const pj_str_t STR_OPTIONS = { "OPTIONS", 7 }; @@ -1292,6 +1446,7 @@ int ast_res_pjsip_init_options_handling(int reload) ast_manager_register2("PJSIPQualify", EVENT_FLAG_SYSTEM | EVENT_FLAG_REPORTING, ami_sip_qualify, NULL, NULL, NULL); ast_cli_register_multiple(cli_options, ARRAY_LEN(cli_options)); + update_all_unqualified_endpoints(); qualify_and_schedule_all(); return 0; |