diff --git a/src/nss.c b/src/nss.c index 93d140a..2e1a90b 100644 --- a/src/nss.c +++ b/src/nss.c @@ -85,8 +85,8 @@ enum nss_status _nss_mdns_gethostbyname_impl(const char* name, int af, userdata_t* u, int* errnop, int* h_errnop) { - int name_allowed; FILE* mdns_allow_file = NULL; + use_name_result_t result; #ifdef NSS_IPV4_ONLY if (af == AF_UNSPEC) { @@ -118,13 +118,14 @@ enum nss_status _nss_mdns_gethostbyname_impl(const char* name, int af, #ifndef MDNS_MINIMAL mdns_allow_file = fopen(MDNS_ALLOW_FILE, "r"); #endif - name_allowed = verify_name_allowed_with_soa(name, mdns_allow_file); + result = verify_name_allowed_with_soa(name, mdns_allow_file, + TEST_LOCAL_SOA_AUTO); #ifndef MDNS_MINIMAL if (mdns_allow_file) fclose(mdns_allow_file); #endif - if (!name_allowed) { + if (result == USE_NAME_RESULT_SKIP) { *errnop = EINVAL; *h_errnop = NO_RECOVERY; return NSS_STATUS_UNAVAIL; @@ -137,6 +138,11 @@ enum nss_status _nss_mdns_gethostbyname_impl(const char* name, int af, case AVAHI_RESOLVE_RESULT_HOST_NOT_FOUND: *errnop = ETIMEDOUT; *h_errnop = HOST_NOT_FOUND; + if (result == USE_NAME_RESULT_OPTIONAL) { + /* continue to dns plugin if DNS .local zone is detected. */ + *h_errnop = TRY_AGAIN; + return NSS_STATUS_UNAVAIL; + } return NSS_STATUS_NOTFOUND; case AVAHI_RESOLVE_RESULT_UNAVAIL: diff --git a/src/util.c b/src/util.c index d5e0290..0a1c28a 100644 --- a/src/util.c +++ b/src/util.c @@ -55,16 +55,24 @@ int ends_with(const char* name, const char* suffix) { return strcasecmp(name + ln - ls, suffix) == 0; } -int verify_name_allowed_with_soa(const char* name, FILE* mdns_allow_file) { +use_name_result_t verify_name_allowed_with_soa(const char* name, + FILE* mdns_allow_file, + test_local_soa_t test) { switch (verify_name_allowed(name, mdns_allow_file)) { case VERIFY_NAME_RESULT_NOT_ALLOWED: - return 0; + return USE_NAME_RESULT_SKIP; case VERIFY_NAME_RESULT_ALLOWED: - return 1; + return USE_NAME_RESULT_AUTHORITATIVE; case VERIFY_NAME_RESULT_ALLOWED_IF_NO_LOCAL_SOA: - return !local_soa(); + if (test == TEST_LOCAL_SOA_YES || + (test == TEST_LOCAL_SOA_AUTO && local_soa()) ) + /* Make multicast resolution not authoritative for .local zone. + * Allow continuing to unicast resolution after multicast had not worked. */ + return USE_NAME_RESULT_OPTIONAL; + else + return USE_NAME_RESULT_AUTHORITATIVE; default: - return 0; + return USE_NAME_RESULT_SKIP; } } diff --git a/src/util.h b/src/util.h index 218c094..80527e3 100644 --- a/src/util.h +++ b/src/util.h @@ -61,6 +61,18 @@ char* buffer_strdup(buffer_t* buf, const char* str); int set_cloexec(int fd); int ends_with(const char* name, const char* suffix); +typedef enum { + USE_NAME_RESULT_SKIP, + USE_NAME_RESULT_AUTHORITATIVE, + USE_NAME_RESULT_OPTIONAL, +} use_name_result_t; + +typedef enum { + TEST_LOCAL_SOA_NO, + TEST_LOCAL_SOA_YES, + TEST_LOCAL_SOA_AUTO, +} test_local_soa_t; + // Returns true if we should try to resolve the name with mDNS. // // If mdns_allow_file is NULL, then this implements the "local" SOA @@ -71,7 +83,9 @@ int ends_with(const char* name, const char* suffix); // // The two heuristics described above are disabled if mdns_allow_file // is not NULL. -int verify_name_allowed_with_soa(const char* name, FILE* mdns_allow_file); +use_name_result_t verify_name_allowed_with_soa(const char* name, + FILE* mdns_allow_file, + test_local_soa_t test); typedef enum { VERIFY_NAME_RESULT_NOT_ALLOWED, diff --git a/tests/check_util.c b/tests/check_util.c index d600a2e..36f1008 100644 --- a/tests/check_util.c +++ b/tests/check_util.c @@ -50,6 +50,24 @@ START_TEST(test_verify_name_allowed_minimal) { VERIFY_NAME_RESULT_NOT_ALLOWED); ck_assert_int_eq(verify_name_allowed(".", NULL), VERIFY_NAME_RESULT_NOT_ALLOWED); + + ck_assert_int_eq(verify_name_allowed_with_soa(".", NULL, TEST_LOCAL_SOA_YES), + USE_NAME_RESULT_SKIP); + ck_assert_int_eq(verify_name_allowed_with_soa(".", NULL, TEST_LOCAL_SOA_NO), + USE_NAME_RESULT_SKIP); + ck_assert_int_eq(verify_name_allowed_with_soa(".", NULL, TEST_LOCAL_SOA_AUTO), + USE_NAME_RESULT_SKIP); + ck_assert_int_eq(verify_name_allowed_with_soa("example3.sub.local", + NULL, TEST_LOCAL_SOA_YES), USE_NAME_RESULT_SKIP); + ck_assert_int_eq(verify_name_allowed_with_soa("example4.sub.local", + NULL, TEST_LOCAL_SOA_NO), USE_NAME_RESULT_SKIP); + ck_assert_int_eq(verify_name_allowed_with_soa("example4.sub.local", + NULL, TEST_LOCAL_SOA_AUTO), USE_NAME_RESULT_SKIP); + ck_assert_int_eq(verify_name_allowed_with_soa("example1.local", + NULL, TEST_LOCAL_SOA_YES), USE_NAME_RESULT_OPTIONAL); + ck_assert_int_eq(verify_name_allowed_with_soa("example2.local", + NULL, TEST_LOCAL_SOA_NO), USE_NAME_RESULT_AUTHORITATIVE); + /* TEST_LOCAL_SOA_AUTO would test actual DNS on host, skip that. */ } END_TEST