Skip to content

Commit

Permalink
add deno-based multiproxy
Browse files Browse the repository at this point in the history
  • Loading branch information
egasimus committed Nov 18, 2024
1 parent 3962204 commit 0fbcd57
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 1 deletion.
2 changes: 1 addition & 1 deletion control_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function main () {
HOST: "0.0.0.0",
PORT: "25551",
NAMADA: "namadan",
CHAIN_ID: "housefire-reduce.e51ecf4264fc3",
CHAIN_ID: "namada-dryrun.abaaeaf7b78cb3ac",
NODE_OUT: "http://node-out:25552"
})
const service = new NamadaService(NAMADA, CHAIN_ID)
Expand Down
115 changes: 115 additions & 0 deletions control_out_proxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env -S deno run --allow-net --allow-env=HOST,PORT,CONFIG

import { initialize, environment, api, respond } from './lib.js'

if (import.meta.main) main()

async function main () {
const t0 = initialize()
const { HOST, PORT, CONFIG } = environment({
HOST: '0.0.0.0',
PORT: '25552',
CONFIG: [
['26666', '65.108.193.224:26656' ].join('='),
['26667', '74.50.93.254:26656' ].join('='),
['26668', '64.118.250.82:46656' ].join('='),
['26669', '138.197.133.118:26656'].join('='),
].join(',')
})
run(HOST, PORT, parseConfig(CONFIG))
}

async function run (localHost, controlPort, proxyConfig) {
// Flag to allow/disallow connections
let connected = true
// List of currently open connections to close
let connections = []
// Print the proxy config that is in use
console.log('Proxy config:', JSON.stringify(proxyConfig, null, 2))
// Launch TCP proxy listeners for each remote peer
await Promise.all(Object.entries(config).map(([localPort, { remoteHost, remotePort }])=>{
tcpProxy(localPort, remoteHost, remotePort)
}))
// Launch control api
api('MultiSync', localHost, controlPort, {
// Report status
['/'] () {
respond(200, { connected, connections: connections.length })
},
// Enable connecting
['/start'] () {
connected = true
respond(200, { connected, connections: connections.length })
},
// Disable connecting
['/pause'] () {
connections = connections.filter(connection=>{
connection.clone()
return false
})
connected = false
respond(200, { connected, connections: connections.length })
},
}, {
onMessage: async ({ event }) => {
const data = JSON.parse(event.data)
if (data.resume) {
console.log('🟢 Resuming sync...')
await service.start()
}
}
})
// Implementation of TCP proxy
async function tcpProxy (localPort, remoteHost, remotePort) {
const listener = Deno.listen({ transport: 'tcp', hostname: localHost, port: localPort })
for await (const connection of listener) {
if (connected) {
console.log(`Establishing connection at ${localPort} for ${remoteHost}:${remotePort}`)
connections.push(connection)
const remote = await Deno.connect({ host: remoteHost, port: remotePort })
await Promise.all([
connection.readable.pipeTo(remote.writable),
remote.readable.pipeTo(connection.writable),
])
} else {
console.log(`Rejecting connection at ${localPort} for ${remoteHost}:${remotePort}`)
}
}
}
}

function parseConfig (text) {
const configError1 = (config) => {
console.error({config})
return new Error(`Invalid config; format: LPORT=RHOST:RPORT[,LPORT=RHOST:RPORT]*`)
}
const configError2 = (port) => new Error(`Invalid local port in config: ${port}`)
const configError3 = (host) => new Error(`Empty remote host in config: ${host}`)
const configError4 = (port) => new Error(`Invalid remote port in config: ${port}`)
const configError5 = (port) => new Error(`Duplicate local port in config: ${port}`)
const config = {}
// Config is comma-separated clauses: LocalPort=RemoteHost:RemotePort
for (let line in text.split(',')) {
// Ignore whitespace and empty lines
line = line.trim()
if (line.length === 0) continue
// Split into left and right parts
let split = line.split('=')
if (split.length !== 2) throw configError1()
// Validate port number
const localPort = Number(split[0])
if (!(localPort > 0 && localPort < 65535)) throw configError2(split[0])
// Parse remote connection details
split = split[1].split(':')
// Validate remote host
const remoteHost = split[0].trim()
if (remoteHost.length === 0) throw configError3(split[0])
// Validate remote port
const remotePort = Number(split[1])
if (!(remotePort > 0 && remotePort < 65535)) throw configError4(split[1])
// Prevent duplicates
if (config[localPort]) throw configError5(localPort)
config[localPort] = { remoteHost, remotePort }
}
return config
}

0 comments on commit 0fbcd57

Please sign in to comment.