88 lines
2.4 KiB
C
88 lines
2.4 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <curl/curl.h>
|
|
#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;
|
|
}
|