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 19, 2023
2 parents ac49819 + 87c023e commit 42c5a76
Show file tree
Hide file tree
Showing 27 changed files with 705 additions and 77 deletions.
50 changes: 50 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,53 @@
# [v3.3.0](https://github.com/MoIzadloo/tsocks/compare/v3.2.3...v3.3.0) (2023-04-19)

## ✨ New Features
- [`63f89d6`](https://github.com/MoIzadloo/tsocks/commit/63f89d6) feat: Add obfuscation support
- [`238d739`](https://github.com/MoIzadloo/tsocks/commit/238d739) feat: Http Obfuscation handshake in progress
- [`5a58b88`](https://github.com/MoIzadloo/tsocks/commit/5a58b88) feat: Http Obfuscation add basic parts
- [`87e858c`](https://github.com/MoIzadloo/tsocks/commit/87e858c) feat: Http Obfuscation post simple complete
- [`6660b34`](https://github.com/MoIzadloo/tsocks/commit/6660b34) feat: Add server useObfs hook
- [`c2526d2`](https://github.com/MoIzadloo/tsocks/commit/c2526d2) feat: Add obfuscation support

## 🐛 Bug Fixes
- [`88fcdde`](https://github.com/MoIzadloo/tsocks/commit/88fcdde) fix: SOCKS4 reply version fixed
- [`a27e5a9`](https://github.com/MoIzadloo/tsocks/commit/a27e5a9) fix: Fix a few bugs

# [v3.2.3](https://github.com/MoIzadloo/tsocks/compare/v3.2.2...v3.2.3) (2023-04-04)

# [v3.2.2](https://github.com/MoIzadloo/tsocks/compare/v3.2.1...v3.2.2) (2023-04-04)

# [v3.2.1](https://github.com/MoIzadloo/tsocks/compare/v3.2.0...v3.2.1) (2023-04-04)

# [v3.2.0](https://github.com/MoIzadloo/tsocks/compare/v3.1.0...v3.2.0) (2023-04-04)

## ✨ New Features
- [`c0e3c1d`](https://github.com/MoIzadloo/tsocks/commit/c0e3c1d) feat: Add bind
- [`4e7a101`](https://github.com/MoIzadloo/tsocks/commit/4e7a101) feat: Add bind

# [v3.1.0](https://github.com/MoIzadloo/tsocks/compare/v3.0.0...v3.1.0) (2023-03-26)

## ✨ New Features
- [`64443ec`](https://github.com/MoIzadloo/tsocks/commit/64443ec) feat: Add server associate implementation
- [`250aa30`](https://github.com/MoIzadloo/tsocks/commit/250aa30) feat: Client associate completed
- [`855a07a`](https://github.com/MoIzadloo/tsocks/commit/855a07a) feat: Add associate support

# [v3.0.0](https://github.com/MoIzadloo/tsocks/compare/v2.0.0...v3.0.0) (2023-03-14)

## ✨ New Features
- [`034fb86`](https://github.com/MoIzadloo/tsocks/commit/034fb86) feat: clinet (connect) in progress
- [`99a5def`](https://github.com/MoIzadloo/tsocks/commit/99a5def) feat: Client connect support added
- [`a55882b`](https://github.com/MoIzadloo/tsocks/commit/a55882b) feat: Client add userId identification
- [`0561e8f`](https://github.com/MoIzadloo/tsocks/commit/0561e8f) feat: Client authentication and connect method completed
- [`ceb0f29`](https://github.com/MoIzadloo/tsocks/commit/ceb0f29) feat: Add client connect

## 🐛 Bug Fixes
- [`b366cfb`](https://github.com/MoIzadloo/tsocks/commit/b366cfb) fix: Client rejection on auth
- [`d6f53e7`](https://github.com/MoIzadloo/tsocks/commit/d6f53e7) fix: Socks4 address type restriction
- [`758c600`](https://github.com/MoIzadloo/tsocks/commit/758c600) fix: Delete wrong address test

## 💥 Breaking Changes
- [`40165e9`](https://github.com/MoIzadloo/tsocks/commit/40165e9) Add client connect

# [v2.0.0](https://github.com/MoIzadloo/tsocks/compare/v1.0.0...v2.0.0) (2023-02-24)

## 💥 Breaking Changes
Expand Down
73 changes: 68 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

# TSocks

TSocks is an implementation of SOCKS protocol.
TSocks is an implementation of the SOCKS protocol.
It's designed to be very flexible and adhesive

- **Modular** to be suited to your needs
Expand All @@ -13,8 +13,8 @@ It's designed to be very flexible and adhesive
# Pros

1. TSocks is powered with hooks and events which give you
the ability to implement different parts of protocol yourself
2. We used an API similar to the [net](https://nodejs.org/api/net.html) module which you are propably familiar with
the ability to implement different parts of the protocol yourself
2. We used an API similar to the [net](https://nodejs.org/api/net.html) module which you are probably familiar with
3. You have access to incoming connections socket through available hooks

## Install
Expand Down Expand Up @@ -176,7 +176,7 @@ server.useReq('associate', (info, socket) => {
const port = info.address.port // Port number
const type = info.address.type // ipv4 | ipv6 | domain
const version = info.version // SOCKS version
// You can implement the rest how ever you want
// You can implement the rest however you want
// Just remember the response should be decided by version
})

Expand Down Expand Up @@ -248,13 +248,35 @@ server.useReq('connect', (info, socket) => {
const port = info.address.port // Port number
const type = info.address.type // ipv4 | ipv6 | domain
const version = info.version // SOCKS version
// You can implement the rest how ever you want
// You can implement the rest however you want
// Just remember the response should be decided by version
})

server.listen(port, host)
```

### SOCKS server obfuscation

You can implement your obfuscation methods by extending your obfuscation class from the [ObfsMethod'](src/obfs/obfs.ts) class
and creating a builder function for your class [ObfsBuilder'](src/obfs/obfs.ts) or use the available obfuscation methods
by, passing their builder function to the useObfs hook as below:

```typescript
import { createServer, obfsMethods } from 'tsocks'

const host = '127.0.0.1'
const port = 1080

const server = createServer({
socks4: true,
socks5: true,
})

server.useObfs(obfsMethods.http())

server.listen(port, host)
```

### Proxy Client

No matter which version of the proxy server you want to interact with V4 or V5
Expand Down Expand Up @@ -529,6 +551,47 @@ try {
}
```

### SOCKS client obfuscation

You can implement your obfuscation methods by extending your obfuscation class from the [ObfsMethod'](src/obfs/obfs.ts) class
and creating a builder function for your class [ObfsBuilder'](src/obfs/obfs.ts) or use the available obfuscation methods
by, passing their builder function to the useObfs hook as below:

```typescript
import { connect, obfsMethods } from 'tsocks'

const host = '127.0.0.1'
const port = 1080
const httpPort = 80

try {
const info = await connect(port, host, 5)
.useObfs(obfsMethods.http())
.connect(httpPort, 'google.com')

// remember to use the obfuscate method before sending your data to the
// SOCKS server and deObfuscate methods when you receive any data from the
// SOCKS server when you are using an obfuscation method
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.on('data', (data) => {
console.log(info.obfs.deObfuscate(data).toString())
info.socket.end()
})
} catch (err) {
console.log(err)
}
```

## References

- [RFC - SOCKS Protocol Version 5](https://www.rfc-editor.org/rfc/rfc1928)
Expand Down
2 changes: 2 additions & 0 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- [ ] Complete documentation
- [ ] Complete test coverage
- [ ] Add encryption and compression to the http obfuscation method

### In Progress

Expand All @@ -16,3 +17,4 @@
- [x] Implementation of client Associate
- [x] Implementation of server Bind
- [x] Implementation of client Bind
- [x] Add obfuscation support
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tsocks",
"version": "2.0.0",
"version": "3.3.0",
"private": false,
"browser": false,
"author": "Mostafa Izadloo <moizadloo@gmail.com>",
Expand Down
86 changes: 62 additions & 24 deletions src/client/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import {
Handler,
HandlerResolve,
} from '../helper/handler'
import Authenticator from './auth/authenticator'
import Address from '../helper/address'
import { AuthMethod } from '../helper/authMethod'
import { COMMANDS } from '../helper/constants'
import Request from '../helper/request'
import { ObfsBuilder } from '../obfs/obfs'
import { none } from '../obfs'
import Authenticator from './auth/authenticator'

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

constructor(port: number, host: string, version: 4 | 5, userId?: string) {
public obfs: ObfsBuilder

constructor(
port: number,
host: string,
version: 4 | 5,
obfs: ObfsBuilder,
userId?: string
) {
this.host = host
this.port = port
this.userId = userId
Expand All @@ -59,6 +69,7 @@ export class Client {
associate: handlers.associate,
bind: handlers.bind,
})
this.obfs = obfs
this.event = new Event<EventTypes>()
}

Expand Down Expand Up @@ -99,7 +110,7 @@ export class Client {
connection.request = new Request(ver, cmd, address, 0, id)
connection.resolve = resolve
connection.reject = reject

connection.obfs = this.obfs(connection)
connection.event.subscribeOnce('error', (err) => {
reject(err.message)
})
Expand All @@ -124,15 +135,20 @@ export class Client {
version,
userId
)
if (connection?.request?.ver === 5) {
const authenticator = new Authenticator(connection)
authenticator.authenticate()
} else if (connection?.request?.ver === 4) {
if (connection?.request?.addr?.type === 'domain') {
reject('The Address type is not supported')
}
connection.handlers.req.bind(connection)
}
connection.socket.once('connect', () => {
connection.obfs.handshake(() => {
if (connection?.request?.ver === 5) {
const authenticator = new Authenticator(connection)
authenticator.authenticate()
}
if (connection?.request?.ver === 4) {
if (connection?.request?.addr?.type === 'domain') {
reject('The Address type is not supported')
}
connection.handlers.req.bind(connection)
}
})
})
})
}

Expand All @@ -154,8 +170,14 @@ export class Client {
reject,
5
)
const authenticator = new Authenticator(connection)
authenticator.authenticate()
connection.socket.once('connect', () => {
connection.obfs.handshake(() => {
if (connection?.request?.ver === 5) {
const authenticator = new Authenticator(connection)
authenticator.authenticate()
}
})
})
} else {
reject("SOCKS V4 doesn't support associate command")
}
Expand All @@ -181,15 +203,20 @@ export class Client {
version,
userId
)
if (connection?.request?.ver === 5) {
const authenticator = new Authenticator(connection)
authenticator.authenticate()
} else if (connection?.request?.ver === 4) {
if (connection?.request?.addr?.type === 'domain') {
reject('The Address type is not supported')
}
connection.handlers.req.connect(connection)
}
connection.socket.once('connect', () => {
connection.obfs.handshake(() => {
if (connection?.request?.ver === 5) {
const authenticator = new Authenticator(connection)
authenticator.authenticate()
}
if (connection?.request?.ver === 4) {
if (connection?.request?.addr?.type === 'domain') {
reject('The Address type is not supported')
}
connection.handlers.req.connect(connection)
}
})
})
})
}

Expand All @@ -213,13 +240,24 @@ export class Client {
this.handlers.req[cmd] = reqHandler(handler)
return this
}

/**
* Get the handler function, and update this.handlers.obfs
* @param handler - Emitted when new request appears
* @returns Server
*/
public useObfs(handler: ObfsBuilder): Client {
this.obfs = handler
return this
}
}

/**
* Open new connection and connect to server
* @param port - Server port
* @param host - Server address
* @param version - Server protocol version
* @param obfs - Obfuscation Method
* @param userId - userId for identification in v4
* @returns void
*/
Expand All @@ -229,5 +267,5 @@ export const connect = (
version: 4 | 5,
userId?: string
) => {
return new Client(port, host, version, userId)
return new Client(port, host, version, none(), userId)
}
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')
})
}
)
10 changes: 5 additions & 5 deletions src/client/handlers/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ 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,
info.address,
0,
info.userId
)
socket.write(request.toBuffer())
socket.write(obfs.obfuscate(request.toBuffer()))
socket.on('data', (data) => {
const reply = Reply.from(data)
if (resolve && reject) {
reply.promiseHandler(socket, resolve, reject)
const reply = Reply.from(obfs.deObfuscate(data))
if (resolve && reject && obfs) {
reply.promiseHandler(socket, obfs, resolve, reject)
}
socket.removeAllListeners('data')
})
Expand Down

0 comments on commit 42c5a76

Please sign in to comment.