Feat: main application logic
This commit is contained in:
+143
@@ -0,0 +1,143 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <curl/curl.h>
|
||||
#include "config.h"
|
||||
#include "gandi.h"
|
||||
#include "ipcheck.h"
|
||||
#include "log.h"
|
||||
#include "selfcheck.h"
|
||||
|
||||
static volatile sig_atomic_t g_running = 1;
|
||||
|
||||
static void handle_signal(int sig) {
|
||||
(void)sig;
|
||||
g_running = 0;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
struct sigaction sa = {0};
|
||||
sa.sa_handler = handle_signal;
|
||||
sigaction(SIGINT, &sa, NULL);
|
||||
sigaction(SIGTERM, &sa, NULL);
|
||||
|
||||
Config cfg = {0};
|
||||
if (config_load(&cfg) != 0) return EXIT_FAILURE;
|
||||
|
||||
log_init();
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
|
||||
int ret = EXIT_SUCCESS;
|
||||
|
||||
if (gandi_init(cfg.api_token) != 0) {
|
||||
log_error("gandi_init failed");
|
||||
ret = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char cached_ip[GANDI_MAX_IP_LEN] = {0};
|
||||
if (gandi_get_self_ip(cfg.dns_keeper_domain, cfg.dns_keeper_record, cached_ip) != 0) {
|
||||
log_error("Failed to fetch initial IP from self record %s.%s",
|
||||
cfg.dns_keeper_record, cfg.dns_keeper_domain);
|
||||
ret = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
log_info("Self record points to %s", cached_ip);
|
||||
|
||||
char self_fqdn[256];
|
||||
snprintf(self_fqdn, sizeof(self_fqdn), "%s.%s", cfg.dns_keeper_record, cfg.dns_keeper_domain);
|
||||
|
||||
if (selfcheck_start_listener(cfg.dns_keeper_port) != 0) {
|
||||
log_error("Failed to start self-check listener on port %d", cfg.dns_keeper_port);
|
||||
ret = EXIT_FAILURE;
|
||||
goto cleanup;
|
||||
}
|
||||
log_info("Listening on UDP port %d", cfg.dns_keeper_port);
|
||||
|
||||
int fail_count = 0; /* consecutive self-check failures */
|
||||
int drift_count = 0; /* when external IP matches cache but self-check fails */
|
||||
|
||||
while (g_running) {
|
||||
if (selfcheck_domain(self_fqdn, cfg.dns_keeper_port) == 0) {
|
||||
fail_count = 0;
|
||||
drift_count = 0;
|
||||
sleep(cfg.poll_interval);
|
||||
continue;
|
||||
}
|
||||
|
||||
fail_count++;
|
||||
if (fail_count < SELFCHECK_FAIL_THRESHOLD) {
|
||||
sleep(cfg.poll_interval);
|
||||
continue;
|
||||
}
|
||||
|
||||
fail_count = 0;
|
||||
char public_ip[IPCHECK_MAX_IP_LEN] = {0};
|
||||
if (ipcheck_get_public_ip(public_ip) != 0) {
|
||||
log_warn("ipcheck: failed to determine public IP, skipping cycle");
|
||||
sleep(cfg.poll_interval);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(public_ip, cached_ip) == 0) {
|
||||
drift_count++;
|
||||
if (drift_count >= SELFCHECK_DRIFT_THRESHOLD) {
|
||||
char gandi_ip[GANDI_MAX_IP_LEN] = {0};
|
||||
if (gandi_get_self_ip(cfg.dns_keeper_domain, cfg.dns_keeper_record, gandi_ip) == 0
|
||||
&& strcmp(gandi_ip, cached_ip) != 0) {
|
||||
log_info("Cache drift: cached %s but Gandi has %s, resyncing",
|
||||
cached_ip, gandi_ip);
|
||||
strncpy(cached_ip, gandi_ip, GANDI_MAX_IP_LEN - 1);
|
||||
cached_ip[GANDI_MAX_IP_LEN - 1] = '\0';
|
||||
}
|
||||
drift_count = 0;
|
||||
}
|
||||
sleep(SELFCHECK_COOLDOWN_S);
|
||||
continue;
|
||||
}
|
||||
|
||||
drift_count = 0;
|
||||
log_info("IP change detected: %s -> %s", cached_ip, public_ip);
|
||||
|
||||
if (selfcheck_ip_str(public_ip, cfg.dns_keeper_port) != 0) {
|
||||
log_warn("Direct check of %s failed, deferring update", public_ip);
|
||||
sleep(SELFCHECK_COOLDOWN_S);
|
||||
continue;
|
||||
}
|
||||
|
||||
int failed_updates = 0;
|
||||
for (int i = 0; i < cfg.domain_count; i++) {
|
||||
if (strcmp(cfg.domains[i], cfg.dns_keeper_domain) == 0) continue;
|
||||
|
||||
int gret = gandi_update_domains(cfg.domains[i], cached_ip, public_ip);
|
||||
if (gret == -3) {
|
||||
log_info("gandi: [%s] no matching A records for %s, skipping",
|
||||
cfg.domains[i], cached_ip);
|
||||
} else if (gret != 0) {
|
||||
failed_updates = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int sret = gandi_update_record(cfg.dns_keeper_domain, cfg.dns_keeper_record,
|
||||
cached_ip, public_ip);
|
||||
if (sret == 0) {
|
||||
strncpy(cached_ip, public_ip, GANDI_MAX_IP_LEN - 1);
|
||||
cached_ip[GANDI_MAX_IP_LEN - 1] = '\0';
|
||||
} else {
|
||||
log_error("Failed to update self record, cached IP not advanced");
|
||||
failed_updates = 1;
|
||||
}
|
||||
|
||||
if (failed_updates) log_error("One or more updates failed this cycle");
|
||||
|
||||
sleep(SELFCHECK_COOLDOWN_S);
|
||||
}
|
||||
|
||||
log_info("Shutting down");
|
||||
cleanup:
|
||||
curl_global_cleanup();
|
||||
config_free(&cfg);
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user