diff --git a/src/main/java/com/tongran/agent/client/utils/AgentUtil.java b/src/main/java/com/tongran/agent/client/utils/AgentUtil.java index 8b4b0b0..ca14971 100644 --- a/src/main/java/com/tongran/agent/client/utils/AgentUtil.java +++ b/src/main/java/com/tongran/agent/client/utils/AgentUtil.java @@ -376,7 +376,7 @@ public class AgentUtil { // 检查IP4信息 if ("N/A".equals(gateway)) continue; - // 添加临时路由 过滤没有ipv4的接口 + // 添加临时路由 // boolean flag = addTempRoute(ipv4, name, gateway); // if (!flag) continue; @@ -501,7 +501,7 @@ public class AgentUtil { /** * 添加临时路由 */ - private static boolean addTempRoute(String ipv4, String name, String gateway) { + public static boolean addTempRoute(String ipv4, String name, String gateway) { String prefix = "32"; AssertLog.info("添加临时路由,ipv4:{}",ipv4); boolean flag = NetworkUtil.addRoute(ipv4,prefix,gateway,name); @@ -516,7 +516,7 @@ public class AgentUtil { /** * 删除临时路由 */ - private static boolean delTempRoute(String ipv4, String name, String gateway) { + public static boolean delTempRoute(String ipv4, String name, String gateway) { String prefix = "32"; AssertLog.info("删除临时路由,ipv4:{}",ipv4); boolean flag = NetworkUtil.deleteRoute(ipv4,prefix,gateway,name); @@ -644,19 +644,34 @@ public class AgentUtil { return null; } - // 提取网关IP地址 - // 示例行: "1 8.8.8.8 3.737 ms 3.940 ms 4.128 ms" - Pattern pattern = Pattern.compile("\\d+\\.\\d+\\.\\d+\\.\\d+"); - Matcher matcher = pattern.matcher(line); + // 情况1:匹配 "1 192.168.9.1 (192.168.9.1)" 这种格式(图片中的格式) + // 先尝试匹配括号中的IP(最可靠) + Pattern bracketPattern = Pattern.compile("\\(([\\d.]+)\\)"); + Matcher bracketMatcher = bracketPattern.matcher(line); - if (matcher.find()) { - String gatewayIp = matcher.group(0); + if (bracketMatcher.find()) { + String gatewayIp = bracketMatcher.group(1); AssertLog.info("traceroute 探测完成,网卡名称:{},网关IP:{}", interfaceName, gatewayIp); return gatewayIp; } - // 如果没有匹配到IP格式,尝试查找主机名 - // 行格式可能是: "1 gateway.local (192.168.1.1) 0.5 ms" + // 情况2:匹配 "1 8.8.8.8 3.737 ms" 这种纯IP格式(没有括号) + // 使用更精确的正则,只匹配数字开头且包含点号的IP地址 + // 确保不匹配到主机名中的点号(如201.108.141.116.adsl-pool.jlccptt.net.cn) + Pattern ipPattern = Pattern.compile("^\\s*1\\s+(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})(?:\\s|$)"); + Matcher ipMatcher = ipPattern.matcher(line); + + if (ipMatcher.find()) { + String gatewayIp = ipMatcher.group(1); + // 验证是否是合法IP(避免匹配到主机名中的数字序列) + if (isValidIP(gatewayIp)) { + AssertLog.info("traceroute 探测完成,网卡名称:{},网关IP:{}", interfaceName, gatewayIp); + return gatewayIp; + } + } + + // 情况3:匹配 "1 gateway.local 0.5 ms" 或 "1 201.108.141.116.adsl-pool.jlccptt.net.cn" 这种主机名格式 + // 提取第一跳后的第二个字段(跳过序号) String[] parts = line.trim().split("\\s+"); if (parts.length >= 2 && !parts[1].equals("*")) { String gateway = parts[1]; @@ -675,6 +690,30 @@ public class AgentUtil { } } + // 辅助方法:验证IP地址是否合法 + private static boolean isValidIP(String ip) { + if (ip == null || ip.isEmpty()) { + return false; + } + + String[] parts = ip.split("\\."); + if (parts.length != 4) { + return false; + } + + try { + for (String part : parts) { + int num = Integer.parseInt(part); + if (num < 0 || num > 255) { + return false; + } + } + return true; + } catch (NumberFormatException e) { + return false; + } + } + // 检查接口是否“已连接”(物理链路状态) private static boolean isInterfaceConnected(String interfaceName) { try { diff --git a/src/main/java/com/tongran/agent/client/utils/NetworkInterfaceUtil.java b/src/main/java/com/tongran/agent/client/utils/NetworkInterfaceUtil.java index adfa407..068b79c 100644 --- a/src/main/java/com/tongran/agent/client/utils/NetworkInterfaceUtil.java +++ b/src/main/java/com/tongran/agent/client/utils/NetworkInterfaceUtil.java @@ -74,21 +74,42 @@ public class NetworkInterfaceUtil { AssertLog.info(" 跳过网卡 {}: 无法获取网关", name); continue; } - + boolean flag = false; + if(!"N/A".equals(ipv4) && gateway != null){ + flag = true; + } // 获取公网IP String publicIp = null; String ipInfo = null; - if (gateway != null) { - ipInfo = PublicIpFetcher.getPublicIp(); - publicIp = PublicIpFetcher.extractIp(ipInfo); - if (publicIp != null) { - AssertLog.info(" 公网 IP: {}", publicIp); - } else { - AssertLog.info(" 未能提取公网 IP 地址"); - } - if (StringUtils.isBlank(publicIp)) { - AssertLog.info(" 跳过网卡 {}: 无公网IP", name); - continue; + if (flag) { + List publicIps = PublicIpFetcher.getPublicIps(); + + if (publicIps != null && !publicIps.isEmpty()) { + AssertLog.info("网卡 {} nslookup获取到 {} 个公网IP: {}", name, publicIps.size(), publicIps); + + for (String ip : publicIps) { + if (ip != null && !ip.isEmpty()) { + // 为每个公网IP添加临时路由 + boolean routeAdded = AgentUtil.addTempRoute(ip, name, gateway); + } + } + ipInfo = PublicIpFetcher.getPublicIp(); + publicIp = PublicIpFetcher.extractIp(ipInfo); + if (publicIp != null) { + AssertLog.info(" 公网 IP: {}", publicIp); + } else { + AssertLog.info(" 未能提取公网 IP 地址"); + } + if (StringUtils.isBlank(publicIp)) { + AssertLog.info(" 跳过网卡 {}: 无公网IP", name); + continue; + } + for (String ip : publicIps) { + if (ip != null && !ip.isEmpty()) { + // 删除临时路由 + boolean routeAdded = AgentUtil.delTempRoute(ip, name, gateway); + } + } } } diff --git a/src/main/java/com/tongran/agent/client/utils/PublicIpFetcher.java b/src/main/java/com/tongran/agent/client/utils/PublicIpFetcher.java index 3692735..b57cb46 100644 --- a/src/main/java/com/tongran/agent/client/utils/PublicIpFetcher.java +++ b/src/main/java/com/tongran/agent/client/utils/PublicIpFetcher.java @@ -7,6 +7,7 @@ import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; import java.net.UnknownHostException; +import java.util.*; import java.util.concurrent.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -14,11 +15,9 @@ import java.util.regex.Pattern; public class PublicIpFetcher { // 主备IP查询服务 - private static final String PRIMARY_IP_SERVICE = "https://myip.ipip.net"; - private static final String FALLBACK_IP_SERVICE = "http://cip.cc"; - - private static final int TIMEOUT_MS = 8_000; // 8秒超时 - private static final int CURL_TIMEOUT_MS = 5_000; // curl命令执行超时 + private static final String PRIMARY_IP_SERVICE = "myip.ipip.net"; + private static final String FALLBACK_IP_SERVICE = "cip.cc"; + private static final int TIMEOUT_MS = 5_000; // 5秒超时 // 正则表达式匹配 private static final String IPV4_PATTERN = "(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})"; @@ -31,50 +30,202 @@ public class PublicIpFetcher { ); /** - * 获取公网 IP 信息(带降级策略) - * 1. 首先尝试 myip.ipip.net - * 2. 如果超时或失败,执行 curl cip.cc - * 3. 如果都失败,返回本地IP + * 获取公网 IP 列表(带降级策略) + * 1. 首先尝试 nslookup myip.ipip.net + * 2. 如果失败,尝试 nslookup cip.cc */ - public static String getPublicIp() { - // 方法1: 尝试主服务 - String ipInfo = tryPrimaryService(); - if (ipInfo != null && !ipInfo.isEmpty()) { - return ipInfo; + public static List getPublicIps() { + List ipList = new ArrayList<>(); + + // 方法1: 尝试nslookup主服务 + ipList = parseNslookupResult(PRIMARY_IP_SERVICE); + if (!ipList.isEmpty()) { + AssertLog.info("nslookup {} 解析到IP: {}", PRIMARY_IP_SERVICE, ipList); + return ipList; } - // 方法2: 尝试备用服务 - ipInfo = tryFallbackService(); - if (ipInfo != null && !ipInfo.isEmpty()) { - return ipInfo; + // 方法2: 尝试nslookup备用服务 + ipList = parseNslookupResult(FALLBACK_IP_SERVICE); + if (!ipList.isEmpty()) { + AssertLog.info("nslookup {} 解析到IP: {}", FALLBACK_IP_SERVICE, ipList); + return ipList; } - // 方法3: 返回本地IP - return getLocalIpInfo(); + return Collections.emptyList(); } /** - * 尝试主服务 (myip.ipip.net) + * 执行nslookup并解析结果 */ - private static String tryPrimaryService() { + private static List parseNslookupResult(String domain) { + List ipList = new ArrayList<>(); + + try { + // 执行nslookup命令 + Process process = new ProcessBuilder("nslookup", domain) + .redirectErrorStream(true) + .start(); + + // 等待命令执行完成 + boolean finished = process.waitFor(TIMEOUT_MS, TimeUnit.MILLISECONDS); + + if (!finished) { + process.destroyForcibly(); + return ipList; + } + + int exitCode = process.exitValue(); + if (exitCode != 0) { + return ipList; + } + + // 读取并解析输出 + try (BufferedReader reader = new BufferedReader( + new InputStreamReader(process.getInputStream()))) { + + StringBuilder output = new StringBuilder(); + String line; + + while ((line = reader.readLine()) != null) { + output.append(line).append("\n"); + + // 1. 查找"Address:"行 + if (line.contains("Address:") && !line.contains("#")) { + Matcher matcher = IP_PATTERN.matcher(line); + while (matcher.find()) { + String ip = matcher.group(1); + // 过滤DNS服务器IP和本地回环地址 + if (!isDnsServerIp(ip) && !isLocalIp(ip)) { + ipList.add(ip); + } + } + } + // 2. 查找"Name:"行后的IP地址 + else if (line.trim().startsWith("Name:")) { + // 读取下一行,通常是Address行 + String nextLine = reader.readLine(); + if (nextLine != null) { + output.append(nextLine).append("\n"); + Matcher matcher = IP_PATTERN.matcher(nextLine); + while (matcher.find()) { + String ip = matcher.group(1); + if (!isDnsServerIp(ip) && !isLocalIp(ip)) { + ipList.add(ip); + } + } + } + } + } + + // 3. 如果以上方法都没找到,尝试在整个输出中匹配 + if (ipList.isEmpty()) { + String allOutput = output.toString(); + Matcher matcher = IP_PATTERN.matcher(allOutput); + while (matcher.find()) { + String ip = matcher.group(1); + // 跳过DNS服务器IP和本地地址 + if (!isDnsServerIp(ip) && !isLocalIp(ip)) { + // 检查是否是DNS服务器IP(通常在输出开头) + boolean isDnsLine = false; + String[] lines = allOutput.split("\n"); + for (int i = 0; i < Math.min(3, lines.length); i++) { + if (lines[i].contains("Server:") && lines[i].contains(ip)) { + isDnsLine = true; + break; + } + } + if (!isDnsLine) { + ipList.add(ip); + } + } + } + } + } + + } catch (IOException e) { + AssertLog.error("执行nslookup命令失败: {}", e.getMessage()); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + AssertLog.error("nslookup命令被中断"); + } catch (Exception e) { + AssertLog.error("nslookup解析异常: {}", e.getMessage()); + } + + // 去重 + return new ArrayList<>(new LinkedHashSet<>(ipList)); + } + + /** + * 检查是否为DNS服务器IP + */ + private static boolean isDnsServerIp(String ip) { + // 常见的DNS服务器IP段 + return ip.startsWith("8.8.") || // Google DNS + ip.startsWith("114.114.") || // 114DNS + ip.startsWith("119.29.") || // 腾讯DNS + ip.startsWith("223.5.5.") || // 阿里DNS + ip.startsWith("180.76.76.76") || // 百度DNS + ip.startsWith("1.2.4.8") || // CNNIC DNS + ip.startsWith("208.67."); // OpenDNS + } + + /** + * 检查是否为本地IP + */ + private static boolean isLocalIp(String ip) { + return ip.startsWith("127.") || + ip.startsWith("192.168.") || + ip.startsWith("10.") || + ip.startsWith("172.16.") || + ip.startsWith("172.17.") || + ip.startsWith("172.18.") || + ip.startsWith("172.19.") || + ip.startsWith("172.20.") || + ip.startsWith("172.21.") || + ip.startsWith("172.22.") || + ip.startsWith("172.23.") || + ip.startsWith("172.24.") || + ip.startsWith("172.25.") || + ip.startsWith("172.26.") || + ip.startsWith("172.27.") || + ip.startsWith("172.28.") || + ip.startsWith("172.29.") || + ip.startsWith("172.30.") || + ip.startsWith("172.31.") || + ip.equals("0.0.0.0") || + ip.equals("255.255.255.255"); + } + + /** + * 获取公网 IP 信息(保持原有逻辑,用于降级) + */ + public static String getPublicIp() { + return getPublicIpInfo(); + } + + /** + * 原有HTTP查询逻辑 + */ + private static String getPublicIpInfo() { ExecutorService executor = Executors.newSingleThreadExecutor(); Future future = executor.submit(() -> { try { - AssertLog.info("myip.ipip.net采集IP信息成功"); - return fetchFromUrl(PRIMARY_IP_SERVICE, "UTF-8"); + String result = fetchFromUrl("https://" + PRIMARY_IP_SERVICE, "UTF-8"); + AssertLog.info("HTTP {}采集IP信息成功", PRIMARY_IP_SERVICE); + return result; } catch (Exception e) { - AssertLog.error("主服务请求失败: {}", e.getMessage()); - return null; + AssertLog.error("HTTP主服务请求失败: {}", e.getMessage()); + return tryHttpFallback(); } }); try { return future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { - AssertLog.error("主服务请求超时"); + AssertLog.error("HTTP请求超时"); future.cancel(true); } catch (Exception e) { - AssertLog.error("主服务执行异常: {}", e.getMessage()); + AssertLog.error("HTTP请求异常: {}", e.getMessage()); } finally { executor.shutdownNow(); } @@ -82,17 +233,16 @@ public class PublicIpFetcher { } /** - * 尝试备用服务 (curl cip.cc) + * HTTP回退查询 */ - private static String tryFallbackService() { + private static String tryHttpFallback() { try { - Process process = new ProcessBuilder("curl", "-s", "--max-time", "5", FALLBACK_IP_SERVICE) + Process process = new ProcessBuilder("curl", "-s", "--max-time", "5", + "http://" + FALLBACK_IP_SERVICE) .redirectErrorStream(true) .start(); - // 等待进程完成 - boolean finished = process.waitFor(CURL_TIMEOUT_MS, TimeUnit.MILLISECONDS); - + boolean finished = process.waitFor(5_000, TimeUnit.MILLISECONDS); if (finished && process.exitValue() == 0) { try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { @@ -101,22 +251,12 @@ public class PublicIpFetcher { while ((line = reader.readLine()) != null) { result.append(line).append("\n"); } - AssertLog.info("curl cip.cc采集IP信息成功"); + AssertLog.info("curl {} 采集IP信息成功", FALLBACK_IP_SERVICE); return parseCipCcResult(result.toString()); } - } else { - if (!finished) { - process.destroyForcibly(); - } - AssertLog.error("curl命令执行失败"); } - } catch (IOException e) { - AssertLog.error("执行curl命令IO异常: {}", e.getMessage()); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - AssertLog.error("curl命令被中断"); } catch (Exception e) { - AssertLog.error("备用服务异常: {}", e.getMessage()); + AssertLog.error("HTTP回退失败: {}", e.getMessage()); } return null; } @@ -127,7 +267,6 @@ public class PublicIpFetcher { private static String fetchFromUrl(String urlString, String charset) throws IOException { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); connection.setConnectTimeout(TIMEOUT_MS); connection.setReadTimeout(TIMEOUT_MS); @@ -164,17 +303,16 @@ public class PublicIpFetcher { String city = matcher.group(4); String carrier = matcher.group(5); - // 构建类似ipip.net的格式 return String.format("当前 IP:%s 来自于:%s %s %s %s", ip, country, province, city, carrier); } - return result; // 返回原始结果 + return result; } /** - * 获取本地IP信息 + * 获取本地IP */ - private static String getLocalIpInfo() { + private static String getLocalIp() { try { String localIp = InetAddress.getLocalHost().getHostAddress(); AssertLog.info("当前 IP:{} 来自于:本地网络", localIp); @@ -185,12 +323,16 @@ public class PublicIpFetcher { } } + private static String getLocalIpInfo() { + String ip = getLocalIp(); + return ip != null ? String.format("当前 IP:%s 来自于:本地网络", ip) : null; + } + /** * 从IP信息中提取IP地址 */ public static String extractIp(String ipInfo) { if (ipInfo == null || ipInfo.isEmpty()) return null; - Matcher matcher = IP_PATTERN.matcher(ipInfo); return matcher.find() ? matcher.group(1) : null; } @@ -266,25 +408,22 @@ public class PublicIpFetcher { } /** - * 使用示例 + * 测试方法 */ public static void main(String[] args) { - System.out.println("开始获取公网IP信息..."); + System.out.println("开始获取公网IP列表..."); - String ipInfo = getPublicIp(); - System.out.println("完整信息: " + ipInfo); + // 测试nslookup解析 + System.out.println("测试解析myip.ipip.net:"); + List ipList1 = parseNslookupResult(PRIMARY_IP_SERVICE); + System.out.println("解析结果: " + ipList1); - String pureIp = extractIp(ipInfo); - System.out.println("公网 IP: " + (pureIp != null ? pureIp : "获取失败")); + System.out.println("\n测试解析cip.cc:"); + List ipList2 = parseNslookupResult(FALLBACK_IP_SERVICE); + System.out.println("解析结果: " + ipList2); - IpLocationInfo location = extractLocation(ipInfo); - System.out.println(location); - - // 提取需要的字段 - if (location.ip != null) { - System.out.println("省份: " + location.getProvince()); - System.out.println("城市: " + location.getCity()); - System.out.println("运营商: " + location.getCarrier()); - } + // 获取公网IP列表 + List allIps = getPublicIps(); + System.out.println("\n最终获取到的公网IP列表: " + allIps); } } \ No newline at end of file