From 6b049e9bbab9685a5548ebe845a3657fbeee46e0 Mon Sep 17 00:00:00 2001 From: takayama Date: Mon, 24 May 2021 11:36:21 +0900 Subject: [PATCH] add auto query server --- index.d.ts | 7 ++-- lib/client.js | 1 + lib/network.js | 98 ++++++++++++++++++++++++++++++++++++++++++----- lib/wtlogin/wt.js | 2 +- 4 files changed, 95 insertions(+), 13 deletions(-) diff --git a/index.d.ts b/index.d.ts index 2db51500..90cf2c6f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -35,9 +35,10 @@ export interface ConfBot { //即使不用相关API(使用`no_cache=true`)强制刷新数据,超过这个时间后内部也会自动刷新 internal_cache_life?: number, - //手动指定ip和port - //默认使用msfwifi.3g.qq.com:8080进行连接,若要修改建议优先更改该域名hosts指向而不是手动指定ip - //@link https://site.ip138.com/msfwifi.3g.qq.com/ 端口通常以下四个都会开放:80,443,8080,14000 + /** 自动选择最优服务器(默认开启),关闭后会一直使用`msfwifi.3g.qq.com`进行连接 */ + auto_server?: boolean; + + /** 手动指定ip和port,不推荐使用,大多数情况下你应该使用auto_server */ remote_ip?: string, remote_port?: number, } diff --git a/lib/client.js b/lib/client.js index f10b269e..50f86eb1 100644 --- a/lib/client.js +++ b/lib/client.js @@ -117,6 +117,7 @@ class Client extends EventEmitter { resend: true, reconn_interval: 5, internal_cache_life: 3600, + auto_server: true, data_dir: path.join(require.main ? require.main.path : process.cwd(), "data"), ...config }; diff --git a/lib/network.js b/lib/network.js index cd060ff6..0e49d40f 100644 --- a/lib/network.js +++ b/lib/network.js @@ -1,9 +1,20 @@ "use strict"; const net = require("net"); -const { BUF0 } = require("./common"); +const https = require("https"); +const { BUF0, timestamp } = require("./common"); +const tea = require("./algo/tea"); +const jce = require("./algo/jce"); + +const default_host = "msfwifi.3g.qq.com"; +const default_port = 8080; +let update_time = 0; +let host_port = { }; class Network extends net.Socket { + closed_time = timestamp(); + host = default_host; + port = default_port; _data = BUF0; /** @@ -21,6 +32,9 @@ class Network extends net.Socket { this._data = BUF0; if (this.remoteAddress) this.c.logger.mark(`${this.remoteAddress}:${this.remotePort} closed`); + if (timestamp() - this.closed_time <= 300) + delete host_port[this.remoteAddress]; + this.closed_time = timestamp(); this.c.emit("internal.offline"); }); @@ -39,19 +53,85 @@ class Network extends net.Socket { }); } - join(cb) { - let ip = "msfwifi.3g.qq.com", port = 8080; - if (net.isIP(this.c.config.remote_ip)) - ip = this.c.config.remote_ip; - if (this.c.config.remote_port > 0 && this.c.config.remote_port < 65536) - port = this.c.config.remote_port; - this.c.logger.mark(`connecting to ${ip}:${port}`); + async join(cb) { + if (timestamp() - this.closed_time > 300) { + // + } else if (net.isIP(this.c.config.remote_ip)) { + this.host = this.c.config.remote_ip; + this.port = 8080; + if (this.c.config.remote_port > 0 && this.c.config.remote_port < 65536) + this.host = this.c.config.remote_port; + } else if (this.c.config.auto_server) { + if (!Object.keys(host_port).length || timestamp() - update_time >= 3600) { + this.c.logger.mark("正在探索可用服务器..."); + try { + await this._queryServerList(); + this.host = Object.keys(host_port)[0]; + this.port = host_port[this.host]; + } catch (err) { + this.c.logger.warn("探索服务器失败: " + (err ? err.message : "timeout")); + } + } else { + this.host = Object.keys(host_port)[0]; + this.port = host_port[this.host]; + } + } + this.c.logger.mark(`connecting to ${this.host}:${this.port}`); this.removeAllListeners("connect"); - this.connect(port, ip, () => { + this.connect(this.port, this.host, () => { this.c.logger.mark(`${this.remoteAddress}:${this.remotePort} connected`); cb(); }); } + + async _queryServerList() { + const key = Buffer.from("F0441F5FF42DA58FDCF7949ABA62D411", "hex"); + const HttpServerListReq = jce.encodeStruct([ + null, + 0, 0, 1, "00000", 100, this.c.apk.subid, this.c.device.imei, 0, 0, 0, + 0, 0, 0, 1 + ]); + const extra = { + service: "ConfigHttp", + method: "HttpServerListReq", + }; + let body = jce.encodeWrapper({ HttpServerListReq }, extra); + const len = Buffer.alloc(4); + len.writeUInt32BE(body.length + 4); + body = Buffer.concat([len, body]); + body = tea.encrypt(body, key); + return await new Promise((resolve, reject) => { + const id = setTimeout(reject, 3000); + https.request("https://configsvr.msf.3g.qq.com/configsvr/serverlist.jsp", { method: "POST" }, (res) => { + let data = []; + res.on("error", reject); + res.on("data", (chunk) => data.push(chunk)); + res.on("end", () => { + try { + clearTimeout(id); + data = Buffer.concat(data); + data = tea.decrypt(data, key).slice(4); + const nested = jce.decode(data); + host_port = { }; + for (let v of nested[2]) { + if (Object.keys(host_port).length >= 3) + continue; + host_port[v[1]] = v[2]; + } + if (Object.keys(host_port).length > 0) { + update_time = timestamp(); + this.c.logger.debug(host_port); + resolve(); + } else { + reject(new Error("no aliveable server")); + } + } catch (err) { + reject(err); + } + }); + }).on("error", reject).end(body); + }); + } } module.exports = Network; diff --git a/lib/wtlogin/wt.js b/lib/wtlogin/wt.js index 81d68c98..fde677ef 100644 --- a/lib/wtlogin/wt.js +++ b/lib/wtlogin/wt.js @@ -464,7 +464,7 @@ async function register(logout = false) { const blob = await this.send(pkt); const rsp = jce.decode(blob); const result = rsp[9] ? true : false; - if (!result) + if (!result && !logout) fs.unlink(path.join(this.dir, "token"), NOOP); return result; } catch {