feature: Add support for getting inbound connection.

Also fixes any bugs I found.
This commit is contained in:
greysoh 2024-05-07 18:35:04 -04:00
parent b99d6a89dc
commit a9fcdea037
Signed by: imterah
GPG key ID: 8FA7DD57BA6CEA37
8 changed files with 146 additions and 17 deletions

View file

@ -5,14 +5,13 @@ meta {
}
post {
url: http://127.0.0.1:3000/api/v1/backends/remove
url: http://127.0.0.1:3000/api/v1/backends/lookup
body: json
auth: none
}
body:json {
{
"token": "f1b89cc337073476289ade17ffbe7a6419b4bd52aa7ede26114bffd76fa263b5cb1bcaf389462e1d9e7acb7f4b6a7c28152a9cc9af83e3ec862f1892b1",
"id": "2"
"token": "7d69814cdada551dd22521ad97b23b22a106278826a2b4e87dd76246594b56f973894e8265437a5d520ed7258d7c856d0d294e89b1de1a98db7fa4a"
}
}

View file

@ -1,4 +1,4 @@
import type { BackendBaseClass } from "./base.js";
import { BackendBaseClass } from "./base.js";
import { PassyFireBackendProvider } from "./passyfire-reimpl/index.js";
import { SSHBackendProvider } from "./ssh.js";
@ -7,3 +7,7 @@ export const backendProviders: Record<string, typeof BackendBaseClass> = {
ssh: SSHBackendProvider,
passyfire: PassyFireBackendProvider,
};
if (process.env.NODE_ENV != "production") {
backendProviders["dummy"] = BackendBaseClass;
}

View file

@ -179,7 +179,7 @@ export class SSHBackendProvider implements BackendBaseClass {
srcConn.write(chunk);
});
destConn.addListener("close", () => {
destConn.addListener("end", () => {
this.clients.splice(this.clients.indexOf(client), 1);
srcConn.end();
});
@ -190,7 +190,7 @@ export class SSHBackendProvider implements BackendBaseClass {
srcConn.on("end", () => {
this.clients.splice(this.clients.indexOf(client), 1);
destConn.close();
destConn.end();
});
},
);

View file

@ -45,19 +45,23 @@ export function route(routeOptions: RouteOptions) {
},
});
if (!forward)
if (!forward) {
return res.status(400).send({
error: "Could not find forward entry",
});
}
if (!backends[forward.destProviderID])
if (!backends[forward.destProviderID]) {
return res.status(400).send({
error: "Backend not found",
});
}
return {
success: true,
data: backends[forward.destProviderID].getAllConnections(),
data: backends[forward.destProviderID].getAllConnections().filter((i) => {
return i.connectionDetails.sourceIP == forward.sourceIP && i.connectionDetails.sourcePort == forward.sourcePort && i.connectionDetails.destPort == forward.destPort;
}),
};
},
);

View file

@ -4,7 +4,7 @@ import { run as connection } from "./commands/connections.js";
import { run as backends } from "./commands/backends.js";
import { run as users } from "./commands/users.js";
export type PrintLine = (...str: string[]) => void;
export type PrintLine = (...str: any[]) => void;
type Command = (
args: string[],

View file

@ -3,12 +3,25 @@ import type { Axios } from "axios";
import { SSHCommand } from "../libs/patchCommander.js";
import type { PrintLine } from "../commands.js";
// https://stackoverflow.com/questions/37938504/what-is-the-best-way-to-find-all-items-are-deleted-inserted-from-original-arra
function difference(a: any[], b: any[]) {
return a.filter(x => b.indexOf(x) < 0);
};
export async function run(
argv: string[],
println: PrintLine,
axios: Axios,
apiKey: string,
token: string,
) {
let resolve: (value: unknown) => void;
let reject: (value: unknown) => void;
const promise = new Promise((ourResolve, ourReject) => {
resolve = ourResolve;
reject = ourReject;
});
const program = new SSHCommand(println);
program.description("Manages connections for NextNet");
program.version("v0.1.0-preprod");
@ -76,6 +89,102 @@ export async function run(
getInbound.argument("<id>", "Tunnel ID to view inbound connections of");
getInbound.option("-t, --tail", "Live-view of connection list");
getInbound.action(async(idStr: string, options: {
tail?: boolean
}): Promise<void> => {
type InboundConnectionSuccess = {
success: true,
data: {
ip: string,
port: number,
connectionDetails: {
sourceIP: string,
sourcePort: number,
destPort: number,
enabled: boolean
}
}[]
};
const id = parseInt(idStr);
if (Number.isNaN(id)) {
println("ID (%s) is not a number\n", idStr);
return;
}
if (options.tail) {
let previousEntries: string[] = [];
while (true) {
const response = await axios.post("/api/v1/forward/connections", {
token,
id
});
if (response.status != 200) {
if (process.env.NODE_ENV != "production") console.log(response);
if (response.data.error) {
println(`Error: ${response.data.error}\n`);
} else {
println("Error requesting connections!\n");
}
return resolve(null);
}
const { data }: InboundConnectionSuccess = response.data;
const simplifiedArray: string[] = data.map((i) => `${i.ip}:${i.port}`);
const insertedItems: string[] = difference(simplifiedArray, previousEntries);
const removedItems: string[] = difference(previousEntries, simplifiedArray);
insertedItems.forEach((i) => println("CONNECTED: %s\n", i));
removedItems.forEach((i) => println("DISCONNECTED: %s\n", i));
previousEntries = simplifiedArray;
await new Promise((i) => setTimeout(i, 2000));
}
} else {
const response = await axios.post("/api/v1/forward/connections", {
token,
id
});
if (response.status != 200) {
if (process.env.NODE_ENV != "production") console.log(response);
if (response.data.error) {
println(`Error: ${response.data.error}\n`);
} else {
println("Error requesting connections!\n");
}
return resolve(null);
}
const { data }: InboundConnectionSuccess = response.data;
if (data.length == 0) {
println("There are currently no connected clients.\n");
return resolve(null);
}
println("Connected clients (for source: %s:%s):\n", data[0].connectionDetails.sourceIP, data[0].connectionDetails.sourcePort);
for (const entry of data) {
println(" - %s:%s\n", entry.ip, entry.port);
}
console.log(response.data);
}
return resolve(null);
});
const removeTunnel = new SSHCommand(println, "rm");
removeTunnel.description("Removes a tunnel");
removeTunnel.argument("<id>", "Tunnel ID to remove");
@ -88,4 +197,7 @@ export async function run(
program.addCommand(removeTunnel);
program.parse(argv);
if (program.hasRecievedExitSignal) return;
await promise;
}

View file

@ -80,8 +80,8 @@ server.on("connection", client => {
"Welcome to NextNet LOM. Run 'help' to see commands.\r\n\r\n~$ ",
);
function println(...str: string[]) {
stream.write(format(...str).replaceAll("\n", "\r\n"));
function println(...data: any[]) {
stream.write(format(...data).replaceAll("\n", "\r\n"));
};
while (true) {

View file

@ -35,7 +35,7 @@ export class SSHCommand extends Command {
if (process.env.NODE_ENV != "production") {
println(
"Caught irrecoverable action (command help call) in patchCommander\n",
"Caught irrecoverable crash (command help call) in patchCommander\n",
);
} else {
println("Aborted\n");
@ -46,12 +46,22 @@ export class SSHCommand extends Command {
}
}
_exit() {
recvExitDispatch() {
this.hasRecievedExitSignal = true;
let parentElement = this.parent;
while (parentElement instanceof SSHCommand) {
parentElement.hasRecievedExitSignal = true;
parentElement = parentElement.parent;
};
}
_exit() {
this.recvExitDispatch();
}
_exitCallback() {
this.hasRecievedExitSignal = true;
this.recvExitDispatch();
}
action(fn: (...args: any[]) => void | Promise<void>): this {
@ -63,7 +73,7 @@ export class SSHCommand extends Command {
// @ts-ignore
this._actionHandler = async (...args: any[]): Promise<void> => {
if (args[0][0] == "--help" || args[0][0] == "-h") return;
if (this.hasRecievedExitSignal) return;
await oldActionHandler(...args);
};