Provide options for binding IP and ports

This commit is contained in:
q13x 2022-12-20 11:31:37 -08:00
parent 083762fce1
commit 2e75776ff1
3 changed files with 148 additions and 25 deletions

View File

@ -2,10 +2,11 @@ import { Config } from "./types.js";
export const config: Config = {
name: "BasedProxy",
port: 80, // 443 if using TLS
bindHost: "0.0.0.0",
bindPort: 80, // 443 if using TLS
maxPlayers: 20,
motd: {
iconURL: null,
iconURL: "./icon.webp",
l1: "hi",
l2: "lol"
},
@ -18,4 +19,8 @@ export const config: Config = {
key: null,
cert: null
}
}
}
export const BRANDING: Readonly<string> = Object.freeze("EaglerXProxy")
export const VERSION: Readonly<string> = "1.0.0"
export const NETWORK_VERSION: Readonly<string> = Object.freeze(BRANDING + "/" + VERSION)

View File

@ -2,12 +2,11 @@ import { readFileSync } from "fs";
import * as http from "http"
import * as https from "https"
import { WebSocketServer } from "ws";
import { ProxiedPlayer } from "./classes.js";
import { config } from "./config.js";
import { BRANDING, config, NETWORK_VERSION, VERSION } from "./config.js";
import { handlePacket } from "./listener.js";
import { Logger } from "./logger.js";
import { BRANDING, NETWORK_VERSION, VERSION } from "./meta.js";
import { State } from "./types.js";
import { disconnect, generateMOTDImage } from "./utils.js";
import { ChatColor, ProxiedPlayer, State } from "./types.js";
import { genUUID } from "./utils.js";
const logger = new Logger("EagXProxy")
@ -22,25 +21,20 @@ global.PROXY = {
secure: false,
proxyUUID: genUUID(config.name),
MOTD: {
icon: null,
icon: config.motd.iconURL ? await generateMOTDImage(readFileSync(config.motd.iconURL)) : null,
motd: [config.motd.l1, config.motd.l2]
},
playerStats: {
max: config.maxPlayers,
onlineCount: 0
},
wsServer: null,
players: new Map(),
logger: logger,
config: config
}
PROXY.playerStats.onlineCount = PROXY.players.size
let server: WebSocketServer
if (PROXY.config.security.enabled) {
logger.info(`Starting SECURE WebSocket proxy on port ${config.port}...`)
logger.info(`Starting SECURE WebSocket proxy on port ${config.bindPort}...`)
if (process.env.REPL_SLUG) {
logger.warn("You appear to be running the proxy on Repl.it with encryption enabled. Please note that Repl.it by default provides encryption, and enabling encryption may or may not prevent you from connecting to the server.")
}
@ -48,29 +42,54 @@ if (PROXY.config.security.enabled) {
server: https.createServer({
key: readFileSync(config.security.key),
cert: readFileSync(config.security.cert)
}).listen(config.port)
}).listen(config.bindPort, config.bindHost)
})
} else {
logger.info(`Starting INSECURE WebSocket proxy on port ${config.port}...`)
logger.info(`Starting INSECURE WebSocket proxy on port ${config.bindPort}...`)
server = new WebSocketServer({
port: config.port
port: config.bindPort,
host: config.bindHost
})
}
PROXY.wsServer = server
server.addListener('connection', c => {
connectionLogger.debug(`[CONNECTION] New inbound WebSocket connection from [/${(c as any)._socket.remoteAddress}:${(c as any)._socket.remotePort}]. (${(c as any)._socket.remotePort} -> ${config.port})`)
connectionLogger.debug(`[CONNECTION] New inbound WebSocket connection from [/${(c as any)._socket.remoteAddress}:${(c as any)._socket.remotePort}]. (${(c as any)._socket.remotePort} -> ${config.bindPort})`)
const plr = new ProxiedPlayer()
plr.ws = c
plr.ip = (c as any)._socket.remoteAddress
plr.remotePort = (c as any)._socket.remotePort
plr.state = State.PRE_HANDSHAKE
plr.queuedEaglerSkinPackets = []
c.on('message', msg => {
handlePacket(msg as Buffer, plr)
})
})
server.on('listening', () => {
logger.info(`Successfully started${config.security.enabled ? " [secure]" : ""} WebSocket proxy on port ${config.port}!`)
logger.info(`Successfully started${config.security.enabled ? " [secure]" : ""} WebSocket proxy on port ${config.bindPort}!`)
})
process.on('uncaughtException', err => {
logger.error(`An uncaught exception was caught! Exception: ${err.stack ?? err}`)
})
process.on('unhandledRejection', err => {
logger.error(`An unhandled promise rejection was caught! Rejection: ${(err != null ? (err as any).stack : err) ?? err}`)
})
process.on('SIGTERM', () => {
logger.info("Cleaning up before exiting...")
for (const [username, plr] of PROXY.players) {
if (plr.remoteConnection != null) plr.remoteConnection.end()
disconnect(plr, ChatColor.YELLOW + "Proxy is shutting down.")
}
process.exit(0)
})
process.on('SIGINT', () => {
logger.info("Cleaning up before exiting...")
for (const [username, plr] of PROXY.players) {
if (plr.remoteConnection != null) plr.remoteConnection.end()
disconnect(plr, ChatColor.YELLOW + "Proxy is shutting down.")
}
process.exit(0)
})

111
types.ts
View File

@ -1,8 +1,9 @@
import { randomUUID } from "crypto"
import { WebSocketServer } from "ws"
import { ProxiedPlayer } from "./classes.js"
import { Client } from "minecraft-protocol"
import { WebSocketServer, WebSocket } from "ws"
import { BRANDING, VERSION, NETWORK_VERSION } from "./config.js"
import { EaglerSkinPacketId, SkinId } from "./eaglerPacketDef.js"
import { Logger } from "./logger.js"
import { BRANDING, NETWORK_VERSION, VERSION } from "./meta.js"
export type UUID = ReturnType<typeof randomUUID>
@ -13,7 +14,7 @@ export enum State {
}
export type MOTD = {
icon?: Int8Array, // 16384
icon?: Buffer, // 16384
motd: [string, string]
}
@ -32,7 +33,6 @@ export type ProxyGlobals = {
proxyUUID: UUID,
MOTD: MOTD,
playerStats: PlayerStats,
wsServer: WebSocketServer,
players: Map<string, ProxiedPlayer>,
logger: Logger,
@ -41,7 +41,8 @@ export type ProxyGlobals = {
export type Config = {
name: string,
port: number,
bindPort: number,
bindHost: string,
maxPlayers: number,
motd: {
iconURL?: string,
@ -57,4 +58,102 @@ export type Config = {
key: string,
cert: string
}
}
export enum ChatColor {
BLACK = "§0",
DARK_BLUE = "§1",
DARK_GREEN = "§2",
DARK_CYAN = "§3",
DARK_RED = "§4",
PURPLE = "§5",
GOLD = "§6",
GRAY = "§7",
DARK_GRAY = "§8",
BLUE = "§9",
BRIGHT_GREEN = "§a",
CYAN = "§b",
RED = "§c",
PINK = "§d",
YELLOW = "§e",
WHITE = "§f",
// text styling
OBFUSCATED = '§k',
BOLD = '§l',
STRIKETHROUGH = '§m',
UNDERLINED = '§n',
ITALIC = '§o',
RESET = '§r'
}
export type ChatExtra = {
text: string,
bold?: boolean,
italic?: boolean,
underlined?: boolean,
strikethrough?: boolean,
obfuscated?: boolean,
color?: ChatColor | 'reset'
}
export type Chat = {
text?: string,
bold?: boolean,
italic?: boolean,
underlined?: boolean,
strikethrough?: boolean,
obfuscated?: boolean,
color?: ChatColor | 'reset',
extra?: ChatExtra[]
}
export class ProxiedPlayer {
public username: string
public uuid: string
public clientBrand: string
public state: State
public ws: WebSocket
public ip: string
public remotePort: number
public remoteConnection: Client
public skin: {
type: "CUSTOM" | "BUILTIN",
skinId?: number,
customSkin?: Buffer
}
public queuedEaglerSkinPackets: UnpackedChannelMessage[]
}
export enum ChannelMessageType {
CLIENT = 0x17,
SERVER = 0x3f
}
export type UnpackedChannelMessage = {
channel: string,
data: Buffer,
type: ChannelMessageType
}
export type DecodedCFetchSkin = {
id: EaglerSkinPacketId.C_FETCH_SKIN,
uuid: UUID
}
export type DecodedSSkinFetchBuiltin = {
id: EaglerSkinPacketId.S_SKIN_DL_BI,
uuid: UUID,
skinId: SkinId
}
export type DecodedSSkinDl = {
id: EaglerSkinPacketId.S_SKIN_DL,
uuid: UUID,
skin: Buffer
}
export type DecodedCSkinReq = {
id: EaglerSkinPacketId.C_REQ_SKIN,
uuid: UUID,
url: string
}