| /* |
| ********************************************************************** |
| * Copyright (C) Miroslav Lichvar 2016, 2021 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of version 2 of the GNU General Public License as |
| * published by the Free Software Foundation. |
| * |
| * This program 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 |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program; if not, write to the Free Software Foundation, Inc., |
| * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
| * |
| ********************************************************************** |
| */ |
| |
| #include <config.h> |
| #include "test.h" |
| |
| #ifdef FEAT_NTP |
| |
| #include <conf.h> |
| #include <cmdparse.h> |
| #include <nameserv_async.h> |
| #include <ntp_core.h> |
| #include <ntp_io.h> |
| |
| static char *requested_name = NULL; |
| static DNS_NameResolveHandler resolve_handler = NULL; |
| static void *resolve_handler_arg = NULL; |
| |
| #define DNS_Name2IPAddressAsync(name, handler, arg) \ |
| requested_name = (name), \ |
| resolve_handler = (handler), \ |
| resolve_handler_arg = (arg) |
| #define NCR_ChangeRemoteAddress(inst, remote_addr, ntp_only) \ |
| change_remote_address(inst, remote_addr, ntp_only) |
| #define NCR_ProcessRxKnown(remote_addr, local_addr, ts, msg, len) (random() % 2) |
| #define NIO_IsServerConnectable(addr) (random() % 2) |
| |
| static void change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr, |
| int ntp_only); |
| |
| #include <ntp_sources.c> |
| |
| #undef NCR_ChangeRemoteAddress |
| |
| static void |
| resolve_random_address(DNS_Status status, int rand_bits) |
| { |
| IPAddr ip_addrs[DNS_MAX_ADDRESSES]; |
| int i, n_addrs; |
| |
| TEST_CHECK(requested_name); |
| requested_name = NULL; |
| |
| if (status == DNS_Success) { |
| n_addrs = random() % DNS_MAX_ADDRESSES + 1; |
| for (i = 0; i < n_addrs; i++) |
| TST_GetRandomAddress(&ip_addrs[i], IPADDR_UNSPEC, rand_bits); |
| } else { |
| n_addrs = 0; |
| } |
| |
| (resolve_handler)(status, n_addrs, ip_addrs, resolve_handler_arg); |
| } |
| |
| static int |
| update_random_address(NTP_Remote_Address *addr, int rand_bits) |
| { |
| NTP_Remote_Address new_addr; |
| NSR_Status status; |
| |
| TST_GetRandomAddress(&new_addr.ip_addr, IPADDR_UNSPEC, rand_bits); |
| new_addr.port = random() % 1024; |
| |
| status = NSR_UpdateSourceNtpAddress(addr, &new_addr); |
| if (status == NSR_InvalidAF) { |
| TEST_CHECK(!UTI_IsIPReal(&addr->ip_addr)); |
| } else { |
| TEST_CHECK(status == NSR_Success || status == NSR_AlreadyInUse); |
| } |
| |
| return status == NSR_Success; |
| } |
| |
| static void |
| change_remote_address(NCR_Instance inst, NTP_Remote_Address *remote_addr, int ntp_only) |
| { |
| int update = !ntp_only && random() % 4 == 0, update_pos = random() % 2, r = 0; |
| |
| TEST_CHECK(record_lock); |
| |
| if (update && update_pos == 0) |
| r = update_random_address(remote_addr, 4); |
| |
| NCR_ChangeRemoteAddress(inst, remote_addr, ntp_only); |
| |
| if (update && update_pos == 1) |
| r = update_random_address(remote_addr, 4); |
| |
| if (r) |
| TEST_CHECK(UTI_IsIPReal(&saved_address_update.old_address.ip_addr)); |
| } |
| |
| void |
| test_unit(void) |
| { |
| char source_line[] = "127.0.0.1 offline", conf[] = "port 0", name[64]; |
| int i, j, k, slot, found, pool, prev_n; |
| uint32_t hash = 0, conf_id; |
| NTP_Remote_Address addrs[256], addr; |
| NTP_Local_Address local_addr; |
| NTP_Local_Timestamp local_ts; |
| struct UnresolvedSource *us; |
| RPT_ActivityReport report; |
| CPS_NTP_Source source; |
| NSR_Status status; |
| NTP_Packet msg; |
| |
| CNF_Initialise(0, 0); |
| CNF_ParseLine(NULL, 1, conf); |
| |
| PRV_Initialise(); |
| LCL_Initialise(); |
| TST_RegisterDummyDrivers(); |
| SCH_Initialise(); |
| SRC_Initialise(); |
| NIO_Initialise(); |
| NCR_Initialise(); |
| REF_Initialise(); |
| NSR_Initialise(); |
| |
| CPS_ParseNTPSourceAdd(source_line, &source); |
| |
| TEST_CHECK(n_sources == 0); |
| |
| for (i = 0; i < 6; i++) { |
| TEST_CHECK(ARR_GetSize(records) == 1); |
| |
| DEBUG_LOG("collision mod %u", 1U << i); |
| |
| for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) { |
| while (1) { |
| do { |
| TST_GetRandomAddress(&addrs[j].ip_addr, IPADDR_UNSPEC, -1); |
| } while (UTI_IPToHash(&addrs[j].ip_addr) % (1U << i) != hash % (1U << i)); |
| |
| for (k = 0; k < j; k++) |
| if (UTI_CompareIPs(&addrs[k].ip_addr, &addrs[j].ip_addr, NULL) == 0) |
| break; |
| if (k == j) |
| break; |
| } |
| |
| addrs[j].port = random() % 1024; |
| |
| if (!j) |
| hash = UTI_IPToHash(&addrs[j].ip_addr); |
| |
| DEBUG_LOG("adding source %s hash %"PRIu32, UTI_IPToString(&addrs[j].ip_addr), |
| UTI_IPToHash(&addrs[j].ip_addr) % (1U << i)); |
| |
| status = NSR_AddSource(&addrs[j], random() % 2 ? NTP_SERVER : NTP_PEER, |
| &source.params, NULL); |
| TEST_CHECK(status == NSR_Success); |
| TEST_CHECK(n_sources == j + 1); |
| |
| TEST_CHECK(strcmp(NSR_GetName(&addrs[j].ip_addr), UTI_IPToString(&addrs[j].ip_addr)) == 0); |
| |
| for (k = 0; k <= j; k++) { |
| addr = addrs[k]; |
| found = find_slot2(&addr, &slot); |
| TEST_CHECK(found == 2); |
| TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr, |
| &addr.ip_addr, NULL)); |
| addr.port++; |
| found = find_slot2(&addr, &slot); |
| TEST_CHECK(found == 1); |
| TEST_CHECK(!UTI_CompareIPs(&get_record(slot)->remote_addr->ip_addr, |
| &addr.ip_addr, NULL)); |
| } |
| |
| status = NSR_AddSource(&addrs[j], NTP_SERVER, &source.params, &conf_id); |
| TEST_CHECK(status == NSR_AlreadyInUse); |
| } |
| |
| for (j = 0; j < sizeof (addrs) / sizeof (addrs[0]); j++) { |
| DEBUG_LOG("removing source %s", UTI_IPToString(&addrs[j].ip_addr)); |
| NSR_RemoveSource(&addrs[j].ip_addr); |
| |
| for (k = 0; k < sizeof (addrs) / sizeof (addrs[0]); k++) { |
| found = find_slot2(&addrs[k], &slot); |
| TEST_CHECK(found == (k <= j ? 0 : 2)); |
| } |
| } |
| } |
| |
| TEST_CHECK(n_sources == 0); |
| |
| status = NSR_AddSourceByName("a b", 0, 0, 0, &source.params, &conf_id); |
| TEST_CHECK(status == NSR_InvalidName); |
| |
| local_addr.ip_addr.family = IPADDR_INET4; |
| local_addr.ip_addr.addr.in4 = 0; |
| local_addr.if_index = -1; |
| local_addr.sock_fd = 0; |
| memset(&local_ts, 0, sizeof (local_ts)); |
| |
| for (i = 0; i < 500; i++) { |
| for (j = 0; j < 20; j++) { |
| snprintf(name, sizeof (name), "ntp%d.example.net", (int)(random() % 10)); |
| pool = random() % 2; |
| prev_n = n_sources; |
| |
| DEBUG_LOG("%d/%d adding source %s pool=%d", i, j, name, pool); |
| status = NSR_AddSourceByName(name, 0, pool, random() % 2 ? NTP_SERVER : NTP_PEER, |
| &source.params, &conf_id); |
| TEST_CHECK(status == NSR_UnresolvedName); |
| |
| TEST_CHECK(n_sources == prev_n + (pool ? source.params.max_sources * 2 : 1)); |
| TEST_CHECK(unresolved_sources); |
| |
| for (us = unresolved_sources; us->next; us = us->next) |
| ; |
| TEST_CHECK(strcmp(us->name, name) == 0); |
| if (pool) { |
| TEST_CHECK(us->address.ip_addr.family == IPADDR_UNSPEC && us->pool_id >= 0); |
| } else { |
| TEST_CHECK(strcmp(NSR_GetName(&us->address.ip_addr), name) == 0); |
| TEST_CHECK(find_slot2(&us->address, &slot) == 2); |
| } |
| |
| if (random() % 2) { |
| if (!resolving_id || random() % 2) { |
| NSR_ResolveSources(); |
| } else { |
| SCH_RemoveTimeout(resolving_id); |
| resolve_sources_timeout(NULL); |
| TEST_CHECK(resolving_id == 0); |
| TEST_CHECK(requested_name); |
| } |
| |
| TEST_CHECK(!!unresolved_sources == (resolving_id != 0) || requested_name); |
| } |
| |
| while (requested_name && random() % 2) { |
| TEST_CHECK(resolving_source); |
| TEST_CHECK(strcmp(requested_name, resolving_source->name) == 0); |
| TEST_CHECK(!record_lock); |
| |
| switch (random() % 3) { |
| case 0: |
| resolve_random_address(DNS_Success, 4); |
| break; |
| case 1: |
| resolve_random_address(DNS_TryAgain, 0); |
| break; |
| case 2: |
| resolve_random_address(DNS_Failure, 0); |
| break; |
| } |
| } |
| |
| while (random() % 8 > 0) { |
| slot = random() % ARR_GetSize(records); |
| if (!get_record(slot)->remote_addr) |
| continue; |
| |
| switch (random() % 5) { |
| case 0: |
| msg.lvm = NTP_LVM(0, NTP_VERSION, random() % 2 ? MODE_CLIENT : MODE_SERVER); |
| NSR_ProcessTx(get_record(slot)->remote_addr, &local_addr, |
| &local_ts, &msg, 0); |
| break; |
| case 1: |
| msg.lvm = NTP_LVM(0, NTP_VERSION, random() % 2 ? MODE_CLIENT : MODE_SERVER); |
| NSR_ProcessRx(get_record(slot)->remote_addr, &local_addr, |
| &local_ts, &msg, 0); |
| break; |
| case 2: |
| NSR_HandleBadSource(&get_record(slot)->remote_addr->ip_addr); |
| break; |
| case 3: |
| NSR_SetConnectivity(NULL, &get_record(slot)->remote_addr->ip_addr, SRC_OFFLINE); |
| break; |
| case 4: |
| update_random_address(get_record(slot)->remote_addr, 4); |
| TEST_CHECK(!UTI_IsIPReal(&saved_address_update.old_address.ip_addr)); |
| break; |
| } |
| |
| TEST_CHECK(!record_lock); |
| } |
| |
| NSR_GetActivityReport(&report); |
| TEST_CHECK(report.online == 0); |
| TEST_CHECK(report.offline >= 0); |
| TEST_CHECK(report.burst_online == 0); |
| TEST_CHECK(report.burst_offline == 0); |
| TEST_CHECK(report.unresolved >= 0); |
| |
| if (random() % 4 == 0) { |
| NSR_RemoveSourcesById(conf_id); |
| TEST_CHECK(n_sources <= prev_n); |
| } else if (random() % 8 == 0) { |
| NSR_RefreshAddresses(); |
| TEST_CHECK(unresolved_sources); |
| } |
| } |
| |
| NSR_RemoveAllSources(); |
| TEST_CHECK(n_sources == 0); |
| |
| for (j = 0; j < ARR_GetSize(pools); j++) { |
| TEST_CHECK(get_pool(j)->sources == 0); |
| TEST_CHECK(get_pool(j)->unresolved_sources == 0); |
| TEST_CHECK(get_pool(j)->confirmed_sources == 0); |
| TEST_CHECK(get_pool(j)->max_sources == 0); |
| } |
| |
| while (requested_name) { |
| TEST_CHECK(resolving_source); |
| resolve_random_address(random() % 2 ? DNS_Success : DNS_TryAgain, 4); |
| } |
| |
| if (unresolved_sources && resolving_id == 0) |
| NSR_ResolveSources(); |
| |
| TEST_CHECK(!!unresolved_sources == (resolving_id != 0)); |
| |
| if (resolving_id) { |
| SCH_RemoveTimeout(resolving_id); |
| resolve_sources_timeout(NULL); |
| } |
| |
| TEST_CHECK(resolving_id == 0); |
| TEST_CHECK(!requested_name); |
| TEST_CHECK(!unresolved_sources); |
| } |
| |
| NSR_Finalise(); |
| REF_Finalise(); |
| NCR_Finalise(); |
| NIO_Finalise(); |
| SRC_Finalise(); |
| SCH_Finalise(); |
| LCL_Finalise(); |
| PRV_Finalise(); |
| CNF_Finalise(); |
| HSH_Finalise(); |
| } |
| |
| #else |
| void |
| test_unit(void) |
| { |
| TEST_REQUIRE(0); |
| } |
| #endif |