// posix_net_helpers.h // Linux/macOS 客户端共用的网络/Shell 工具:execCmd / httpGet / getPublicIP / // jsonExtract / getGeoLocation。Windows 端已有等价实现,不应包含此头。 // // 全部 inline,header-only,避免新增 .cpp / 改 CMakeLists。 // // 设计说明: // - httpGet 优先 curl,备选 wget(Linux 默认自带;macOS 默认无 wget,缺失时 // wget 命令失败、execCmd 返空——无副作用,等价于"只用 curl") // - getPublicIP 轮询多个公网 IP 查询源,按顺序尝试直到成功 // - jsonExtract 仅做最简单的 "key":"value" 提取,不依赖 jsoncpp // - getGeoLocation 通过 ipinfo.io 反查地理位置,与 Windows IPConverter 同源 #pragma once #include #include #include #include "common/logger.h" namespace PosixNet { // 执行 shell 命令,捕获其 stdout 输出(trim 末尾空白后返回) inline std::string execCmd(const std::string& cmd) { std::unique_ptr 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