Files
SimpleRemoter/common/posix_net_helpers.h

100 lines
3.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// posix_net_helpers.h
// Linux/macOS 客户端共用的网络/Shell 工具execCmd / httpGet / getPublicIP /
// jsonExtract / getGeoLocation。Windows 端已有等价实现,不应包含此头。
//
// 全部 inlineheader-only避免新增 .cpp / 改 CMakeLists。
//
// 设计说明:
// - httpGet 优先 curl备选 wgetLinux 默认自带macOS 默认无 wget缺失时
// wget 命令失败、execCmd 返空——无副作用,等价于"只用 curl"
// - getPublicIP 轮询多个公网 IP 查询源,按顺序尝试直到成功
// - jsonExtract 仅做最简单的 "key":"value" 提取,不依赖 jsoncpp
// - getGeoLocation 通过 ipinfo.io 反查地理位置,与 Windows IPConverter 同源
#pragma once
#include <cstdio>
#include <memory>
#include <string>
#include "common/logger.h"
namespace PosixNet {
// 执行 shell 命令,捕获其 stdout 输出trim 末尾空白后返回)
inline std::string execCmd(const std::string& cmd)
{
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) return "";
char buf[4096];
std::string result;
while (fgets(buf, sizeof(buf), pipe.get())) {
result += buf;
}
while (!result.empty() && (result.back() == '\n' || result.back() == '\r' || result.back() == ' '))
result.pop_back();
return result;
}
// HTTP GET 请求:优先 curl备选 wget
inline std::string httpGet(const std::string& url, int timeoutSec = 5)
{
std::string t = std::to_string(timeoutSec);
std::string r = execCmd("curl -s --max-time " + t + " \"" + url + "\" 2>/dev/null");
if (!r.empty()) return r;
r = execCmd("wget -qO- --timeout=" + t + " \"" + url + "\" 2>/dev/null");
return r;
}
// 获取公网 IP轮询多个查询源与 Windows 端 IPConverter 一致)
inline std::string getPublicIP()
{
static const char* urls[] = {
"https://checkip.amazonaws.com",
"https://api.ipify.org",
"https://ipinfo.io/ip",
"https://icanhazip.com",
"https://ifconfig.me/ip",
};
for (auto& url : urls) {
std::string ip = httpGet(url, 3);
if (!ip.empty() && ip.find('.') != std::string::npos && ip.size() <= 45) {
Mprintf("getPublicIP: %s (from %s)\n", ip.c_str(), url);
return ip;
}
}
Mprintf("getPublicIP: all sources failed\n");
return "";
}
// 从 JSON 字符串中提取指定 key 的 string 值(简易解析,不依赖 jsoncpp
// 仅支持 "key": "value" 或 "key":"value" 格式
inline std::string jsonExtract(const std::string& json, const std::string& key)
{
std::string needle = "\"" + key + "\"";
size_t pos = json.find(needle);
if (pos == std::string::npos) return "";
pos = json.find(':', pos + needle.size());
if (pos == std::string::npos) return "";
pos = json.find('"', pos + 1);
if (pos == std::string::npos) return "";
size_t end = json.find('"', pos + 1);
if (end == std::string::npos) return "";
return json.substr(pos + 1, end - pos - 1);
}
// 获取 IP 地理位置ipinfo.io与 Windows IPConverter 同源)
inline std::string getGeoLocation(const std::string& ip)
{
if (ip.empty()) return "";
std::string json = httpGet("https://ipinfo.io/" + ip + "/json", 5);
if (json.empty()) return "";
std::string country = jsonExtract(json, "country");
std::string city = jsonExtract(json, "city");
if (city.empty() && country.empty()) return "";
if (city.empty()) return country;
if (country.empty()) return city;
return city + ", " + country;
}
} // namespace PosixNet