Skip to content

Commit

Permalink
✨ feat: Http Obfuscation post simple complete
Browse files Browse the repository at this point in the history
  • Loading branch information
MoIzadloo committed Apr 18, 2023
1 parent 5a58b88 commit 87e858c
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 65 deletions.
6 changes: 3 additions & 3 deletions src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Address from '../helper/address'
import { AuthMethod } from '../helper/authMethod'
import { COMMANDS } from '../helper/constants'
import Request from '../helper/request'
import ObfsMethod, { ObfsBuilder } from '../obfs/obfs'
import { ObfsBuilder } from '../obfs/obfs'
import { none } from '../obfs'
import Authenticator from './auth/authenticator'

Expand Down Expand Up @@ -51,13 +51,13 @@ export class Client {
*/
private readonly userId?: string

private obfs: ObfsBuilder
public obfs: ObfsBuilder

constructor(
port: number,
host: string,
version: 4 | 5,
obfs = none(),
obfs: ObfsBuilder,
userId?: string
) {
this.host = host
Expand Down
4 changes: 2 additions & 2 deletions src/client/handlers/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ export const bind = handler((info, socket, obfs, event, resolve, reject) => {
0,
info.userId
)
socket.write(request.toBuffer())
socket.write(obfs.obfuscate(request.toBuffer()))
socket.on('data', (data) => {
const reply = Reply.from(data)
const reply = Reply.from(obfs.deObfuscate(data))
if (resolve && reject && obfs) {
reply.promiseHandler(socket, obfs, resolve, reject)
}
Expand Down
2 changes: 1 addition & 1 deletion src/client/handlers/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const connect = handler((info, socket, obfs, event, resolve, reject) => {
)
socket.write(obfs.obfuscate(request.toBuffer()))
socket.on('data', (data) => {
const reply = Reply.from(data)
const reply = Reply.from(obfs.deObfuscate(data))
if (resolve && reject && obfs) {
reply.promiseHandler(socket, obfs, resolve, reject)
}
Expand Down
2 changes: 1 addition & 1 deletion src/helper/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class Connection {
* @returns void
*/
public write(writable: Writable): void {
this.socket.write(writable.toBuffer())
this.socket.write(this.obfs.obfuscate(writable.toBuffer()))
}

/**
Expand Down
12 changes: 10 additions & 2 deletions src/helper/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,17 @@ export const obfsHttpMethods: ObfsHttpMethods = Object.freeze({
})

interface EncryptionMethods {
none: 'None'
none: string
}

export const encryptionMethods: EncryptionMethods = {
none: 'None',
none: 'none',
}

interface CompressionMethods {
none: string
}

export const compressionMethods: CompressionMethods = {
none: 'none',
}
13 changes: 9 additions & 4 deletions src/helper/tcpRelay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Reply from './reply'
import { SOCKS5REPLY } from './constants'
import { Info } from './handler'
import Address from './address'
import ObfsMethod from '../obfs/obfs'

/**
* Starts listening on a new TCP port and the proxy relays
Expand All @@ -22,7 +23,7 @@ class TcpRelay {
*/
public port: number

constructor(port: number, info: Info, socket: net.Socket) {
constructor(port: number, info: Info, socket: net.Socket, obfs: ObfsMethod) {
this.port = port
this.tcpRelay = net.createServer()
this.tcpRelay.once('connection', (remoteSocket) => {
Expand All @@ -34,13 +35,17 @@ class TcpRelay {
SOCKS5REPLY.succeeded.code,
relayAddress
)
socket.write(reply.toBuffer())
socket.write(obfs.obfuscate(reply.toBuffer()))
}
remoteSocket.on('close', () => {
this.close()
})
socket.pipe(remoteSocket)
remoteSocket.pipe(socket)
socket.on('data', (data) => {
remoteSocket.write(obfs.deObfuscate(data))
})
remoteSocket.on('data', (data) => {
socket.write(obfs.obfuscate(data))
})
})
this.tcpRelay.listen(this.port, '127.0.0.1')
}
Expand Down
59 changes: 38 additions & 21 deletions src/obfs/http.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import ObfsMethod from './obfs'
import Connection from '../helper/connection'
import { encryptionMethods, obfsHttpMethods } from '../helper/constants'
import {
encryptionMethods,
obfsHttpMethods,
compressionMethods,
} from '../helper/constants'

class Http extends ObfsMethod {
public path
public name = 'HTTP'
public encryption: string
public compression: string
public method: string
constructor(
connection: Connection,
type: typeof ObfsMethod.CLIENT | typeof ObfsMethod.SERVER,
path: string,
compression: string,
encryption: string,
method: string
) {
super(connection, type)
this.path = path
this.compression = compression
this.encryption = encryption
this.method = method
}
Expand All @@ -26,16 +33,10 @@ class Http extends ObfsMethod {

check(message: Buffer) {
const regex = new RegExp(
`^(?<method>GET|POST|PUT|DELETE|HEAD|OPTIONS).\\/(?<path>[^HTTP]*)HTTP\\/(?<version>.*)`
`^(?<method>GET|POST|PUT|DELETE|HEAD|OPTIONS).\\/(?<path>[^HTTP]*) HTTP\\/(?<version>.*)`
)
let m
while ((m = regex.exec(message.toString())) !== null) {
// This is necessary to avoid infinite loops with zero-width matches
if (m.index === regex.lastIndex) {
regex.lastIndex++
}

// The result can be accessed through the `m`-variable.
const m = regex.exec(message.toString())
if (m) {
if (m[1] !== this.method || m[2] !== this.path || m[3] !== '1.1') {
return false
}
Expand All @@ -45,22 +46,37 @@ class Http extends ObfsMethod {

deObfuscate(message: Buffer): Buffer {
const http = message.toString()
if (this.type === ObfsMethod.SERVER) {
const parts = http.split('\r\n')
console.log(parts)
}
return message
const bodyIndex = http.indexOf('\r\n\r\n')
const parts = http.slice(0, bodyIndex).split('\r\n')
const start = parts[0]
const headers = parts.splice(1, parts.length)
const body = Buffer.from(http.slice(bodyIndex + 4), 'binary')
const parsedHeaders: any = {}
const regex = new RegExp(`([\\w-]+): (.*)`)
headers.forEach((value) => {
const m = regex.exec(value)
if (m) {
parsedHeaders[m[1]] = m[2]
}
})
return body
}

obfuscate(message: Buffer): Buffer {
let http = ''
if (this.type === ObfsMethod.CLIENT) {
http += `POST ${this.path} HTTP/1.1\r\n`
if (this.type === ObfsMethod.SERVER) {
http += `HTTP/1.1 200 OK\r\n`
http += `Connection: keep-alive\r\n`
http += `Content-Type: text/html; charset=utf-8\r\n`
http += `Content-Length: ${message.length}\r\n\r\n`
http += message.toString('binary')
} else {
http += `POST /${this.path} HTTP/1.1\r\n`
http += `Host: ${this.connection.socket.remoteAddress}:${this.connection.socket.remotePort}\r\n`
http += `Connection: keep-alive\r\n`
http += `content-length: ${message.length}\r\n`
http += message
http += `\r\n\r\n`
http += `Accept: text/html; charset=utf-8\r\n`
http += `Content-Length: ${message.length}\r\n\r\n`
http += message.toString('binary')
}
return Buffer.from(http)
}
Expand All @@ -69,6 +85,7 @@ class Http extends ObfsMethod {
export const http =
(
path = '',
compression = compressionMethods.none,
encryption = encryptionMethods.none,
method = obfsHttpMethods.post
) =>
Expand All @@ -78,5 +95,5 @@ export const http =
| typeof ObfsMethod.CLIENT
| typeof ObfsMethod.SERVER = ObfsMethod.CLIENT
) => {
return new Http(connection, type, path, encryption, method)
return new Http(connection, type, path, compression, encryption, method)
}
12 changes: 7 additions & 5 deletions src/server/handlers/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as tcpPortUsed from 'tcp-port-used'
import * as net from 'net'
import { EventTypes } from '../../index'
import Event from '../../helper/event'
import ObfsMethod from '../../obfs/obfs'

/**
* Default implementation of bind
Expand All @@ -18,7 +19,7 @@ export const bind = handler((info, socket, obfs, event) => {
if (relayPort === 0) {
relayPort = randPort()
}
createRelay(relayPort, info, socket, event)
createRelay(relayPort, info, socket, obfs, event)
})

/**
Expand All @@ -45,6 +46,7 @@ const createRelay = (
port: number,
info: Info,
socket: net.Socket,
obfs: ObfsMethod,
event?: Event<EventTypes>
) => {
let version: number
Expand All @@ -58,13 +60,13 @@ const createRelay = (
.check(port)
.then((inUse) => {
if (inUse) {
createRelay(randPort(), info, socket)
createRelay(randPort(), info, socket, obfs)
} else {
const relayHost =
info.address.host === '127.0.0.1'
? '127.0.0.1'
: ip.address('private')
const relay = new TcpRelay(port, info, socket)
const relay = new TcpRelay(port, info, socket, obfs)
event?.subscribeOnce('terminate', () => {
relay.close()
})
Expand All @@ -74,7 +76,7 @@ const createRelay = (
} else if (info.version === SOCKSVERSIONS.socks4) {
reply = new Reply(version, SOCKS4REPLY.granted.code, relayAddress)
}
socket.write(reply.toBuffer())
socket.write(obfs.obfuscate(reply.toBuffer()))
}
})
.catch((err) => {
Expand All @@ -90,6 +92,6 @@ const createRelay = (
)
}
}
socket.write(reply.toBuffer())
socket.write(obfs.obfuscate(reply.toBuffer()))
})
}
10 changes: 7 additions & 3 deletions src/server/handlers/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,13 @@ export const connect = handler((info, socket, obfs) => {
} else if (info.version === SOCKSVERSIONS.socks4) {
reply = new Reply(version, SOCKS4REPLY.granted.code, address)
}
socket.write(reply.toBuffer())
socket.pipe(connection)
connection.pipe(socket)
socket.write(obfs.obfuscate(reply.toBuffer()))
socket.on('data', (data) => {
connection.write(obfs.deObfuscate(data))
})
connection.on('data', (data) => {
socket.write(obfs.obfuscate(data))
})
}
})
connection.on('error', (err) => {
Expand Down
85 changes: 62 additions & 23 deletions test/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,29 +241,68 @@ describe('client useObfs', () => {
})
})

// test('useObfs HTTP with no cipher', (done) => {
// connect(serverPort, serverHost, 4)
// .useObfs(obfsMethods.http())
// .connect(httpPort, '142.251.1.101')
// .then((info) => {
// info.socket.write(
// Buffer.from(
// 'GET / HTTP/1.1\r\n' +
// 'Host: www.google.com:80\r\n' +
// 'Connection: close\r\n' +
// '\r\n'
// )
// )
// info.socket.once('data', (data) => {
// info.socket.destroy()
// expect(data.toString()).toMatch(/20[01] OK/)
// done()
// })
// })
// .catch((reason) => {
// expect(true).toBe(false)
// })
// })
test('useObfs HTTP with no cipher connect', (done) => {
connect(serverPort, serverHost, 4)
.useObfs(obfsMethods.http())
.connect(httpPort, '142.251.1.101')
.then((info) => {
info.socket.write(
info.obfs.obfuscate(
Buffer.from(
'GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n'
)
)
)
info.socket.once('data', (data) => {
info.socket.destroy()
expect(info.obfs.deObfuscate(data).toString()).toMatch(/20[01] OK/)
done()
})
})
.catch((reason) => {
expect(true).toBe(false)
})
})

test('bind', (done) => {
connect(serverPort, serverHost, 4)
.useObfs(obfsMethods.http())
.bind(0, '0.0.0.0')
.then((info) => {
const remote = net.connect(info.address.port, info.address.host)
const states = ['information', 'relay']
let readable, port, host, relayAddr
let state = 0
info.socket.on('data', (data) => {
switch (states[state]) {
case states[1]:
expect(info.obfs.deObfuscate(data).toString()).toBe('Hello')
remote.destroy()
info.socket.destroy()
done()
break
default:
readable = new Readable(info.obfs.deObfuscate(data))
readable.read(3)
port = readable.read(2)
host = readable.read(4)
relayAddr = Address.buffToAddrFactory(
port,
host,
ADDRESSTYPES.ipv4
)
remote.write(Buffer.from('Hello'))
state++
}
})
})
.catch((reason) => {
expect(true).toBe(false)
})
})

afterAll((done) => {
server.close()
Expand Down

0 comments on commit 87e858c

Please sign in to comment.