dns: async_dns модуль (libdns) + неблокирующий NTP sync
- Вендор libdns (MIT) как lib/dns.{c,h}; правка dns_quietinit для GCC 5+
- Новый lib/async_dns.{c,h}: неблокирующий A-резолвер поверх uasync
- src/ntp_time.c: убраны блокирующие getaddrinfo+select; async DNS + неблокирующая машина состояний UDP-запроса
- test_async_dns.c (юнит) + test_ntp.c сценарий hostname→DNS
- сборка: -DDNS_RANDOM=RAND_bytes, -lcrypto в COMMON_LIBS
This commit is contained in:
1 parent
d04e9d58b6
commit
1f307046f9
10 files changed
+11488
-256
No files matched your search
+261
-136
@@ -5,19 +5,10 @@
|
||||
#include "../lib/socket_compat.h"
|
||||
#include "../lib/u_async.h"
|
||||
#include "../lib/mem.h"
|
||||
#include "../lib/async_dns.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#define NTP_DELTA 2208988800ULL // seconds from 1900 to 1970
|
||||
#define NTP_PORT 123
|
||||
#define NTP_TIMEOUT_SEC 2
|
||||
@@ -41,7 +32,32 @@ struct ntp_packet {
|
||||
|
||||
_Static_assert(sizeof(struct ntp_packet) == 48, "NTP packet must be 48 bytes");
|
||||
|
||||
/* ─── синхронизация: неблокирующая машина состояний на uasync ─── */
|
||||
|
||||
struct ntp_sync {
|
||||
struct UTUN_INSTANCE* inst;
|
||||
int server_idx; /* следующий сервер для попытки */
|
||||
int query_idx; /* сервер, который сейчас опрашиваем (-1 для test_addr) */
|
||||
int resolving_idx; /* сервер в процессе DNS-резолва */
|
||||
struct adns_query* dns_q; /* активный DNS-запрос (NULL когда нет) */
|
||||
socket_t sock; /* NTP UDP-сокет (SOCKET_INVALID когда закрыт) */
|
||||
void* socket_id; /* handle uasync */
|
||||
void* timer; /* handle таймаута запроса */
|
||||
uint8_t req[48]; /* NTP-запрос (xmit_ts заполняется один раз на сервер) */
|
||||
uint64_t t1_ntp; /* originate timestamp */
|
||||
int tries; /* попыток на текущем сервере */
|
||||
struct sockaddr_in qaddr;/* адрес текущего сервера */
|
||||
};
|
||||
|
||||
static void ntp_time_sync_cb(void* arg);
|
||||
static void ntp_try_next(struct ntp_sync* ctx);
|
||||
static void ntp_start_query(struct ntp_sync* ctx, int idx, const struct sockaddr_in* addr);
|
||||
static void ntp_send_request(struct ntp_sync* ctx);
|
||||
static void ntp_query_fail(struct ntp_sync* ctx);
|
||||
static void ntp_finish_cycle(struct ntp_sync* ctx, int synced);
|
||||
static void ntp_resolve_done_cb(const struct adns_result* res, void* arg);
|
||||
static void ntp_query_read_cb(socket_t sock, void* arg);
|
||||
static void ntp_query_timeout_cb(void* arg);
|
||||
|
||||
static uint64_t timeval_to_ntp(struct timeval *tv) {
|
||||
uint64_t sec = (uint64_t)(tv->tv_sec + NTP_DELTA);
|
||||
@@ -55,13 +71,9 @@ static int64_t ntp64_to_us(uint64_t ntp) {
|
||||
return sec * 1000000LL + frac;
|
||||
}
|
||||
|
||||
static int ntp_do_query(const struct sockaddr_in* sin, const char* server_name, int64_t* offset_us_out, int* stratum_out) {
|
||||
socket_t sock = socket_create_udp(AF_INET);
|
||||
if (sock == SOCKET_INVALID) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: socket() failed: %s", socket_strerror(socket_get_error()));
|
||||
return -1;
|
||||
}
|
||||
/* ─── чистые функции сборки/разбора пакета ─── */
|
||||
|
||||
static void ntp_build_request(uint8_t* req, uint64_t* t1_ntp_out) {
|
||||
struct ntp_packet request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.li_vn_mode = (0 << 6) | (4 << 3) | 3;
|
||||
@@ -73,43 +85,23 @@ static int ntp_do_query(const struct sockaddr_in* sin, const char* server_name,
|
||||
#endif
|
||||
uint64_t t1_ntp = timeval_to_ntp(&t1_tv);
|
||||
request.xmit_ts = htobe64(t1_ntp);
|
||||
memcpy(req, &request, sizeof(request));
|
||||
if (t1_ntp_out) *t1_ntp_out = t1_ntp;
|
||||
}
|
||||
|
||||
int send_ok = 0;
|
||||
int tries;
|
||||
for (tries = 0; tries < NTP_MAX_TRIES; tries++) {
|
||||
ssize_t sent = sendto(sock, (const char*)&request, sizeof(request), 0,
|
||||
(const struct sockaddr*)sin, sizeof(*sin));
|
||||
if (sent < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: sendto(%s) failed: %s", server_name, socket_strerror(socket_get_error()));
|
||||
break;
|
||||
}
|
||||
/* возвращает 0 если ответ валиден; иначе -1 (пакет игнорируется, ждём timeout) */
|
||||
static int ntp_parse_reply(const uint8_t* buf, size_t len, uint64_t t1_ntp,
|
||||
int64_t* offset_us_out, int64_t* rtt_us_out, int* stratum_out) {
|
||||
if (len < sizeof(struct ntp_packet)) return -1;
|
||||
const struct ntp_packet* reply = (const struct ntp_packet*)buf;
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
struct timeval tv = {NTP_TIMEOUT_SEC, 0};
|
||||
int r = select((int)(sock + 1), &fds, NULL, NULL, &tv);
|
||||
if (r < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: select() failed: %s", socket_strerror(socket_get_error()));
|
||||
break;
|
||||
}
|
||||
if (r > 0) { send_ok = 1; break; }
|
||||
}
|
||||
if (!send_ok) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP: no response from %s after %d tries", server_name, tries);
|
||||
socket_close_wrapper(sock);
|
||||
return -1;
|
||||
}
|
||||
uint8_t mode = reply->li_vn_mode & 0x07;
|
||||
if (mode != 4) return -1;
|
||||
|
||||
struct ntp_packet reply;
|
||||
struct sockaddr_in from;
|
||||
socklen_t from_len = sizeof(from);
|
||||
ssize_t n = recvfrom(sock, (char*)&reply, sizeof(reply), 0, (struct sockaddr*)&from, &from_len);
|
||||
if (n < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: recvfrom(%s) failed: %s", server_name, socket_strerror(socket_get_error()));
|
||||
socket_close_wrapper(sock);
|
||||
return -1;
|
||||
}
|
||||
int stratum = reply->stratum;
|
||||
if (stratum == 0) return -1;
|
||||
|
||||
if (be64toh(reply->orig_ts) != t1_ntp) return -1;
|
||||
|
||||
struct timeval t4_tv;
|
||||
#ifdef _WIN32
|
||||
@@ -117,64 +109,193 @@ static int ntp_do_query(const struct sockaddr_in* sin, const char* server_name,
|
||||
#else
|
||||
gettimeofday(&t4_tv, NULL);
|
||||
#endif
|
||||
socket_close_wrapper(sock);
|
||||
|
||||
if (n < (ssize_t)sizeof(reply)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: short reply from %s (got %zd, expected %zu)", server_name, n, sizeof(reply));
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t mode = reply.li_vn_mode & 0x07;
|
||||
if (mode != 4) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: unexpected mode %d from %s (expected 4)", mode, server_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int stratum = reply.stratum;
|
||||
if (stratum == 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: kiss-o-death from %s", server_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint64_t reply_orig = be64toh(reply.orig_ts);
|
||||
if (reply_orig != t1_ntp) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: originate timestamp mismatch from %s", server_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t t1_us = ntp64_to_us(t1_ntp);
|
||||
int64_t t2_us = ntp64_to_us(be64toh(reply.recv_ts));
|
||||
int64_t t3_us = ntp64_to_us(be64toh(reply.xmit_ts));
|
||||
int64_t t2_us = ntp64_to_us(be64toh(reply->recv_ts));
|
||||
int64_t t3_us = ntp64_to_us(be64toh(reply->xmit_ts));
|
||||
int64_t t4_us = ntp64_to_us(timeval_to_ntp(&t4_tv));
|
||||
|
||||
int64_t offset_us = ((t2_us - t1_us) + (t3_us - t4_us)) / 2;
|
||||
int64_t rtt_us = (t4_us - t1_us) - (t3_us - t2_us);
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP: synced from %s offset=%lldus rtt=%lldus stratum=%d",
|
||||
server_name, (long long)offset_us, (long long)rtt_us, stratum);
|
||||
|
||||
*offset_us_out = offset_us;
|
||||
*rtt_us_out = rtt_us;
|
||||
*stratum_out = stratum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ntp_query_server(const char* server, int64_t* offset_us_out, int* stratum_out) {
|
||||
struct addrinfo hints, *result = NULL;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
/* ─── машина состояний ─── */
|
||||
|
||||
char port_str[8];
|
||||
snprintf(port_str, sizeof(port_str), "%d", NTP_PORT);
|
||||
int gai_err = getaddrinfo(server, port_str, &hints, &result);
|
||||
if (gai_err != 0 || !result) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: failed to resolve %s: %s", server, gai_strerror(gai_err));
|
||||
return -1;
|
||||
static uint16_t ntp_query_port(const struct NTP_TIME* ntp) {
|
||||
return ntp->test_port ? ntp->test_port : NTP_PORT;
|
||||
}
|
||||
|
||||
static void ntp_try_next(struct ntp_sync* ctx) {
|
||||
struct NTP_TIME* ntp = &ctx->inst->ntp;
|
||||
|
||||
for (int i = 0; i < ntp->server_count; i++) {
|
||||
int idx = ctx->server_idx;
|
||||
ctx->server_idx = (idx + 1) % ntp->server_count;
|
||||
const char* name = ntp->servers[idx];
|
||||
|
||||
struct sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(ntp_query_port(ntp));
|
||||
if (inet_pton(AF_INET, name, &addr.sin_addr) == 1) {
|
||||
ntp_start_query(ctx, idx, &addr);
|
||||
return;
|
||||
}
|
||||
|
||||
struct adns_opts dopts, *popts = NULL;
|
||||
if (ntp->test_dns.sin_port != 0) {
|
||||
memset(&dopts, 0, sizeof(dopts));
|
||||
dopts.server = ntp->test_dns;
|
||||
popts = &dopts;
|
||||
}
|
||||
|
||||
ctx->resolving_idx = idx;
|
||||
ctx->dns_q = adns_resolve(ctx->inst->ua, name, popts, ntp_resolve_done_cb, ctx);
|
||||
if (ctx->dns_q) return;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP: resolve '%s' failed immediately", name);
|
||||
}
|
||||
|
||||
int ret = ntp_do_query((const struct sockaddr_in*)result->ai_addr, server, offset_us_out, stratum_out);
|
||||
freeaddrinfo(result);
|
||||
return ret;
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP: all servers unreachable, retry in %ds", ntp->resync_interval_sec);
|
||||
ntp_finish_cycle(ctx, 0);
|
||||
}
|
||||
|
||||
static void ntp_resolve_done_cb(const struct adns_result* res, void* arg) {
|
||||
struct ntp_sync* ctx = (struct ntp_sync*)arg;
|
||||
ctx->dns_q = NULL;
|
||||
struct NTP_TIME* ntp = &ctx->inst->ntp;
|
||||
int idx = ctx->resolving_idx;
|
||||
|
||||
if (res->status == ADNS_OK && res->count > 0) {
|
||||
struct sockaddr_in addr = res->addrs[0];
|
||||
addr.sin_port = htons(ntp_query_port(ntp));
|
||||
ntp_start_query(ctx, idx, &addr);
|
||||
} else {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP: resolve '%s' failed (status=%d)", ntp->servers[idx], res->status);
|
||||
ntp_try_next(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
static void ntp_start_query(struct ntp_sync* ctx, int idx, const struct sockaddr_in* addr) {
|
||||
ctx->query_idx = idx;
|
||||
ctx->qaddr = *addr;
|
||||
ctx->tries = 0;
|
||||
ntp_send_request(ctx);
|
||||
}
|
||||
|
||||
static void ntp_send_request(struct ntp_sync* ctx) {
|
||||
struct UTUN_INSTANCE* inst = ctx->inst;
|
||||
|
||||
if (ctx->sock == SOCKET_INVALID) {
|
||||
ctx->sock = socket_create_udp(AF_INET);
|
||||
if (ctx->sock == SOCKET_INVALID) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: socket() failed: %s", socket_strerror(socket_get_error()));
|
||||
ntp_query_fail(ctx);
|
||||
return;
|
||||
}
|
||||
socket_set_nonblocking(ctx->sock);
|
||||
}
|
||||
|
||||
if (ctx->tries == 0) ntp_build_request(ctx->req, &ctx->t1_ntp);
|
||||
|
||||
ssize_t sent = socket_sendto(ctx->sock, ctx->req, sizeof(ctx->req),
|
||||
(const struct sockaddr*)&ctx->qaddr, sizeof(ctx->qaddr));
|
||||
if (sent < 0) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: sendto failed: %s", socket_strerror(socket_get_error()));
|
||||
ntp_query_fail(ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx->socket_id) {
|
||||
ctx->socket_id = uasync_add_socket_t(inst->ua, ctx->sock, ntp_query_read_cb, NULL, NULL, ctx);
|
||||
if (!ctx->socket_id) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: uasync_add_socket_t failed");
|
||||
ntp_query_fail(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->timer = uasync_set_timeout(inst->ua, NTP_TIMEOUT_SEC * 10000, ctx, ntp_query_timeout_cb, "ntp_q");
|
||||
if (!ctx->timer) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: uasync_set_timeout failed");
|
||||
ntp_query_fail(ctx);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void ntp_query_read_cb(socket_t sock, void* arg) {
|
||||
(void)sock;
|
||||
struct ntp_sync* ctx = (struct ntp_sync*)arg;
|
||||
struct ntp_packet reply;
|
||||
struct sockaddr_in from;
|
||||
socklen_t from_len = sizeof(from);
|
||||
ssize_t n = socket_recvfrom(ctx->sock, (void*)&reply, sizeof(reply), (struct sockaddr*)&from, &from_len);
|
||||
if (n < 0) return; /* EAGAIN / ICMP (WSAECONNRESET) — ждём timeout */
|
||||
|
||||
int64_t offset_us, rtt_us;
|
||||
int stratum;
|
||||
if (ntp_parse_reply((const uint8_t*)&reply, (size_t)n, ctx->t1_ntp, &offset_us, &rtt_us, &stratum) != 0)
|
||||
return; /* невалидный ответ — ждём (timeout перезапросит) */
|
||||
|
||||
struct NTP_TIME* ntp = &ctx->inst->ntp;
|
||||
ntp->offset_us = offset_us;
|
||||
ntp->synced = 1;
|
||||
ntp->last_sync_tb = get_time_tb();
|
||||
if (ctx->query_idx >= 0)
|
||||
ntp->server_current = (ctx->query_idx + 1) % ntp->server_count;
|
||||
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP: synced from %s offset=%lldus rtt=%lldus stratum=%d",
|
||||
ctx->query_idx >= 0 ? ntp->servers[ctx->query_idx] : "test-server",
|
||||
(long long)offset_us, (long long)rtt_us, stratum);
|
||||
|
||||
ntp_finish_cycle(ctx, 1);
|
||||
}
|
||||
|
||||
static void ntp_query_timeout_cb(void* arg) {
|
||||
struct ntp_sync* ctx = (struct ntp_sync*)arg;
|
||||
ctx->timer = NULL;
|
||||
ctx->tries++;
|
||||
if (ctx->tries < NTP_MAX_TRIES) {
|
||||
ntp_send_request(ctx);
|
||||
return;
|
||||
}
|
||||
ntp_query_fail(ctx);
|
||||
}
|
||||
|
||||
static void ntp_query_fail(struct ntp_sync* ctx) {
|
||||
struct NTP_TIME* ntp = &ctx->inst->ntp;
|
||||
const char* name = ctx->query_idx >= 0 ? ntp->servers[ctx->query_idx] : "test-server";
|
||||
|
||||
if (ctx->timer) { uasync_cancel_timeout(ctx->inst->ua, ctx->timer); ctx->timer = NULL; }
|
||||
if (ctx->socket_id) { uasync_remove_socket_t(ctx->inst->ua, ctx->sock); ctx->socket_id = NULL; }
|
||||
if (ctx->sock != SOCKET_INVALID) { socket_close_wrapper(ctx->sock); ctx->sock = SOCKET_INVALID; }
|
||||
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP: no response from %s after %d tries", name, ctx->tries);
|
||||
ntp_try_next(ctx);
|
||||
}
|
||||
|
||||
static void ntp_finish_cycle(struct ntp_sync* ctx, int synced) {
|
||||
struct UTUN_INSTANCE* inst = ctx->inst;
|
||||
struct NTP_TIME* ntp = &inst->ntp;
|
||||
|
||||
if (ctx->timer) { uasync_cancel_timeout(inst->ua, ctx->timer); ctx->timer = NULL; }
|
||||
if (ctx->socket_id) { uasync_remove_socket_t(inst->ua, ctx->sock); ctx->socket_id = NULL; }
|
||||
if (ctx->sock != SOCKET_INVALID) { socket_close_wrapper(ctx->sock); ctx->sock = SOCKET_INVALID; }
|
||||
if (ctx->dns_q) { adns_cancel(ctx->dns_q); ctx->dns_q = NULL; }
|
||||
|
||||
ntp->sync = NULL;
|
||||
u_free(ctx);
|
||||
|
||||
if (synced) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP: time corrected, offset=%lldus", (long long)ntp->offset_us);
|
||||
ntp_node_sync_peers(inst);
|
||||
}
|
||||
|
||||
ntp->timer = uasync_set_timeout(inst->ua, ntp->resync_interval_sec * 10000,
|
||||
inst, ntp_time_sync_cb, "ntp_sync");
|
||||
}
|
||||
|
||||
static void ntp_time_sync_cb(void* arg) {
|
||||
@@ -189,52 +310,30 @@ static void ntp_time_sync_cb(void* arg) {
|
||||
return;
|
||||
}
|
||||
|
||||
int64_t offset_us = 0;
|
||||
int stratum = 0;
|
||||
int synced = 0;
|
||||
if (ntp->sync) {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP: sync already in progress, skipping");
|
||||
ntp->timer = uasync_set_timeout(instance->ua, ntp->resync_interval_sec * 10000,
|
||||
instance, ntp_time_sync_cb, "ntp_sync");
|
||||
return;
|
||||
}
|
||||
|
||||
struct ntp_sync* ctx = u_calloc(1, sizeof(*ctx));
|
||||
if (!ctx) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: calloc sync ctx failed");
|
||||
ntp->timer = uasync_set_timeout(instance->ua, ntp->resync_interval_sec * 10000,
|
||||
instance, ntp_time_sync_cb, "ntp_sync");
|
||||
return;
|
||||
}
|
||||
ctx->inst = instance;
|
||||
ctx->sock = SOCKET_INVALID;
|
||||
ntp->sync = ctx;
|
||||
|
||||
if (ntp->test_addr.sin_port != 0) {
|
||||
if (ntp_do_query(&ntp->test_addr, "test-server", &offset_us, &stratum) == 0) {
|
||||
ntp->offset_us = offset_us;
|
||||
ntp->synced = 1;
|
||||
ntp->last_sync_tb = get_time_tb();
|
||||
synced = 1;
|
||||
}
|
||||
ntp_start_query(ctx, -1, &ntp->test_addr);
|
||||
} else {
|
||||
const char* server = ntp->servers[ntp->server_current];
|
||||
if (ntp_query_server(server, &offset_us, &stratum) == 0) {
|
||||
ntp->offset_us = offset_us;
|
||||
ntp->synced = 1;
|
||||
ntp->last_sync_tb = get_time_tb();
|
||||
ntp->server_current = (ntp->server_current + 1) % ntp->server_count;
|
||||
synced = 1;
|
||||
} else {
|
||||
for (int i = 0; i < ntp->server_count; i++) {
|
||||
ntp->server_current = (ntp->server_current + 1) % ntp->server_count;
|
||||
const char* next_server = ntp->servers[ntp->server_current];
|
||||
if (strcmp(next_server, server) == 0) break;
|
||||
if (ntp_query_server(next_server, &offset_us, &stratum) == 0) {
|
||||
ntp->offset_us = offset_us;
|
||||
ntp->synced = 1;
|
||||
ntp->last_sync_tb = get_time_tb();
|
||||
synced = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ctx->server_idx = ntp->server_current;
|
||||
ntp_try_next(ctx);
|
||||
}
|
||||
|
||||
if (ntp->synced) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "NTP: time corrected, offset=%lldus",
|
||||
(long long)ntp->offset_us);
|
||||
ntp_node_sync_peers(instance);
|
||||
} else {
|
||||
DEBUG_WARN(DEBUG_CATEGORY_GENERAL, "NTP: all servers unreachable, retry in %ds",
|
||||
ntp->resync_interval_sec);
|
||||
}
|
||||
|
||||
ntp->timer = uasync_set_timeout(instance->ua, ntp->resync_interval_sec * 10000,
|
||||
instance, ntp_time_sync_cb, "ntp_sync");
|
||||
}
|
||||
|
||||
int ntp_time_init(struct UTUN_INSTANCE* instance) {
|
||||
@@ -251,7 +350,10 @@ int ntp_time_init(struct UTUN_INSTANCE* instance) {
|
||||
ntp->server_count = g->ntp_server_count;
|
||||
ntp->server_current = 0;
|
||||
ntp->resync_interval_sec = g->ntp_resync_interval;
|
||||
ntp->sync = NULL;
|
||||
ntp->test_port = 0;
|
||||
memset(&ntp->test_addr, 0, sizeof(ntp->test_addr));
|
||||
memset(&ntp->test_dns, 0, sizeof(ntp->test_dns));
|
||||
|
||||
if (g->ntp_server_count > 0) {
|
||||
ntp->servers = u_malloc(g->ntp_server_count * sizeof(char*));
|
||||
@@ -298,6 +400,16 @@ void ntp_time_destroy(struct UTUN_INSTANCE* instance) {
|
||||
if (!instance) return;
|
||||
struct NTP_TIME* ntp = &instance->ntp;
|
||||
|
||||
if (ntp->sync && instance->ua) {
|
||||
struct ntp_sync* ctx = (struct ntp_sync*)ntp->sync;
|
||||
if (ctx->timer) { uasync_cancel_timeout(instance->ua, ctx->timer); ctx->timer = NULL; }
|
||||
if (ctx->socket_id) { uasync_remove_socket_t(instance->ua, ctx->sock); ctx->socket_id = NULL; }
|
||||
if (ctx->sock != SOCKET_INVALID) socket_close_wrapper(ctx->sock);
|
||||
if (ctx->dns_q) adns_cancel(ctx->dns_q);
|
||||
u_free(ctx);
|
||||
ntp->sync = NULL;
|
||||
}
|
||||
|
||||
if (ntp->timer && instance->ua) {
|
||||
uasync_cancel_timeout(instance->ua, ntp->timer);
|
||||
ntp->timer = NULL;
|
||||
@@ -350,3 +462,16 @@ void ntp_time_set_test_addr(struct NTP_TIME* ntp, const char* ip, uint16_t port)
|
||||
memset(&ntp->test_addr, 0, sizeof(ntp->test_addr));
|
||||
}
|
||||
}
|
||||
|
||||
void ntp_time_set_test_dns(struct NTP_TIME* ntp, const char* ip, uint16_t dns_port, uint16_t ntp_port) {
|
||||
if (!ntp || !ip) return;
|
||||
memset(&ntp->test_dns, 0, sizeof(ntp->test_dns));
|
||||
ntp->test_dns.sin_family = AF_INET;
|
||||
ntp->test_dns.sin_port = htons(dns_port);
|
||||
ntp->test_port = ntp_port;
|
||||
if (inet_pton(AF_INET, ip, &ntp->test_dns.sin_addr) != 1) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "NTP: test dns parse failed for %s", ip);
|
||||
memset(&ntp->test_dns, 0, sizeof(ntp->test_dns));
|
||||
ntp->test_port = 0;
|
||||
}
|
||||
}
|
||||
@@ -25,12 +25,16 @@ struct NTP_TIME {
|
||||
int server_current;
|
||||
int resync_interval_sec;
|
||||
struct sockaddr_in test_addr; // test mode: direct address when sin_port != 0
|
||||
struct sockaddr_in test_dns; // test mode: DNS server for hostname resolution (sin_port != 0)
|
||||
uint16_t test_port; // test mode: NTP query port override (0 = 123)
|
||||
void* sync; // активный контекст синхронизации (struct ntp_sync*, opaque)
|
||||
};
|
||||
|
||||
int ntp_time_init(struct UTUN_INSTANCE* instance);
|
||||
void ntp_time_destroy(struct UTUN_INSTANCE* instance);
|
||||
|
||||
void ntp_time_set_test_addr(struct NTP_TIME* ntp, const char* ip, uint16_t port);
|
||||
void ntp_time_set_test_dns(struct NTP_TIME* ntp, const char* ip, uint16_t dns_port, uint16_t ntp_port);
|
||||
|
||||
int64_t ntp_time_get_us(struct UTUN_INSTANCE* instance);
|
||||
time_t ntp_time_get_seconds(struct UTUN_INSTANCE* instance);
|
||||
|
||||
Reference in new issue
Block a user