A SOCKS proxy server implemented with the powerful python cooperative concurrency framework asyncio.
- Supports both TCP and UDP with the implementation of SOCKS5 protocol
- Supports username/password authentication
- Provides optional strict mode that follows RFC1928 and RFC1929 without compromise
- Driven by the python standard library, no third-party dependencies
Install with pip if Python version 3.8.0 or higher is available.
pip install asyncio-socks-serverOr pull a docker image from the Docker Hub registry.
docker pull amaindex/asyncio-socks-serverWhen installed with pip, you can invoke asyncio-socks-server from the command-line:
asyncio_socks_server [-h] [-v]
[-H HOST] [-P PORT] [-A METHOD]
[--access-log] [--debug] [--strict]
[--bind-addr BIND_ADDR]
[--env-prefix ENV_PREFIX]
[--config PATH]where:
asyncio_socks_server: You could use python -m asyncio_socks_server in development.-h,--help: Show a help message and exit.-v,--version: Show program's version number and exit.-H HOST,--host HOST: Host address to listen (default 0.0.0.0).-P PORT,--port PORT: Port to listen (default 1080).-A METHOD,--auth METHOD: Authentication method (default 0). Possible values: 0 (no auth), 2 (username/password auth)--access-log: Display access log.--debug: Work in debug mode.--strict: Work in strict compliance with RFC1928 and RFC1929.--bind-addr BIND_ADDR: Value of BIND.ADDR field in the reply (default 0.0.0.0). It is not necessary for most clients.
If the value of METHOD is 2, that is, when the username/password authentication
is specified, you need to provide a config file containing the usernames and passwords
in json format with the --config option.
You can also list other options in the config file instead of the command:
config.json:
{
"LISTEN_HOST": "0.0.0.0",
"LISTEN_PORT": 1080,
"AUTH_METHOD": 2,
"ACCESS_LOG": true,
"DEBUG": true,
"STRICT": true,
"BIND_HOST": "0.0.0.0",
"USERS": {
"username1": "password1",
"username2": "password2",
"username3": "password3"
}
}
asyncio_socks_server --config ${ENV}/config.jsonIn addition, any environment variable named starting with AIOSS_ will also be applied
to the option.
The prefix can be changed by specifying the --env-prefix option,for example:
export MY_LISTEN_HOST=127.0.0.1
export MY_LISTEN_PORT=9999
asyncio_socks_server --env-prefix MY_NOTE: The loading order of the options is: config file, environment variables, command options. The latter will overwrite the former if options are given in multiple ways.
Alternatively, if you use the docker image, you can launch the asyncio-socks-server with the following command:
docker run amaindex/asyncio-socks-server [-h] [-v]
[-H HOST] [-P PORT] [-A METHOD]
[--access-log] [--debug] [--strict]
[--bind-addr BIND_ADDR]
[--env-prefix ENV_PREFIX]
[--config PATH]The network mode host is recommended since asyncio-socks-server uses multiple ports dynamically.
If you also want to provide a config file, it should be mounted manually.
docker run \
--rm \
--net=host \
-v /host/path/config.json:/config.json \
amaindex/asyncio-socks-server \
--config /config.jsonFor various reasons, asyncio-socks-server has made some compromises on the Implementation details of the protocols. Therefore, in the following scenes, asyncio-socks-server’s behavior will be divergent from that described in RFC1928 and RFC1929.
In the SOCKS5 negotiation, a UDP ASSOCIATE request formed as follows is used to establish an association within the UDP relay process to handle UDP datagrams:
+----+-----+-------+------+----------+----------+
|VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
Normally, the DST.ADDR and DST.PORT fields should contain the address and port that the client expects to use to send UDP datagrams on for the association, or use a port number and address of all zeros if the client does not possess this information.
However, some non-standard clients did not follow this principle correctly, but used a private address as DST.ADDR and DST.PORT due to the NAT. To deal with this, in non-strict mode, asyncio-socks-server relays all UDP datagrams it receives instead of using DST.ADDR and DST.PORT to limit the access.
Once the client selects the username/password authentication during negotiation, it will conduct a sub-negotiation with the server. This sub-negotiation begins with the client producing a request:
+----+------+----------+------+----------+
|VER | ULEN | UNAME | PLEN | PASSWD |
+----+------+----------+------+----------+
| 1 | 1 | 1 to 255 | 1 | 1 to 255 |
+----+------+----------+------+----------+
The VER field contains the current version of the sub-negotiation, which is X'01' but often considered as X'05' since it's a bit counter-intuitive. So asyncio-socks-server allows requests with VER X'05' in non-strict mode.
To disable the compromise described above, you can specify the --strict option:
asyncio_socks_server --strictAt the end of the negotiation, the server returns a reply formed as follows:
+----+-----+-------+------+----------+----------+
|VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1 | 1 | X'00' | 1 | Variable | 2 |
+----+-----+-------+------+----------+----------+
For multi-homed servers, the BND.ADDR may be different from the address that the
client uses to reach the server. However, this field is not necessary for most clients, because
they assume that the relay server is on the same host as the proxy server
(it's right, for asyncio-socks-server) so they can just use the address of the latter.
But if you want the clients running in "strict mode" to work properly,
you need to specify this field explicitly with --bind-addr option:
asyncio_socks_server --host 0.0.0.0 --bind-addr www.bindaddress.com