From b7bc5240f0e4fb8a6fbfa24e5f78535bef4f1333 Mon Sep 17 00:00:00 2001 From: Michael Jerris Date: Wed, 11 Feb 2009 17:12:33 +0000 Subject: [PATCH] Fri Jan 23 11:13:41 CST 2009 Pekka Pessi * sresolv: caching SRES_RECORD_ERR in case a CNAME is returned, too Tracing the CNAMEs when doing cache lookups. git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@11844 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- libs/sofia-sip/.update | 2 +- .../libsofia-sip-ua/nua/check_simple.c | 522 ++++++++++++++++++ .../libsofia-sip-ua/sresolv/example.com | 5 + libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c | 35 +- .../libsofia-sip-ua/sresolv/sres_cache.c | 144 +++-- .../libsofia-sip-ua/sresolv/test_sresolv.c | 131 ++++- 6 files changed, 783 insertions(+), 56 deletions(-) create mode 100644 libs/sofia-sip/libsofia-sip-ua/nua/check_simple.c diff --git a/libs/sofia-sip/.update b/libs/sofia-sip/.update index dcb1fe5f5d..ec01cf3665 100644 --- a/libs/sofia-sip/.update +++ b/libs/sofia-sip/.update @@ -1 +1 @@ -Wed Feb 11 11:08:29 CST 2009 +Wed Feb 11 11:12:10 CST 2009 diff --git a/libs/sofia-sip/libsofia-sip-ua/nua/check_simple.c b/libs/sofia-sip/libsofia-sip-ua/nua/check_simple.c new file mode 100644 index 0000000000..d5bc3b5afa --- /dev/null +++ b/libs/sofia-sip/libsofia-sip-ua/nua/check_simple.c @@ -0,0 +1,522 @@ +/* + * This file is part of the Sofia-SIP package + * + * Copyright (C) 2008 Nokia Corporation. + * + * Contact: Pekka Pessi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +/**@CFILE check_events.c + * + * @brief NUA module tests for SIP events + * + * @author Pekka Pessi + * + * @copyright (C) 2008 Nokia Corporation. + */ + +#include "config.h" + +#include "check_nua.h" + +#include "test_s2.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* define XXX as 1 in order to see all failing test cases */ +#ifndef XXX +#define XXX (0) +#endif + +/* ====================================================================== */ + +static nua_t *nua; +static soa_session_t *soa = NULL; +static struct dialog *dialog = NULL; + +#define CRLF "\r\n" + +void s2_dialog_setup(void) +{ + nua = s2_nua_setup("simple", + SIPTAG_ORGANIZATION_STR("Pussy Galore's Flying Circus"), + NUTAG_OUTBOUND("no-options-keepalive, no-validate"), + TAG_END()); + + soa = soa_create(NULL, s2->root, NULL); + + fail_if(!soa); + + soa_set_params(soa, + SOATAG_USER_SDP_STR("m=audio 5008 RTP/AVP 8 0" CRLF + "m=video 5010 RTP/AVP 34" CRLF), + TAG_END()); + + dialog = su_home_new(sizeof *dialog); fail_if(!dialog); + + s2_register_setup(); +} + +void s2_dialog_teardown(void) +{ + s2_teardown_started("simple"); + + s2_register_teardown(); + + nua_shutdown(nua); + + fail_unless(s2_check_event(nua_r_shutdown, 200)); + + s2_nua_teardown(); +} + + +static char const presence_open[] = + "\n" + "\n" + " \n" + " open\n" + " sip:bob@example.org\n" + " \n" + "\n"; + +static char const presence_closed[] = + "\n" + "\n" + " \n" + " closed\n" + " \n" + "\n"; + +static char const *event_type = "presence"; +static char const *event_mime_type = "application/pidf+xml"; +static char const *event_state = presence_open; +static char const *subscription_state = "active;expires=600"; + +static struct event * +respond_to_subscribe(struct message *subscribe, + nua_event_t expect_event, + enum nua_substate expect_substate, + int status, char const *phrase, + tag_type_t tag, tag_value_t value, ...) +{ + struct event *event; + ta_list ta; + + ta_start(ta, tag, value); + s2_respond_to(subscribe, dialog, status, phrase, + ta_tags(ta)); + ta_end(ta); + + event = s2_wait_for_event(expect_event, status); fail_if(!event); + fail_unless(s2_check_substate(event, expect_substate)); + return event; +} + +static struct event * +notify_to_nua(enum nua_substate expect_substate, + tag_type_t tag, tag_value_t value, ...) +{ + struct event *event; + struct message *response; + ta_list ta; + + ta_start(ta, tag, value); + fail_if(s2_request_to(dialog, SIP_METHOD_NOTIFY, NULL, + SIPTAG_CONTENT_TYPE_STR(event_mime_type), + SIPTAG_PAYLOAD_STR(event_state), + ta_tags(ta))); + ta_end(ta); + + response = s2_wait_for_response(200, SIP_METHOD_NOTIFY); + fail_if(!response); + s2_free_message(response); + + event = s2_wait_for_event(nua_i_notify, 200); fail_if(!event); + fail_unless(s2_check_substate(event, expect_substate)); + + return event; +} + +static int send_notify_before_response = 0; + +static struct event * +subscription_by_nua(nua_handle_t *nh, + enum nua_substate current, + tag_type_t tag, tag_value_t value, ...) +{ + struct message *subscribe; + struct event *notify, *event; + ta_list ta; + enum nua_substate substate = nua_substate_active; + char const *substate_str = subscription_state; + char const *expires = "600"; + + subscribe = s2_wait_for_request(SIP_METHOD_SUBSCRIBE); + if (event_type) + fail_if(!subscribe->sip->sip_event || + strcmp(event_type, subscribe->sip->sip_event->o_type)); + + if (subscribe->sip->sip_expires && subscribe->sip->sip_expires->ex_delta == 0) { + substate = nua_substate_terminated; + substate_str = "terminated;reason=timeout"; + expires = "0"; + } + + ta_start(ta, tag, value); + + if (send_notify_before_response) { + s2_save_uas_dialog(dialog, subscribe->sip); + notify = notify_to_nua(substate, + SIPTAG_EVENT(subscribe->sip->sip_event), + SIPTAG_SUBSCRIPTION_STATE_STR(substate_str), + ta_tags(ta)); + event = respond_to_subscribe(subscribe, nua_r_subscribe, substate, + SIP_200_OK, + SIPTAG_EXPIRES_STR(expires), + TAG_END()); + s2_free_event(event); + } + else { + event = respond_to_subscribe(subscribe, nua_r_subscribe, current, + SIP_202_ACCEPTED, + SIPTAG_EXPIRES_STR(expires), + TAG_END()); + s2_free_event(event); + notify = notify_to_nua(substate, + SIPTAG_EVENT(subscribe->sip->sip_event), + SIPTAG_SUBSCRIPTION_STATE_STR(substate_str), + ta_tags(ta)); + } + + s2_free_message(subscribe); + + return notify; +} + +static void +unsubscribe_by_nua(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...) +{ + struct message *subscribe, *response; + struct event *event; + + nua_unsubscribe(nh, TAG_END()); + subscribe = s2_wait_for_request(SIP_METHOD_SUBSCRIBE); + + s2_respond_to(subscribe, dialog, SIP_200_OK, SIPTAG_EXPIRES_STR("0"), TAG_END()); + + event = s2_wait_for_event(nua_r_unsubscribe, 200); fail_if(!event); + fail_unless(s2_check_substate(event, nua_substate_active)); + s2_free_event(event); + + fail_if(s2_request_to(dialog, SIP_METHOD_NOTIFY, NULL, + SIPTAG_EVENT(subscribe->sip->sip_event), + SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=tiemout"), + SIPTAG_CONTENT_TYPE_STR(event_mime_type), + SIPTAG_PAYLOAD_STR(event_state), + TAG_END())); + + event = s2_wait_for_event(nua_i_notify, 200); fail_if(!event); + fail_unless(s2_check_substate(event, nua_substate_terminated)); + s2_free_event(event); + + response = s2_wait_for_response(200, SIP_METHOD_NOTIFY); + fail_if(!response); + s2_free_message(response); s2_free_message(subscribe); +} + +/* ====================================================================== */ +/* 6 - Subscribe/notify */ + +START_TEST(subscribe_6_1_1) +{ + nua_handle_t *nh; + struct event *notify; + s2_case("6.1.1", "Basic subscription", + "NUA sends SUBSCRIBE, waits for NOTIFY, sends un-SUBSCRIBE"); + + nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END()); + nua_subscribe(nh, SIPTAG_EVENT_STR(event_type), TAG_END()); + notify = subscription_by_nua(nh, nua_substate_embryonic, TAG_END()); + s2_free_event(notify); + unsubscribe_by_nua(nh, TAG_END()); + nua_handle_destroy(nh); +} +END_TEST + +START_TEST(subscribe_6_1_2) +{ + nua_handle_t *nh; + struct message *subscribe, *response; + struct event *notify, *event; + + s2_case("6.1.2", "Basic subscription with refresh", + "NUA sends SUBSCRIBE, waits for NOTIFY, " + "sends re-SUBSCRIBE, waits for NOTIFY, " + "sends un-SUBSCRIBE"); + + send_notify_before_response = 1; + + nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END()); + nua_subscribe(nh, SIPTAG_EVENT_STR(event_type), TAG_END()); + notify = subscription_by_nua(nh, nua_substate_embryonic, TAG_END()); + s2_free_event(notify); + + /* Wait for refresh */ + s2_fast_forward(600); + subscribe = s2_wait_for_request(SIP_METHOD_SUBSCRIBE); + s2_respond_to(subscribe, dialog, SIP_200_OK, + SIPTAG_EXPIRES_STR("600"), + TAG_END()); + + event = s2_wait_for_event(nua_r_subscribe, 200); fail_if(!event); + fail_unless(s2_check_substate(event, nua_substate_active)); + s2_free_event(event); + + fail_if(s2_request_to(dialog, SIP_METHOD_NOTIFY, NULL, + SIPTAG_EVENT(subscribe->sip->sip_event), + SIPTAG_SUBSCRIPTION_STATE_STR("active;expires=600"), + SIPTAG_CONTENT_TYPE_STR(event_mime_type), + SIPTAG_PAYLOAD_STR(event_state), + TAG_END())); + event = s2_wait_for_event(nua_i_notify, 200); fail_if(!event); + fail_unless(s2_check_substate(event, nua_substate_active)); + s2_free_event(event); + response = s2_wait_for_response(200, SIP_METHOD_NOTIFY); + fail_if(!response); + s2_free_message(response); + + unsubscribe_by_nua(nh, TAG_END()); + + nua_handle_destroy(nh); +} +END_TEST + +START_TEST(subscribe_6_1_3) +{ + nua_handle_t *nh; + struct message *response; + struct event *notify, *event; + + s2_case("6.1.3", "Subscription terminated by notifier", + "NUA sends SUBSCRIBE, waits for NOTIFY, " + "gets NOTIFY terminating the subscription,"); + + nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END()); + nua_subscribe(nh, SIPTAG_EVENT_STR(event_type), TAG_END()); + notify = subscription_by_nua(nh, nua_substate_embryonic, TAG_END()); + s2_free_event(notify); + + fail_if(s2_request_to(dialog, SIP_METHOD_NOTIFY, NULL, + SIPTAG_EVENT_STR(event_type), + SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=noresource"), + TAG_END())); + event = s2_wait_for_event(nua_i_notify, 200); fail_if(!event); + fail_unless(s2_check_substate(event, nua_substate_terminated)); + s2_free_event(event); + response = s2_wait_for_response(200, SIP_METHOD_NOTIFY); + fail_if(!response); + s2_free_message(response); + + nua_handle_destroy(nh); +} +END_TEST + +START_TEST(subscribe_6_1_4) +{ + nua_handle_t *nh; + struct message *response; + struct event *notify, *event; + + s2_case("6.1.4", "Subscription terminated by notifier, re-established", + "NUA sends SUBSCRIBE, waits for NOTIFY, " + "gets NOTIFY terminating the subscription,"); + + nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END()); + nua_subscribe(nh, SIPTAG_EVENT_STR(event_type), TAG_END()); + notify = subscription_by_nua(nh, nua_substate_embryonic, TAG_END()); + s2_free_event(notify); + + fail_if(s2_request_to(dialog, SIP_METHOD_NOTIFY, NULL, + SIPTAG_EVENT_STR(event_type), + SIPTAG_SUBSCRIPTION_STATE_STR("terminated;reason=deactivated"), + TAG_END())); + event = s2_wait_for_event(nua_i_notify, 200); fail_if(!event); + fail_unless(s2_check_substate(event, nua_substate_embryonic)); + s2_free_event(event); + response = s2_wait_for_response(200, SIP_METHOD_NOTIFY); + fail_if(!response); + s2_free_message(response); + + su_home_unref((void *)dialog), dialog = su_home_new(sizeof *dialog); fail_if(!dialog); + + s2_fast_forward(5); + /* nua re-establishes the subscription */ + notify = subscription_by_nua(nh, nua_substate_embryonic, TAG_END()); + s2_free_event(notify); + + /* Unsubscribe with nua_subscribe() Expires: 0 */ + nua_subscribe(nh, SIPTAG_EVENT_STR(event_type), SIPTAG_EXPIRES_STR("0"), TAG_END()); + notify = subscription_by_nua(nh, nua_substate_active, TAG_END()); + s2_free_event(notify); + + nua_handle_destroy(nh); +} +END_TEST + +TCase *subscribe_tcase(void) +{ + TCase *tc = tcase_create("6.1 - Basic SUBSCRIBE_"); + tcase_add_checked_fixture(tc, s2_dialog_setup, s2_dialog_teardown); + { + tcase_add_test(tc, subscribe_6_1_1); + tcase_add_test(tc, subscribe_6_1_2); + tcase_add_test(tc, subscribe_6_1_3); + tcase_add_test(tc, subscribe_6_1_4); + } + return tc; +} + +START_TEST(fetch_6_2_1) +{ + nua_handle_t *nh; + struct event *notify; + + s2_case("6.2.1", "Event fetch - NOTIFY after 202", + "NUA sends SUBSCRIBE with Expires 0, waits for NOTIFY"); + + nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END()); + nua_subscribe(nh, SIPTAG_EVENT_STR(event_type), SIPTAG_EXPIRES_STR("0"), TAG_END()); + notify = subscription_by_nua(nh, nua_substate_embryonic, TAG_END()); + s2_check_substate(notify, nua_substate_terminated); + s2_free_event(notify); + nua_handle_destroy(nh); +} +END_TEST + +START_TEST(fetch_6_2_2) +{ + nua_handle_t *nh; + struct event *notify; + + s2_case("6.2.2", "Event fetch - NOTIFY before 200", + "NUA sends SUBSCRIBE with Expires 0, waits for NOTIFY"); + + send_notify_before_response = 1; + + nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END()); + nua_subscribe(nh, SIPTAG_EVENT_STR(event_type), SIPTAG_EXPIRES_STR("0"), TAG_END()); + notify = subscription_by_nua(nh, nua_substate_embryonic, TAG_END()); + s2_check_substate(notify, nua_substate_terminated); + s2_free_event(notify); + nua_handle_destroy(nh); +} +END_TEST + +START_TEST(fetch_6_2_3) +{ + nua_handle_t *nh; + struct message *subscribe; + struct event *event; + + s2_case("6.2.3", "Event fetch - no NOTIFY", + "NUA sends SUBSCRIBE with Expires 0, waits for NOTIFY, times out"); + + nh = nua_handle(nua, NULL, SIPTAG_TO(s2->local), TAG_END()); + nua_subscribe(nh, SIPTAG_EVENT_STR(event_type), SIPTAG_EXPIRES_STR("0"), TAG_END()); + subscribe = s2_wait_for_request(SIP_METHOD_SUBSCRIBE); + s2_respond_to(subscribe, dialog, SIP_202_ACCEPTED, + SIPTAG_EXPIRES_STR("0"), TAG_END()); + s2_free_message(subscribe); + + event = s2_wait_for_event(nua_r_subscribe, 202); fail_if(!event); + fail_unless(s2_check_substate(event, nua_substate_embryonic)); + s2_free_event(event); + + s2_fast_forward(600); + + event = s2_wait_for_event(nua_i_notify, 408); fail_if(!event); + fail_unless(s2_check_substate(event, nua_substate_terminated)); + s2_free_event(event); + + nua_handle_destroy(nh); +} +END_TEST + + +TCase *fetch_tcase(void) +{ + TCase *tc = tcase_create("6.2 - Event fetch"); + tcase_add_checked_fixture(tc, s2_dialog_setup, s2_dialog_teardown); + { + tcase_add_test(tc, fetch_6_2_1); + tcase_add_test(tc, fetch_6_2_2); + tcase_add_test(tc, fetch_6_2_3); + } + return tc; +} + +/* ====================================================================== */ + +/* Test case template */ + +START_TEST(empty) +{ + s2_case("0.0.0", "Empty test case", + "Detailed explanation for empty test case."); + + tport_set_params(s2->master, TPTAG_LOG(1), TAG_END()); + s2_setup_logs(7); + s2_setup_logs(0); + tport_set_params(s2->master, TPTAG_LOG(0), TAG_END()); +} + +END_TEST + +static TCase *empty_tcase(void) +{ + TCase *tc = tcase_create("0 - Empty"); + tcase_add_checked_fixture(tc, s2_dialog_setup, s2_dialog_teardown); + tcase_add_test(tc, empty); + + return tc; +} + +/* ====================================================================== */ + +void check_simple_cases(Suite *suite) +{ + suite_add_tcase(suite, subscribe_tcase()); + suite_add_tcase(suite, fetch_tcase()); + + if (0) /* Template */ + suite_add_tcase(suite, empty_tcase()); +} + diff --git a/libs/sofia-sip/libsofia-sip-ua/sresolv/example.com b/libs/sofia-sip/libsofia-sip-ua/sresolv/example.com index 2103afca27..d1c424acd3 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sresolv/example.com +++ b/libs/sofia-sip/libsofia-sip-ua/sresolv/example.com @@ -42,6 +42,11 @@ _sips._udp SRV 3 100 5061 sip00 SRV 4 50 5051 sip02 SRV 5 10 5061 sip01 +cloop CNAME cloop0 +cloop0 CNAME cloop1 +cloop1 CNAME cloop2 +cloop2 CNAME cloop0 + sip CNAME sip00 subnet A6 0 3ff0:0:: diff --git a/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c b/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c index 2f96e0aab7..67837c844a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c +++ b/libs/sofia-sip/libsofia-sip-ua/sresolv/sres.c @@ -495,7 +495,9 @@ static int sres_decode_msg(sres_resolver_t *res, static char const *sres_toplevel(char buf[], size_t bsize, char const *domain); -static sres_record_t *sres_create_record(sres_resolver_t *, sres_message_t *m); +static sres_record_t *sres_create_record(sres_resolver_t *, + sres_message_t *m, + int nth); static sres_record_t *sres_init_rr_soa(sres_cache_t *cache, sres_soa_record_t *, @@ -3539,7 +3541,7 @@ sres_decode_msg(sres_resolver_t *res, total = errorcount + m->m_ancount + m->m_nscount + m->m_arcount; - answers = su_zalloc(chome, (total + 1) * sizeof answers[0]); + answers = su_zalloc(chome, (total + 2) * sizeof answers[0]); if (!answers) return -1; @@ -3548,7 +3550,7 @@ sres_decode_msg(sres_resolver_t *res, if (i < errorcount) rr = error = sres_create_error_rr(res->res_cache, query, err); else - rr = sres_create_record(res, m); + rr = sres_create_record(res, m, i - errorcount); if (!rr) { SU_DEBUG_5(("sres_create_record: %s\n", m->m_error)); @@ -3565,12 +3567,31 @@ sres_decode_msg(sres_resolver_t *res, } if (i < total) { + SU_DEBUG_5(("sres_decode_msg: %s\n", "less records than promised")); for (i = 0; i < total; i++) sres_cache_free_record(res->res_cache, answers[i]); su_free(chome, answers); return -1; } + if (m->m_ancount > 0 && errorcount == 0 && query->q_type < sres_qtype_tsig) { + char b0[8], b1[8]; + for (i = 0; i < m->m_ancount; i++) { + if (query->q_type == answers[i]->sr_type) + break; + } + + if (i == m->m_ancount) { + /* The queried request was not found. CNAME? */ + SU_DEBUG_5(("sres_decode_msg: sent query %s, got %s\n", + sres_record_type(query->q_type, b0), + sres_record_type(answers[0]->sr_type, b1))); + rr = sres_create_error_rr(res->res_cache, query, err = SRES_RECORD_ERR); + memmove(answers + 1, answers, (sizeof answers[0]) * total++); + answers[errorcount++] = rr; + } + } + for (i = 0; i < total; i++) { rr = answers[i]; @@ -3591,7 +3612,7 @@ sres_decode_msg(sres_resolver_t *res, static sres_record_t * -sres_create_record(sres_resolver_t *res, sres_message_t *m) +sres_create_record(sres_resolver_t *res, sres_message_t *m, int nth) { sres_cache_t *cache = res->res_cache; sres_record_t *sr, sr0[1]; @@ -3614,7 +3635,11 @@ sres_create_record(sres_resolver_t *res, sres_message_t *m) name[len] = 0; - SU_DEBUG_9(("RR received %s %s %s %d rdlen=%d\n", name, + SU_DEBUG_9(("%s RR received %s %s %s %d rdlen=%d\n", + nth < m->m_ancount ? "ANSWER" : + nth < m->m_ancount + m->m_nscount ? "AUTHORITY" : + "ADDITIONAL", + name, sres_record_type(sr->sr_type, btype), sres_record_class(sr->sr_class, bclass), sr->sr_ttl, sr->sr_rdlen)); diff --git a/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c b/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c index 053b527062..7c5a6bfe1a 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c +++ b/libs/sofia-sip/libsofia-sip-ua/sresolv/sres_cache.c @@ -169,15 +169,98 @@ void sres_cache_unref(sres_cache_t *cache) su_home_unref(cache->cache_home); } +struct frame { + struct frame *previous; + char const *domain; +}; + +/** Count or get matching records from cache */ +static int +sres_cache_get0(sres_htable_t *htable, + sres_rr_hash_entry_t **iter, + uint16_t type, + char const *domain, + time_t now, + sres_record_t **cached, + int len, + struct frame *previous) +{ + sres_cname_record_t *cname = NULL; + int dcount = 0, derrorcount = 0, ccount = 0; + + for (; iter && *iter; iter = sres_htable_next(htable, iter)) { + sres_record_t *rr = (*iter)->rr; + + if (rr == NULL) + continue; + if (now > (*iter)->rr_expires) + continue; + if (rr->sr_name == NULL) + continue; + if (!su_casematch(rr->sr_name, domain)) + continue; + + if (rr->sr_type == type || type == sres_qtype_any) { + if (rr->sr_status == SRES_RECORD_ERR && type == sres_qtype_any) + continue; + if (cached) { + if (dcount >= len) + return -1; + cached[dcount] = rr, rr->sr_refcount++; + } + dcount++; + if (rr->sr_status) + derrorcount++; + } + + if (type != sres_type_cname && rr->sr_type == sres_type_cname) { + if (rr->sr_status == 0) + cname = rr->sr_cname; + } + } + + if (cname && dcount == derrorcount) { + /* Nothing found, trace CNAMEs */ + struct frame *f, frame = { previous, domain }; + unsigned hash = sres_hash_key(domain = cname->cn_cname); + + if (cached) { + if (dcount >= len) + return -1; + cached[dcount] = (sres_record_t *)cname; + cname->cn_record->r_refcount++; + } + dcount++; + + /* Check for cname loops */ + for (f = previous; f; f = f->previous) { + if (su_casematch(domain, f->domain)) + break; + } + + if (f == NULL) { + ccount = sres_cache_get0(htable, sres_htable_hash(htable, hash), + type, domain, now, + cached ? cached + dcount : NULL, + cached ? len - dcount : 0, + &frame); + } + if (ccount < 0) + return ccount; + } + + return dcount + ccount; +} + /** Get a list of matching records from cache. */ int sres_cache_get(sres_cache_t *cache, uint16_t type, char const *domain, sres_record_t ***return_cached) { - sres_record_t **result = NULL, *rr = NULL; - sres_rr_hash_entry_t **rr_iter, **rr_iter2; - int result_size, rr_count = 0; + sres_record_t **result = NULL; + sres_rr_hash_entry_t **slot; + int result_size, i, j; unsigned hash; time_t now; char b[8]; @@ -198,28 +281,16 @@ int sres_cache_get(sres_cache_t *cache, time(&now); /* First pass: just count the number of rr:s for array allocation */ - rr_iter2 = sres_htable_hash(cache->cache_hash, hash); + slot = sres_htable_hash(cache->cache_hash, hash); - /* Find the domain records from the hash table */ - for (rr_iter = rr_iter2; - rr_iter && *rr_iter; - rr_iter = sres_htable_next(cache->cache_hash, rr_iter)) { - rr = (*rr_iter)->rr; - - if (rr != NULL && - now <= (*rr_iter)->rr_expires && - (type == sres_qtype_any || rr->sr_type == type) && - rr->sr_name != NULL && - su_casematch(rr->sr_name, domain)) - rr_count++; - } - - if (rr_count == 0) { + i = sres_cache_get0(cache->cache_hash, slot, type, domain, now, + NULL, 0, NULL); + if (i <= 0) { UNLOCK(cache); return 0; } - result_size = (sizeof *result) * (rr_count + 1); + result_size = (sizeof *result) * (i + 1); result = su_zalloc(cache->cache_home, result_size); if (result == NULL) { UNLOCK(cache); @@ -227,35 +298,30 @@ int sres_cache_get(sres_cache_t *cache, } /* Second pass: add the rr pointers to the allocated array */ - - for (rr_iter = rr_iter2, rr_count = 0; - rr_iter && *rr_iter; - rr_iter = sres_htable_next(cache->cache_hash, rr_iter)) { - rr = (*rr_iter)->rr; - - if (rr != NULL && - now <= (*rr_iter)->rr_expires && - (type == sres_qtype_any || rr->sr_type == type) && - rr->sr_name != NULL && - su_casematch(rr->sr_name, domain)) { - SU_DEBUG_9(("rr found in cache: %s %02d\n", - rr->sr_name, rr->sr_type)); - - result[rr_count++] = rr; - rr->sr_refcount++; + j = sres_cache_get0(cache->cache_hash, slot, type, domain, now, + result, i, NULL); + if (i != j) { + /* Uh-oh. */ + SU_DEBUG_9(("%s(%p, %s, \"%s\") got %d != %d\n", "sres_cache_get", + (void *)cache, sres_record_type(type, b), domain, i, j)); + for (i = 0; i < result_size; i++) { + if (result[i]) + result[i]->sr_refcount--; } + su_free(cache->cache_home, result); + return 0; } - result[rr_count] = NULL; + result[i] = NULL; UNLOCK(cache); SU_DEBUG_9(("%s(%p, %s, \"%s\") returned %d entries\n", "sres_cache_get", - (void *)cache, sres_record_type(type, b), domain, rr_count)); + (void *)cache, sres_record_type(type, b), domain, i)); *return_cached = result; - return rr_count; + return i; } sres_record_t * diff --git a/libs/sofia-sip/libsofia-sip-ua/sresolv/test_sresolv.c b/libs/sofia-sip/libsofia-sip-ua/sresolv/test_sresolv.c index 868c88fcd9..5e20e58a47 100644 --- a/libs/sofia-sip/libsofia-sip-ua/sresolv/test_sresolv.c +++ b/libs/sofia-sip/libsofia-sip-ua/sresolv/test_sresolv.c @@ -90,6 +90,10 @@ typedef unsigned _int32 uint32_t; #include #endif +#include + +extern su_log_t sresolv_log[]; + char const name[] = "test_sresolv"; struct sres_context_s @@ -982,24 +986,133 @@ int test_srv(sres_context_t *ctx) int test_cname(sres_context_t *ctx) { sres_resolver_t *res = ctx->resolver; - sres_record_t **result; - const sres_cname_record_t *rr; + sres_record_t **result, *sr; + const sres_cname_record_t *cn; char const *domain = "sip.example.com"; BEGIN(); + TEST_1(sres_query(res, test_answer, ctx, sres_type_naptr, domain)); + TEST_RUN(ctx); + + TEST_1(result = ctx->result); + TEST_1(sr = result[0]); + TEST_1(sr->sr_record->r_status == SRES_RECORD_ERR); + TEST_1(cn = result[1]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "sip00.example.com."); + + sres_free_answers(res, ctx->result), ctx->result = NULL; + + TEST_1(result = sres_cached_answers(res, sres_type_naptr, domain)); + TEST_1(sr = result[0]); + TEST_1(sr->sr_record->r_status == SRES_RECORD_ERR); + TEST_1(cn = result[1]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "sip00.example.com."); + + sres_free_answers(res, result), ctx->result = NULL; + + TEST_1(result = sres_cached_answers(res, sres_qtype_any, domain)); + TEST_1(cn = result[0]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "sip00.example.com."); + /* We might have A record, or then not */ + + sres_free_answers(res, result), ctx->result = NULL; + TEST_1(sres_query(res, test_answer, ctx, sres_type_a, domain)); TEST_RUN(ctx); TEST_1(result = ctx->result); - TEST_1(rr = result[0]->sr_cname); - TEST(rr->cn_record->r_class, sres_class_in); - TEST(rr->cn_record->r_type, sres_type_cname); - TEST(rr->cn_record->r_ttl, 60); - TEST_S(rr->cn_cname, "sip00.example.com."); + TEST_1(cn = result[0]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "sip00.example.com."); + TEST_1(sr = result[1]); + TEST(sr->sr_record->r_class, sres_class_in); + TEST(sr->sr_record->r_type, sres_type_a); sres_free_answers(res, ctx->result), ctx->result = NULL; + TEST_1(result = sres_cached_answers(res, sres_type_a, domain)); + TEST_1(cn = result[0]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "sip00.example.com."); + TEST_1(sr = result[1]); + TEST(sr->sr_record->r_class, sres_class_in); + TEST(sr->sr_record->r_type, sres_type_a); + + sres_free_answers(res, result); + + domain = "cloop.example.com"; + + TEST_1(sres_query(res, test_answer, ctx, sres_type_a, domain)); + TEST_RUN(ctx); + + TEST_1(result = ctx->result); + TEST_1(sr = result[0]); + TEST_1(sr->sr_record->r_status == SRES_RECORD_ERR); + TEST_1(cn = result[1]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "cloop0.example.com."); + TEST_1(cn = result[2]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "cloop1.example.com."); + TEST_1(cn = result[3]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "cloop2.example.com."); + TEST_1(cn = result[4]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "cloop0.example.com."); + TEST_1(result[5] == NULL); + + sres_free_answers(res, ctx->result), ctx->result = NULL; + + TEST_1(result = sres_cached_answers(res, sres_type_a, domain)); + TEST_1(sr = result[0]); + TEST_1(sr->sr_record->r_status == SRES_RECORD_ERR); + TEST_1(cn = result[1]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "cloop0.example.com."); + TEST_1(cn = result[2]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "cloop1.example.com."); + TEST_1(cn = result[3]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "cloop2.example.com."); + TEST_1(cn = result[4]->sr_cname); + TEST(cn->cn_record->r_class, sres_class_in); + TEST(cn->cn_record->r_type, sres_type_cname); + TEST(cn->cn_record->r_ttl, 60); + TEST_S(cn->cn_cname, "cloop0.example.com."); + TEST_1(result[5] == NULL); + + sres_free_answers(res, result); + END(); } @@ -1892,10 +2005,6 @@ void usage(int exitcode) exit(exitcode); } -#include - -extern su_log_t sresolv_log[]; - int main(int argc, char **argv) { int i;