Skip to content

Commit

Permalink
✨ feat: Add obfuscation support
Browse files Browse the repository at this point in the history
  • Loading branch information
MoIzadloo committed Apr 14, 2023
1 parent ac49819 commit 63f89d6
Show file tree
Hide file tree
Showing 16 changed files with 131 additions and 26 deletions.
2 changes: 2 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

- [ ] Complete documentation
- [ ] Complete test coverage
- [ ] Add obfuscation support

### In Progress

- [ ] Complete documentation
- [ ] Complete test coverage
- [ ] Add obfuscation support

### Done ✓

Expand Down
7 changes: 6 additions & 1 deletion src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import Address from '../helper/address'
import { AuthMethod } from '../helper/authMethod'
import { COMMANDS } from '../helper/constants'
import Request from '../helper/request'
import Obfs from '../obfs/obfs'
import { None } from '../obfs/none'
import obfs from '../obfs/obfs'

/**
* The Client class is responsible for creating a TCP socket connection,
Expand Down Expand Up @@ -49,6 +52,8 @@ export class Client {
*/
private readonly userId?: string

private obfs: Obfs = new None()

constructor(port: number, host: string, version: 4 | 5, userId?: string) {
this.host = host
this.port = port
Expand Down Expand Up @@ -99,7 +104,7 @@ export class Client {
connection.request = new Request(ver, cmd, address, 0, id)
connection.resolve = resolve
connection.reject = reject

connection.obfs = this.obfs
connection.event.subscribeOnce('error', (err) => {
reject(err.message)
})
Expand Down
24 changes: 13 additions & 11 deletions src/client/handlers/associate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,16 @@ import Reply from '../../helper/reply'
* Handle udp associate request
* @returns void
*/
export const associate = handler((info, socket, event, resolve, reject) => {
const request = new Request(info.version, COMMANDS.associate, info.address)
socket.write(request.toBuffer())
socket.on('data', (data) => {
const reply = Reply.from(data)
if (resolve && reject) {
reply.promiseHandler(socket, resolve, reject)
}
socket.removeAllListeners('data')
})
})
export const associate = handler(
(info, socket, obfs, event, resolve, reject) => {
const request = new Request(info.version, COMMANDS.associate, info.address)
socket.write(request.toBuffer())
socket.on('data', (data) => {
const reply = Reply.from(data)
if (resolve && reject && obfs) {
reply.promiseHandler(socket, obfs, resolve, reject)
}
socket.removeAllListeners('data')
})
}
)
6 changes: 3 additions & 3 deletions src/client/handlers/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Reply from '../../helper/reply'
* Handle bind request
* @returns void
*/
export const bind = handler((info, socket, event, resolve, reject) => {
export const bind = handler((info, socket, obfs, event, resolve, reject) => {
const request = new Request(
info.version,
COMMANDS.bind,
Expand All @@ -18,8 +18,8 @@ export const bind = handler((info, socket, event, resolve, reject) => {
socket.write(request.toBuffer())
socket.on('data', (data) => {
const reply = Reply.from(data)
if (resolve && reject) {
reply.promiseHandler(socket, resolve, reject)
if (resolve && reject && obfs) {
reply.promiseHandler(socket, obfs, resolve, reject)
}
socket.removeAllListeners('data')
})
Expand Down
6 changes: 3 additions & 3 deletions src/client/handlers/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Reply from '../../helper/reply'
* Handle connect request
* @returns void
*/
export const connect = handler((info, socket, event, resolve, reject) => {
export const connect = handler((info, socket, obfs, event, resolve, reject) => {
const request = new Request(
info.version,
COMMANDS.connect,
Expand All @@ -18,8 +18,8 @@ export const connect = handler((info, socket, event, resolve, reject) => {
socket.write(request.toBuffer())
socket.on('data', (data) => {
const reply = Reply.from(data)
if (resolve && reject) {
reply.promiseHandler(socket, resolve, reject)
if (resolve && reject && obfs) {
reply.promiseHandler(socket, obfs, resolve, reject)
}
socket.removeAllListeners('data')
})
Expand Down
7 changes: 5 additions & 2 deletions src/helper/connection.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as net from 'net'
import { State } from './state'
import Readable from './readable'
import { IdentifierState } from '../server/state/socks5'
import { ObfsState } from '../server/state/socks5'
import Writable from './writable'
import { Handlers } from './handlers'
import { HandlerResolve } from './handler'
import Event from './event'
import Request from './request'
import Obfs from '../obfs/obfs'

export type EventTypes = {
data: (data: Buffer) => void
Expand Down Expand Up @@ -38,7 +39,7 @@ class Connection {
/**
* Current state
*/
private state: State = new IdentifierState(this)
private state: State = new ObfsState(this)

/**
* Connection socket
Expand Down Expand Up @@ -68,6 +69,8 @@ class Connection {
socks4: true,
}

public obfs?: Obfs

/**
* Clients Request
*/
Expand Down
6 changes: 5 additions & 1 deletion src/helper/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Address from './address'
import Connection, { EventTypes } from './connection'
import * as net from 'net'
import Event from './event'
import Obfs from '../obfs/obfs'

export interface Info {
version: number
Expand All @@ -12,13 +13,15 @@ export interface Info {
export interface HandlerResolve {
socket: net.Socket
address: Address
obfs: Obfs
rsv?: number
args?: any
}

export type Handler = (
info: Info,
socket: net.Socket,
obfs: Obfs,
event?: Event<EventTypes>,
resolve?: (value: PromiseLike<HandlerResolve> | HandlerResolve) => void,
reject?: ((reason?: any) => void) | undefined
Expand All @@ -32,14 +35,15 @@ export type Handler = (
export const handler =
(callback: Handler) =>
(connection: Connection): void => {
if (connection.request) {
if (connection.request && connection.obfs) {
callback(
{
version: connection.request.ver,
address: connection.request.addr,
userId: connection.request.userId,
},
connection.socket,
connection.obfs,
connection.event,
connection.resolve,
connection.reject
Expand Down
8 changes: 6 additions & 2 deletions src/helper/reply.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
import Writable from './writable'
import { HandlerResolve } from './handler'
import * as net from 'net'
import Obfs from '../obfs/obfs'

class Replay {
/**
Expand Down Expand Up @@ -95,11 +96,13 @@ class Replay {
/**
* Resolves the promise with the Relay object information
* @param socket - Clients socket
* @param resolve - returns information and socket to the client
* @param reject - returns errors to the client
* @param obfs - Obfuscator Object
* @param resolve - Returns information and socket to the client
* @param reject - Returns errors to the client
*/
promiseHandler(
socket: net.Socket,
obfs: Obfs,
resolve: (value: PromiseLike<HandlerResolve> | HandlerResolve) => void,
reject: (reason?: any) => void
) {
Expand All @@ -121,6 +124,7 @@ class Replay {
resolve({
address: this.addr,
socket: socket,
obfs,
rsv: this.rsv,
})
}
Expand Down
37 changes: 37 additions & 0 deletions src/obfs/http.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import Obfs from './obfs'

export class Http extends Obfs {
public path
name = 'HTTP'
constructor(path = '') {
super()
this.path = path
}
check(message: Buffer) {
const regex = new RegExp(
`^(?<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.
m.forEach((match, groupIndex) => {
console.log(match)
console.log(`Found match, group ${groupIndex}: ${match}`)
})
}
return true
}

DeObfuscate(message: Buffer): Buffer {
return message
}

obfuscate(message: Buffer): Buffer {
return message
}
}
2 changes: 2 additions & 0 deletions src/obfs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './http'
export * from './none'
17 changes: 17 additions & 0 deletions src/obfs/none.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import Obfs from './obfs'

export class None extends Obfs {
name = 'None'

check(message: Buffer): boolean {
return message[0] === 5 || message[0] === 4 || message[0] === 1
}

DeObfuscate(message: Buffer): Buffer {
return message
}

obfuscate(message: Buffer): Buffer {
return message
}
}
7 changes: 7 additions & 0 deletions src/obfs/obfs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
abstract class Obfs {
public abstract name: string
public abstract check(message: Buffer): boolean
public abstract DeObfuscate(message: Buffer): Buffer
public abstract obfuscate(message: Buffer): Buffer
}
export default Obfs
2 changes: 1 addition & 1 deletion src/server/handlers/associate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import Reply from '../../helper/reply'
* Default implementation of associate
* @returns void
*/
export const associate = handler((info, socket, event) => {
export const associate = handler((info, socket, obfs, event) => {
const relayPort = socket.address()
let reply: Reply
if ('port' in relayPort) {
Expand Down
2 changes: 1 addition & 1 deletion src/server/handlers/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import Event from '../../helper/event'
* Default implementation of bind
* @returns void
*/
export const bind = handler((info, socket, event) => {
export const bind = handler((info, socket, obfs, event) => {
let relayPort = info.address.port
if (relayPort === 0) {
relayPort = randPort()
Expand Down
2 changes: 1 addition & 1 deletion src/server/handlers/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import Address from '../../helper/address'
* Default implementation of connect
* @returns void
*/
export const connect = handler((info, socket) => {
export const connect = handler((info, socket, obfs) => {
let version: number
let reply: Reply
if (info.version === SOCKSVERSIONS.socks5) {
Expand Down
22 changes: 22 additions & 0 deletions src/server/state/socks5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,28 @@ import Request from '../../helper/request'
import * as socks4 from './socks4'
import { State } from '../../helper/state'
import Authenticator from '../auth/authenticator'
import { Http, None } from '../../obfs'

export class ObfsState extends State {
private obfsMethods = [new None(), new Http()]

parse(): void {
let message = this.context.cat()
for (const method of this.obfsMethods) {
if (method.check(message)) {
message = method.DeObfuscate(message)
this.context.obfs = method
break
}
}
}

reply(): void {
this.context.transitionTo(new IdentifierState(this.context))
this.context.parse()
this.context.reply()
}
}

/**
* The IdentifierState class identifies the version of the
Expand Down

0 comments on commit 63f89d6

Please sign in to comment.