Skip to content

Commit

Permalink
✨ feat: Client authentication and connect method completed
Browse files Browse the repository at this point in the history
  • Loading branch information
MoIzadloo committed Mar 10, 2023
1 parent a55882b commit 0561e8f
Show file tree
Hide file tree
Showing 13 changed files with 375 additions and 71 deletions.
149 changes: 144 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ 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/server/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/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
Expand All @@ -79,7 +79,7 @@ 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
Expand All @@ -92,7 +92,7 @@ both useAuth and useIdent together.
})

server.useAuth(
authMethods.userPass((user, pass) => {
serverAuthMethods.userPass((user, pass) => {
return user === username && pass === password
})
)
Expand Down Expand Up @@ -124,7 +124,7 @@ 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
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,145 @@ 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.once('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)
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.once('data', (data) => {
console.log(data)
})
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.once('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. user/pass authentication socks5

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

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

const socket = await connect(port, host, 5)
.useAuth(clientAuthMethods.userPass('user', 'pass'))
.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.once('data', (data) => {
console.log(data)
})
```
2. Identification socks4
```typescript
import { connect } from 'tsocks'

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

const socket = await connect(port, host, 4, 'your 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.once('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)
10 changes: 5 additions & 5 deletions src/client/auth/authenticator.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import Connection from '../../helper/connection'
import { Handlers } from '../../helper/handlers'
import Writable from '../../helper/writable'
import { none } from './methods/none'
import { none } from './methods'
import { SOCKSVERSIONS } from '../../helper/constants'
import { MethodSelectionState } from '../state/socks5'

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

Expand All @@ -29,7 +28,8 @@ class Authenticator {
}

/**
* Negotiates for authentication method with the user and authenticates users
* Negotiates for authentication method with the server and handle corresponding
* Authentication method
* @returns void
*/
public authenticate(): void {
Expand Down
5 changes: 4 additions & 1 deletion src/client/auth/methods/none.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AUTHMODES } from '../../../helper/constants'
import { AuthMethod } from '../../../helper/authMethod'
import { RequestState } from '../../state/socks5'

/**
* No authentication method
Expand All @@ -9,7 +10,9 @@ export const none = (): AuthMethod => {
return {
method: AUTHMODES.none,
authenticate: (connection) => {
connection.handlers.req.connect(connection)
connection.transitionTo(new RequestState(connection))
connection.parse()
connection.reply()
},
}
}
55 changes: 47 additions & 8 deletions src/client/auth/methods/userPass.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,59 @@
import { AUTHMODES } from '../../../helper/constants'
import { AUTHMODES, SOCKSVERSIONS } from '../../../helper/constants'
import { AuthMethod } from '../../../helper/authMethod'
import Connection from '../../../helper/connection'
import Writable from '../../../helper/writable'
import { State } from '../../../helper/state'
import { RequestState } from '../../state/socks5'

/**
* Extract user/pass from user authentication request and,
* execute the handler function
* @param handler - Check the authorization of user/pass
* Get the username and password from the user and,
* Handle the authentication procedure
* @param user - Username
* @param pass - Password
* @returns AuthMethod
*/
export const userPass = (
handler: (user: string, pass: string) => boolean
): AuthMethod => {
export const userPass = (user: string, pass: string): AuthMethod => {
return {
method: AUTHMODES.userPass,
authenticate: (connection: Connection) => {
console.log('hey userPass')
const writeable = new Writable()
writeable.push(
0x01,
user.length,
Buffer.from(user),
pass.length,
Buffer.from(pass)
)
connection.write(writeable)
connection.transitionTo(new UserPassReqState(connection))
},
}
}

class UserPassReqState extends State {
/**
* Authentication result
*/
private status?: number

/**
* Parse authentication result
*/
parse(): void {
this.context.read(1)
this.status = this.context.read(1).readInt8()
}

/**
* Proceed to sending (connect | bind | associate) request
*/
reply(): void {
if (this.status !== 0x00) {
this.context.close()
} else {
this.context.transitionTo(new RequestState(this.context))
this.context.parse()
this.context.reply()
}
}
}
Loading

0 comments on commit 0561e8f

Please sign in to comment.