int ast_sip_for_each_auth(const struct ast_sip_auth_array *array, ao2_callback_fn on_auth, void *arg) { int i; if (!array || !array->num) { return 0; } for (i = 0; i < array->num; ++i) { RAII_VAR(struct ast_sip_auth *, auth, ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), SIP_SORCERY_AUTH_TYPE, array->names[i]), ao2_cleanup); if (!auth) { continue; } if (on_auth(auth, arg, 0)) { return -1; } } return 0; }
int ast_sip_initialize_sorcery_qualify(void) { struct ast_sorcery *sorcery = ast_sip_get_sorcery(); /* initialize sorcery ast_sip_contact_status resource */ ast_sorcery_apply_default(sorcery, CONTACT_STATUS, "memory", NULL); if (ast_sorcery_internal_object_register(sorcery, CONTACT_STATUS, contact_status_alloc, NULL, NULL)) { ast_log(LOG_ERROR, "Unable to register ast_sip_contact_status in sorcery\n"); return -1; } snprintf(status_value_unknown, sizeof(status_value_unknown), "%u", UNKNOWN); ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "last_status", status_value_unknown, OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, last_status)); snprintf(status_value_created, sizeof(status_value_created), "%u", CREATED); ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "status", status_value_created, OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, status)); ast_sorcery_object_field_register_custom_nodoc(sorcery, CONTACT_STATUS, "rtt_start", "0.0", rtt_start_handler, rtt_start_to_str, NULL, 0, 0); ast_sorcery_object_field_register_nodoc(sorcery, CONTACT_STATUS, "rtt", "0", OPT_UINT_T, 1, FLDSET(struct ast_sip_contact_status, rtt)); return 0; }
/*! \brief Helper function which returns a UDP transport bound to the given address and port */ static pjsip_transport *multihomed_get_udp_transport(pj_str_t *address, int port) { struct ao2_container *transports = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "transport", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); struct ast_sip_transport *transport; struct ao2_iterator iter; pjsip_transport *sip_transport = NULL; if (!transports) { return NULL; } for (iter = ao2_iterator_init(transports, 0); (transport = ao2_iterator_next(&iter)); ao2_ref(transport, -1)) { if ((transport->type != AST_TRANSPORT_UDP) || (pj_strcmp(&transport->state->transport->local_name.host, address)) || (transport->state->transport->local_name.port != port)) { continue; } sip_transport = transport->state->transport; ao2_ref(transport, -1); break; } ao2_iterator_destroy(&iter); ao2_ref(transports, -1); return sip_transport; }
static void create_mwi_subscriptions(void) { struct ao2_container *mwi_subscriptions = ao2_container_alloc(MWI_BUCKETS, mwi_sub_hash, mwi_sub_cmp); RAII_VAR(struct ao2_container *, old_mwi_subscriptions, ao2_global_obj_ref(unsolicited_mwi), ao2_cleanup); RAII_VAR(struct ao2_container *, endpoints, ast_sorcery_retrieve_by_fields( ast_sip_get_sorcery(), "endpoint", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL), ao2_cleanup); if (!mwi_subscriptions) { return; } /* We remove all the old stasis subscriptions first before applying the new configuration. This * prevents a situation where there might be multiple overlapping stasis subscriptions for an * endpoint for mailboxes. Though there may be mailbox changes during the gap between unsubscribing * and resubscribing, up-to-date mailbox state will be sent out to the endpoint when the * new stasis subscription is established */ if (old_mwi_subscriptions) { ao2_callback(old_mwi_subscriptions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); } ao2_callback(endpoints, OBJ_NODATA, create_mwi_subscriptions_for_endpoint, mwi_subscriptions); ao2_global_obj_replace_unref(unsolicited_mwi, mwi_subscriptions); ao2_ref(mwi_subscriptions, -1); }
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 load_module(void) { CHECK_PJSIP_MODULE_LOADED(); if (ast_sip_register_subscription_handler(&mwi_handler)) { return AST_MODULE_LOAD_DECLINE; } unsolicited_mwi = ao2_container_alloc(MWI_BUCKETS, mwi_sub_hash, mwi_sub_cmp); if (!unsolicited_mwi) { ast_sip_unregister_subscription_handler(&mwi_handler); return AST_MODULE_LOAD_DECLINE; } create_mwi_subscriptions(); ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &mwi_contact_observer); if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) { ast_sip_push_task(NULL, send_initial_notify_all, NULL); } else { stasis_subscribe_pool(ast_manager_get_topic(), mwi_startup_event_cb, NULL); } return AST_MODULE_LOAD_SUCCESS; }
static int load_module(void) { CHECK_PJSIP_MODULE_LOADED(); contact_autoexpire = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, CONTACT_AUTOEXPIRE_BUCKETS, contact_expiration_hash, contact_expiration_cmp); if (!contact_autoexpire) { ast_log(LOG_ERROR, "Could not create container for contact auto-expiration\n"); return AST_MODULE_LOAD_FAILURE; } if (!(sched = ast_sched_context_create())) { ast_log(LOG_ERROR, "Could not create scheduler for contact auto-expiration\n"); unload_module(); return AST_MODULE_LOAD_FAILURE; } if (ast_sched_start_thread(sched)) { ast_log(LOG_ERROR, "Could not start scheduler thread for contact auto-expiration\n"); unload_module(); return AST_MODULE_LOAD_FAILURE; } contact_expiration_initialize_existing(); if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "contact", &contact_expiration_observer)) { ast_log(LOG_ERROR, "Could not add observer for notifications about contacts for contact auto-expiration\n"); unload_module(); return AST_MODULE_LOAD_FAILURE; } return AST_MODULE_LOAD_SUCCESS; }
static int format_contact_status(void *obj, void *arg, int flags) { struct ast_sip_contact_wrapper *wrapper = obj; struct ast_sip_contact *contact = wrapper->contact; struct ast_sip_ami *ami = arg; struct ast_sip_contact_status *status; struct ast_str *buf; const struct ast_sip_endpoint *endpoint = ami->arg; buf = ast_sip_create_ami_event("ContactStatusDetail", ami); if (!buf) { return -1; } status = ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id); ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri); ast_str_append(&buf, 0, "Status: %s\r\n", ast_sip_get_contact_status_label(status->status)); if (status->status == UNKNOWN) { ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n"); } else { ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt); } ast_str_append(&buf, 0, "EndpointName: %s\r\n", ast_sorcery_object_get_id(endpoint)); astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); ami->count++; ast_free(buf); ao2_cleanup(status); return 0; }
/*! * \brief Retrieve a ast_sip_contact_status object from sorcery creating * one if not found. */ struct ast_sip_contact_status *ast_res_pjsip_find_or_create_contact_status(const struct ast_sip_contact *contact) { struct ast_sip_contact_status *status; SCOPED_MUTEX(lock, &creation_lock); status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (status) { return status; } status = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (!status) { ast_log(LOG_ERROR, "Unable to create ast_sip_contact_status for contact %s/%s\n", contact->aor, contact->uri); return NULL; } ast_string_field_set(status, uri, contact->uri); status->rtt_start = ast_tv(0, 0); status->rtt = 0; if (ast_sorcery_create(ast_sip_get_sorcery(), status)) { ast_log(LOG_ERROR, "Unable to persist ast_sip_contact_status for contact %s\n", contact->uri); ao2_ref(status, -1); return NULL; } /* The permanent contact added after asterisk start should be qualified. */ if (ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED) && ast_tvzero(contact->expiration_time)) { /* * The FULLY_BOOTED to filter out contacts that already existed when asterisk started. * The zero expiration_time to select only permanent contacts. */ ao2_ref((struct ast_sip_contact *) contact, +1); if (ast_sip_push_task(NULL, qualify_and_schedule_aor_contact, (struct ast_sip_contact *) contact)) { ao2_ref((struct ast_sip_contact *) contact, -1); } } ast_statsd_log_string_va("PJSIP.contacts.states.%s", AST_STATSD_GAUGE, "+1", 1.0, ast_sip_get_contact_status_label(status->status)); return status; }
/*! * \internal * \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) { struct ast_sip_contact_status *status; struct ast_sip_contact_status *update; status = ast_res_pjsip_find_or_create_contact_status(contact); if (!status) { ast_log(LOG_ERROR, "Unable to find ast_sip_contact_status for contact %s\n", contact->uri); return; } update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(status)); if (!update) { ast_log(LOG_ERROR, "Unable to allocate ast_sip_contact_status for contact %s\n", contact->uri); return; } 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); 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", contact->uri); } ao2_ref(status, -1); ao2_ref(update, -1); }
static int unload_module(void) { ao2_callback(unsolicited_mwi, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); ao2_ref(unsolicited_mwi, -1); ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &mwi_contact_observer); ast_sip_unregister_subscription_handler(&mwi_handler); return 0; }
static struct ao2_container *cli_get_auths(void) { struct ao2_container *auths; auths = ast_sorcery_retrieve_by_fields(ast_sip_get_sorcery(), "auth", AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL); return auths; }
/*! \brief Custom handler for permanent URIs */ static int permanent_uri_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) { struct ast_sip_aor *aor = obj; const char *aor_id = ast_sorcery_object_get_id(aor); char *contacts; char *contact_uri; if (ast_strlen_zero(var->value)) { return 0; } contacts = ast_strdupa(var->value); while ((contact_uri = ast_strip(strsep(&contacts, ",")))) { struct ast_sip_contact *contact; struct ast_sip_contact_status *status; char hash[33]; char contact_id[strlen(aor_id) + sizeof(hash) + 2]; if (ast_strlen_zero(contact_uri)) { continue; } if (ast_sip_validate_uri_length(contact_uri)) { ast_log(LOG_ERROR, "Contact uri or hostname length exceeds pjproject limit: %s\n", contact_uri); return -1; } if (!aor->permanent_contacts) { aor->permanent_contacts = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, permanent_uri_sort_fn, NULL); if (!aor->permanent_contacts) { return -1; } } ast_md5_hash(hash, contact_uri); snprintf(contact_id, sizeof(contact_id), "%s@@%s", aor_id, hash); contact = ast_sorcery_alloc(ast_sip_get_sorcery(), "contact", contact_id); if (!contact) { return -1; } ast_string_field_set(contact, uri, contact_uri); status = ast_res_pjsip_find_or_create_contact_status(contact); if (!status) { ao2_ref(contact, -1); return -1; } ao2_ref(status, -1); ao2_link(aor->permanent_contacts, contact); ao2_ref(contact, -1); } return 0; }
/*! * \internal * \brief A contact has been deleted remove status tracking. */ static void contact_deleted(const void *obj) { struct ast_sip_contact *contact = (struct ast_sip_contact *) obj; struct ast_sip_contact_status *status; unschedule_qualify(contact); status = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); if (!status) { return; } if (ast_sorcery_delete(ast_sip_get_sorcery(), status)) { ast_log(LOG_ERROR, "Unable to delete ast_sip_contact_status for contact %s\n", contact->uri); } ao2_ref(status, -1); }
static int unload_module(void) { ast_cli_unregister_multiple(cli_pjsip, ARRAY_LEN(cli_pjsip)); ast_sip_unregister_service(&logging_module); ast_sorcery_observer_remove( ast_sip_get_sorcery(), "global", &global_observer); return 0; }
int ast_sip_format_contact_ami(void *obj, void *arg, int flags) { struct ast_sip_contact_wrapper *wrapper = obj; struct ast_sip_contact *contact = wrapper->contact; struct ast_sip_ami *ami = arg; struct ast_sip_contact_status *status; struct ast_str *buf; const struct ast_sip_endpoint *endpoint = ami->arg; buf = ast_sip_create_ami_event("ContactStatusDetail", ami); if (!buf) { return -1; } status = ast_sorcery_retrieve_by_id( ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(contact)); ast_str_append(&buf, 0, "AOR: %s\r\n", wrapper->aor_id); ast_str_append(&buf, 0, "URI: %s\r\n", contact->uri); ast_str_append(&buf, 0, "UserAgent: %s\r\n", contact->user_agent); ast_str_append(&buf, 0, "RegExpire: %ld\r\n", contact->expiration_time.tv_sec); if (!ast_strlen_zero(contact->via_addr)) { ast_str_append(&buf, 0, "ViaAddress: %s", contact->via_addr); if (contact->via_port) { ast_str_append(&buf, 0, ":%d", contact->via_port); } ast_str_append(&buf, 0, "\r\n"); } if (!ast_strlen_zero(contact->call_id)) { ast_str_append(&buf, 0, "CallID: %s\r\n", contact->call_id); } ast_str_append(&buf, 0, "Status: %s\r\n", ast_sip_get_contact_status_label(status->status)); if (status->status == UNKNOWN) { ast_str_append(&buf, 0, "RoundtripUsec: N/A\r\n"); } else { ast_str_append(&buf, 0, "RoundtripUsec: %" PRId64 "\r\n", status->rtt); } ast_str_append(&buf, 0, "EndpointName: %s\r\n", endpoint ? ast_sorcery_object_get_id(endpoint) : S_OR(contact->endpoint_name, "")); ast_str_append(&buf, 0, "ID: %s\r\n", ast_sorcery_object_get_id(contact)); ast_str_append(&buf, 0, "AuthenticateQualify: %d\r\n", contact->authenticate_qualify); ast_str_append(&buf, 0, "OutboundProxy: %s\r\n", contact->outbound_proxy); ast_str_append(&buf, 0, "Path: %s\r\n", contact->path); ast_str_append(&buf, 0, "QualifyFrequency: %u\r\n", contact->qualify_frequency); ast_str_append(&buf, 0, "QualifyTimeout: %.3f\r\n", contact->qualify_timeout); astman_append(ami->s, "%s\r\n", ast_str_buffer(buf)); ami->count++; ast_free(buf); ao2_cleanup(status); return 0; }
/*! \brief Scheduler function which deletes a contact */ static int contact_expiration_expire(const void *data) { struct contact_expiration *expiration = (void *) data; expiration->sched = -1; /* This will end up invoking the deleted observer callback, which will perform the unlinking and such */ ast_sorcery_delete(ast_sip_get_sorcery(), expiration->contact); ao2_ref(expiration, -1); return 0; }
int ast_sip_destroy_sorcery_location(void) { ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &aor_observer); ast_cli_unregister_multiple(cli_commands, ARRAY_LEN(cli_commands)); ast_sip_unregister_cli_formatter(contact_formatter); ast_sip_unregister_cli_formatter(aor_formatter); internal_sip_unregister_endpoint_formatter(&endpoint_aor_formatter); return 0; }
void ast_res_pjsip_cleanup_options_handling(void) { ast_cli_unregister_multiple(cli_options, ARRAY_LEN(cli_options)); ast_manager_unregister("PJSIPQualify"); internal_sip_unregister_endpoint_formatter(&contact_status_formatter); ast_sorcery_observer_remove(ast_sip_get_sorcery(), "aor", &observer_callbacks_options); pjsip_endpt_unregister_module(ast_sip_get_pjsip_endpoint(), &options_module); ao2_cleanup(sched_qualifies); sched_qualifies = NULL; }
static int unload_module(void) { RAII_VAR(struct ao2_container *, mwi_subscriptions, ao2_global_obj_ref(unsolicited_mwi), ao2_cleanup); if (mwi_subscriptions) { ao2_callback(mwi_subscriptions, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, unsubscribe, NULL); ao2_global_obj_release(unsolicited_mwi); ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &mwi_contact_observer); } ast_sip_unregister_subscription_handler(&mwi_handler); return 0; }
static int ami_show_auths(struct mansession *s, const struct message *m) { struct ast_sip_ami ami = { .s = s, .m = m, .action_id = astman_get_header(m, "ActionID"), }; struct ao2_container *auths; auths = cli_get_auths(); if (!auths) { astman_send_error(s, m, "Could not get Auths\n"); return 0; } if (!ao2_container_count(auths)) { astman_send_error(s, m, "No Auths found\n"); ao2_ref(auths, -1); return 0; } astman_send_listack(s, m, "A listing of Auths follows, presented as AuthList events", "start"); ao2_callback(auths, OBJ_NODATA, format_ami_authlist_handler, &ami); astman_send_list_complete_start(s, m, "AuthListComplete", ami.count); astman_send_list_complete_end(s); ao2_ref(auths, -1); return 0; } static struct ao2_container *cli_get_container(const char *regex) { RAII_VAR(struct ao2_container *, container, NULL, ao2_cleanup); struct ao2_container *s_container; container = ast_sorcery_retrieve_by_regex(ast_sip_get_sorcery(), "auth", regex); if (!container) { return NULL; } s_container = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, ast_sorcery_object_id_sort, ast_sorcery_object_id_compare); if (!s_container) { return NULL; } if (ao2_container_dup(s_container, container, 0)) { ao2_ref(s_container, -1); return NULL; } return s_container; }
static struct ast_sip_endpoint *anonymous_identify(pjsip_rx_data *rdata) { char domain_name[DOMAIN_NAME_LEN + 1]; struct ast_sip_endpoint *endpoint; if (get_endpoint_details(rdata, domain_name, sizeof(domain_name))) { return NULL; } if (!ast_sip_get_disable_multi_domain()) { struct ast_sip_domain_alias *alias; struct ao2_container *transport_states; struct ast_sip_transport_state *transport_state = NULL; struct ast_sip_transport *transport = NULL; char id[sizeof("anonymous@") + DOMAIN_NAME_LEN]; /* Attempt to find the endpoint given the name and domain provided */ snprintf(id, sizeof(id), "anonymous@%s", domain_name); endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id); if (endpoint) { goto done; } /* See if an alias exists for the domain provided */ alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain_name); if (alias) { snprintf(id, sizeof(id), "anonymous@%s", alias->domain); ao2_ref(alias, -1); endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id); if (endpoint) { goto done; } } /* See if the transport this came in on has a provided domain */ if ((transport_states = ast_sip_get_transport_states()) && (transport_state = ao2_callback(transport_states, 0, find_transport_state_in_use, rdata)) && (transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", transport_state->id)) && !ast_strlen_zero(transport->domain)) { snprintf(id, sizeof(id), "anonymous@%s", transport->domain); endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", id); } ao2_cleanup(transport); ao2_cleanup(transport_state); ao2_cleanup(transport_states); if (endpoint) { goto done; } } /* Fall back to no domain */ endpoint = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "endpoint", "anonymous"); done: if (endpoint) { ast_debug(3, "Retrieved anonymous endpoint '%s'\n", ast_sorcery_object_get_id(endpoint)); } return endpoint; }
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); } }
static char *find_aor_name(const char *username, const char *domain, const char *aors) { char *configured_aors; char *aors_buf; char *aor_name; char *id_domain; struct ast_sip_domain_alias *alias; id_domain = ast_alloca(strlen(username) + strlen(domain) + 2); sprintf(id_domain, "%s@%s", username, domain); aors_buf = ast_strdupa(aors); /* Look for exact match on username@domain */ configured_aors = aors_buf; while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) { if (match_aor(aor_name, id_domain)) { return ast_strdup(aor_name); } } /* If there's a domain alias, look for exact match on username@domain_alias */ alias = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "domain_alias", domain); if (alias) { char *id_domain_alias = ast_alloca(strlen(username) + strlen(alias->domain) + 2); sprintf(id_domain, "%s@%s", username, alias->domain); ao2_cleanup(alias); configured_aors = strcpy(aors_buf, aors);/* Safe */ while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) { if (match_aor(aor_name, id_domain_alias)) { return ast_strdup(aor_name); } } } if (ast_strlen_zero(username)) { /* No username, no match */ return NULL; } /* Look for exact match on username only */ configured_aors = strcpy(aors_buf, aors);/* Safe */ while ((aor_name = ast_strip(strsep(&configured_aors, ",")))) { if (match_aor(aor_name, username)) { return ast_strdup(aor_name); } } return NULL; }
/*! \brief Callback function which deletes a contact if it has expired or sets up auto-expiry */ static int contact_expiration_setup(void *obj, void *arg, int flags) { struct ast_sip_contact *contact = obj; int expires = MAX(0, ast_tvdiff_ms(contact->expiration_time, ast_tvnow())); if (!expires) { ast_sorcery_delete(ast_sip_get_sorcery(), contact); } else { contact_expiration_observer_created(contact); } return 0; }
/*! \brief Initialize auto-expiration of any existing contacts */ static void contact_expiration_initialize_existing(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) { return; } ao2_callback(contacts, OBJ_NODATA, contact_expiration_setup, NULL); ao2_ref(contacts, -1); }
/*! * \internal * \brief Attempt to qualify the contact * * \details Sends a SIP OPTIONS request to the given contact in order to make * sure that contact is available. */ static int qualify_contact(struct ast_sip_endpoint *endpoint, struct ast_sip_contact *contact) { pjsip_tx_data *tdata; RAII_VAR(struct ast_sip_endpoint *, endpoint_local, NULL, ao2_cleanup); if (endpoint) { endpoint_local = ao2_bump(endpoint); } else { 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_WARNING, "Unable to find an endpoint to qualify contact %s. Deleting this contact\n", contact->uri); contact_deleted(contact); return -1; } } if (ast_sip_create_request("OPTIONS", NULL, endpoint_local, NULL, contact, &tdata)) { ast_log(LOG_ERROR, "Unable to create request to qualify contact %s\n", contact->uri); return -1; } /* If an outbound proxy is specified set it on this request */ if (!ast_strlen_zero(contact->outbound_proxy) && ast_sip_set_outbound_proxy(tdata, contact->outbound_proxy)) { pjsip_tx_data_dec_ref(tdata); ast_log(LOG_ERROR, "Unable to apply outbound proxy on request to qualify contact %s\n", contact->uri); return -1; } init_start_time(contact); ao2_ref(contact, +1); if (ast_sip_send_out_of_dialog_request(tdata, endpoint_local, (int)(contact->qualify_timeout * 1000), contact, qualify_contact_cb) != PJ_SUCCESS) { ast_log(LOG_ERROR, "Unable to send request to qualify contact %s\n", contact->uri); update_contact_status(contact, UNAVAILABLE, 0); ao2_ref(contact, -1); return -1; } return 0; }
static int unload_module(void) { ast_sorcery_observer_remove(ast_sip_get_sorcery(), "contact", &contact_expiration_observer); if (sched) { ao2_callback(contact_autoexpire, OBJ_MULTIPLE | OBJ_NODATA | OBJ_UNLINK, unload_observer_delete, NULL); ast_sched_context_destroy(sched); sched = NULL; } ao2_cleanup(contact_autoexpire); contact_autoexpire = NULL; return 0; }
static int load_module(void) { if (ast_sorcery_observer_add(ast_sip_get_sorcery(), "global", &global_observer)) { ast_log(LOG_WARNING, "Unable to add global observer\n"); return AST_MODULE_LOAD_DECLINE; } check_debug(); ast_sip_register_service(&logging_module); ast_cli_register_multiple(cli_pjsip, ARRAY_LEN(cli_pjsip)); return AST_MODULE_LOAD_SUCCESS; }
/*! * \internal * \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) { struct ast_sip_contact_status *status; struct ast_sip_contact_status *update; status = find_or_create_contact_status(contact); if (!status) { return; } update = ast_sorcery_alloc(ast_sip_get_sorcery(), CONTACT_STATUS, ast_sorcery_object_get_id(status)); if (!update) { ast_log(LOG_ERROR, "Unable to create update ast_sip_contact_status for contact %s\n", contact->uri); ao2_ref(status, -1); return; } 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 ? ast_tvdiff_us(ast_tvnow(), status->rtt_start) : 0; update->rtt_start = ast_tv(0, 0); if (ast_sorcery_update(ast_sip_get_sorcery(), update)) { ast_log(LOG_ERROR, "Unable to update ast_sip_contact_status for contact %s\n", contact->uri); } ao2_ref(update, -1); ao2_ref(status, -1); }