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-server
Or pull a docker image from the Docker Hub registry.
docker pull amaindex/asyncio-socks-server
When 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]
[--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.
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,
"USERS": {
"username1": "password1",
"username2": "password2",
"username3": "password3"
}
}
asyncio_socks_server --config ${ENV}/config.json
In 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]
[--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.json
For 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 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. Therefore, when the client is working in a network that uses NAT, the DST.ADDR with all zeros should be used to avoid errors. But in case some clients did not follow this principle correctly, asyncio-socks-server relays all UDP datagrams it receives by default 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 --strict