diff --git a/main/dns_query_set.c b/main/dns_query_set.c index 147c737f6f31b43cd0c3cbb09c88c4539d0e50b4..40a89e168eb73ba157783ad61585f90b61f7817a 100644 --- a/main/dns_query_set.c +++ b/main/dns_query_set.c @@ -130,7 +130,10 @@ int ast_dns_query_set_add(struct ast_dns_query_set *query_set, const char *name, return -1; } - AST_VECTOR_APPEND(&query_set->queries, query); + if (AST_VECTOR_APPEND(&query_set->queries, query)) { + ao2_ref(query.query, -1); + return -1; + } return 0; } @@ -175,6 +178,11 @@ void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dn query_set->callback = callback; query_set->user_data = ao2_bump(data); + /* + * Bump the query_set ref in case all queries complete + * before we are done kicking them off. + */ + ao2_ref(query_set, +1); for (idx = 0; idx < AST_VECTOR_SIZE(&query_set->queries); ++idx) { struct dns_query_set_query *query = AST_VECTOR_GET_ADDR(&query_set->queries, idx); @@ -187,6 +195,17 @@ void ast_dns_query_set_resolve_async(struct ast_dns_query_set *query_set, ast_dn dns_query_set_callback(query->query); } + if (!idx) { + /* + * There were no queries in the set; + * therefore all queries are "completed". + * Invoke the final callback. + */ + query_set->callback(query_set); + ao2_cleanup(query_set->user_data); + query_set->user_data = NULL; + } + ao2_ref(query_set, -1); } /*! \brief Structure used for signaling back for synchronous resolution completion */ diff --git a/res/res_pjsip/pjsip_resolver.c b/res/res_pjsip/pjsip_resolver.c index 4573e4ce18cdc497dc2224f8307282f19f39e513..915d1d90a53672b02bf1f1a79ede6c31114cf7bb 100644 --- a/res/res_pjsip/pjsip_resolver.c +++ b/res/res_pjsip/pjsip_resolver.c @@ -516,7 +516,7 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip resolve->callback = cb; resolve->token = token; - if (AST_VECTOR_INIT(&resolve->resolving, 2)) { + if (AST_VECTOR_INIT(&resolve->resolving, 4)) { ao2_ref(resolve, -1); cb(PJ_ENOMEM, token, NULL); return; @@ -565,6 +565,12 @@ static void sip_resolve(pjsip_resolver_t *resolver, pj_pool_t *pool, const pjsip cb(PJ_ENOMEM, token, NULL); return; } + if (!resolve->queries) { + ast_debug(2, "[%p] No resolution queries for target '%s'\n", resolve, host); + ao2_ref(resolve, -1); + cb(PJLIB_UTIL_EDNSNOANSWERREC, token, NULL); + return; + } ast_debug(2, "[%p] Starting initial resolution using parallel queries for target '%s'\n", resolve, host); ast_dns_query_set_resolve_async(resolve->queries, sip_resolve_callback, resolve); diff --git a/tests/test_dns_query_set.c b/tests/test_dns_query_set.c index 2efc88121be31dcc19dc762cec0f7c3f716cb4e1..98a6051db5f1d21139881a7bd4b1296a285087de 100644 --- a/tests/test_dns_query_set.c +++ b/tests/test_dns_query_set.c @@ -306,6 +306,26 @@ AST_TEST_DEFINE(query_set) return query_set_test(test, 4, 0); } +AST_TEST_DEFINE(query_set_empty) +{ + switch (cmd) { + case TEST_INIT: + info->name = "query_set_empty"; + info->category = "/main/dns/query_set/"; + info->summary = "Test nominal asynchronous empty DNS query set"; + info->description = + "This tests nominal query set in the following ways:\n" + "\t* No queries are added to a query set\n" + "\t* Asynchronous resolution of the query set is started\n" + "\t* We ensure that the query set callback is invoked upon completion"; + return AST_TEST_NOT_RUN; + case TEST_EXECUTE: + break; + } + + return query_set_test(test, 0, 0); +} + AST_TEST_DEFINE(query_set_nominal_cancel) { switch (cmd) { @@ -352,6 +372,7 @@ AST_TEST_DEFINE(query_set_off_nominal_cancel) static int unload_module(void) { AST_TEST_UNREGISTER(query_set); + AST_TEST_UNREGISTER(query_set_empty); AST_TEST_UNREGISTER(query_set_nominal_cancel); AST_TEST_UNREGISTER(query_set_off_nominal_cancel); @@ -361,6 +382,7 @@ static int unload_module(void) static int load_module(void) { AST_TEST_REGISTER(query_set); + AST_TEST_REGISTER(query_set_empty); AST_TEST_REGISTER(query_set_nominal_cancel); AST_TEST_REGISTER(query_set_off_nominal_cancel);