From 01c6885a2b06e47cc32245671d10f055b45dcfa5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BC=96=E7=A0=81=E7=8C=BF?= Date: Tue, 9 Sep 2025 08:57:01 +0800 Subject: [PATCH] first commit --- Dockerfile | 20 +++++++++ config/index.js | 4 ++ index.js | 29 ++++++++++++ package.json | 16 +++++++ utils/ip.js | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 186 insertions(+) create mode 100644 Dockerfile create mode 100644 config/index.js create mode 100644 index.js create mode 100644 package.json create mode 100644 utils/ip.js diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..9ce7359 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +# 使用基础镜像(根据需要选择合适的版本) +FROM debian:bullseye-slim # 或 ubuntu:22.04 + +# 设置非交互模式,避免安装过程中出现交互提示 +ENV DEBIAN_FRONTEND=noninteractive + +# 更新软件源并安装iproute2,同时清理缓存减小镜像体积 +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + iproute2 \ # 包含ip命令的工具包 + && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# 可选:设置工作目录 +WORKDIR /app + +# 可选:添加启动命令 +CMD ["bash"] + \ No newline at end of file diff --git a/config/index.js b/config/index.js new file mode 100644 index 0000000..dda6e21 --- /dev/null +++ b/config/index.js @@ -0,0 +1,4 @@ +module.exports = { + port: 9900, + command: 'ip -6 addr show enp3s0' +} \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..7c7d706 --- /dev/null +++ b/index.js @@ -0,0 +1,29 @@ +const { execSync } = require('child_process'); +const ip = require('./utils/ip'); +const { port, command } = require('./config'); + +const express = require('express') +const app = express() + + +app.get('/ip', async (req, res) => { + // let output = execSync(command, { encoding: "utf8" }); + let output = `2: enp3s0: mtu 1500 qdisc mq state UP group default qlen 1000 + inet6 240e:362:2245:9b00::68c/128 scope global dynamic noprefixroute + valid_lft 24426sec preferred_lft 24426sec + inet6 240e:362:2245:9b00:2e0:4cff:fe09:1f7f/64 scope global dynamic mngtmpaddr noprefixroute + valid_lft 222142sec preferred_lft 135742sec + inet6 240e:362:223c:2800:2e0:4cff:fe09:1f7f/64 scope global dynamic mngtmpaddr noprefixroute + valid_lft 221829sec preferred_lft 135429sec + inet6 fe80::2e0:4cff:fe09:1f7f/64 scope link + valid_lft forever preferred_lft forever` + res.json(await ip.parseNetworkInfo(output)) +}) + +app.listen(port, () => { + console.log(`app listening on port: http://127.0.0.1:${port}`) +}) + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..c6745ca --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "ipv6", + "version": "1.0.0", + "description": "", + "main": "index.js", + "dependencies": { + "express": "^5.1.0" + }, + "devDependencies": {}, + "scripts": { + "start": "node-dev index.js" + }, + "keywords": [], + "author": "", + "license": "ISC" +} \ No newline at end of file diff --git a/utils/ip.js b/utils/ip.js new file mode 100644 index 0000000..fcf5ded --- /dev/null +++ b/utils/ip.js @@ -0,0 +1,117 @@ +const net = require('net'); + +class Ip { + static async parseNetworkInfo(output) { + // 初始化结果对象 + const result = { + interface: '', + flags: [], + mtu: '', + state: '', + ipv6Addresses: [] + }; + + // 按行分割输入 + const lines = output.split('\n').map(line => line.trim()); + + // 解析第一行的基本信息 + if (lines.length > 0) { + const firstLineParts = lines[0].split(/\s+/); + // 提取接口名称 + result.interface = firstLineParts[1].replace(':', ''); + + // 提取标志 + if (firstLineParts[2] && firstLineParts[2].startsWith('<') && firstLineParts[2].endsWith('>')) { + result.flags = firstLineParts[2].slice(1, -1).split(','); + } + + // 提取MTU + const mtuIndex = firstLineParts.indexOf('mtu'); + if (mtuIndex !== -1 && mtuIndex + 1 < firstLineParts.length) { + result.mtu = firstLineParts[mtuIndex + 1]; + } + + // 提取状态 + const stateIndex = firstLineParts.indexOf('state'); + if (stateIndex !== -1 && stateIndex + 1 < firstLineParts.length) { + result.state = firstLineParts[stateIndex + 1]; + } + } + + // 解析IPv6地址信息 + let currentIpv6 = null; + for (let i = 1; i < lines.length; i++) { + const line = lines[i]; + if (line.startsWith('inet6')) { + // 如果有当前正在处理的IPv6地址,先保存 + if (currentIpv6) { + result.ipv6Addresses.push(currentIpv6); + } + + // 开始解析新的IPv6地址 + const parts = line.split(/\s+/); + const ipv6Address = parts[1].split("/")[0] + currentIpv6 = { + connect: await Ip.checkIPv6WithTcp(ipv6Address), + address: parts[1].split("/")[0], + scope: parts[2].replace('scope', ''), + properties: parts.slice(3), + validLft: '', + preferredLft: '' + }; + } else if (currentIpv6 && line.includes('valid_lft')) { + // 解析有效期信息 + const lftParts = line.split(/\s+/); + currentIpv6.validLft = lftParts[0].replace('valid_lft', ''); + currentIpv6.preferredLft = lftParts[1].replace('preferred_lft', ''); + } + } + + // 添加最后一个IPv6地址 + if (currentIpv6) { + result.ipv6Addresses.push(currentIpv6); + } + + return result; + } + + /** + * 检测IPv6地址是否可访问(通过尝试建立TCP连接) + * @param {string} ipv6 - 要检测的IPv6地址 + * @param {number} port - 目标端口,默认 21100 + * @param {number} timeout - 超时时间(毫秒),默认1000 + * @returns {Promise} - 是否可访问 + */ + static checkIPv6WithTcp(ipv6, port = 21100, timeout = 1000) { + return new Promise((resolve) => { + // 验证IPv6格式 + if (!net.isIPv6(ipv6)) { + resolve(false); + return; + } + + const socket = new net.Socket({ family: 6 }); // 指定IPv6 + + // 设置超时 + const timer = setTimeout(() => { + socket.destroy(); + resolve(false); + }, timeout); + + // 尝试连接 + socket.connect(port, ipv6, () => { + clearTimeout(timer); + socket.destroy(); + resolve(true); + }); + + // 错误处理 + socket.on('error', () => { + clearTimeout(timer); + resolve(false); + }); + }); + } +} + +module.exports = Ip \ No newline at end of file