{ "version": 3, "sources": ["../../../server/monitor.ts"], "sourcesContent": ["/**\r\n * Monitor\r\n * Pokemon Showdown - http://pokemonshowdown.com/\r\n *\r\n * Various utility functions to make sure PS is running healthily.\r\n *\r\n * @license MIT\r\n */\r\n\r\nimport {exec, ExecException, ExecOptions} from 'child_process';\r\nimport {crashlogger, FS} from \"../lib\";\r\n\r\nconst MONITOR_CLEAN_TIMEOUT = 2 * 60 * 60 * 1000;\r\n\r\n/**\r\n * This counts the number of times an action has been committed, and tracks the\r\n * delta of time since the last time it was committed. Actions include\r\n * connecting to the server, starting a battle, validating a team, and\r\n * sending/receiving data over a connection's socket.\r\n */\r\nexport class TimedCounter extends Map {\r\n\t/**\r\n\t * Increments the number of times an action has been committed by one, and\r\n\t * updates the delta of time since it was last committed.\r\n\t *\r\n\t * @returns [action count, time delta]\r\n\t */\r\n\tincrement(key: string, timeLimit: number): [number, number] {\r\n\t\tconst val = this.get(key);\r\n\t\tconst now = Date.now();\r\n\t\tif (!val || now > val[1] + timeLimit) {\r\n\t\t\tthis.set(key, [1, Date.now()]);\r\n\t\t\treturn [1, 0];\r\n\t\t} else {\r\n\t\t\tval[0]++;\r\n\t\t\treturn [val[0], now - val[1]];\r\n\t\t}\r\n\t}\r\n}\r\n\r\n// Config.loglevel is:\r\n// 0 = everything\r\n// 1 = debug (same as 0 for now)\r\n// 2 = notice (default)\r\n// 3 = warning\r\n// (4 is currently unused)\r\n// 5 = supposedly completely silent, but for now a lot of PS output doesn't respect loglevel\r\nif (('Config' in global) &&\r\n\t\t(typeof Config.loglevel !== 'number' || Config.loglevel < 0 || Config.loglevel > 5)) {\r\n\tConfig.loglevel = 2;\r\n}\r\n\r\nexport const Monitor = new class {\r\n\tconnections = new TimedCounter();\r\n\tnetRequests = new TimedCounter();\r\n\tbattles = new TimedCounter();\r\n\tbattlePreps = new TimedCounter();\r\n\tgroupChats = new TimedCounter();\r\n\ttickets = new TimedCounter();\r\n\r\n\tactiveIp: string | null = null;\r\n\tnetworkUse: {[k: string]: number} = {};\r\n\tnetworkCount: {[k: string]: number} = {};\r\n\thotpatchLock: {[k: string]: {by: string, reason: string}} = {};\r\n\r\n\tTimedCounter = TimedCounter;\r\n\r\n\tupdateServerLock = false;\r\n\tcleanInterval: NodeJS.Timeout | null = null;\r\n\t/**\r\n\t * Inappropriate userid : has the user logged in since the FR\r\n\t */\r\n\treadonly forceRenames = new Map();\r\n\r\n\t/*********************************************************\r\n\t * Logging\r\n\t *********************************************************/\r\n\tcrashlog(err: any, source = 'The main process', details: AnyObject | null = null) {\r\n\t\tconst error = (err || {}) as Error;\r\n\t\tif ((error.stack || '').startsWith('@!!@')) {\r\n\t\t\ttry {\r\n\t\t\t\tconst stack = (error.stack || '');\r\n\t\t\t\tconst nlIndex = stack.indexOf('\\n');\r\n\t\t\t\t[error.name, error.message, source, details] = JSON.parse(stack.slice(4, nlIndex));\r\n\t\t\t\terror.stack = stack.slice(nlIndex + 1);\r\n\t\t\t} catch {}\r\n\t\t}\r\n\t\tconst crashType = crashlogger(error, source, details);\r\n\t\tRooms.global.reportCrash(error, source);\r\n\t\tif (crashType === 'lockdown') {\r\n\t\t\tConfig.autolockdown = false;\r\n\t\t\tRooms.global.startLockdown(error);\r\n\t\t}\r\n\t}\r\n\r\n\tlog(text: string) {\r\n\t\tthis.notice(text);\r\n\t\tconst staffRoom = Rooms.get('staff');\r\n\t\tif (staffRoom) {\r\n\t\t\tstaffRoom.add(`|c|~|${text}`).update();\r\n\t\t}\r\n\t}\r\n\r\n\tadminlog(text: string) {\r\n\t\tthis.notice(text);\r\n\t\tconst upperstaffRoom = Rooms.get('upperstaff');\r\n\t\tif (upperstaffRoom) {\r\n\t\t\tupperstaffRoom.add(`|c|~|${text}`).update();\r\n\t\t}\r\n\t}\r\n\r\n\tlogHTML(text: string) {\r\n\t\tthis.notice(text);\r\n\t\tconst staffRoom = Rooms.get('staff');\r\n\t\tif (staffRoom) {\r\n\t\t\tstaffRoom.add(`|html|${text}`).update();\r\n\t\t}\r\n\t}\r\n\r\n\terror(text: string) {\r\n\t\t(Rooms.get('development') || Rooms.get('staff') || Rooms.get('lobby'))?.add(`|error|${text}`).update();\r\n\t\tif (Config.loglevel <= 3) console.error(text);\r\n\t}\r\n\r\n\tdebug(text: string) {\r\n\t\tif (Config.loglevel <= 1) console.log(text);\r\n\t}\r\n\r\n\twarn(text: string) {\r\n\t\tif (Config.loglevel <= 3) console.log(text);\r\n\t}\r\n\r\n\tnotice(text: string) {\r\n\t\tif (Config.loglevel <= 2) console.log(text);\r\n\t}\r\n\r\n\tslow(text: string) {\r\n\t\tconst logRoom = Rooms.get('slowlog');\r\n\t\tif (logRoom) {\r\n\t\t\tlogRoom.add(`|c|&|/log ${text}`).update();\r\n\t\t} else {\r\n\t\t\tthis.warn(text);\r\n\t\t}\r\n\t}\r\n\r\n\t/*********************************************************\r\n\t * Resource Monitor\r\n\t *********************************************************/\r\n\r\n\tclean() {\r\n\t\tthis.clearNetworkUse();\r\n\t\tthis.battlePreps.clear();\r\n\t\tthis.battles.clear();\r\n\t\tthis.connections.clear();\r\n\t\tIPTools.dnsblCache.clear();\r\n\t}\r\n\r\n\t/**\r\n\t * Counts a connection. Returns true if the connection should be terminated for abuse.\r\n\t */\r\n\tcountConnection(ip: string, name = '') {\r\n\t\tif (Config.noipchecks || Config.nothrottle) return false;\r\n\t\tconst [count, duration] = this.connections.increment(ip, 30 * 60 * 1000);\r\n\t\tif (count === 500) {\r\n\t\t\tthis.adminlog(`[ResourceMonitor] IP ${ip} banned for cflooding (${count} times in ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tif (count > 500) {\r\n\t\t\tif (count % 500 === 0) {\r\n\t\t\t\tconst c = count / 500;\r\n\t\t\t\tif (c === 2 || c === 4 || c === 10 || c === 20 || c % 40 === 0) {\r\n\t\t\t\t\tthis.adminlog(`[ResourceMonitor] IP ${ip} still cflooding (${count} times in ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * Counts battles created. Returns true if the connection should be\r\n\t * terminated for abuse.\r\n\t */\r\n\tcountBattle(ip: string, name = '') {\r\n\t\tif (Config.noipchecks || Config.nothrottle) return false;\r\n\t\tconst [count, duration] = this.battles.increment(ip, 30 * 60 * 1000);\r\n\t\tif (duration < 5 * 60 * 1000 && count % 30 === 0) {\r\n\t\t\tthis.adminlog(`[ResourceMonitor] IP ${ip} has battled ${count} times in the last ${Chat.toDurationString(duration)}${name ? ': ' + name : ''})`);\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\tif (count % 150 === 0) {\r\n\t\t\tthis.adminlog(`[ResourceMonitor] IP ${ip} has battled ${count} times in the last ${Chat.toDurationString(duration)}${name ? ': ' + name : ''}`);\r\n\t\t\treturn true;\r\n\t\t}\r\n\r\n\t\treturn false;\r\n\t}\r\n\r\n\t/**\r\n\t * Counts team validations. Returns true if too many.\r\n\t */\r\n\tcountPrepBattle(ip: string, connection: Connection) {\r\n\t\tif (Config.noipchecks || Config.nothrottle) return false;\r\n\t\tconst count = this.battlePreps.increment(ip, 3 * 60 * 1000)[0];\r\n\t\tif (count <= 12) return false;\r\n\t\tif (count < 120 && Punishments.isSharedIp(ip)) return false;\r\n\t\tconnection.popup('Due to high load, you are limited to 12 battles and team validations every 3 minutes.');\r\n\t\treturn true;\r\n\t}\r\n\r\n\t/**\r\n\t * Counts concurrent battles. Returns true if too many.\r\n\t */\r\n\tcountConcurrentBattle(count: number, connection: Connection) {\r\n\t\tif (Config.noipchecks || Config.nothrottle) return false;\r\n\t\tif (count <= 5) return false;\r\n\t\tconnection.popup(`Due to high load, you are limited to 5 games at the same time.`);\r\n\t\treturn true;\r\n\t}\r\n\t/**\r\n\t * Counts group chat creation. Returns true if too much.\r\n\t */\r\n\tcountGroupChat(ip: string) {\r\n\t\tif (Config.noipchecks) return false;\r\n\t\tconst count = this.groupChats.increment(ip, 60 * 60 * 1000)[0];\r\n\t\treturn count > 4;\r\n\t}\r\n\r\n\t/**\r\n\t * Counts commands that use HTTPs requests. Returns true if too many.\r\n\t */\r\n\tcountNetRequests(ip: string) {\r\n\t\tif (Config.noipchecks || Config.nothrottle) return false;\r\n\t\tconst [count] = this.netRequests.increment(ip, 1 * 60 * 1000);\r\n\t\tif (count <= 10) return false;\r\n\t\tif (count < 120 && Punishments.isSharedIp(ip)) return false;\r\n\t\treturn true;\r\n\t}\r\n\r\n\t/**\r\n\t * Counts ticket creation. Returns true if too much.\r\n\t */\r\n\tcountTickets(ip: string) {\r\n\t\tif (Config.noipchecks || Config.nothrottle) return false;\r\n\t\tconst count = this.tickets.increment(ip, 60 * 60 * 1000)[0];\r\n\t\tif (Punishments.isSharedIp(ip)) {\r\n\t\t\treturn count >= 20;\r\n\t\t} else {\r\n\t\t\treturn count >= 5;\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Counts the data length received by the last connection to send a\r\n\t * message, as well as the data length in the server's response.\r\n\t */\r\n\tcountNetworkUse(size: number) {\r\n\t\tif (\r\n\t\t\t!Config.emergency || typeof this.activeIp !== 'string' ||\r\n\t\t\tConfig.noipchecks || Config.nothrottle\r\n\t\t) {\r\n\t\t\treturn;\r\n\t\t}\r\n\t\tif (this.activeIp in this.networkUse) {\r\n\t\t\tthis.networkUse[this.activeIp] += size;\r\n\t\t\tthis.networkCount[this.activeIp]++;\r\n\t\t} else {\r\n\t\t\tthis.networkUse[this.activeIp] = size;\r\n\t\t\tthis.networkCount[this.activeIp] = 1;\r\n\t\t}\r\n\t}\r\n\r\n\twriteNetworkUse() {\r\n\t\tlet buf = '';\r\n\t\tfor (const i in this.networkUse) {\r\n\t\t\tbuf += `${this.networkUse[i]}\\t${this.networkCount[i]}\\t${i}\\n`;\r\n\t\t}\r\n\t\tvoid FS('logs/networkuse.tsv').write(buf);\r\n\t}\r\n\r\n\tclearNetworkUse() {\r\n\t\tif (Config.emergency) {\r\n\t\t\tthis.networkUse = {};\r\n\t\t\tthis.networkCount = {};\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Counts roughly the size of an object to have an idea of the server load.\r\n\t */\r\n\tsizeOfObject(object: AnyObject) {\r\n\t\tconst objectCache: Set<[] | object> = new Set();\r\n\t\tconst stack: any[] = [object];\r\n\t\tlet bytes = 0;\r\n\r\n\t\twhile (stack.length) {\r\n\t\t\tconst value = stack.pop();\r\n\t\t\tswitch (typeof value) {\r\n\t\t\tcase 'boolean':\r\n\t\t\t\tbytes += 4;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'string':\r\n\t\t\t\tbytes += value.length * 2;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'number':\r\n\t\t\t\tbytes += 8;\r\n\t\t\t\tbreak;\r\n\t\t\tcase 'object':\r\n\t\t\t\tif (!objectCache.has(value)) objectCache.add(value);\r\n\t\t\t\tif (Array.isArray(value)) {\r\n\t\t\t\t\tfor (const el of value) stack.push(el);\r\n\t\t\t\t} else {\r\n\t\t\t\t\tfor (const i in value) stack.push(value[i]);\r\n\t\t\t\t}\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\treturn bytes;\r\n\t}\r\n\r\n\tsh(command: string, options: ExecOptions = {}): Promise<[number, string, string]> {\r\n\t\treturn new Promise((resolve, reject) => {\r\n\t\t\texec(command, options, (error: ExecException | null, stdout: string | Buffer, stderr: string | Buffer) => {\r\n\t\t\t\tresolve([error?.code || 0, '' + stdout, '' + stderr]);\r\n\t\t\t});\r\n\t\t});\r\n\t}\r\n\r\n\tasync version() {\r\n\t\tlet hash;\r\n\t\ttry {\r\n\t\t\tawait FS('.git/index').copyFile('logs/.gitindex');\r\n\t\t\tconst index = FS('logs/.gitindex');\r\n\t\t\tconst options = {\r\n\t\t\t\tcwd: __dirname,\r\n\t\t\t\tenv: {GIT_INDEX_FILE: index.path},\r\n\t\t\t};\r\n\r\n\t\t\tlet [code, stdout, stderr] = await this.sh(`git add -A`, options);\r\n\t\t\tif (code || stderr) return;\r\n\t\t\t[code, stdout, stderr] = await this.sh(`git write-tree`, options);\r\n\r\n\t\t\tif (code || stderr) return;\r\n\t\t\thash = stdout.trim();\r\n\r\n\t\t\tawait this.sh(`git reset`, options);\r\n\t\t\tawait index.unlinkIfExists();\r\n\t\t} catch {}\r\n\t\treturn hash;\r\n\t}\r\n};\r\n\r\nMonitor.cleanInterval = setInterval(() => Monitor.clean(), MONITOR_CLEAN_TIMEOUT);\r\n"], "mappings": "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", "names": [] }