Skip to content

Add Rammerhead! #12

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ This is our third edition of [Ruby](https://github.com/ruby-network/ruby-v1). Th

- [Ultraviolet](https://github.com/titaniumnetwork-dev/ultraviolet)
- [Dynamic](https://github.com/nebulaservices/dynamic)
- [RammerHead](https://github.com/binary-person/rammerhead) **COMING SOON**
- [RammerHead](https://github.com/binary-person/rammerhead)
---


Expand All @@ -85,8 +85,6 @@ This is our third edition of [Ruby](https://github.com/ruby-network/ruby-v1). Th

- Partners page for all of our wonderful partners

- More proxy backends (RammerHead)

- Apps

- History page
Expand All @@ -105,6 +103,7 @@ This is our third edition of [Ruby](https://github.com/ruby-network/ruby-v1). Th
- [Bare Server Node](https://github.com/tomphttp/bare-server-node)
- [Ultraviolet](https://github.com/titaniumnetwork-dev/ultraviolet)
- [Dynamic](https://github.com/nebulaServices/dynamic)
- [RammerHead](https://github.com/binary-person/rammerhead)
- HTML, CSS, and JavaScript
<!-- [Particles.js](https://vincentgarreau.com/particles.js/) -->

Expand Down
3 changes: 3 additions & 0 deletions build.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import chalk from 'chalk';
import fs from 'fs';
import path from 'path';
const __dirname = path.resolve();
import buildRH from './buildFiles/rhbuild.js';
console.log(chalk.green('Setting up Files and folders'));
if (fs.existsSync('./src/public/js/dynamic/')) {
if (fs.existsSync('./src/public/js/dynamic/dynamic.config.js')) {
Expand All @@ -25,3 +26,5 @@ fs.renameSync('./dynamic/dist/', './src/public/js/dynamic/');
fs.renameSync('./tmp/dynamic.config.js', './src/public/js/dynamic/dynamic.config.js');
fs.rmdirSync('./tmp');
console.log(chalk.bgGreen.black('Dynamic build complete'));
console.log(chalk.green('Starting RH Browser build'));
buildRH();
41 changes: 41 additions & 0 deletions buildFiles/rhbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as esbuild from 'esbuild';
async function buildRH() {
const rh = await esbuild.build({
entryPoints: ['rammerhead/rh.js'],
bundle: true,
format: 'iife',
minify: true,
platform: 'browser',
sourcemap: true,
target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
outfile: 'src/public/js/rh/rh.js',
metafile: true,
plugins: []
});
const rhApi = await esbuild.build({
entryPoints: ['rammerhead/rhAPI.js'],
bundle: true,
format: 'iife',
minify: true,
platform: 'browser',
sourcemap: true,
target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
outfile: 'src/public/js/rh/rhAPI.js',
metafile: true,
plugins: []
});
const rhHelper = await esbuild.build({
entryPoints: ['rammerhead/rhHelper.js'],
bundle: true,
format: 'iife',
minify: true,
platform: 'browser',
sourcemap: true,
target: ['chrome58', 'firefox57', 'safari11', 'edge16'],
outfile: 'src/public/js/rh/rhHelper.js',
metafile: true,
plugins: []
});
console.log(await esbuild.analyzeMetafile(rh.metafile));
}
export default buildRH;
29 changes: 25 additions & 4 deletions node-server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import fastifyMiddie from '@fastify/middie';
import fastifyHttpProxy from '@fastify/http-proxy';
import { fileURLToPath } from 'node:url';
import { createBareServer } from '@tomphttp/bare-server-node';
import createRammerhead from "rammerhead/src/server/index.js";
import { createServer } from 'http';
import fs from 'fs'
import YAML from 'yaml';
Expand All @@ -14,22 +15,42 @@ import compile from './compile.js';
let rubyPort = process.argv.find((arg) => arg.startsWith('--ruby-port')).split('=')[1] || 9292;
let nodePort = process.argv.find((arg) => arg.startsWith('--node-port')).split('=')[1] || 9293;

const bareServer = createBareServer('/bare/');
const bare = createBareServer('/bare/');
const rh = createRammerhead();
const rammerheadScopes = [ "/rammerhead.js", "/hammerhead.js", "/transport-worker.js", "/task.js", "/iframe-task.js", "/worker-hammerhead.js", "/messaging", "/sessionexists", "/deletesession", "/newsession", "/editsession", "/needpassword", "/syncLocalStorage", "/api/shuffleDict", "/mainport" ];
const rammerheadSession = /^\/[a-z0-9]{32}/;
function shouldRouteRh(req) {
const url = new URL(req.url, "http://0.0.0.0");
return (rammerheadScopes.includes(url.pathname) || rammerheadSession.test(url.pathname));
}
function routeRhRequest(req, res) { rh.emit("request", req, res) }
function routeRhUpgrade(req, socket, head) { rh.emit("upgrade", req, socket, head) }
console.log(chalk.red('Compiling...'))
compile();

const bareHandler = (handler, opts) => {
const proxyHandler = (handler, opts) => {
return createServer().on('request', (req, res) => {
bareServer.shouldRoute(req) ? bareServer.routeRequest(req, res) : handler(req, res);
if (bare.shouldRoute(req)) {
bare.routeRequest(req, res);
}
else if (shouldRouteRh(req)) {
routeRhRequest(req, res);
}
else {
handler(req, res);
}
})
.on('upgrade', (req, socket, head) => {
if ( bareServer.shouldRoute(req) ) {
bareServer.routeUpgrade(req, socket, head);
}
else if (shouldRouteRh(req)) {
routeRhUpgrade(req, socket, head);
}
});
};

const app = Fastify({ logger: false, serverFactory: bareHandler })
const app = Fastify({ logger: false, serverFactory: proxyHandler })
await app
.register(fastifyHttpProxy, {
upstream: 'http://localhost:9292',
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"install": "bundle install && npm run build",
"build": "node ./build.js"
},
"engines": {
"engines": {
"node": ">=18.0.0"
},
"dependencies": {
Expand All @@ -19,8 +19,10 @@
"@titaniumnetwork-dev/ultraviolet": "^2.0.0",
"@tomphttp/bare-server-node": "^2.0.0",
"chalk": "^5.2.0",
"esbuild": "^0.19.2",
"fastify": "^4.21.0",
"progress-estimator": "^0.3.1",
"rammerhead": "https://github.com/Ruby-Network/rammerhead/releases/download/v1/rammerhead-2.tgz",
"sass": "^1.62.1",
"yaml": "^2.3.1"
}
Expand Down
2 changes: 2 additions & 0 deletions rammerhead/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Rammerhead source files for the browser. Compiled for older browsers via [Esbuild](https://esbuild.github.io/).

21 changes: 21 additions & 0 deletions rammerhead/bad.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { RammerheadAPI, StrShuffler } from './rhAPI.js';

async function startRH(){
let api = new RammerheadAPI('http://localhost:3000');
await fetch('http://localhost:3000');
if ( localStorage.getItem('rammerhead_session') && (await api.sessionExists(localStorage.getItem('rammerhead_session'))) ) {
const test = await fetch( new URL(localStorage.getItem('rammerhead_session'), 'http://localhost:3000') );
await api.deleteSession(localStorage.getItem('rammerhead_session'));
// 404 = good, 403 = Sessions must come from the same IP
if (test.status === 403) localStorage.removeItem('rammerhead_session');
} else {
localStorage.removeItem('rammerhead_session');
}
let session = (async api.newSession());
localStorage.setItem('rammerhead_session', session)
await api.editSession(session, false, true);
const dict = await api.shuffleDict(session);
const shuffler = new StrShuffler(dict);
location.replace(new URL(`${session}/${shuffler.shuffle('https://google.com')}`, 'http://localhost:3000'));
}
window.onload = startRH()
2 changes: 2 additions & 0 deletions rammerhead/rh.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { rhFunc } from './rhHelper.js';
window.rh = rhFunc;
139 changes: 139 additions & 0 deletions rammerhead/rhAPI.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
export class RammerheadAPI {
constructor(server, signal, password) {
this.server = server
this.signal = signal
this.password = password
}
async get(url) {
if (this.password) {
// really cheap way of adding a query parameter
if (url.includes("?")) {
url += "&pwd=" + this.password
} else {
url += "?pwd=" + this.password
}
}

try {
const request = await fetch(new URL(url, this.server), {
signal: this.signal
})

if (request.status === 200) {
return await request.text()
} else {
throw new Error(
`unexpected server response to not match "200". Server says "${await request.text()}"`
)
}
} catch (err) {
console.error(err)
throw new Error("Cannot communicate with the server")
}
}
async needPassword() {
return (await this.get("needpassword")) === "true"
}
async newSession() {
return await this.get("newsession")
}
async editSession(id, httpProxy, enableShuffling) {
const res = await this.get(
"editsession?id=" +
encodeURIComponent(id) +
(httpProxy ? "&httpProxy=" + encodeURIComponent(httpProxy) : "") +
"&enableShuffling=" +
(enableShuffling ? "1" : "0")
)

if (res !== "Success") {
throw new Error(`unexpected response from server. received ${res}`)
}
}
async sessionExists(id) {
const res = await this.get("sessionexists?id=" + encodeURIComponent(id))

switch (res) {
case "exists":
return true
case "not found":
return false
default:
throw new Error(`unexpected response from server. received ${res}`)
}
}
async deleteSession(id) {
if (await this.sessionExists(id)) {
const res = await this.get("deletesession?id=" + id)

if (res !== "Success" && res !== "not found") {
throw new Error(`unexpected response from server. received ${res}`)
}
}
}
async shuffleDict(id) {
const res = await this.get("api/shuffleDict?id=" + encodeURIComponent(id))
return JSON.parse(res)
}
}

export class StrShuffler {
baseDictionary =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-"
shuffledIndicator = "_rhs"
constructor(dictionary) {
dictionary ||= this.generateDictionary()
this.dictionary = dictionary
}
mod(n, m) {
return ((n % m) + m) % m
}
generateDictionary() {
let str = ""
const split = this.baseDictionary.split("")
while (split.length > 0)
str += split.splice(Math.floor(Math.random() * split.length), 1)[0]

return str
}
shuffle(str) {
if (str.startsWith(this.shuffledIndicator)) return str
let shuffledStr = ""
for (let i = 0; i < str.length; i++) {
const char = str.charAt(i)
const idx = this.baseDictionary.indexOf(char)
if (char === "%" && str.length - i >= 3) {
shuffledStr += char
shuffledStr += str.charAt(++i)
shuffledStr += str.charAt(++i)
} else if (idx === -1) shuffledStr += char
else
shuffledStr += this.dictionary.charAt(
this.mod(idx + i, this.baseDictionary.length)
)
}
return this.shuffledIndicator + shuffledStr
}
unshuffle(str) {
if (!str.startsWith(this.shuffledIndicator)) return str

str = str.slice(this.shuffledIndicator.length)

let unshuffledStr = ""
for (let i = 0; i < str.length; i++) {
const char = str.charAt(i)
const idx = this.dictionary.indexOf(char)
if (char === "%" && str.length - i >= 3) {
unshuffledStr += char
unshuffledStr += str.charAt(++i)
unshuffledStr += str.charAt(++i)
} else if (idx === -1) unshuffledStr += char
else
unshuffledStr += this.baseDictionary.charAt(
this.mod(idx - i, this.baseDictionary.length)
)
}
return unshuffledStr
}
}

44 changes: 44 additions & 0 deletions rammerhead/rhHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { RammerheadAPI, StrShuffler } from './rhAPI.js';

let rhFunc = {}

rhFunc = {
rhInteract:
async function(origin, url){
let api = new RammerheadAPI(origin);
await fetch(origin);
if ( localStorage.getItem('rammerhead_session') && (await api.sessionExists(localStorage.getItem('rammerhead_session'))) ) {
const test = await fetch( new URL(localStorage.getItem('rammerhead_session'), origin) );
//await api.deleteSession(localStorage.getItem('rammerhead_session'));
// 404 = good, 403 = Sessions must come from the same IP
if (test.status === 403) localStorage.removeItem('rammerhead_session');
} else {
localStorage.removeItem('rammerhead_session');
}
let session;
switch (localStorage.getItem('rammerhead_session')) {
case null:
case undefined:
case 'null':
case 'undefined':
session = (await api.newSession());
break;
default:
session = localStorage.getItem('rammerhead_session')
}
localStorage.setItem('rammerhead_session', session)
await api.editSession(session, false, true);
const dict = await api.shuffleDict(session);
const shuffler = new StrShuffler(dict);
return new URL(`${session}/${shuffler.shuffle(url)}`, origin);
},
rhDecrypt:
async function(origin, value){
let api = new RammerheadAPI(origin);
let session = localStorage.getItem('rammerhead_session')
const dict = await api.shuffleDict(session);
const shuffler = new StrShuffler(dict);
return shuffler.unshuffle(value)
}
}
export {rhFunc}
3 changes: 3 additions & 0 deletions src/public/js/controls.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ function updateURLBar(val) {
val = val.replace(__dynam$ic.prefix, '');
val = __dynam$ic.decodeUrl(val);
}
if (currentProx === 'rammerhead') {
val = '';
}
if (val === "a`owt8bnalk") {
val = "";
}
Expand Down
50 changes: 21 additions & 29 deletions src/public/js/dynamic/dynamic.client.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/public/js/dynamic/dynamic.client.js.map

Large diffs are not rendered by default.

50 changes: 21 additions & 29 deletions src/public/js/dynamic/dynamic.handler.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/public/js/dynamic/dynamic.handler.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/public/js/dynamic/dynamic.html.js.map

Large diffs are not rendered by default.

48 changes: 20 additions & 28 deletions src/public/js/dynamic/dynamic.worker.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/public/js/dynamic/dynamic.worker.js.map

Large diffs are not rendered by default.

Loading