Skip to content

Commit

Permalink
✨ feat: Add client connect
Browse files Browse the repository at this point in the history
  • Loading branch information
MoIzadloo committed Mar 14, 2023
2 parents de8b4c9 + 758c600 commit ceb0f29
Show file tree
Hide file tree
Showing 40 changed files with 1,125 additions and 261 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
*.config.ts
*.test.js
*.test.ts
coverage
dist
2 changes: 1 addition & 1 deletion .releaserc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
releaseRules: {
major: [':boom:'],
minor: [':sparkles:'],
patch: [':bug:', ':ambulance:', ':lock:', ':books:'],
patch: [':bug:', ':ambulance:', ':lock:', ':memo:'],
},
},
],
Expand Down
168 changes: 155 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ const server = createServer({

// For development purposes
server.on('data', (data) => {
// Log incoming requests
// Log incoming data
console.log(data)
})

Expand All @@ -67,10 +67,10 @@ server.listen(port, host)
There are two different authentication methods independent
of which version of the socks protocol you are working with.
in case you are using V5, you can use the useAuth hook and use
one of the available methods from the [methods](./src/auth/methods)
one of the available methods from the [methods'](src/server/auth/methods)
directory, or if you want to create your own implementations or
custom methods, you have to define a function that implements the
AuthMethod interface, like [userPass.ts](./src/auth/methods/userPass.ts) and pass it as an argument to useAuth hook, in case you are using V4 you have to use useIdent hook which receives a callback function which gives you userId, a string in which
AuthMethod interface, like [userPass.ts](src/server/auth/methods/userPass.ts) and pass it as an argument to useAuth hook, in case you are using V4 you have to use useIdent hook which receives a callback function which gives you userId, a string in which
should be processed and the result, which is going to be
a boolean should be return from the callback function, in case you want to support
both versions and also authenticate users on both you can use
Expand All @@ -79,20 +79,20 @@ both useAuth and useIdent together.
1. Server socks5

```typescript
import { createServer, authMethods } from 'tsocks'
import { createServer, serverAuthMethods } from 'tsocks'

const host = '127.0.0.1'
const port = 1080
const username = 'tsocks'
const password = 'tsocks'
const username = 'user'
const password = 'pass'

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

server.useAuth(
authMethods.userPass((user, pass) => {
serverAuthMethods.userPass((user, pass) => {
return user === username && pass === password
})
)
Expand All @@ -107,7 +107,7 @@ both useAuth and useIdent together.

const host = '127.0.0.1'
const port = 1080
const id = 'tsocks'
const id = 'user:pass'

const server = createServer({
socks4: true,
Expand All @@ -124,13 +124,13 @@ both useAuth and useIdent together.
3. Server socks4 and socks 5

```typescript
import { createServer, authMethods } from 'tsocks'
import { createServer, serverAuthMethods } from 'tsocks'

const host = '127.0.0.1'
const port = 1080
const id = 'tsocks'
const username = 'tsocks'
const password = 'tsocks'
const id = 'user:pass'
const username = 'user'
const password = 'pass'

const server = createServer({
socks4: true,
Expand All @@ -139,7 +139,7 @@ both useAuth and useIdent together.

// useAuth for socks5 requests
server.useAuth(
authMethods.userPass((user, pass) => {
serverAuthMethods.userPass((user, pass) => {
// You can use an array or database query instead
return user === username && pass === password
})
Expand Down Expand Up @@ -185,6 +185,148 @@ server.useReq('connect', (info, socket) => {
server.listen(port, host)
```

### Proxy Client

No matter which version of the proxy server you want to interact with V4 or V5
you can easily implement it in a matter of seconds.

1. Single connection

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

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

const socket = await connect(port, host, 5).connect(httpPort, 'google.com')

socket.write(
Buffer.from(
'GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n'
)
)

socket.on('data', (data) => {
console.log(data)
})
```

2. Multiple connections

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

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

const client = connect(port, host, 5)

const socket1 = await client.connect(httpPort, 'google.com')

socket1.write(
Buffer.from(
'GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n'
)
)

socket1.on('data', (data) => {
console.log(data)
})

const socket2 = await client.connect(httpPort, 'google.com')

socket2.write(
Buffer.from(
'GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n'
)
)

socket2.on('data', (data) => {
console.log(data)
})
```

### Proxy client with authentication

There are two different authentication methods independent
of which version of the socks protocol you are working with.
in case you are using V5, you can use the useAuth hook and use
one of the available methods from the [methods'](src/client/auth/methods)
directory, or if you want to create your own implementations or
custom methods, you have to define a function that implements the
AuthMethod interface, like [userPass.ts](src/client/auth/methods/userPass.ts) and pass it as an argument to useAuth hook,
in case you are using V4 you have to add your identification token
as an argument (userId) to the request handler (connect | bind | associate).

1. Authentication socks5

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

const host = '127.0.0.1'
const port = 1080
const username = 'user'
const password = 'pass'
const httpPort = 80

const socket = await connect(port, host, 5)
.useAuth(clientAuthMethods.userPass(username, password))
.connect(httpPort, 'google.com')

socket.write(
Buffer.from(
'GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n'
)
)

socket.on('data', (data) => {
console.log(data)
})
```

2. Identification socks4

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

const host = '127.0.0.1'
const port = 1080
const userId = 'user:pass'
const httpPort = 80

const socket = await connect(port, host, 4, userId).connect(
httpPort,
'142.251.1.101'
)

socket.write(
Buffer.from(
'GET / HTTP/1.1\r\n' +
'Host: www.google.com:80\r\n' +
'Connection: close\r\n' +
'\r\n'
)
)

socket.on('data', (data) => {
console.log(data)
})
```

## References

- [RFC - SOCKS Protocol Version 5](https://www.rfc-editor.org/rfc/rfc1928)
Expand Down
2 changes: 1 addition & 1 deletion TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
### In Progress

- [ ] Implementation of server (Bind | Associate)
- [ ] Implementation of client (Connect)

### Done ✓

- [x] Implementation of server Connect
- [x] Implementation of client (Connect)
6 changes: 0 additions & 6 deletions src/auth/methods/method.ts

This file was deleted.

17 changes: 0 additions & 17 deletions src/auth/methods/none.ts

This file was deleted.

46 changes: 46 additions & 0 deletions src/client/auth/authenticator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import Connection from '../../helper/connection'
import { Handlers } from '../../helper/handlers'
import Writable from '../../helper/writable'
import { none } from './methods'
import { SOCKSVERSIONS } from '../../helper/constants'
import { MethodSelectionState } from '../state/socks5'

/**
* The Authenticator Class is responsible for resolving the authentication process
*/
class Authenticator {
/**
* Client preferred methods
*/
private readonly availableMethods: Handlers['auth']

/**
* Corresponding connection
*/
private readonly connection: Connection

constructor(connection: Connection) {
this.connection = connection
if (connection.handlers.auth.length <= 0) {
connection.handlers.auth.push(none())
}
this.availableMethods = connection.handlers.auth
}

/**
* Negotiates for authentication method with the server and handle corresponding
* Authentication method
* @returns void
*/
public authenticate(): void {
const writable = new Writable()
writable.push(SOCKSVERSIONS.socks5, this.availableMethods.length)
for (const method of this.availableMethods) {
writable.push(method.method)
}
this.connection.transitionTo(new MethodSelectionState(this.connection))
this.connection.write(writable)
}
}

export default Authenticator
File renamed without changes.
18 changes: 18 additions & 0 deletions src/client/auth/methods/none.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AUTHMODES } from '../../../helper/constants'
import { AuthMethod } from '../../../helper/authMethod'
import { RequestState } from '../../state/socks5'

/**
* No authentication method
* @returns AuthMethod
*/
export const none = (): AuthMethod => {
return {
method: AUTHMODES.none,
authenticate: (connection) => {
connection.transitionTo(new RequestState(connection))
connection.parse()
connection.reply()
},
}
}

0 comments on commit ceb0f29

Please sign in to comment.