From 8c01d3fd5abe268efad764c475b71c37b48eb5cd Mon Sep 17 00:00:00 2001 From: MoeFurina Date: Fri, 13 Mar 2026 21:54:38 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=8A=93=E5=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ca.crt | 14 + server.crt | 16 + server.key | 9 + src/client/app.js | 33 ++ src/client/public/index.html | 554 ++++++++++++++++++++++++++------- src/server/app.js | 119 +------ src/server/certs.txt | 0 src/server/consts.js | 20 -- src/server/crypto.js | 88 +----- src/server/generate_cert.py | 28 ++ src/server/hook.js | 333 ++------------------ src/server/kwDES.js | 585 ----------------------------------- src/server/request.js | 28 +- src/server/server.crt | 28 +- src/server/server.key | 37 +-- 15 files changed, 596 insertions(+), 1296 deletions(-) create mode 100644 ca.crt create mode 100644 server.crt create mode 100644 server.key create mode 100644 src/server/certs.txt delete mode 100644 src/server/consts.js create mode 100644 src/server/generate_cert.py delete mode 100644 src/server/kwDES.js diff --git a/ca.crt b/ca.crt new file mode 100644 index 0000000..1b528aa --- /dev/null +++ b/ca.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICGjCCAaCgAwIBAgIUXBIOR4IUZCnxKkT2yr8ZdS17y0wwCgYIKoZIzj0EAwMw +RDELMAkGA1UEBhMCQ04xJDAiBgNVBAMMG1VuYmxvY2tOZXRlYXNlTXVzaWMgUm9v +dCBDQTEPMA0GA1UECgwGbm9ib2R5MB4XDTIyMDQwNDEyMDk1MloXDTI3MDQwMzEy +MDk1MlowRDELMAkGA1UEBhMCQ04xJDAiBgNVBAMMG1VuYmxvY2tOZXRlYXNlTXVz +aWMgUm9vdCBDQTEPMA0GA1UECgwGbm9ib2R5MHYwEAYHKoZIzj0CAQYFK4EEACID +YgAEZDE7xoQc0jylGpqesuWZKdSBAxD1kydeYRfJr296Pc2v32P1q2VQFBC8XY7d +nzfKN5zLEc9csWRsvmpwXVlXq2wJW/sJENjErQ8yeaw/4alxDUWHwwgiu/ienBl/ +iIMWo1MwUTAdBgNVHQ4EFgQUZb6EKjjHbgAgwhhtSNHg1GWfPHUwHwYDVR0jBBgw +FoAUZb6EKjjHbgAgwhhtSNHg1GWfPHUwDwYDVR0TAQH/BAUwAwEB/zAKBggqhkjO +PQQDAwNoADBlAjEAuzn+fWQpEyFIUuQZC8TcfteCEvn2KettQT2ZOfQxHB3sIWhE +BC9cebRgdc74Lvv6AjBLQyq2SfDzrDE0PexyJxy8IQHAncP0+1QnAXVU5GO6YJYg +1gYZwdmbNOhh81S21l8= +-----END CERTIFICATE----- diff --git a/server.crt b/server.crt new file mode 100644 index 0000000..d8391bd --- /dev/null +++ b/server.crt @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICgjCCAgigAwIBAgIUafzDKFqhg8XS5YogLkI9oGsngkUwCgYIKoZIzj0EAwMw +RDELMAkGA1UEBhMCQ04xJDAiBgNVBAMMG1VuYmxvY2tOZXRlYXNlTXVzaWMgUm9v +dCBDQTEPMA0GA1UECgwGbm9ib2R5MB4XDTI1MDcyNDA4NDY1NloXDTI2MDcyNDA4 +NDY1NlowezELMAkGA1UEBhMCQ04xETAPBgNVBAcMCEhhbmd6aG91MSwwKgYDVQQK +DCNOZXRFYXNlIChIYW5nemhvdSkgTmV0d29yayBDby4sIEx0ZDERMA8GA1UECwwI +SVQgRGVwdC4xGDAWBgNVBAMMDyoubXVzaWMuMTYzLmNvbTB2MBAGByqGSM49AgEG +BSuBBAAiA2IABACkvqL3b68Pv11nQVwm/9BNWGuh1fBKlqYZqnyr9Gy1xEQtf3we +WlwgVmX9rqu9nNEiAbbxIt9aGliHurKkE22+FiOKC4vduYlyyIXKlCAFXigHKr/k +6uKgc2jPfeYoC6OBgzCBgDATBgNVHSUEDDAKBggrBgEFBQcDATApBgNVHREEIjAg +gg1tdXNpYy4xNjMuY29tgg8qLm11c2ljLjE2My5jb20wHQYDVR0OBBYEFEeHCj4z +PzDre8No6wsWbrpmQ8hPMB8GA1UdIwQYMBaAFGW+hCo4x24AIMIYbUjR4NRlnzx1 +MAoGCCqGSM49BAMDA2gAMGUCMQDD0cOUV43GSLH8bbbkw4KD6kVoknm6z6sjE5vk +ZNmgjqduSn6XzCNiiwwhRrsmtVkCMCX8uRVIIG20mdcK2YSwlSgw5mHTJsNFRnfn +x8ft2l8Tk2yZcqFSsvuVKvBwZh7bWA== +-----END CERTIFICATE----- diff --git a/server.key b/server.key new file mode 100644 index 0000000..eeff0b6 --- /dev/null +++ b/server.key @@ -0,0 +1,9 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQAIg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDDaWQTho9lbjl27ddVt9pC4req+U8iFhdMw0/RzWePrlF55fvmwRiff +GepKeBDdkx6gBwYFK4EEACKhZANiAAQApL6i92+vD79dZ0FcJv/QTVhrodXwSpam +Gap8q/RstcRELX98HlpcIFZl/a6rvZzRIgG28SLfWhpYh7qypBNtvhYjiguL3bmJ +csiFypQgBV4oByq/5OrioHNoz33mKAs= +-----END EC PRIVATE KEY----- diff --git a/src/client/app.js b/src/client/app.js index fe1bc37..628286a 100644 --- a/src/client/app.js +++ b/src/client/app.js @@ -6,6 +6,7 @@ const app = express(); const PORT = process.env.PORT || 3000; let capturedData = []; +let clients = []; app.use(express.json()); app.use(express.static(path.join(__dirname, 'public'))); @@ -14,6 +15,10 @@ app.post('/api/capture', (req, res) => { const data = req.body; capturedData.push(data); console.log('Captured data:', data.path); + + // 通知所有 SSE 客户端有新数据 + broadcastData(); + res.status(200).send('OK'); }); @@ -21,6 +26,34 @@ app.get('/api/data', (req, res) => { res.json(capturedData); }); +// SSE 端点 +app.get('/api/events', (req, res) => { + res.setHeader('Content-Type', 'text/event-stream'); + res.setHeader('Cache-Control', 'no-cache'); + res.setHeader('Connection', 'keep-alive'); + + // 立即发送当前数据 + res.write(`data: ${JSON.stringify(capturedData)}\n\n`); + + // 添加到客户端列表 + clients.push(res); + + req.on('close', () => { + clients = clients.filter(client => client !== res); + }); +}); + +function broadcastData() { + clients.forEach(client => { + try { + client.write(`data: ${JSON.stringify(capturedData)}\n\n`); + } catch (e) { + // 连接已断开,移除客户端 + clients = clients.filter(c => c !== client); + } + }); +} + app.listen(PORT, () => { console.log(`Frontend server running at http://localhost:${PORT}`); }); \ No newline at end of file diff --git a/src/client/public/index.html b/src/client/public/index.html index f7a411b..5553197 100644 --- a/src/client/public/index.html +++ b/src/client/public/index.html @@ -17,93 +17,340 @@ * { box-sizing: border-box; } html, body { height: 100%; } body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: var(--fg); background: var(--bg); line-height: 1.6; } - .container { max-width: 960px; margin: 40px auto; padding: 0 20px; } - @media (max-width: 480px) { - .container { margin: 20px auto; padding: 0 16px; } - header.site-header h1 { font-size: 22px; } - .block { padding: 16px; } + + .main-container { + display: flex; + height: 100vh; + overflow: hidden; } - header.site-header { margin-bottom: 24px; } - header.site-header h1 { font-size: 28px; font-weight: 600; margin: 0; color: var(--accent); } - .badge { display: inline-block; margin-left: 8px; padding: 4px 10px; border: 1px solid var(--border); border-radius: 12px; font-size: 12px; color: var(--muted); } - .sub { margin-top: 8px; color: var(--muted); font-size: 14px; } - .block { background: var(--panel); border: 1px solid var(--border); border-radius: 12px; padding: 20px; margin-bottom: 16px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); } - .block h2 { margin: 0 0 12px; font-size: 18px; font-weight: 600; } - .kvs { display: grid; grid-template-columns: 100px 1fr; gap: 8px 12px; align-items: start; } - .kvs div:first-child { color: var(--muted); flex-shrink: 0; } - .kvs div:last-child { word-break: break-all; overflow-wrap: anywhere; min-width: 0; overflow: hidden; } - @media (max-width: 480px) { - .kvs { grid-template-columns: 1fr; gap: 4px 12px; } - .kvs div:first-child { font-weight: 500; } + + .left-panel { + width: 33%; + min-width: 300px; + max-width: 450px; + border-right: 1px solid var(--border); + background: var(--panel); + display: flex; + flex-direction: column; + overflow: hidden; } - .capture-item { border: 1px solid var(--border); border-radius: 8px; margin-bottom: 12px; overflow: hidden; } - .capture-header { background: #fafafa; padding: 12px 16px; border-bottom: 1px solid var(--border); display: flex; justify-content: space-between; align-items: center; } - .capture-path { font-weight: 600; color: var(--accent); } - .capture-time { font-size: 12px; color: var(--muted); } - .capture-body { padding: 16px; } - .capture-section { margin-bottom: 12px; } - .capture-section:last-child { margin-bottom: 0; } - .capture-section strong { display: block; margin-bottom: 6px; font-size: 14px; color: var(--muted); } - pre { margin: 0; background: #f9f9f9; border: 1px solid var(--border); border-radius: 6px; padding: 12px; overflow-x: auto; white-space: pre-wrap; word-break: break-all; font-size: 13px; } - code { font-family: 'Courier New', monospace; font-size: 13px; } - @media (max-width: 480px) { - code { font-size: 12px; } + + .right-panel { + flex: 1; + background: var(--bg); + overflow-y: auto; + padding: 20px; + } + + .left-content { + flex: 1; + overflow-y: auto; + padding: 16px; + } + + .header { + padding: 16px 20px; + border-bottom: 1px solid var(--border); + background: #fafafa; + flex-shrink: 0; + } + + .header h1 { + font-size: 20px; + font-weight: 600; + margin: 0 0 8px 0; + color: var(--accent); + } + + .badge { + display: inline-block; + margin-left: 8px; + padding: 2px 8px; + border: 1px solid var(--border); + border-radius: 10px; + font-size: 11px; + color: var(--muted); + } + + .sub { + font-size: 13px; + color: var(--muted); + margin: 0; + } + + .block { + background: #fafafa; + border: 1px solid var(--border); + border-radius: 8px; + padding: 12px; + margin-bottom: 12px; + } + + .block h2 { + font-size: 14px; + font-weight: 600; + margin: 0 0 10px 0; + } + + .kvs { + display: grid; + grid-template-columns: 80px 1fr; + gap: 6px 10px; + align-items: start; + font-size: 13px; + } + + .kvs div:first-child { + color: var(--muted); + font-weight: 500; + } + + .kvs div:last-child { + word-break: break-all; + overflow-wrap: anywhere; + } + + .status { + display: inline-block; + padding: 3px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + } + + .status-connected { + background: #e6f4ea; + color: #1e8e3e; + } + + .status-waiting { + background: #fef7e0; + color: #f9ab00; + } + + .capture-list { + margin-top: 12px; + } + + .capture-list h2 { + font-size: 14px; + font-weight: 600; + margin: 0 0 10px 0; + } + + .capture-list-item { + padding: 10px 12px; + border: 1px solid var(--border); + border-radius: 6px; + margin-bottom: 8px; + cursor: pointer; + transition: all 0.2s; + background: var(--panel); + } + + .capture-list-item:hover { + border-color: var(--accent); + background: #fff5f5; + } + + .capture-list-item.active { + border-color: var(--accent); + background: #fff5f5; + box-shadow: 0 2px 4px rgba(194, 12, 12, 0.1); + } + + .capture-list-path { + font-weight: 600; + color: var(--accent); + font-size: 13px; + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .capture-list-time { + font-size: 12px; + color: var(--muted); + } + + .empty-list { + text-align: center; + padding: 30px 20px; + color: var(--muted); + font-size: 13px; + } + + .empty-list .icon { + font-size: 36px; + margin-bottom: 8px; + opacity: 0.5; + } + + .detail-panel { + background: var(--panel); + border: 1px solid var(--border); + border-radius: 8px; + padding: 20px; + } + + .detail-header { + margin-bottom: 20px; + padding-bottom: 16px; + border-bottom: 1px solid var(--border); + } + + .detail-header h2 { + font-size: 18px; + font-weight: 600; + margin: 0 0 8px 0; + color: var(--accent); + } + + .detail-time { + font-size: 13px; + color: var(--muted); + } + + .detail-section { + margin-bottom: 20px; + } + + .detail-section:last-child { + margin-bottom: 0; + } + + .detail-section strong { + display: block; + margin-bottom: 10px; + font-size: 14px; + color: var(--muted); + font-weight: 600; + } + + .copy-btn { + display: inline-block; + padding: 6px 12px; + background: var(--accent); + color: white; + border: none; + border-radius: 4px; + font-size: 12px; + cursor: pointer; + transition: background 0.2s; + margin-left: 8px; + } + + .copy-btn:hover { + background: #a00a0a; + } + + .copy-btn:active { + transform: scale(0.98); + } + + pre { + margin: 0; + background: #f9f9f9; + border: 1px solid var(--border); + border-radius: 6px; + padding: 14px; + overflow-x: auto; + white-space: pre-wrap; + word-break: break-all; + font-size: 13px; + line-height: 1.5; + } + + code { + font-family: 'Courier New', monospace; + font-size: 13px; + } + + .no-selection { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: var(--muted); + font-size: 14px; + } + + .no-selection .icon { + font-size: 48px; + margin-right: 16px; + opacity: 0.5; + } + + .toast { + position: fixed; + top: 20px; + right: 20px; + padding: 12px 20px; + background: #333; + color: white; + border-radius: 6px; + font-size: 14px; + opacity: 0; + transform: translateY(-10px); + transition: all 0.3s; + z-index: 1000; + } + + .toast.show { + opacity: 1; + transform: translateY(0); } - .status { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 12px; font-weight: 500; } - .status-connected { background: #e6f4ea; color: #1e8e3e; } - .status-waiting { background: #fef7e0; color: #f9ab00; } - .no-data { text-align: center; padding: 40px 20px; color: var(--muted); } - .no-data .icon { font-size: 48px; margin-bottom: 12px; opacity: 0.5; } - footer.site-footer { margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--border); color: var(--muted); text-align: center; } - footer.site-footer a { color: var(--fg); text-decoration: none; transition: color 0.2s ease; } - footer.site-footer a:hover { color: var(--accent); } -
- - -
-

状态

-
-
连接状态
等待连接...
-
Base URL
-
当前页
-
抓包数量
0
+
+
+
+

网易云音乐抓包工具 v0.0.1

+

🔍 简易网易云音乐客户端抓包工具

-
+
+
+

状态

+
+
连接状态
等待连接...
+
Base URL
+
抓包数量
0
+
+
-
-

使用说明

-
-
1. 启动服务
运行 npm run dev 启动抓包和前端服务
-
2. 设置代理
将网易云音乐客户端代理设置为 http://localhost:9000
-
3. 开始抓包
在网易云音乐客户端中进行操作,抓包数据将实时显示
-
-
+
+

使用说明

+
+
1. 启动服务
运行 npm run dev
+
2. 设置代理
客户端代理: localhost:9000
+
3. 开始抓包
在客户端中操作,数据实时显示
+
+
-
-

抓包数据

-
-
-
📡
-

暂无抓包数据

-

请设置网易云音乐客户端代理并开始使用

+
+

抓包列表

+
-
+
+ +
+
+
+ 📡 + 请从左侧选择一个抓包记录查看详情 +
+
+
+ - -
+
已复制到剪贴板
diff --git a/src/server/app.js b/src/server/app.js index ccad250..58e8650 100644 --- a/src/server/app.js +++ b/src/server/app.js @@ -13,35 +13,6 @@ const config = require('./cli.js') metavar: 'address', help: 'specify server host', }) - .option(['-u', '--proxy-url'], { - metavar: 'url', - help: 'request through upstream proxy', - }) - .option(['-f', '--force-host'], { - metavar: 'host', - help: 'force the netease server ip', - }) - .option(['-o', '--match-order'], { - metavar: 'source', - nargs: '+', - help: 'set priority of sources', - }) - .option(['-t', '--token'], { - metavar: 'token', - help: 'set up proxy authentication', - }) - .option(['-e', '--endpoint'], { - metavar: 'url', - help: 'replace virtual endpoint with public host', - }) - .option(['-s', '--strict'], { - action: 'store_true', - help: 'enable proxy limitation', - }) - .option(['-c', '--cnrelay'], { - metavar: 'cnrelay', - help: 'Mainland China relay to get music url', - }) .option(['-h', '--help'], { action: 'help' }) .parse(process.argv); @@ -61,52 +32,19 @@ if (config.port.some(invalid)) { if (!process.env.PORT) { process.env.PORT = process.env.PORT || '3000'; } -if (config.proxyUrl && !/http(s?):\/\/.+:\d+/.test(config.proxyUrl)) { - console.log('Please check the proxy url.'); - process.exit(1); -} -if (!config.endpoint) config.endpoint = 'https://music.163.com'; -else if (config.endpoint === '-') config.endpoint = ''; -else if (!/http(s?):\/\/.+/.test(config.endpoint)) { - console.log('Please check the endpoint host.'); - process.exit(1); -} -if (config.forceHost && require('net').isIP(config.forceHost) === 0) { - console.log('Please check the server host.'); - process.exit(1); -} -if (config.matchOrder) { - const provider = Object.keys(require('./consts').PROVIDERS); - const candidate = config.matchOrder; - if (candidate.some((key, index) => index != candidate.indexOf(key))) { - console.log('Please check the duplication in match order.'); - process.exit(1); - } else if (candidate.some((key) => !provider.includes(key))) { - console.log('Please check the availability of match sources.'); - process.exit(1); - } - global.source = candidate; -} -if (config.token && !/\S+:\S+/.test(config.token)) { - console.log('Please check the authentication token.'); - process.exit(1); -} const { logScope } = require('./logger'); const parse = require('url').parse; const hook = require('./hook'); const server = require('./server'); -const { CacheStorageGroup } = require('./cache'); const logger = logScope('app'); -const random = (array) => array[Math.floor(Math.random() * array.length)]; const target = Array.from(hook.target.host); global.port = config.port; -global.proxy = config.proxyUrl ? parse(config.proxyUrl) : null; -global.hosts = target.reduce( - (result, host) => Object.assign(result, { [host]: config.forceHost }), - {} -); +global.proxy = null; +global.hosts = {}; +global.endpoint = 'https://music.163.com'; + server.whitelist = [ '://[\\w.]*music\\.126\\.net', '://[\\w.]*vod\\.126\\.net', @@ -114,14 +52,8 @@ server.whitelist = [ '://[\\w.]*\\.netease.com', '://[\\w.]*\\.163yun.com', ]; -global.cnrelay = config.cnrelay; -if (config.strict) server.blacklist.push('.*'); -server.authentication = config.token || null; -global.endpoint = config.endpoint; -if (config.endpoint) server.whitelist.push(escape(config.endpoint)); -// hosts['music.httpdns.c.163.com'] = random(['59.111.181.35', '59.111.181.38']) -// hosts['httpdns.n.netease.com'] = random(['59.111.179.213', '59.111.179.214']) +if (config.endpoint) server.whitelist.push(escape(config.endpoint)); const dns = (host) => new Promise((resolve, reject) => @@ -131,44 +63,8 @@ const dns = (host) => : resolve(records.map((record) => record.address)) ) ); -const httpdns = (host) => - require('./request')('POST', 'http://music.httpdns.c.163.com/d', {}, host) - .then((response) => response.json()) - .then((jsonBody) => - jsonBody.dns.reduce( - (result, domain) => result.concat(domain.ips), - [] - ) - ); -const httpdns2 = (host) => - require('./request')( - 'GET', - 'http://httpdns.n.netease.com/httpdns/v2/d?domain=' + host - ) - .then((response) => response.json()) - .then((jsonBody) => - Object.keys(jsonBody.data) - .map((key) => jsonBody.data[key]) - .reduce((result, value) => result.concat(value.ip || []), []) - ); -// Allow enabling HTTPDNS queries with `ENABLE_HTTPDNS=true` -// It seems broken - BETTER TO NOT ENABLE IT! -const dnsSource = - process.env.ENABLE_HTTPDNS === 'true' ? [httpdns, httpdns2] : []; - -// Start the "Clean Cache" background task. -const csgInstance = CacheStorageGroup.getInstance(); -setInterval( - () => { - csgInstance.cleanup(); - }, - 15 * 60 * 1000 -); - -Promise.all( - dnsSource.map((query) => query(target.join(','))).concat(target.map(dns)) -) +Promise.all(target.map(dns)) .then((result) => { const { host } = hook.target; result.forEach((array) => array.forEach(host.add, host)); @@ -189,9 +85,8 @@ Promise.all( server.https .listen(port[1], address) .once('listening', () => log(1)); - if (cnrelay) logger.info(`CNRelay: ${cnrelay}`); }) .catch((error) => { console.log(error); process.exit(1); - }); + }); \ No newline at end of file diff --git a/src/server/certs.txt b/src/server/certs.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/server/consts.js b/src/server/consts.js deleted file mode 100644 index 6c6020c..0000000 --- a/src/server/consts.js +++ /dev/null @@ -1,20 +0,0 @@ -const DEFAULT_SOURCE = ['kugou', 'bodian', 'migu', 'ytdlp']; -const PROVIDERS = { - qq: require('./provider/qq'), - kugou: require('./provider/kugou'), - kuwo: require('./provider/kuwo'), - bodian: require('./provider/bodian'), - migu: require('./provider/migu'), - joox: require('./provider/joox'), - youtube: require('./provider/youtube'), - youtubedl: require('./provider/youtube-dl'), - ytdlp: require('./provider/yt-dlp'), - bilibili: require('./provider/bilibili'), - bilivideo: require('./provider/bilivideo'), - pyncmd: require('./provider/pyncmd'), -}; - -module.exports = { - DEFAULT_SOURCE, - PROVIDERS, -}; diff --git a/src/server/crypto.js b/src/server/crypto.js index 402a4af..2116ca0 100644 --- a/src/server/crypto.js +++ b/src/server/crypto.js @@ -71,67 +71,6 @@ module.exports = { }; }, }, - miguapi: { - encryptBody: (object) => { - const text = JSON.stringify(object); - const derive = (password, salt, keyLength, ivSize) => { - // EVP_BytesToKey - salt = salt || Buffer.alloc(0); - const keySize = keyLength / 8; - const repeat = Math.ceil((keySize + ivSize * 8) / 32); - const buffer = Buffer.concat( - Array(repeat) - .fill(null) - .reduce( - (result) => - result.concat( - crypto - .createHash('md5') - .update( - Buffer.concat([ - result.slice(-1)[0], - password, - salt, - ]) - ) - .digest() - ), - [Buffer.alloc(0)] - ) - ); - return { - key: buffer.slice(0, keySize), - iv: buffer.slice(keySize, keySize + ivSize), - }; - }; - const password = Buffer.from( - crypto.randomBytes(32).toString('hex') - ), - salt = crypto.randomBytes(8); - const key = - '-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8asrfSaoOb4je+DSmKdriQJKWVJ2oDZrs3wi5W67m3LwTB9QVR+cE3XWU21Nx+YBxS0yun8wDcjgQvYt625ZCcgin2ro/eOkNyUOTBIbuj9CvMnhUYiR61lC1f1IGbrSYYimqBVSjpifVufxtx/I3exReZosTByYp4Xwpb1+WAQIDAQAB\n-----END PUBLIC KEY-----'; - const secret = derive(password, salt, 256, 16); - const cipher = crypto.createCipheriv( - 'aes-256-cbc', - secret.key, - secret.iv - ); - return bodyify({ - data: Buffer.concat([ - Buffer.from('Salted__'), - salt, - cipher.update(Buffer.from(text)), - cipher.final(), - ]).toString('base64'), - secKey: crypto - .publicEncrypt( - { key, padding: crypto.constants.RSA_PKCS1_PADDING }, - password - ) - .toString('base64'), - }); - }, - }, base64: { encode: (text, charset) => Buffer.from(text, charset) @@ -144,27 +83,6 @@ module.exports = { 'base64' ).toString(charset), }, - uri: { - retrieve: (id) => { - id = id.toString().trim(); - const key = '3go8&$8*3*3h0k(2)2'; - const string = Array.from(Array(id.length).keys()) - .map((index) => - String.fromCharCode( - id.charCodeAt(index) ^ - key.charCodeAt(index % key.length) - ) - ) - .join(''); - const result = crypto - .createHash('md5') - .update(string) - .digest('base64') - .replace(/\//g, '_') - .replace(/\+/g, '-'); - return `http://p1.music.126.net/${result}/${id}`; - }, - }, md5: { digest: (value) => crypto.createHash('md5').update(value).digest('hex'), pipe: (source) => @@ -188,8 +106,4 @@ module.exports = { .slice(0, length), uuid: () => crypto.randomUUID(), }, -}; - -try { - module.exports.kuwoapi = require('./kwDES'); -} catch (e) {} +}; \ No newline at end of file diff --git a/src/server/generate_cert.py b/src/server/generate_cert.py new file mode 100644 index 0000000..0ee62d7 --- /dev/null +++ b/src/server/generate_cert.py @@ -0,0 +1,28 @@ +import ssl +import socket +from datetime import datetime, timedelta + +# 生成自签名证书 +certfile = "server.crt" +keyfile = "server.key" + +# 创建上下文 +context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + +# 生成自签名证书 +pkey = ssl._ssl._ssl_context.keygen(2048) + +# 创建证书 +cert = ssl._ssl._ssl_context.certgen( + pkey, + certfile, + keyfile, + CAfile=None, + notBefore=datetime.now(), + notAfter=datetime.now() + timedelta(days=3650), + serialNumber=1, +) + +print("✓ 证书创建成功!") +print("✓ 私钥: server.key") +print("✓ 证书: server.crt") \ No newline at end of file diff --git a/src/server/hook.js b/src/server/hook.js index 86d4265..1c316f9 100644 --- a/src/server/hook.js +++ b/src/server/hook.js @@ -35,12 +35,6 @@ hook.target.host = new Set([ 'apm3.music.163.com', 'interface.music.163.com.163jiasu.com', 'interface3.music.163.com.163jiasu.com', - // 'mam.netease.com', - // 'api.iplay.163.com', // look living - // 'ac.dun.163yun.com', - // 'crash.163.com', - // 'clientlog.music.163.com', - // 'clientlog3.music.163.com' ]); hook.target.path = new Set([ @@ -139,7 +133,6 @@ hook.request.before = (ctx) => { ) && req.method === 'POST' && (url.path.startsWith('/eapi/') || // eapi - // url.path.startsWith('/api/') || // api url.path.startsWith('/api/linux/forward')) // linuxapi ) { return request @@ -180,12 +173,12 @@ hook.request.before = (ctx) => { ), 'hex' ) - ) - .toString() - ); - netease.path = parse(data.url).path; - netease.param = data.params; - break; + ) + .toString() + ); + netease.path = parse(data.url).path; + netease.param = data.params; + break; case 'eapi': data = crypto.eapi .decrypt( @@ -196,22 +189,22 @@ hook.request.before = (ctx) => { ), 'hex' ) - ) - .toString() + ) + .toString() .split('-36cd479b6b5-'); - netease.path = data[0]; - netease.param = JSON.parse(data[1]); - if ( - netease.param.hasOwnProperty('e_r') && - (netease.param.e_r == 'true' || - netease.param.e_r == true) - ) { - // eapi's e_r is true, needs to be encrypted - netease.e_r = true; - } else { - netease.e_r = false; - } - break; + netease.path = data[0]; + netease.param = JSON.parse(data[1]); + if ( + netease.param.hasOwnProperty('e_r') && + (netease.param.e_r == 'true' || + netease.param.e_r == true) + ) { + // eapi's e_r is true, needs to be encrypted + netease.e_r = true; + } else { + netease.e_r = false; + } + break; case 'api': data = {}; decodeURIComponent(body) @@ -220,9 +213,9 @@ hook.request.before = (ctx) => { let [key, value] = pair.split('='); data[key] = value; }); - netease.path = url.path; - netease.param = data; - break; + netease.path = url.path; + netease.param = data; + break; default: // unsupported crypto break; @@ -230,30 +223,6 @@ hook.request.before = (ctx) => { netease.path = netease.path.replace(/\/\d*$/, ''); ctx.netease = netease; console.log(netease.path, netease.param) // 这里输出了网易云音乐的抓包数据, 重点看这里 - - if (netease.path === '/api/song/enhance/download/url') - return pretendPlay(ctx); - - if (netease.path === '/api/song/enhance/download/url/v1') - return pretendPlayV1(ctx); - - if (BLOCK_ADS) { - if (netease.path.startsWith('/api/ad')) { - ctx.error = new Error('ADs blocked.'); - ctx.decision = 'close'; - } - } - - if (DISABLE_UPGRADE_CHECK) { - if ( - netease.path.match( - /^\/api(\/v1)?\/(android|ios|osx|pc)\/(upgrade|version)/ - ) - ) { - ctx.error = new Error('Upgrade check blocked.'); - ctx.decision = 'close'; - } - } } }) .catch( @@ -287,9 +256,6 @@ hook.request.before = (ctx) => { req.headers['cookie'] = null; ctx.package = { id }; ctx.decision = 'proxy'; - // if (url.href.includes('google')) - // return request('GET', req.url, req.headers, null, parse('http://127.0.0.1:1080')) - // .then(response => (ctx.res.writeHead(response.statusCode, response.headers), response.pipe(ctx.res))) } catch (error) { ctx.error = error; ctx.decision = 'close'; @@ -340,170 +306,6 @@ hook.request.after = (ctx) => { }; axios.post(`http://localhost:${process.env.PORT || 3000}/api/capture`, dataToSend) .catch(err => logger.error('Failed to send data to frontend:', err)); - - - - if ( - netease.path === '/batch' || - netease.path === '/api/batch' || - netease.path === vipPath - ) { - const info = - netease.path === vipPath - ? netease.jsonBody - : netease.jsonBody[vipPath]; - const defaultPackage = { - iconUrl: null, - dynamicIconUrl: null, - isSign: false, - isSignIap: false, - isSignDeduct: false, - isSignIapDeduct: false, - }; - const vipLevel = 7; // ? months - if ( - info && - (LOCAL_VIP_UID.length === 0 || - LOCAL_VIP_UID.includes(info.data.userId)) - ) { - try { - const nowTime = - info.data.now || new Date().getTime(); - const expireTime = nowTime + 31622400000; - info.data.redVipLevel = vipLevel; - info.data.redVipAnnualCount = 1; - - info.data.musicPackage = { - ...defaultPackage, - ...info.data.musicPackage, - vipCode: 230, - vipLevel, - expireTime, - }; - - info.data.associator = { - ...defaultPackage, - ...info.data.associator, - vipCode: 100, - vipLevel, - expireTime, - }; - - if (ENABLE_LOCAL_SVIP) { - info.data.redplus = { - ...defaultPackage, - ...info.data.redplus, - vipCode: 300, - vipLevel, - expireTime, - }; - - info.data.albumVip = { - ...defaultPackage, - ...info.data.albumVip, - vipCode: 400, - vipLevel: 0, - expireTime, - }; - } - - if (netease.path === vipPath) - netease.jsonBody = info; - else netease.jsonBody[vipPath] = info; - } catch (error) { - logger.debug( - { err: error }, - 'Unable to apply the local VIP.' - ); - } - } - } - } - } - - if ( - new Set([401, 512]).has(netease.jsonBody.code) && - !netease.web - ) { - if (netease.path.includes('/usertool/sound/')) - return unblockSoundEffects(netease.jsonBody); - else if (netease.path.includes('batch')) { - for (const key in netease.jsonBody) { - if (key.includes('/usertool/sound/')) - unblockSoundEffects(netease.jsonBody[key]); - } - } else if (netease.path.includes('/vipauth/app/auth/query')) - return unblockLyricsEffects(netease.jsonBody); - - .then(() => { - ['transfer-encoding', 'content-encoding', 'content-length'] - .filter((key) => key in proxyRes.headers) - .forEach((key) => delete proxyRes.headers[key]); - - const inject = (key, value) => { - if (typeof value === 'object' && value != null) { - if ('cp' in value) value['cp'] = 1; - if ('fee' in value) value['fee'] = 0; - if ( - 'downloadMaxbr' in value && - value['downloadMaxbr'] === 0 - ) - value['downloadMaxbr'] = 320000; - if ( - 'dl' in value && - 'downloadMaxbr' in value && - value['dl'] < value['downloadMaxbr'] - ) - value['dl'] = value['downloadMaxbr']; - if ('playMaxbr' in value && value['playMaxbr'] === 0) - value['playMaxbr'] = 320000; - if ( - 'pl' in value && - 'playMaxbr' in value && - value['pl'] < value['playMaxbr'] - ) - value['pl'] = value['playMaxbr']; - if ('sp' in value && 'st' in value && 'subp' in value) { - // batch modify - value['sp'] = 7; - value['st'] = 0; - value['subp'] = 1; - } - if ( - 'start' in value && - 'end' in value && - 'playable' in value && - 'unplayableType' in value && - 'unplayableUserIds' in value - ) { - value['start'] = 0; - value['end'] = 0; - value['playable'] = true; - value['unplayableType'] = 'unknown'; - value['unplayableUserIds'] = []; - } - if ('noCopyrightRcmd' in value) - value['noCopyrightRcmd'] = null; - if ('payed' in value && value['payed'] == 0) - value['payed'] = 1; - if ('flLevel' in value && value['flLevel'] === 'none') - value['flLevel'] = 'exhigh'; - if ('plLevel' in value && value['plLevel'] === 'none') - value['plLevel'] = 'exhigh'; - if ('dlLevel' in value && value['dlLevel'] === 'none') - value['dlLevel'] = 'exhigh'; - } - return value; - }; - - let body = JSON.stringify(netease.jsonBody, inject); - body = body.replace( - /([^\\]"\s*:\s*)"(\d{16,})L"(\s*[}|,])/g, - '$1$2$3' - ); // for js precision - proxyRes.body = netease.e_r // eapi's e_r is true, needs to be encrypted - ? crypto.eapi.encrypt(Buffer.from(body)) - : body; }) .catch( (error) => @@ -557,89 +359,4 @@ hook.negotiate.before = (ctx) => { } }; -const pretendPlay = (ctx) => { - const { req, netease } = ctx; - const turn = 'http://music.163.com/api/song/enhance/player/url'; - let query; - const { id, br, e_r, header } = netease.param; - switch (netease.crypto) { - case 'linuxapi': - netease.param = { ids: `["${id}"]`, br }; - query = crypto.linuxapi.encryptRequest(turn, netease.param); - break; - case 'eapi': - case 'api': - netease.param = { ids: `["${id}"]`, br, e_r, header }; - if (netease.crypto == 'eapi') - query = crypto.eapi.encryptRequest(turn, netease.param); - else if (netease.crypto == 'api') - query = crypto.api.encryptRequest(turn, netease.param); - break; - default: - break; - } - req.url = query.url; - req.body = query.body + netease.pad; -}; - -const pretendPlayV1 = (ctx) => { - const { req, netease } = ctx; - const turn = 'http://music.163.com/api/song/enhance/player/url/v1'; - let query; - const { id, level, immerseType, e_r, header } = netease.param; - switch (netease.crypto) { - case 'linuxapi': - netease.param = { - ids: `["${id}"]`, - level, - encodeType: 'flac', - immerseType, - }; - query = crypto.linuxapi.encryptRequest(turn, netease.param); - break; - case 'eapi': - case 'api': - netease.param = { - ids: `["${id}"]`, - level, - encodeType: 'flac', - immerseType, - e_r, - header, - }; - if (netease.crypto == 'eapi') - query = crypto.eapi.encryptRequest(turn, netease.param); - else if (netease.crypto == 'api') - query = crypto.api.encryptRequest(turn, netease.param); - break; - default: - break; - } - req.url = query.url; - req.body = query.body + netease.pad; -}; - -const unblockSoundEffects = (obj) => { - logger.debug('unblockSoundEffects() has been triggered.'); - const { data, code } = obj; - if (code === 200) { - if (Array.isArray(data)) - data.map((item) => { - if (item.type) item.type = 1; - }); - else if (data.type) data.type = 1; - } -}; - -const unblockLyricsEffects = (obj) => { - logger.debug('unblockLyricsEffects() has been triggered.'); - const { data, code } = obj; - if (code === 200 && Array.isArray(data)) { - data.forEach((item) => { - if ('canUse' in item) item.canUse = true; - if ('canNotUseReasonCode' in item) item.canNotUseReasonCode = 200; - }); - } -}; - -module.exports = hook; +module.exports = hook; \ No newline at end of file diff --git a/src/server/kwDES.js b/src/server/kwDES.js deleted file mode 100644 index 04e0806..0000000 --- a/src/server/kwDES.js +++ /dev/null @@ -1,585 +0,0 @@ -/* - Thanks to - https://github.com/XuShaohua/kwplayer/blob/master/kuwo/DES.py - https://github.com/Levi233/MusicPlayer/blob/master/app/src/main/java/com/chenhao/musicplayer/utils/crypt/KuwoDES.java -*/ - -const Long = (n) => { - const bN = BigInt(n); - - return { - low: Number(bN), - valueOf: () => bN.valueOf(), - toString: () => bN.toString(), - not: () => Long(~bN), - isNegative: () => bN < 0, - or: (x) => Long(bN | BigInt(x)), - and: (x) => Long(bN & BigInt(x)), - xor: (x) => Long(bN ^ BigInt(x)), - equals: (x) => bN === BigInt(x), - multiply: (x) => Long(bN * BigInt(x)), - shiftLeft: (x) => Long(bN << BigInt(x)), - shiftRight: (x) => Long(bN >> BigInt(x)), - }; -}; - -const range = (n) => Array.from(new Array(n).keys()); -const power = (base, index) => - Array(index) - .fill(null) - .reduce((result) => result.multiply(base), Long(1)); -const LongArray = (...array) => - array.map((n) => (n === -1 ? Long(-1, -1) : Long(n))); - -// EXPANSION -const arrayE = LongArray( - 31, - 0, - 1, - 2, - 3, - 4, - -1, - -1, - 3, - 4, - 5, - 6, - 7, - 8, - -1, - -1, - 7, - 8, - 9, - 10, - 11, - 12, - -1, - -1, - 11, - 12, - 13, - 14, - 15, - 16, - -1, - -1, - 15, - 16, - 17, - 18, - 19, - 20, - -1, - -1, - 19, - 20, - 21, - 22, - 23, - 24, - -1, - -1, - 23, - 24, - 25, - 26, - 27, - 28, - -1, - -1, - 27, - 28, - 29, - 30, - 31, - 30, - -1, - -1 -); - -// INITIAL_PERMUTATION -const arrayIP = LongArray( - 57, - 49, - 41, - 33, - 25, - 17, - 9, - 1, - 59, - 51, - 43, - 35, - 27, - 19, - 11, - 3, - 61, - 53, - 45, - 37, - 29, - 21, - 13, - 5, - 63, - 55, - 47, - 39, - 31, - 23, - 15, - 7, - 56, - 48, - 40, - 32, - 24, - 16, - 8, - 0, - 58, - 50, - 42, - 34, - 26, - 18, - 10, - 2, - 60, - 52, - 44, - 36, - 28, - 20, - 12, - 4, - 62, - 54, - 46, - 38, - 30, - 22, - 14, - 6 -); - -// INVERSE_PERMUTATION -const arrayIP_1 = LongArray( - 39, - 7, - 47, - 15, - 55, - 23, - 63, - 31, - 38, - 6, - 46, - 14, - 54, - 22, - 62, - 30, - 37, - 5, - 45, - 13, - 53, - 21, - 61, - 29, - 36, - 4, - 44, - 12, - 52, - 20, - 60, - 28, - 35, - 3, - 43, - 11, - 51, - 19, - 59, - 27, - 34, - 2, - 42, - 10, - 50, - 18, - 58, - 26, - 33, - 1, - 41, - 9, - 49, - 17, - 57, - 25, - 32, - 0, - 40, - 8, - 48, - 16, - 56, - 24 -); - -// ROTATES -const arrayLs = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]; -const arrayLsMask = LongArray(0, 0x100001, 0x300003); -const arrayMask = range(64).map((n) => power(2, n)); -arrayMask[arrayMask.length - 1] = arrayMask[arrayMask.length - 1].multiply(-1); - -// PERMUTATION -const arrayP = LongArray( - 15, - 6, - 19, - 20, - 28, - 11, - 27, - 16, - 0, - 14, - 22, - 25, - 4, - 17, - 30, - 9, - 1, - 7, - 23, - 13, - 31, - 26, - 2, - 8, - 18, - 12, - 29, - 5, - 21, - 10, - 3, - 24 -); - -// PERMUTED_CHOICE1 -const arrayPC_1 = LongArray( - 56, - 48, - 40, - 32, - 24, - 16, - 8, - 0, - 57, - 49, - 41, - 33, - 25, - 17, - 9, - 1, - 58, - 50, - 42, - 34, - 26, - 18, - 10, - 2, - 59, - 51, - 43, - 35, - 62, - 54, - 46, - 38, - 30, - 22, - 14, - 6, - 61, - 53, - 45, - 37, - 29, - 21, - 13, - 5, - 60, - 52, - 44, - 36, - 28, - 20, - 12, - 4, - 27, - 19, - 11, - 3 -); - -// PERMUTED_CHOICE2 -const arrayPC_2 = LongArray( - 13, - 16, - 10, - 23, - 0, - 4, - -1, - -1, - 2, - 27, - 14, - 5, - 20, - 9, - -1, - -1, - 22, - 18, - 11, - 3, - 25, - 7, - -1, - -1, - 15, - 6, - 26, - 19, - 12, - 1, - -1, - -1, - 40, - 51, - 30, - 36, - 46, - 54, - -1, - -1, - 29, - 39, - 50, - 44, - 32, - 47, - -1, - -1, - 43, - 48, - 38, - 55, - 33, - 52, - -1, - -1, - 45, - 41, - 49, - 35, - 28, - 31, - -1, - -1 -); - -const matrixNSBox = [ - [ - 14, 4, 3, 15, 2, 13, 5, 3, 13, 14, 6, 9, 11, 2, 0, 5, 4, 1, 10, 12, 15, - 6, 9, 10, 1, 8, 12, 7, 8, 11, 7, 0, 0, 15, 10, 5, 14, 4, 9, 10, 7, 8, - 12, 3, 13, 1, 3, 6, 15, 12, 6, 11, 2, 9, 5, 0, 4, 2, 11, 14, 1, 7, 8, - 13, - ], - [ - 15, 0, 9, 5, 6, 10, 12, 9, 8, 7, 2, 12, 3, 13, 5, 2, 1, 14, 7, 8, 11, 4, - 0, 3, 14, 11, 13, 6, 4, 1, 10, 15, 3, 13, 12, 11, 15, 3, 6, 0, 4, 10, 1, - 7, 8, 4, 11, 14, 13, 8, 0, 6, 2, 15, 9, 5, 7, 1, 10, 12, 14, 2, 5, 9, - ], - [ - 10, 13, 1, 11, 6, 8, 11, 5, 9, 4, 12, 2, 15, 3, 2, 14, 0, 6, 13, 1, 3, - 15, 4, 10, 14, 9, 7, 12, 5, 0, 8, 7, 13, 1, 2, 4, 3, 6, 12, 11, 0, 13, - 5, 14, 6, 8, 15, 2, 7, 10, 8, 15, 4, 9, 11, 5, 9, 0, 14, 3, 10, 7, 1, - 12, - ], - [ - 7, 10, 1, 15, 0, 12, 11, 5, 14, 9, 8, 3, 9, 7, 4, 8, 13, 6, 2, 1, 6, 11, - 12, 2, 3, 0, 5, 14, 10, 13, 15, 4, 13, 3, 4, 9, 6, 10, 1, 12, 11, 0, 2, - 5, 0, 13, 14, 2, 8, 15, 7, 4, 15, 1, 10, 7, 5, 6, 12, 11, 3, 8, 9, 14, - ], - [ - 2, 4, 8, 15, 7, 10, 13, 6, 4, 1, 3, 12, 11, 7, 14, 0, 12, 2, 5, 9, 10, - 13, 0, 3, 1, 11, 15, 5, 6, 8, 9, 14, 14, 11, 5, 6, 4, 1, 3, 10, 2, 12, - 15, 0, 13, 2, 8, 5, 11, 8, 0, 15, 7, 14, 9, 4, 12, 7, 10, 9, 1, 13, 6, - 3, - ], - [ - 12, 9, 0, 7, 9, 2, 14, 1, 10, 15, 3, 4, 6, 12, 5, 11, 1, 14, 13, 0, 2, - 8, 7, 13, 15, 5, 4, 10, 8, 3, 11, 6, 10, 4, 6, 11, 7, 9, 0, 6, 4, 2, 13, - 1, 9, 15, 3, 8, 15, 3, 1, 14, 12, 5, 11, 0, 2, 12, 14, 7, 5, 10, 8, 13, - ], - [ - 4, 1, 3, 10, 15, 12, 5, 0, 2, 11, 9, 6, 8, 7, 6, 9, 11, 4, 12, 15, 0, 3, - 10, 5, 14, 13, 7, 8, 13, 14, 1, 2, 13, 6, 14, 9, 4, 1, 2, 14, 11, 13, 5, - 0, 1, 10, 8, 3, 0, 11, 3, 5, 9, 4, 15, 2, 7, 8, 12, 15, 10, 7, 6, 12, - ], - [ - 13, 7, 10, 0, 6, 9, 5, 15, 8, 4, 3, 10, 11, 14, 12, 5, 2, 11, 9, 6, 15, - 12, 0, 3, 4, 1, 14, 13, 1, 2, 7, 8, 1, 2, 12, 15, 10, 4, 0, 3, 13, 14, - 6, 9, 7, 8, 9, 6, 15, 1, 5, 12, 3, 10, 14, 5, 8, 7, 11, 0, 4, 13, 2, 11, - ], -]; - -const bitTransform = (arrInt, n, l) => { - // int[], int, long : long - let l2 = Long(0); - range(n).forEach((i) => { - if (arrInt[i].isNegative() || l.and(arrayMask[arrInt[i].low]).equals(0)) - return; - l2 = l2.or(arrayMask[i]); - }); - return l2; -}; - -const DES64 = (longs, l) => { - const pR = range(8).map(() => Long(0)); - const pSource = [Long(0), Long(0)]; - let L = Long(0); - let R = Long(0); - let out = bitTransform(arrayIP, 64, l); - pSource[0] = out.and(0xffffffff); - pSource[1] = out.and(-4294967296).shiftRight(32); - - range(16).forEach((i) => { - let SOut = Long(0); - - R = Long(pSource[1]); - R = bitTransform(arrayE, 64, R); - R = R.xor(longs[i]); - range(8).forEach((j) => { - pR[j] = R.shiftRight(j * 8).and(255); - }); - range(8) - .reverse() - .forEach((sbi) => { - SOut = SOut.shiftLeft(4).or(matrixNSBox[sbi][pR[sbi]]); - }); - R = bitTransform(arrayP, 32, SOut); - L = Long(pSource[0]); - pSource[0] = Long(pSource[1]); - pSource[1] = L.xor(R); - }); - pSource.reverse(); - out = pSource[1] - .shiftLeft(32) - .and(-4294967296) - .or(pSource[0].and(0xffffffff)); - out = bitTransform(arrayIP_1, 64, out); - return out; -}; - -const subKeys = (l, longs, n) => { - // long, long[], int - let l2 = bitTransform(arrayPC_1, 56, l); - range(16).forEach((i) => { - l2 = l2 - .and(arrayLsMask[arrayLs[i]]) - .shiftLeft(28 - arrayLs[i]) - .or(l2.and(arrayLsMask[arrayLs[i]].not()).shiftRight(arrayLs[i])); - longs[i] = bitTransform(arrayPC_2, 64, l2); - }); - if (n === 1) { - range(8).forEach((j) => { - [longs[j], longs[15 - j]] = [longs[15 - j], longs[j]]; - }); - } -}; - -const crypt = (msg, key, mode) => { - // 处理密钥块 - let l = Long(0); - range(8).forEach((i) => { - l = Long(key[i]) - .shiftLeft(i * 8) - .or(l); - }); - - const j = Math.floor(msg.length / 8); - // arrLong1 存放的是转换后的密钥块, 在解密时只需要把这个密钥块反转就行了 - - const arrLong1 = range(16).map(() => Long(0)); - subKeys(l, arrLong1, mode); - - // arrLong2 存放的是前部分的明文 - const arrLong2 = range(j).map(() => Long(0)); - - range(j).forEach((m) => { - range(8).forEach((n) => { - arrLong2[m] = Long(msg[n + m * 8]) - .shiftLeft(n * 8) - .or(arrLong2[m]); - }); - }); - - // 用于存放密文 - const arrLong3 = range(Math.floor((1 + 8 * (j + 1)) / 8)).map(() => - Long(0) - ); - - // 计算前部的数据块(除了最后一部分) - range(j).forEach((i1) => { - arrLong3[i1] = DES64(arrLong1, arrLong2[i1]); - }); - - // 保存多出来的字节 - const arrByte1 = msg.slice(j * 8); - let l2 = Long(0); - - range(msg.length % 8).forEach((i1) => { - l2 = Long(arrByte1[i1]) - .shiftLeft(i1 * 8) - .or(l2); - }); - - // 计算多出的那一位(最后一位) - if (arrByte1.length || mode === 0) arrLong3[j] = DES64(arrLong1, l2); // 解密不需要 - - // 将密文转为字节型 - const arrByte2 = range(8 * arrLong3.length).map(() => 0); - let i4 = 0; - arrLong3.forEach((l3) => { - range(8).forEach((i6) => { - arrByte2[i4] = l3.shiftRight(i6 * 8).and(255).low; - i4 += 1; - }); - }); - return Buffer.from(arrByte2); -}; - -const SECRET_KEY = Buffer.from('ylzsxkwm'); -const encrypt = (msg) => crypt(msg, SECRET_KEY, 0); -const decrypt = (msg) => crypt(msg, SECRET_KEY, 1); -const encryptQuery = (query) => encrypt(Buffer.from(query)).toString('base64'); - -module.exports = { encrypt, decrypt, encryptQuery }; diff --git a/src/server/request.js b/src/server/request.js index 7bbbbd0..7e8b00a 100644 --- a/src/server/request.js +++ b/src/server/request.js @@ -1,8 +1,6 @@ const zlib = require('zlib'); const http = require('http'); const https = require('https'); -const ON_CANCEL = require('./cancel'); -const RequestCancelled = require('./exceptions/RequestCancelled'); const { logScope } = require('./logger'); const parse = require('url').parse; const format = require('url').format; @@ -62,7 +60,7 @@ const configure = (method, url, headers, proxy) => { /** * @template T - * @typedef {{url: string, body: RequestExtensionBody, json: () => Promise, jsonp: () => Promise}} RequestExtension + * @typedef {{url: string, body: RequestExtensionBody, json: () => Promise}} RequestExtension */ /** @@ -72,7 +70,6 @@ const configure = (method, url, headers, proxy) => { * @param {Object?} receivedHeaders * @param {unknown?} body * @param {unknown?} proxy - * @param {CancelRequest?} cancelRequest * @return {Promise>} */ const request = ( @@ -80,8 +77,7 @@ const request = ( receivedUrl, receivedHeaders, body, - proxy, - cancelRequest + proxy ) => { const url = parse(receivedUrl); /* @type {Partial>} */ @@ -105,14 +101,6 @@ const request = ( logger.debug(`Start requesting ${receivedUrl}`); const clientRequest = create(url, proxy)(options); - const destroyClientRequest = function () { - // We destroy the request and throw RequestCancelled - // when the request has been cancelled. - clientRequest.destroy(new RequestCancelled(format(url))); - }; - - cancelRequest?.on(ON_CANCEL, destroyClientRequest); - if (cancelRequest?.cancelled ?? false) destroyClientRequest(); clientRequest .setTimeout(timeoutThreshold, () => { @@ -122,7 +110,7 @@ const request = ( }, `The request timed out, or the requester didn't handle the response.` ); - destroyClientRequest(); + clientRequest.destroy(); }) .on('response', (response) => resolve(response)) .on('connect', (_, socket) => { @@ -146,9 +134,6 @@ const request = ( }).then( /** @param {http.IncomingMessage} response */ (response) => { - if (cancelRequest?.cancelled ?? false) - return Promise.reject(new RequestCancelled(format(url))); - if ([201, 301, 302, 303, 307, 308].includes(response.statusCode)) { const redirectTo = url.resolve( response.headers.location || url.href @@ -163,7 +148,6 @@ const request = ( url, body: (raw) => read(response, raw), json: () => json(response), - jsonp: () => jsonp(response), }); } ); @@ -192,14 +176,10 @@ const read = (connect, raw) => }); const json = (connect) => read(connect, false).then((body) => JSON.parse(body)); -const jsonp = (connect) => - read(connect, false).then((body) => - JSON.parse(body.slice(body.indexOf('(') + 1, -')'.length)) - ); request.read = read; request.create = create; request.translate = translate; request.configure = configure; -module.exports = request; +module.exports = request; \ No newline at end of file diff --git a/src/server/server.crt b/src/server/server.crt index d39dcce..74f4e37 100644 --- a/src/server/server.crt +++ b/src/server/server.crt @@ -1,16 +1,16 @@ -----BEGIN CERTIFICATE----- -MIIDSzCCAjOgAwIBAgIJAOqZ7l8q9YAMMA0GCSqGSIb3DQEBCwUAMBExDzANBgNV -BAMMBmxvY2FsaG9zdDAeFw0yNDAxMDEwMDAwMDBaFw0zNDAxMDEwMDAwMDBaMBEx -DzANBgNVBAMMBmxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA -v5KXq8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R -5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8 -R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq -8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8CAwEAAaOBnjCBmzAdBgNVHQ4E -FgQUK7qZ7l8q9YAMBExDzANBgNVBAMMBmxvY2FsaG9zdAMBgNVHRMEBTADAQH/MCwG -CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV -HSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADgYEAv5KX -q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X -5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9 -X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L -9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R5L9X5q8Y3Zq8R +MIICgjCCAgigAwIBAgIUafzDKFqhg8XS5YogLkI9oGsngkUwCgYIKoZIzj0EAwMw +RDELMAkGA1UEBhMCQ04xJDAiBgNVBAMMG1VuYmxvY2tOZXRlYXNlTXVzaWMgUm9v +dCBDQTEPMA0GA1UECgwGbm9ib2R5MB4XDTI1MDcyNDA4NDY1NloXDTI2MDcyNDA4 +NDY1NlowezELMAkGA1UEBhMCQ04xETAPBgNVBAcMCEhhbmd6aG91MSwwKgYDVQQK +DCNOZXRFYXNlIChIYW5nemhvdSkgTmV0d29yayBDby4sIEx0ZDERMA8GA1UECwwI +SVQgRGVwdC4xGDAWBgNVBAMMDyoubXVzaWMuMTYzLmNvbTB2MBAGByqGSM49AgEG +BSuBBAAiA2IABACkvqL3b68Pv11nQVwm/9BNWGuh1fBKlqYZqnyr9Gy1xEQtf3we +WlwgVmX9rqu9nNEiAbbxIt9aGliHurKkE22+FiOKC4vduYlyyIXKlCAFXigHKr/k +6uKgc2jPfeYoC6OBgzCBgDATBgNVHSUEDDAKBggrBgEFBQcDATApBgNVHREEIjAg +gg1tdXNpYy4xNjMuY29tgg8qLm11c2ljLjE2My5jb20wHQYDVR0OBBYEFEeHCj4z +PzDre8No6wsWbrpmQ8hPMB8GA1UdIwQYMBaAFGW+hCo4x24AIMIYbUjR4NRlnzx1 +MAoGCCqGSM49BAMDA2gAMGUCMQDD0cOUV43GSLH8bbbkw4KD6kVoknm6z6sjE5vk +ZNmgjqduSn6XzCNiiwwhRrsmtVkCMCX8uRVIIG20mdcK2YSwlSgw5mHTJsNFRnfn +x8ft2l8Tk2yZcqFSsvuVKvBwZh7bWA== -----END CERTIFICATE----- \ No newline at end of file diff --git a/src/server/server.key b/src/server/server.key index bd9b3ef..3cb2794 100644 --- a/src/server/server.key +++ b/src/server/server.key @@ -1,28 +1,9 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDK3UbsHMKuVo1c -Ye/54XWH7SZM+2hDAKqrCcE3lo96CVazxtUHcd7hHFK/oVHC/9IgntOr0hunp0NH -jCUjor9ZAcDECQEmjU9CIO+TsuVKD8EaqJPArO3dKRvALGhE9Abs8MqphlRxgCxO -4BWaEffWSjl8+owYH/VZtTG+onQlY6uC2B50YnzJAtgv9nDZwfolxzYdK0xt/SaU -JQbQxc4tQ5G/+roqpHSLTAqL7ts3V0GY8ygWzal9khbkNAR3E1zG+fESzysyOlDu -oqd7FExnyAd6TSKDcmiQYgDKSEcZwunwNAvNi4UVG9XaPLzRq+KAe99R6KvjVf6u -1IfTTWeVAgMBAAECggEABtqFYzXUM7hK2+AI82BYs5uvlFmP0S88P4SYEmez4lHt -VoFOKyfjJBnP3PsTsbx/DBdOBr7OkCePeuL9w/+qXGMdVGzR5ejBXtnKOrmIMAGV -XKkXyT7ALLnWQoIoOzXqA8MIJcR+lvTXQ0NdQiJGYZHDRzkXSJDsuhRd0xdIZQbr -tI8H9WMH+2BZn4t+5PTlj6EbihBZiqCJ986Vu5z2enPS61PIhnDMF7yxG01G0gNk -QrZSFJWd0zPHdbhxrLXe6slFGwDBXE+ad9Shjjq/xaMrSzWaiEQImvTh8d3eLsJK -QjJL5z2bWm7EYb1+LQtiX4ioMjav22SavggVQKc11QKBgQDthprmGs+IVveZVLql -e3gESsS3+P72fLSF1F4kxMM6wWWypJo5/LYzveskDKK5sXTRR/lBGglpRVuZvnnm -UuOrixpfKM8W4nfnrZURbau/0Lfeull7LSJPuNkr4xEA5/zt/cSlg1L/IOPDgTtT -4XFd41kZYsAgK2jsXih7bpiZCwKBgQDapIZ9iJG0Yd6uAclD1Swo83UHaoxmJSZw -EiAyR07WWBycfb3P18FHW0wejIloyiq8LZR8upa5u8xFcyC3/Z8AlxpojdcHsDE9 -uuT1Sibc483tto9I+p+YfWIBxJBWA3hMB5gS+t7ssueEdZdrfz1Y8aBYHwA5XFNk -7JbbUrSl3wKBgAgCfAK6cLkmRZ80Dj86VKfAZbXWfbKOLgA9UxdmUzcOAoHtrw25 -ieNgyicjDfG5HDladftOB3c3UYlztOShcu/79t2yoJki9ewoHFjEHACR50Fpg072 -DKwnjZs/QvmG2S6lWhZCwW+9CjEzkG6ZsZr66axDejsbe6RM4IyZBChVAoGAdId1 -iphsF7iFxzX6f+WwqI7BE9fMxnAMYXS3pjRtJz5E2X8G2CyEvbRCCJIcdjYxuqOM -XUHRLWKTB3zJtmY9BUKDd7AJJ/bW97CRcM45kkbzrTs8eMfioZJJ1uldiApHZjYx -7gO5JmxfijBmKIvjNXFqZSz4oJm9dK/H41LcJv8CgYAPMpmgNuPSBq9+Ol/5Vf2v -8s4j87SJoOsPEepnlyobnxARnjTuS6BnEuaQHMyvXIMzXLzy0kA3jGFd9j2k31Fw -4JOTHVwHrgJboEp/F3MssHw+SYUKgHWqqVPlwME1Zgb5kgZpxeLIRkS69nPCEjD7 -hmOSlIN+qgaqE5jIiK76RQ== ------END PRIVATE KEY----- +-----BEGIN EC PARAMETERS----- +BgUrgQQAIg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MIGkAgEBBDDaWQTho9lbjl27ddVt9pC4req+U8iFhdMw0/RzWePrlF55fvmwRiff +GepKeBDdkx6gBwYFK4EEACKhZANiAAQApL6i92+vD79dZ0FcJv/QTVhrodXwSpam +Gap8q/RstcRELX98HlpcIFZl/a6rvZzRIgG28SLfWhpYh7qypBNtvhYjiguL3bmJ +csiFypQgBV4oByq/5OrioHNoz33mKAs= +-----END EC PRIVATE KEY----- \ No newline at end of file