diff --git a/channels/chan_sip.c b/channels/chan_sip.c index e1dfaead4e55905e6833373eeee70050185bd872..698894f9f2b67de73e5256795701fe2851ad2fa5 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -2897,6 +2897,11 @@ static int match_and_cleanup_peer_sched(void *peerobj, void *arg, int flags) 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; @@ -4681,8 +4686,6 @@ static void sip_destroy_peer(struct sip_peer *peer) 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); @@ -5423,6 +5426,12 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer) 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 */ @@ -5525,9 +5534,7 @@ static int create_addr(struct sip_pvt *dialog, const char *opeer, struct ast_soc } 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; @@ -5747,7 +5754,6 @@ static void sip_registry_destroy(struct sip_registry *reg) ast_string_field_free_memory(reg); ast_atomic_fetchadd_int(®objs, -1); - ast_dnsmgr_release(reg->dnsmgr); ast_free(reg); } @@ -5761,7 +5767,6 @@ static void sip_subscribe_mwi_destroy(struct sip_subscription_mwi *mwi) AST_SCHED_DEL(sched, mwi->resub); ast_string_field_free_memory(mwi); - ast_dnsmgr_release(mwi->dnsmgr); ast_free(mwi); } @@ -12721,6 +12726,72 @@ static int sip_subscribe_mwi_do(const void *data) 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) { @@ -12730,7 +12801,11 @@ 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 */ @@ -13411,17 +13486,8 @@ static int sip_reg_timeout(const void *data) } 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, @@ -13510,7 +13576,12 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char * * 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"); @@ -13543,18 +13614,21 @@ static int transmit_register(struct sip_registry *r, int sipmethod, const char * } /* 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 */ @@ -14392,9 +14466,7 @@ static enum parse_register_result parse_register_contact(struct sip_pvt *pvt, st 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); @@ -28679,11 +28751,17 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str 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); @@ -28817,7 +28895,7 @@ static void cleanup_all_regs(void) /* 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 */ @@ -28830,6 +28908,11 @@ static void cleanup_all_regs(void) 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)); } @@ -31496,6 +31579,16 @@ static int unload_module(void) 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); diff --git a/include/asterisk/dnsmgr.h b/include/asterisk/dnsmgr.h index b62c2a14b26bb533ce3f0993b9ad5c46801326d1..07d6268bab1928e5dd2b00e79b78895d4282305e 100644 --- a/include/asterisk/dnsmgr.h +++ b/include/asterisk/dnsmgr.h @@ -37,6 +37,8 @@ extern "C" { */ struct ast_dnsmgr_entry; +typedef void (*dns_update_func)(struct ast_sockaddr *old_addr, struct ast_sockaddr *new_addr, void *data); + /*! * \brief Allocate a new DNS manager entry * @@ -104,6 +106,31 @@ void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry); */ int ast_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_dnsmgr_entry **dnsmgr, const char *service); +/*! + * \brief Allocate and initialize a DNS manager entry, with update callback + * + * \param name the hostname + * \param result The addr which is intended to be updated in the update callback when DNS manager calls it on refresh. + * The address family is used as an input parameter to filter the returned addresses. + * If it is 0, both IPv4 and IPv6 addresses can be returned. + * \param dnsmgr Where to store the allocate DNS manager entry + * \param service + * \param func The update callback function + * The update callback will be called when DNS manager detects that an IP address has been changed. + * Instead of updating the addr itself, DNS manager will call this callback function with the old + * and new addresses. It is the responsibility of the callback to perform any updates + * \param data A pointer to data that will be passed through to the callback function + * + * \note + * This function allocates a new DNS manager entry object, and fills it with + * the provided hostname and IP address. This function _does_ force an initial + * lookup, so it may block for some period of time. + * + * \retval 0 success + * \retval non-zero failure + */ +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); + /*! * \brief Force a refresh of a dnsmgr entry * diff --git a/main/dnsmgr.c b/main/dnsmgr.c index d11cd99e3cd4ca0e4a78e4b59cc4bc980dcee498..7cdcd0ced51ef7bf26d5e82cb148ee67ecd323de 100644 --- a/main/dnsmgr.c +++ b/main/dnsmgr.c @@ -58,6 +58,10 @@ struct ast_dnsmgr_entry { 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 */ @@ -130,7 +134,7 @@ void ast_dnsmgr_release(struct ast_dnsmgr_entry *entry) 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; @@ -165,9 +169,21 @@ int ast_dnsmgr_lookup(const char *name, struct ast_sockaddr *result, struct ast_ 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 */ @@ -187,16 +203,19 @@ static int dnsmgr_refresh(struct ast_dnsmgr_entry *entry, int verbose) 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; + } } }