#include #include #include #include #include "ipcheck.h" #include "log.h" static const char *SERVICES[] = { "https://ipv4.icanhazip.com", "https://api4.ipify.org", "https://ipv4.wtfismyip.com/text", "https://v4.ident.me", "https://ipv4.am.i.mullvad.net/ip", "https://4.ident.me", "https://4.tnedi.me", }; #define SERVICE_COUNT (int)(sizeof(SERVICES) / sizeof(SERVICES[0])) typedef struct { char buf[IPCHECK_MAX_IP_LEN]; size_t len; } IPBuf; static size_t write_cb(void *data, size_t size, size_t nmemb, void *userp) { IPBuf *out = (IPBuf *)userp; size_t total = size * nmemb; if (out->len + total >= IPCHECK_MAX_IP_LEN) return 0; memcpy(out->buf + out->len, data, total); out->len += total; return total; } int ipcheck_get_public_ip(char *out_ip) { char results[SERVICE_COUNT][IPCHECK_MAX_IP_LEN]; int valid = 0; CURL *curl = curl_easy_init(); if (!curl) return -1; curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_TIMEOUT, IPCHECK_TIMEOUT_SECS); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); for (int i = 0; i < SERVICE_COUNT; i++) { IPBuf buf = { .buf = {0}, .len = 0 }; curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf); curl_easy_setopt(curl, CURLOPT_URL, SERVICES[i]); CURLcode res = curl_easy_perform(curl); if (res != CURLE_OK) continue; long http_code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); if (http_code != 200) continue; buf.buf[buf.len] = '\0'; if (sscanf(buf.buf, IPCHECK_IP_SCAN_FMT, results[valid]) != 1) continue; valid++; } curl_easy_cleanup(curl); if (valid < IPCHECK_MIN_RESPONDERS) { log_warn("ipcheck: only %d/%d services responded, need at least %d", valid, SERVICE_COUNT, IPCHECK_MIN_RESPONDERS); return -1; } for (int i = 0; i < valid; i++) { int count = 0; for (int j = 0; j < valid; j++) { if (strcmp(results[i], results[j]) == 0) count++; } if (count * 2 > valid) { strncpy(out_ip, results[i], IPCHECK_MAX_IP_LEN - 1); out_ip[IPCHECK_MAX_IP_LEN - 1] = '\0'; return 0; } } log_warn("ipcheck: no strict majority among %d/%d responders", valid, SERVICE_COUNT); return -2; }