优化&修复同步机制
parent
667fc8c8a6
commit
5d72d092fc
|
@ -7,94 +7,114 @@ import { aesDecrypt, aesEncrypt, getComputerName, rsaEncrypt } from '../utils'
|
||||||
|
|
||||||
const requestIps = new Map<string, number>()
|
const requestIps = new Map<string, number>()
|
||||||
|
|
||||||
|
|
||||||
|
const getAvailableIP = (req: http.IncomingMessage) => {
|
||||||
|
let ip = getIP(req)
|
||||||
|
return ip && (requestIps.get(ip) ?? 0) < 10 ? ip : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyByKey = (encryptMsg: string, userId: string) => {
|
||||||
|
const keyInfo = getClientKeyInfo(userId)
|
||||||
|
if (!keyInfo) return null
|
||||||
|
let text
|
||||||
|
try {
|
||||||
|
text = aesDecrypt(encryptMsg, keyInfo.key)
|
||||||
|
} catch (err) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
// console.log(text)
|
||||||
|
if (text.startsWith(SYNC_CODE.authMsg)) {
|
||||||
|
const deviceName = text.replace(SYNC_CODE.authMsg, '') || 'Unknown'
|
||||||
|
if (deviceName != keyInfo.deviceName) {
|
||||||
|
keyInfo.deviceName = deviceName
|
||||||
|
saveClientKeyInfo(keyInfo)
|
||||||
|
}
|
||||||
|
return aesEncrypt(SYNC_CODE.helloMsg, keyInfo.key)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const verifyByCode = (encryptMsg: string, password: string) => {
|
||||||
|
let key = ''.padStart(16, Buffer.from(password).toString('hex'))
|
||||||
|
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
||||||
|
key = Buffer.from(key).toString('base64')
|
||||||
|
// console.log(req.headers.m, authCode, key)
|
||||||
|
let text
|
||||||
|
try {
|
||||||
|
text = aesDecrypt(encryptMsg, key)
|
||||||
|
} catch (err) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
// console.log(text)
|
||||||
|
if (text.startsWith(SYNC_CODE.authMsg)) {
|
||||||
|
const data = text.split('\n')
|
||||||
|
const publicKey = `-----BEGIN PUBLIC KEY-----\n${data[1]}\n-----END PUBLIC KEY-----`
|
||||||
|
const deviceName = data[2] || 'Unknown'
|
||||||
|
const isMobile = data[3] == 'lx_music_mobile'
|
||||||
|
const keyInfo = createClientKeyInfo(deviceName, isMobile)
|
||||||
|
return rsaEncrypt(Buffer.from(JSON.stringify({
|
||||||
|
clientId: keyInfo.clientId,
|
||||||
|
key: keyInfo.key,
|
||||||
|
serverName: getComputerName(),
|
||||||
|
})), publicKey)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
export const authCode = async(req: http.IncomingMessage, res: http.ServerResponse, password: string) => {
|
export const authCode = async(req: http.IncomingMessage, res: http.ServerResponse, password: string) => {
|
||||||
let code = 401
|
let code = 401
|
||||||
let msg: string = SYNC_CODE.msgAuthFailed
|
let msg: string = SYNC_CODE.msgAuthFailed
|
||||||
|
|
||||||
let ip = getIP(req)
|
|
||||||
// console.log(req.headers)
|
// console.log(req.headers)
|
||||||
if (typeof req.headers.m == 'string') {
|
let ip = getAvailableIP(req)
|
||||||
if (ip && (requestIps.get(ip) ?? 0) < 10) {
|
if (ip) {
|
||||||
if (req.headers.m) {
|
if (typeof req.headers.m == 'string' && req.headers.m) {
|
||||||
label:
|
const userId = req.headers.i
|
||||||
if (req.headers.i) { // key验证
|
const _msg = typeof userId == 'string' && userId
|
||||||
if (typeof req.headers.i != 'string') break label
|
? verifyByKey(req.headers.m, userId)
|
||||||
const keyInfo = getClientKeyInfo(req.headers.i)
|
: verifyByCode(req.headers.m, password)
|
||||||
if (!keyInfo) break label
|
if (_msg != null) {
|
||||||
let text
|
msg = _msg
|
||||||
try {
|
code = 200
|
||||||
text = aesDecrypt(req.headers.m, keyInfo.key)
|
|
||||||
} catch (err) {
|
|
||||||
break label
|
|
||||||
}
|
|
||||||
// console.log(text)
|
|
||||||
if (text.startsWith(SYNC_CODE.authMsg)) {
|
|
||||||
code = 200
|
|
||||||
const deviceName = text.replace(SYNC_CODE.authMsg, '') || 'Unknown'
|
|
||||||
if (deviceName != keyInfo.deviceName) {
|
|
||||||
keyInfo.deviceName = deviceName
|
|
||||||
saveClientKeyInfo(keyInfo)
|
|
||||||
}
|
|
||||||
msg = aesEncrypt(SYNC_CODE.helloMsg, keyInfo.key)
|
|
||||||
}
|
|
||||||
} else { // 连接码验证
|
|
||||||
let key = ''.padStart(16, Buffer.from(password).toString('hex'))
|
|
||||||
// const iv = Buffer.from(key.split('').reverse().join('')).toString('base64')
|
|
||||||
key = Buffer.from(key).toString('base64')
|
|
||||||
// console.log(req.headers.m, authCode, key)
|
|
||||||
let text
|
|
||||||
try {
|
|
||||||
text = aesDecrypt(req.headers.m, key)
|
|
||||||
} catch (err) {
|
|
||||||
break label
|
|
||||||
}
|
|
||||||
// console.log(text)
|
|
||||||
if (text.startsWith(SYNC_CODE.authMsg)) {
|
|
||||||
code = 200
|
|
||||||
const data = text.split('\n')
|
|
||||||
const publicKey = `-----BEGIN PUBLIC KEY-----\n${data[1]}\n-----END PUBLIC KEY-----`
|
|
||||||
const deviceName = data[2] || 'Unknown'
|
|
||||||
const isMobile = data[3] == 'lx_music_mobile'
|
|
||||||
const keyInfo = createClientKeyInfo(deviceName, isMobile)
|
|
||||||
msg = rsaEncrypt(Buffer.from(JSON.stringify({
|
|
||||||
clientId: keyInfo.clientId,
|
|
||||||
key: keyInfo.key,
|
|
||||||
serverName: getComputerName(),
|
|
||||||
})), publicKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
code = 403
|
|
||||||
msg = SYNC_CODE.msgBlockedIp
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (code != 200) {
|
||||||
|
const num = requestIps.get(ip) ?? 0
|
||||||
|
// if (num > 20) return
|
||||||
|
requestIps.set(ip, num + 1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
code = 403
|
||||||
|
msg = SYNC_CODE.msgBlockedIp
|
||||||
}
|
}
|
||||||
|
|
||||||
res.writeHead(code)
|
res.writeHead(code)
|
||||||
res.end(msg)
|
res.end(msg)
|
||||||
|
|
||||||
if (ip && code != 200) {
|
|
||||||
const num = requestIps.get(ip) ?? 0
|
|
||||||
if (num > 20) return
|
|
||||||
requestIps.set(ip, num + 1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const verifyConnection = (encryptMsg: string, userId: string) => {
|
||||||
|
const keyInfo = getClientKeyInfo(userId)
|
||||||
|
if (!keyInfo) return false
|
||||||
|
let text
|
||||||
|
try {
|
||||||
|
text = aesDecrypt(encryptMsg, keyInfo.key)
|
||||||
|
} catch (err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// console.log(text)
|
||||||
|
return text == SYNC_CODE.msgConnect
|
||||||
|
}
|
||||||
export const authConnect = async(req: http.IncomingMessage) => {
|
export const authConnect = async(req: http.IncomingMessage) => {
|
||||||
const query = querystring.parse((req.url as string).split('?')[1])
|
let ip = getAvailableIP(req)
|
||||||
const i = query.i
|
if (ip) {
|
||||||
const t = query.t
|
const query = querystring.parse((req.url as string).split('?')[1])
|
||||||
label:
|
const i = query.i
|
||||||
if (typeof i == 'string' && typeof t == 'string') {
|
const t = query.t
|
||||||
const keyInfo = getClientKeyInfo(i)
|
if (typeof i == 'string' && typeof t == 'string' && verifyConnection(t, i)) return
|
||||||
if (!keyInfo) break label
|
|
||||||
let text
|
const num = requestIps.get(ip) ?? 0
|
||||||
try {
|
requestIps.set(ip, num + 1)
|
||||||
text = aesDecrypt(t, keyInfo.key)
|
|
||||||
} catch (err) {
|
|
||||||
break label
|
|
||||||
}
|
|
||||||
// console.log(text)
|
|
||||||
if (text == SYNC_CODE.msgConnect) return
|
|
||||||
}
|
}
|
||||||
throw new Error('failed')
|
throw new Error('failed')
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,16 +40,18 @@ const codeTools: {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkDuplicateClient = (newSocket: LX.Sync.Server.Socket) => {
|
||||||
|
for (const client of [...wss!.clients]) {
|
||||||
|
if (client === newSocket || client.keyInfo.clientId != newSocket.keyInfo.clientId) continue
|
||||||
|
console.log('duplicate client', client.keyInfo.deviceName)
|
||||||
|
client.isReady = false
|
||||||
|
client.close(SYNC_CLOSE_CODE.normal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingMessage) => {
|
const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingMessage) => {
|
||||||
const queryData = url.parse(request.url as string, true).query as Record<string, string>
|
const queryData = url.parse(request.url as string, true).query as Record<string, string>
|
||||||
|
|
||||||
socket.onClose(() => {
|
|
||||||
// console.log('disconnect', reason)
|
|
||||||
status.devices.splice(status.devices.findIndex(k => k.clientId == keyInfo?.clientId), 1)
|
|
||||||
sendServerStatus(status)
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
// // if (typeof socket.handshake.query.i != 'string') return socket.disconnect(true)
|
// // if (typeof socket.handshake.query.i != 'string') return socket.disconnect(true)
|
||||||
const keyInfo = getClientKeyInfo(queryData.i)
|
const keyInfo = getClientKeyInfo(queryData.i)
|
||||||
if (!keyInfo) {
|
if (!keyInfo) {
|
||||||
|
@ -60,6 +62,8 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
||||||
saveClientKeyInfo(keyInfo)
|
saveClientKeyInfo(keyInfo)
|
||||||
// // socket.lx_keyInfo = keyInfo
|
// // socket.lx_keyInfo = keyInfo
|
||||||
socket.keyInfo = keyInfo
|
socket.keyInfo = keyInfo
|
||||||
|
checkDuplicateClient(socket)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await syncList(wss as LX.Sync.Server.SocketServer, socket)
|
await syncList(wss as LX.Sync.Server.SocketServer, socket)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -68,6 +72,11 @@ const handleConnection = async(socket: LX.Sync.Server.Socket, request: IncomingM
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
status.devices.push(keyInfo)
|
status.devices.push(keyInfo)
|
||||||
|
socket.onClose(() => {
|
||||||
|
// console.log('disconnect', reason)
|
||||||
|
status.devices.splice(status.devices.findIndex(k => k.clientId == keyInfo?.clientId), 1)
|
||||||
|
sendServerStatus(status)
|
||||||
|
})
|
||||||
// handleConnection(io, socket)
|
// handleConnection(io, socket)
|
||||||
sendServerStatus(status)
|
sendServerStatus(status)
|
||||||
|
|
||||||
|
@ -216,15 +225,15 @@ const handleStartServer = async(port = 9527, ip = '0.0.0.0') => await new Promis
|
||||||
})
|
})
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
wss?.clients.forEach(ws => {
|
wss?.clients.forEach(socket => {
|
||||||
if (ws.isAlive == false) {
|
if (socket.isAlive == false) {
|
||||||
ws.terminate()
|
socket.terminate()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ws.isAlive = false
|
socket.isAlive = false
|
||||||
ws.ping(noop)
|
socket.ping(noop)
|
||||||
if (ws.keyInfo.isMobile) ws.send('ping', noop)
|
if (socket.keyInfo.isMobile) socket.send('ping', noop)
|
||||||
})
|
})
|
||||||
}, 30000)
|
}, 30000)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue