Refactor code to use CommonJS syntax, update README and package.json for clarity and add binary packaging. Also added initial custom client init stuff.
Signed-off-by: Hammer1279 <hammer@ht-dev.de>
This commit is contained in:
parent
4b3c6e861d
commit
8b31201ad5
41
.github/workflows/pkg.yml
vendored
Normal file
41
.github/workflows/pkg.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
arch:
|
||||
- x64
|
||||
- arm64
|
||||
os:
|
||||
- linux
|
||||
- win
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v3
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
|
||||
- name: Package into node binary
|
||||
uses: lando/pkg-action@v2
|
||||
with:
|
||||
entrypoint: index.js
|
||||
arch: ${{ matrix.arch }}
|
||||
os: ${{ matrix.os }}
|
||||
env:
|
||||
PKG_ARCH: ${{ matrix.arch }}
|
||||
PKG_OS: ${{ matrix.os }}
|
@ -1,3 +1,7 @@
|
||||
# node-telnet-client
|
||||
|
||||
To be used with HTDev BBS Server for encrypted communication and extended functionality
|
||||
To be used with HTDev BBS Server for encrypted communication and extended functionality.
|
||||
|
||||
It has zero dependencies itself so to run a copy locally, just run it with `node index.js`
|
||||
|
||||
To build the application, run `npm install` and then `npm run build`.
|
||||
|
157
index.js
157
index.js
@ -1,68 +1,112 @@
|
||||
import { createConnection } from 'net'
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { createConnection } = require('net');
|
||||
|
||||
const port = 23;
|
||||
const hostname = "localhost";
|
||||
const hostname = process.argv[2] || (process.pkg ? "dom.ht-dev.de" : "localhost");
|
||||
const delayMs = 100; // Command delay in milliseconds
|
||||
|
||||
export const IAC = Buffer.from([0xff]); // Interpret As Command
|
||||
export const DONT = Buffer.from([0xfe]); // Don't
|
||||
export const DO = Buffer.from([0xfd]); // Do
|
||||
export const WONT = Buffer.from([0xfc]); // Won't
|
||||
export const WILL = Buffer.from([0xfb]); // Will
|
||||
export const SB = Buffer.from([0xfa]); // Subnegotiation Begin
|
||||
export const GA = Buffer.from([0xf9]); // Go Ahead
|
||||
export const EL = Buffer.from([0xf8]); // Erase Line
|
||||
export const EC = Buffer.from([0xf7]); // Erase Character
|
||||
export const AYT = Buffer.from([0xf6]); // Are You There
|
||||
export const AO = Buffer.from([0xf5]); // Abort Output
|
||||
export const IP = Buffer.from([0xf4]); // Interrupt Process
|
||||
export const BRK = Buffer.from([0xf3]); // Break
|
||||
export const DM = Buffer.from([0xf2]); // Data Mark
|
||||
export const NOP = Buffer.from([0xf1]); // No Operation
|
||||
export const SE = Buffer.from([0xf0]); // Subnegotiation End
|
||||
// Telnet commands
|
||||
const IAC = Buffer.from([0xff]); // Interpret As Command
|
||||
const DONT = Buffer.from([0xfe]); // Don't
|
||||
const DO = Buffer.from([0xfd]); // Do
|
||||
const WONT = Buffer.from([0xfc]); // Won't
|
||||
const WILL = Buffer.from([0xfb]); // Will
|
||||
const SB = Buffer.from([0xfa]); // Subnegotiation Begin
|
||||
const GA = Buffer.from([0xf9]); // Go Ahead
|
||||
const EL = Buffer.from([0xf8]); // Erase Line
|
||||
const EC = Buffer.from([0xf7]); // Erase Character
|
||||
const AYT = Buffer.from([0xf6]); // Are You There
|
||||
const AO = Buffer.from([0xf5]); // Abort Output
|
||||
const IP = Buffer.from([0xf4]); // Interrupt Process
|
||||
const BRK = Buffer.from([0xf3]); // Break
|
||||
const DM = Buffer.from([0xf2]); // Data Mark
|
||||
const NOP = Buffer.from([0xf1]); // No Operation
|
||||
const SE = Buffer.from([0xf0]); // Subnegotiation End
|
||||
|
||||
// Common Telnet options or features
|
||||
export const ECHO = Buffer.from([0x01]); // Echo
|
||||
export const SUPPRESS_GO_AHEAD = Buffer.from([0x03]); // Suppress Go Ahead
|
||||
export const STATUS = Buffer.from([0x05]); // Status
|
||||
export const TIMING_MARK = Buffer.from([0x06]); // Timing Mark
|
||||
export const TERMINAL_TYPE = Buffer.from([0x18]); // Terminal Type
|
||||
export const NAWS = Buffer.from([0x1f]); // Negotiate About Window Size
|
||||
export const TERMINAL_SPEED = Buffer.from([0x20]); // Terminal Speed
|
||||
export const REMOTE_FLOW_CONTROL = Buffer.from([0x21]); // Remote Flow Control
|
||||
export const LINEMODE = Buffer.from([0x22]); // Line Mode
|
||||
export const ENVIRONMENT_VARIABLES = Buffer.from([0x24]); // Environment Variables
|
||||
const ECHO = Buffer.from([0x01]); // Echo
|
||||
const SUPPRESS_GO_AHEAD = Buffer.from([0x03]); // Suppress Go Ahead
|
||||
const STATUS = Buffer.from([0x05]); // Status
|
||||
const TIMING_MARK = Buffer.from([0x06]); // Timing Mark
|
||||
const TERMINAL_TYPE = Buffer.from([0x18]); // Terminal Type
|
||||
const NAWS = Buffer.from([0x1f]); // Negotiate About Window Size
|
||||
const TERMINAL_SPEED = Buffer.from([0x20]); // Terminal Speed
|
||||
const REMOTE_FLOW_CONTROL = Buffer.from([0x21]); // Remote Flow Control
|
||||
const LINEMODE = Buffer.from([0x22]); // Line Mode
|
||||
const ENVIRONMENT_VARIABLES = Buffer.from([0x24]); // Environment Variables
|
||||
|
||||
// custom codes for own client
|
||||
export const CUSTOM_CLIENT_INIT = Buffer.from([0x80]); // Custom Client Initialization
|
||||
export const KEY_EXCHANGE = Buffer.from([0x81]); // Key Exchange
|
||||
export const ENCRYPTION = Buffer.from([0x82]); // Encryption
|
||||
export const AUTHENTICATION = Buffer.from([0x83]); // Authentication
|
||||
const CUSTOM_CLIENT_INIT = Buffer.from([0x80]); // Custom Client Initialization
|
||||
const KEY_EXCHANGE = Buffer.from([0x81]); // Key Exchange
|
||||
const ENCRYPTION = Buffer.from([0x82]); // Encryption
|
||||
const AUTHENTICATION = Buffer.from([0x83]); // Authentication
|
||||
// 0x84 reserved for future use
|
||||
export const FILE_TRANSFER = Buffer.from([0x85]); // File Transfer
|
||||
export const PAUSE = Buffer.from([0x86]); // Pause
|
||||
export const RESUME = Buffer.from([0x87]); // Resume
|
||||
const FILE_TRANSFER = Buffer.from([0x85]); // File Transfer
|
||||
const PAUSE = Buffer.from([0x86]); // Pause
|
||||
const RESUME = Buffer.from([0x87]); // Resume
|
||||
|
||||
// Set the path of the project folder base on whether it is run with nodejs or as an executable
|
||||
let project_folder;
|
||||
if (process.pkg) {
|
||||
// It is run as an executable
|
||||
project_folder = path.dirname(process.execPath)
|
||||
|
||||
} else {
|
||||
// It is run with nodejs
|
||||
project_folder = __dirname
|
||||
}
|
||||
|
||||
if (process.pkg) {
|
||||
console.debug = () => {}; // Disable debug logging when run as an executable
|
||||
fs.writeFileSync(path.join(project_folder, "README.md"), fs.readFileSync(path.join(__dirname, "README.md"))); // copy newest readme to folder
|
||||
fs.writeFileSync(path.join(project_folder, "LICENSE"), fs.readFileSync(path.join(__dirname, "LICENSE"))); // copy newest license to folder
|
||||
}
|
||||
|
||||
const socket = createConnection(port, hostname);
|
||||
|
||||
let hold = false; // Hold input
|
||||
|
||||
process.stdin.setEncoding("ascii");
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.resume();
|
||||
|
||||
// format keys before sending to server
|
||||
process.stdin.on('data', (key) => {
|
||||
if (key === '\u0003') { // Ctrl+C
|
||||
process.stdin.on('data', async (key) => {
|
||||
if (key === '\u0004') { // Ctrl+D
|
||||
process.exit();
|
||||
} else if (key === '\u0003') { // Ctrl+C
|
||||
send("\u0003");
|
||||
} else if (key === '\u0018') { // Ctrl+X
|
||||
console.log("Entered client command mode");
|
||||
hold = true; // Hold input
|
||||
process.stdin.setRawMode(false);
|
||||
// Add any additional functionality for Ctrl+X here
|
||||
}
|
||||
|
||||
// Log hex values of input for debugging
|
||||
// console.log("Key pressed:", Array.from(key).map(b => '0x' + b.toString(16)));
|
||||
|
||||
|
||||
// Log Unicode values of input for debugging
|
||||
console.debug("Key pressed:", Array.from(key).map(b => '\\u' + b.toString(16).padStart(4, '0')));
|
||||
|
||||
if (key == "\r") {
|
||||
// Enter key
|
||||
socket.write("\r\n");
|
||||
} else {
|
||||
} else if (!hold) {
|
||||
socket.write(key);
|
||||
} else {
|
||||
console.debug("Input on hold");
|
||||
const command = key.replace(/\r?\n|\r/g, ''); // Remove new line characters
|
||||
if (["exit", "quit"].includes(command)) {
|
||||
hold = false; // Release input
|
||||
process.stdin.setRawMode(true);
|
||||
console.log("Exited client command mode");
|
||||
process.stdout.write("> ");
|
||||
} else if (command == "disconnect") {
|
||||
send("\u0003");
|
||||
} else if (command == "help") {
|
||||
// Add any additional functionality for client side commands
|
||||
} else {
|
||||
process.stdout.write("$ ");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -71,6 +115,12 @@ socket.on("data", (data) => {
|
||||
process.stdin.pause();
|
||||
} else if (data.equals(RESUME)) {
|
||||
process.stdin.resume();
|
||||
} else if (data.equals(IP)) {
|
||||
process.exit();
|
||||
} else if (data.equals(Buffer.concat([IAC, DO, CUSTOM_CLIENT_INIT]))) {
|
||||
socket.write(Buffer.concat([IAC, WILL, KEY_EXCHANGE])); // Start Key Exchange
|
||||
} else if (data.equals(Buffer.concat([IAC, DO, KEY_EXCHANGE]))) {
|
||||
// Key Exchange to be implemented here
|
||||
} else {
|
||||
process.stdout.write(data);
|
||||
}
|
||||
@ -78,24 +128,41 @@ socket.on("data", (data) => {
|
||||
|
||||
socket.on("connect", async () => {
|
||||
console.log("Connected to server");
|
||||
|
||||
// initialization
|
||||
socket.write(Buffer.concat([IAC, WILL, NAWS, SB, NAWS, Buffer.from([/* 24x80 */ 0x00, 0x18, 0x00, 0x50]), SE])); // Negotiate About Window Size
|
||||
await delay(delayMs); // Wait for server to process
|
||||
socket.write(Buffer.concat([IAC, WILL, TERMINAL_TYPE, IAC, SB, TERMINAL_TYPE, Buffer.from("xterm-256color"), IAC, SE])); // Terminal Type
|
||||
await delay(delayMs);
|
||||
socket.write(Buffer.concat([IAC, WILL, ECHO])); // Echo
|
||||
await delay(delayMs);
|
||||
socket.write(Buffer.concat([IAC, WILL, CUSTOM_CLIENT_INIT])); // Custom Client Initialization
|
||||
await delay(delayMs);
|
||||
socket.write(Buffer.from([0x0d, 0x0a])); // Line Feed
|
||||
await delay(delayMs);
|
||||
// initialization complete
|
||||
|
||||
await send("help");
|
||||
await delay(500);
|
||||
process.stdout.write("\rCtrl+X for client side commands\r\nCtrl+C to exit, Ctrl+D to force close\r\n> ");
|
||||
});
|
||||
|
||||
export const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
socket.on("end", () => {
|
||||
console.log("\nDisconnected from server");
|
||||
process.exit();
|
||||
});
|
||||
|
||||
export const send = async (command) => {
|
||||
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
|
||||
const send = async (command) => {
|
||||
await delay(delayMs);
|
||||
socket.write(command); // Command
|
||||
await delay(delayMs);
|
||||
socket.write("\r\n"); // Line Feed
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
delay,
|
||||
send,
|
||||
project_folder
|
||||
};
|
||||
|
1197
package-lock.json
generated
1197
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@ -1,10 +1,12 @@
|
||||
{
|
||||
"name": "telnet-client",
|
||||
"version": "1.0.0",
|
||||
"version": "0.0.1",
|
||||
"description": "To be used with HTDev BBS Server for encrypted communication and extended functionality.",
|
||||
"main": "index.js",
|
||||
"bin": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "npx pkg package.json -C GZip"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -12,8 +14,23 @@
|
||||
},
|
||||
"author": "Hammer1279",
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"telnet-client": "file:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@yao-pkg/pkg": "^5.12.1"
|
||||
},
|
||||
"type": "commonjs",
|
||||
"pkg": {
|
||||
"assets": [
|
||||
"README.md",
|
||||
"LICENSE",
|
||||
"package.json"
|
||||
],
|
||||
"targets": [
|
||||
"latest-win-x64",
|
||||
"latest-linux-x64"
|
||||
],
|
||||
"outputPath": "dist"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user