if (which == SIP_PEERS_ALL || peer->the_mark) {
peer_sched_cleanup(peer);
+ if (peer->dnsmgr) {
+ ast_dnsmgr_release(peer->dnsmgr);
+ peer->dnsmgr = NULL;
+ sip_unref_peer(peer, "Release peer from dnsmgr");
+ }
return CMP_MATCH;
}
return 0;
ao2_t_ref(peer->auth, -1, "Removing peer authentication");
peer->auth = NULL;
}
- if (peer->dnsmgr)
- ast_dnsmgr_release(peer->dnsmgr);
if (peer->socket.tcptls_session) {
ao2_ref(peer->socket.tcptls_session, -1);
return 0;
}
+/*! \brief The default sip port for the given transport */
+static inline int default_sip_port(enum sip_transport type)
+{
+ return type == SIP_TRANSPORT_TLS ? STANDARD_TLS_PORT : STANDARD_SIP_PORT;
+}
+
/*! \brief create address structure from device name
* Or, if peer not found, find it in the global DNS
* returns TRUE (-1) on failure, FALSE on success */
}
if (!ast_sockaddr_port(&dialog->sa)) {
- ast_sockaddr_set_port(&dialog->sa,
- (dialog->socket.type == SIP_TRANSPORT_TLS) ?
- STANDARD_TLS_PORT : STANDARD_SIP_PORT);
+ ast_sockaddr_set_port(&dialog->sa, default_sip_port(dialog->socket.type));
}
ast_sockaddr_copy(&dialog->recv, &dialog->sa);
return 0;
ast_string_field_free_memory(reg);
ast_atomic_fetchadd_int(®objs, -1);
- ast_dnsmgr_release(reg->dnsmgr);
ast_free(reg);
}
AST_SCHED_DEL(sched, mwi->resub);
ast_string_field_free_memory(mwi);
- ast_dnsmgr_release(mwi->dnsmgr);
ast_free(mwi);
}
return 0;
}
+static void on_dns_update_registry(struct ast_sockaddr *old, struct ast_sockaddr *new, void *data)
+{
+ struct sip_registry *reg = data;
+ const char *old_str;
+
+ /* This shouldn't happen, but just in case */
+ if (ast_sockaddr_isnull(new)) {
+ ast_debug(1, "Empty sockaddr change...ignoring!\n");
+ return;
+ }
+
+ if (!ast_sockaddr_port(new)) {
+ ast_sockaddr_set_port(new, reg->portno);
+ }
+
+ old_str = ast_strdupa(ast_sockaddr_stringify(old));
+
+ ast_debug(1, "Changing registry %s from %s to %s\n", S_OR(reg->peername, reg->hostname), old_str, ast_sockaddr_stringify(new));
+ ast_sockaddr_copy(®->us, new);
+}
+
+static void on_dns_update_peer(struct ast_sockaddr *old, struct ast_sockaddr *new, void *data)
+{
+ struct sip_peer *peer = data;
+ const char *old_str;
+
+ /* This shouldn't happen, but just in case */
+ if (ast_sockaddr_isnull(new)) {
+ ast_debug(1, "Empty sockaddr change...ignoring!\n");
+ return;
+ }
+
+ if (!ast_sockaddr_isnull(&peer->addr)) {
+ ao2_unlink(peers_by_ip, peer);
+ }
+
+ if (!ast_sockaddr_port(new)) {
+ ast_sockaddr_set_port(new, default_sip_port(peer->socket.type));
+ }
+
+ old_str = ast_strdupa(ast_sockaddr_stringify(old));
+ ast_debug(1, "Changing peer %s address from %s to %s\n", peer->name, old_str, ast_sockaddr_stringify(new));
+
+ ao2_lock(peer);
+ ast_sockaddr_copy(&peer->addr, new);
+ ao2_unlock(peer);
+
+ ao2_link(peers_by_ip, peer);
+}
+
+static void on_dns_update_mwi(struct ast_sockaddr *old, struct ast_sockaddr *new, void *data)
+{
+ struct sip_subscription_mwi *mwi = data;
+ const char *old_str;
+
+ /* This shouldn't happen, but just in case */
+ if (ast_sockaddr_isnull(new)) {
+ ast_debug(1, "Empty sockaddr change...ignoring!\n");
+ return;
+ }
+
+ old_str = ast_strdupa(ast_sockaddr_stringify(old));
+ ast_debug(1, "Changing mwi %s from %s to %s\n", mwi->hostname, old_str, ast_sockaddr_stringify(new));
+ ast_sockaddr_copy(&mwi->us, new);
+}
+
/*! \brief Actually setup an MWI subscription or resubscribe */
static int __sip_subscribe_mwi_do(struct sip_subscription_mwi *mwi)
{
snprintf(transport, sizeof(transport), "_%s._%s", get_srv_service(mwi->transport), get_srv_protocol(mwi->transport));
mwi->us.ss.ss_family = get_address_family_filter(&bindaddr); /* Filter address family */
- ast_dnsmgr_lookup(mwi->hostname, &mwi->us, &mwi->dnsmgr, sip_cfg.srvlookup ? transport : NULL);
+ ASTOBJ_REF(mwi); /* Add a ref for storing the mwi on the dnsmgr for updates */
+ ast_dnsmgr_lookup_cb(mwi->hostname, &mwi->us, &mwi->dnsmgr, sip_cfg.srvlookup ? transport : NULL, on_dns_update_mwi, mwi);
+ if (!mwi->dnsmgr) {
+ ASTOBJ_UNREF(mwi, sip_subscribe_mwi_destroy); /* dnsmgr disabled, remove reference */
+ }
}
/* If we already have a subscription up simply send a resubscription */
}
if (r->dnsmgr) {
- struct sip_peer *peer;
/* If the registration has timed out, maybe the IP changed. Force a refresh. */
ast_dnsmgr_refresh(r->dnsmgr);
- /* If we are resolving a peer, we have to make sure the refreshed address gets copied */
- if ((peer = sip_find_peer(r->hostname, NULL, TRUE, FINDPEERS, FALSE, 0))) {
- ast_sockaddr_copy(&peer->addr, &r->us);
- if (r->portno) {
- ast_sockaddr_set_port(&peer->addr, r->portno);
- }
- peer = sip_unref_peer(peer, "unref after sip_find_peer");
- }
}
/* If the initial tranmission failed, we may not have an existing dialog,
* or peer NULL. Since we're only concerned with its existence, we're not going to
* bother getting a ref to the proxy*/
if (!obproxy_get(r->call, peer)) {
- ast_dnsmgr_lookup(peer ? peer->tohost : r->hostname, &r->us, &r->dnsmgr, sip_cfg.srvlookup ? transport : NULL);
+ registry_addref(r, "add reg ref for dnsmgr");
+ ast_dnsmgr_lookup_cb(peer ? peer->tohost : r->hostname, &r->us, &r->dnsmgr, sip_cfg.srvlookup ? transport : NULL, on_dns_update_registry, r);
+ if (!r->dnsmgr) {
+ /*dnsmgr refresh disabled, no reference added! */
+ registry_unref(r, "remove reg ref, dnsmgr disabled");
+ }
}
if (peer) {
peer = sip_unref_peer(peer, "removing peer ref for dnsmgr_lookup");
}
/* Use port number specified if no SRV record was found */
- if (!ast_sockaddr_port(&r->us) && r->portno) {
- ast_sockaddr_set_port(&r->us, r->portno);
- }
-
- /* It is possible that DNS is unavailable at the time the peer is created. Here, if
- * we've updated the address in the registry, we copy it to the peer so that
- * create_addr() can copy it to the dialog via create_addr_from_peer */
- if ((peer = sip_find_peer(r->hostname, NULL, TRUE, FINDPEERS, FALSE, 0))) {
- if (ast_sockaddr_isnull(&peer->addr) && !(ast_sockaddr_isnull(&r->us))) {
- ast_sockaddr_copy(&peer->addr, &r->us);
+ if (!ast_sockaddr_isnull(&r->us)) {
+ if (!ast_sockaddr_port(&r->us) && r->portno) {
+ ast_sockaddr_set_port(&r->us, r->portno);
+ }
+
+ /* It is possible that DNS was unavailable at the time the peer was created.
+ * Here, if we've updated the address in the registry via manually calling
+ * ast_dnsmgr_lookup_cb() above, then we call the same function that dnsmgr would
+ * call if it was updating a peer's address */
+ if ((peer = sip_find_peer(S_OR(r->peername, r->hostname), NULL, TRUE, FINDPEERS, FALSE, 0))) {
+ if (ast_sockaddr_cmp(&peer->addr, &r->us)) {
+ on_dns_update_peer(&peer->addr, &r->us, peer);
+ }
+ peer = sip_unref_peer(peer, "unref after sip_find_peer");
}
- peer = sip_unref_peer(peer, "unref after sip_find_peer");
}
/* Find address to hostname */
peer->portinuri = ast_sockaddr_port(&testsa) ? TRUE : FALSE;
if (!ast_sockaddr_port(&testsa)) {
- ast_sockaddr_set_port(&testsa,
- transport_type == SIP_TRANSPORT_TLS ?
- STANDARD_TLS_PORT : STANDARD_SIP_PORT);
+ ast_sockaddr_set_port(&testsa, default_sip_port(transport_type));
}
ast_sockaddr_copy(&peer->addr, &testsa);
snprintf(transport, sizeof(transport), "_%s._%s", get_srv_service(peer->socket.type), get_srv_protocol(peer->socket.type));
peer->addr.ss.ss_family = get_address_family_filter(&bindaddr); /* Filter address family */
- if (ast_dnsmgr_lookup(_srvlookup, &peer->addr, &peer->dnsmgr, sip_cfg.srvlookup && !peer->portinuri ? transport : NULL)) {
+ if (ast_dnsmgr_lookup_cb(_srvlookup, &peer->addr, &peer->dnsmgr, sip_cfg.srvlookup && !peer->portinuri ? transport : NULL,
+ on_dns_update_peer, sip_ref_peer(peer, "Store peer on dnsmgr"))) {
ast_log(LOG_ERROR, "srvlookup failed for host: %s, on peer %s, removing peer\n", _srvlookup, peer->name);
+ sip_unref_peer(peer, "dnsmgr lookup failed, getting rid of peer dnsmgr ref");
sip_unref_peer(peer, "getting rid of a peer pointer");
return NULL;
}
+ if (!peer->dnsmgr) {
+ /* dnsmgr refresh disabeld, release reference */
+ sip_unref_peer(peer, "dnsmgr disabled, unref peer");
+ }
ast_string_field_set(peer, tohost, srvlookup);
/* First, destroy all outstanding registry calls */
/* This is needed, since otherwise active registry entries will not be destroyed */
ASTOBJ_CONTAINER_TRAVERSE(®l, 1, do { /* regl is locked */
- ASTOBJ_RDLOCK(iterator); /* now regl is locked, and the object is also locked */
+ ASTOBJ_WRLOCK(iterator); /* now regl is locked, and the object is also locked */
if (iterator->call) {
ast_debug(3, "Destroying active SIP dialog for registry %s@%s\n", iterator->username, iterator->hostname);
/* This will also remove references to the registry */
if (iterator->timeout > -1) {
AST_SCHED_DEL_UNREF(sched, iterator->timeout, registry_unref(iterator, "reg ptr unref from reload config"));
}
+ if (iterator->dnsmgr) {
+ ast_dnsmgr_release(iterator->dnsmgr);
+ iterator->dnsmgr = NULL;
+ registry_unref(iterator, "reg ptr unref from dnsmgr");
+ }
ASTOBJ_UNLOCK(iterator);
} while(0));
}
cleanup_all_regs();
ASTOBJ_CONTAINER_DESTROYALL(®l, sip_registry_destroy);
ASTOBJ_CONTAINER_DESTROY(®l);
+
+ ASTOBJ_CONTAINER_TRAVERSE(&submwil, 1, do {
+ ASTOBJ_WRLOCK(iterator);
+ if (iterator->dnsmgr) {
+ ast_dnsmgr_release(iterator->dnsmgr);
+ iterator->dnsmgr = NULL;
+ ASTOBJ_UNREF(iterator, sip_subscribe_mwi_destroy);
+ }
+ ASTOBJ_UNLOCK(iterator);
+ } while(0));
ASTOBJ_CONTAINER_DESTROYALL(&submwil, sip_subscribe_mwi_destroy);
ASTOBJ_CONTAINER_DESTROY(&submwil);
unsigned int family;
/*! Set to 1 if the entry changes */
unsigned int changed:1;
+ /*! Data to pass back to update_func */
+ void *data;
+ /*! The callback function to execute on address update */
+ dns_update_func update_func;
ast_mutex_t lock;
AST_RWLIST_ENTRY(ast_dnsmgr_entry) list;
/*! just 1 here, but we use calloc to allocate the correct size */
ast_free(entry);
}
-int ast_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_dnsmgr_entry **dnsmgr, const char *service)
+static int internal_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_dnsmgr_entry **dnsmgr, const char *service, dns_update_func func, void *data)
{
unsigned int family;
ast_verb(3, "adding dns manager for '%s'\n", name);
*dnsmgr = ast_dnsmgr_get_family(name, result, service, family);
+ (*dnsmgr)->update_func = func;
+ (*dnsmgr)->data = data;
return !*dnsmgr;
}
+int ast_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_dnsmgr_entry **dnsmgr, const char *service)
+{
+ return internal_dnsmgr_lookup(name, result, dnsmgr, service, NULL, NULL);
+}
+
+int ast_dnsmgr_lookup_cb(const char *name, struct ast_sockaddr *result, struct ast_dnsmgr_entry **dnsmgr, const char *service, dns_update_func func, void *data)
+{
+ return internal_dnsmgr_lookup(name, result, dnsmgr, service, func, data);
+}
+
/*
* Refresh a dnsmgr entry
*/
if (!ast_sockaddr_port(&tmp)) {
ast_sockaddr_set_port(&tmp, ast_sockaddr_port(entry->result));
}
-
if (ast_sockaddr_cmp(&tmp, entry->result)) {
const char *old_addr = ast_strdupa(ast_sockaddr_stringify(entry->result));
const char *new_addr = ast_strdupa(ast_sockaddr_stringify(&tmp));
- ast_log(LOG_NOTICE, "dnssrv: host '%s' changed from %s to %s\n",
- entry->name, old_addr, new_addr);
+ if (entry->update_func) {
+ entry->update_func(entry->result, &tmp, entry->data);
+ } else {
+ ast_log(LOG_NOTICE, "dnssrv: host '%s' changed from %s to %s\n",
+ entry->name, old_addr, new_addr);
- ast_sockaddr_copy(entry->result, &tmp);
- changed = entry->changed = 1;
+ ast_sockaddr_copy(entry->result, &tmp);
+ changed = entry->changed = 1;
+ }
}
}