0.1.0
This commit is contained in:
24
.gitignore
vendored
Normal file
24
.gitignore
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
*.pyw
|
||||
*.pyz
|
||||
*.pywz
|
||||
*.pyzz
|
||||
*.pywzz
|
||||
*.pyzzz
|
||||
*.pywzzz
|
||||
*.pyzzzz
|
||||
*.pywzzzz
|
||||
|
||||
.env
|
||||
|
||||
.venv/
|
||||
|
||||
.DS_Store
|
||||
|
||||
.idea/
|
||||
|
||||
*.log
|
||||
|
||||
32
README.md
Normal file
32
README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Proxynet
|
||||
|
||||
A decentralized proxy over Reticulum to route internet traffic through Reticulum.
|
||||
|
||||
## Installation
|
||||
```bash
|
||||
poetry install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Server Mode
|
||||
```bash
|
||||
# Basic server startup
|
||||
poetry run proxynet --server --identity my_server
|
||||
|
||||
# Verbose server startup
|
||||
poetry run proxynet --server --identity my_server -v
|
||||
```
|
||||
|
||||
### Client Mode
|
||||
```bash
|
||||
# Start SOCKS5 proxy on localhost:1080 with a preferred server
|
||||
poetry run proxynet --client --port 1080 --preferred <server_hash>
|
||||
|
||||
# Start proxy using any discovered servers (decentralized mode)
|
||||
poetry run proxynet --client --port 1080
|
||||
```
|
||||
|
||||
## Privacy
|
||||
Traffic over Reticulum is end-to-end encrypted and anonymized between the client and the server using Reticulum's native Link encryption (X25519 and AES-256) and anonymity features. For internet traffic, use HTTPS/TLS to ensure the server operator cannot see your data contents. If operators are using wireshark, they will still be able to see what websites/IP you are connecting to.
|
||||
|
||||
239
poetry.lock
generated
Normal file
239
poetry.lock
generated
Normal file
@@ -0,0 +1,239 @@
|
||||
# This file is automatically @generated by Poetry 2.2.1 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "cffi"
|
||||
version = "2.0.0"
|
||||
description = "Foreign Function Interface for Python calling C code."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
groups = ["main"]
|
||||
markers = "platform_python_implementation != \"PyPy\""
|
||||
files = [
|
||||
{file = "cffi-2.0.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:0cf2d91ecc3fcc0625c2c530fe004f82c110405f101548512cce44322fa8ac44"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f73b96c41e3b2adedc34a7356e64c8eb96e03a3782b535e043a986276ce12a49"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5eda85d6d1879e692d546a078b44251cdd08dd1cfb98dfb77b670c97cee49ea0"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9332088d75dc3241c702d852d4671613136d90fa6881da7d770a483fd05248b4"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-win32.whl", hash = "sha256:1f72fb8906754ac8a2cc3f9f5aaa298070652a0ffae577e0ea9bd480dc3c931a"},
|
||||
{file = "cffi-2.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:b18a3ed7d5b3bd8d9ef7a8cb226502c6bf8308df1525e1cc676c3680e7176739"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:b4c854ef3adc177950a8dfc81a86f5115d2abd545751a304c5bcf2c2c7283cfe"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2de9a304e27f7596cd03d16f1b7c72219bd944e99cc52b84d0145aefb07cbd3c"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:6824f87845e3396029f3820c206e459ccc91760e8fa24422f8b0c3d1731cbec5"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:9de40a7b0323d889cf8d23d1ef214f565ab154443c42737dfe52ff82cf857664"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-win32.whl", hash = "sha256:c649e3a33450ec82378822b3dad03cc228b8f5963c0c12fc3b1e0ab940f768a5"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:66f011380d0e49ed280c789fbd08ff0d40968ee7b665575489afa95c98196ab5"},
|
||||
{file = "cffi-2.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:c6638687455baf640e37344fe26d37c404db8b80d037c3d29f58fe8d1c3b194d"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6d02d6655b0e54f54c4ef0b94eb6be0607b70853c45ce98bd278dc7de718be5d"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8eca2a813c1cb7ad4fb74d368c2ffbbb4789d377ee5bb8df98373c2cc0dee76c"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:1e3a615586f05fc4065a8b22b8152f0c1b00cdbc60596d187c2a74f9e3036e4e"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:81afed14892743bbe14dacb9e36d9e0e504cd204e0b165062c488942b9718037"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-win32.whl", hash = "sha256:da902562c3e9c550df360bfa53c035b2f241fed6d9aef119048073680ace4a18"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:da68248800ad6320861f129cd9c1bf96ca849a2771a59e0344e88681905916f5"},
|
||||
{file = "cffi-2.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:4671d9dd5ec934cb9a73e7ee9676f9362aba54f7f34910956b84d727b0d73fb6"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:00bdf7acc5f795150faa6957054fbbca2439db2f775ce831222b66f192f03beb"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:45d5e886156860dc35862657e1494b9bae8dfa63bf56796f2fb56e1679fc0bca"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:f93fd8e5c8c0a4aa1f424d6173f14a892044054871c771f8566e4008eaa359d2"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:dd4f05f54a52fb558f1ba9f528228066954fee3ebe629fc1660d874d040ae5a3"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-win32.whl", hash = "sha256:74a03b9698e198d47562765773b4a8309919089150a0bb17d829ad7b44b60d27"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:19f705ada2530c1167abacb171925dd886168931e0a7b78f5bffcae5c6b5be75"},
|
||||
{file = "cffi-2.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:256f80b80ca3853f90c21b23ee78cd008713787b1b1e93eae9f3d6a7134abd91"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25"},
|
||||
{file = "cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6"},
|
||||
{file = "cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:fe562eb1a64e67dd297ccc4f5addea2501664954f2692b69a76449ec7913ecbf"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:e6e73b9e02893c764e7e8d5bb5ce277f1a009cd5243f8228f75f842bf937c534"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:cb527a79772e5ef98fb1d700678fe031e353e765d1ca2d409c92263c6d43e09f"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-win32.whl", hash = "sha256:2081580ebb843f759b9f617314a24ed5738c51d2aee65d31e02f6f7a2b97707a"},
|
||||
{file = "cffi-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:b882b3df248017dba09d6b16defe9b5c407fe32fc7c65a9c69798e6175601be9"},
|
||||
{file = "cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
pycparser = {version = "*", markers = "implementation_name != \"PyPy\""}
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "46.0.3"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
optional = false
|
||||
python-versions = "!=3.9.0,!=3.9.1,>=3.8"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "cryptography-46.0.3-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:109d4ddfadf17e8e7779c39f9b18111a09efb969a301a31e987416a0191ed93a"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:09859af8466b69bc3c27bdf4f5d84a665e0f7ab5088412e9e2ec49758eca5cbc"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:01ca9ff2885f3acc98c29f1860552e37f6d7c7d013d7334ff2a9de43a449315d"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6eae65d4c3d33da080cff9c4ab1f711b15c1d9760809dad6ea763f3812d254cb"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5bf0ed4490068a2e72ac03d786693adeb909981cc596425d09032d372bcc849"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:5ecfccd2329e37e9b7112a888e76d9feca2347f12f37918facbb893d7bb88ee8"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a2c0cd47381a3229c403062f764160d57d4d175e022c1df84e168c6251a22eec"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:549e234ff32571b1f4076ac269fcce7a808d3bf98b76c8dd560e42dbc66d7d91"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:c0a7bb1a68a5d3471880e264621346c48665b3bf1c3759d682fc0864c540bd9e"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:10b01676fc208c3e6feeb25a8b83d81767e8059e1fe86e1dc62d10a3018fa926"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:0abf1ffd6e57c67e92af68330d05760b7b7efb243aab8377e583284dbab72c71"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a04bee9ab6a4da801eb9b51f1b708a1b5b5c9eb48c03f74198464c66f0d344ac"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-win32.whl", hash = "sha256:f260d0d41e9b4da1ed1e0f1ce571f97fe370b152ab18778e9e8f67d6af432018"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-win_amd64.whl", hash = "sha256:a9a3008438615669153eb86b26b61e09993921ebdd75385ddd748702c5adfddb"},
|
||||
{file = "cryptography-46.0.3-cp311-abi3-win_arm64.whl", hash = "sha256:5d7f93296ee28f68447397bf5198428c9aeeab45705a55d53a6343455dcb2c3c"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:00a5e7e87938e5ff9ff5447ab086a5706a957137e6e433841e9d24f38a065217"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c8daeb2d2174beb4575b77482320303f3d39b8e81153da4f0fb08eb5fe86a6c5"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39b6755623145ad5eff1dab323f4eae2a32a77a7abef2c5089a04a3d04366715"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:db391fa7c66df6762ee3f00c95a89e6d428f4d60e7abc8328f4fe155b5ac6e54"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:78a97cf6a8839a48c49271cdcbd5cf37ca2c1d6b7fdd86cc864f302b5e9bf459"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:dfb781ff7eaa91a6f7fd41776ec37c5853c795d3b358d4896fdbb5df168af422"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:6f61efb26e76c45c4a227835ddeae96d83624fb0d29eb5df5b96e14ed1a0afb7"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:23b1a8f26e43f47ceb6d6a43115f33a5a37d57df4ea0ca295b780ae8546e8044"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:b419ae593c86b87014b9be7396b385491ad7f320bde96826d0dd174459e54665"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:50fc3343ac490c6b08c0cf0d704e881d0d660be923fd3076db3e932007e726e3"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:22d7e97932f511d6b0b04f2bfd818d73dcd5928db509460aaf48384778eb6d20"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:d55f3dffadd674514ad19451161118fd010988540cee43d8bc20675e775925de"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-win32.whl", hash = "sha256:8a6e050cb6164d3f830453754094c086ff2d0b2f3a897a1d9820f6139a1f0914"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:760f83faa07f8b64e9c33fc963d790a2edb24efb479e3520c14a45741cd9b2db"},
|
||||
{file = "cryptography-46.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:516ea134e703e9fe26bcd1277a4b59ad30586ea90c365a87781d7887a646fe21"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:cb3d760a6117f621261d662bccc8ef5bc32ca673e037c83fbe565324f5c46936"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4b7387121ac7d15e550f5cb4a43aef2559ed759c35df7336c402bb8275ac9683"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:15ab9b093e8f09daab0f2159bb7e47532596075139dd74365da52ecc9cb46c5d"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:46acf53b40ea38f9c6c229599a4a13f0d46a6c3fa9ef19fc1a124d62e338dfa0"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10ca84c4668d066a9878890047f03546f3ae0a6b8b39b697457b7757aaf18dbc"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:36e627112085bb3b81b19fed209c05ce2a52ee8b15d161b7c643a7d5a88491f3"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:1000713389b75c449a6e979ffc7dcc8ac90b437048766cef052d4d30b8220971"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:b02cf04496f6576afffef5ddd04a0cb7d49cf6be16a9059d793a30b035f6b6ac"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:71e842ec9bc7abf543b47cf86b9a743baa95f4677d22baa4c7d5c69e49e9bc04"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:402b58fc32614f00980b66d6e56a5b4118e6cb362ae8f3fda141ba4689bd4506"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ef639cb3372f69ec44915fafcd6698b6cc78fbe0c2ea41be867f6ed612811963"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b51b8ca4f1c6453d8829e1eb7299499ca7f313900dd4d89a24b8b87c0a780d4"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-win32.whl", hash = "sha256:6276eb85ef938dc035d59b87c8a7dc559a232f954962520137529d77b18ff1df"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-win_amd64.whl", hash = "sha256:416260257577718c05135c55958b674000baef9a1c7d9e8f306ec60d71db850f"},
|
||||
{file = "cryptography-46.0.3-cp38-abi3-win_arm64.whl", hash = "sha256:d89c3468de4cdc4f08a57e214384d0471911a3830fcdaf7a8cc587e42a866372"},
|
||||
{file = "cryptography-46.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a23582810fedb8c0bc47524558fb6c56aac3fc252cb306072fd2815da2a47c32"},
|
||||
{file = "cryptography-46.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e7aec276d68421f9574040c26e2a7c3771060bc0cff408bae1dcb19d3ab1e63c"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7ce938a99998ed3c8aa7e7272dca1a610401ede816d36d0693907d863b10d9ea"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:191bb60a7be5e6f54e30ba16fdfae78ad3a342a0599eb4193ba88e3f3d6e185b"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c70cc23f12726be8f8bc72e41d5065d77e4515efae3690326764ea1b07845cfb"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:9394673a9f4de09e28b5356e7fff97d778f8abad85c9d5ac4a4b7e25a0de7717"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:94cd0549accc38d1494e1f8de71eca837d0509d0d44bf11d158524b0e12cebf9"},
|
||||
{file = "cryptography-46.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:6b5063083824e5509fdba180721d55909ffacccc8adbec85268b48439423d78c"},
|
||||
{file = "cryptography-46.0.3.tar.gz", hash = "sha256:a8b17438104fed022ce745b362294d9ce35b4c2e45c1d958ad4a4b019285f4a1"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cffi = {version = ">=2.0.0", markers = "python_full_version >= \"3.9.0\" and platform_python_implementation != \"PyPy\""}
|
||||
|
||||
[package.extras]
|
||||
docs = ["sphinx (>=5.3.0)", "sphinx-inline-tabs", "sphinx-rtd-theme (>=3.0.0)"]
|
||||
docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"]
|
||||
nox = ["nox[uv] (>=2024.4.15)"]
|
||||
pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.14)", "ruff (>=0.11.11)"]
|
||||
sdist = ["build (>=1.0.0)"]
|
||||
ssh = ["bcrypt (>=3.1.5)"]
|
||||
test = ["certifi (>=2024)", "cryptography-vectors (==46.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
|
||||
test-randomorder = ["pytest-randomly"]
|
||||
|
||||
[[package]]
|
||||
name = "pycparser"
|
||||
version = "2.23"
|
||||
description = "C parser in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
groups = ["main"]
|
||||
markers = "platform_python_implementation != \"PyPy\" and implementation_name != \"PyPy\""
|
||||
files = [
|
||||
{file = "pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934"},
|
||||
{file = "pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pyserial"
|
||||
version = "3.5"
|
||||
description = "Python Serial Port Extension"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"},
|
||||
{file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
cp2110 = ["hidapi"]
|
||||
|
||||
[[package]]
|
||||
name = "pysocks"
|
||||
version = "1.7.1"
|
||||
description = "A Python SOCKS client module. See https://github.com/Anorov/PySocks for more information."
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "PySocks-1.7.1-py27-none-any.whl", hash = "sha256:08e69f092cc6dbe92a0fdd16eeb9b9ffbc13cadfe5ca4c7bd92ffb078b293299"},
|
||||
{file = "PySocks-1.7.1-py3-none-any.whl", hash = "sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5"},
|
||||
{file = "PySocks-1.7.1.tar.gz", hash = "sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rns"
|
||||
version = "1.0.4"
|
||||
description = "Self-configuring, encrypted and resilient mesh networking stack for LoRa, packet radio, WiFi and everything in between"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "rns-1.0.4-1-py3-none-any.whl", hash = "sha256:f1804f8b07a8b8e1c1b61889f929fdb5cfbd57f4c354108c417135f0d67c5ef6"},
|
||||
{file = "rns-1.0.4-py3-none-any.whl", hash = "sha256:7a2b7893410833b42c0fa7f9a9e3369cebb085cdd26bd83f3031fa6c1051653c"},
|
||||
{file = "rns-1.0.4.tar.gz", hash = "sha256:e70667a767fe523bab8e7ea0627447258c4e6763b7756fbba50c6556dbb84399"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
cryptography = ">=3.4.7"
|
||||
pyserial = ">=3.5"
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.13"
|
||||
content-hash = "15934b753c7666b5c299a25fdd1036f3a57f9f7c27410b58b16e2e68c743d7d0"
|
||||
21
pyproject.toml
Normal file
21
pyproject.toml
Normal file
@@ -0,0 +1,21 @@
|
||||
[project]
|
||||
name = "quad4-proxynet"
|
||||
version = "0.1.0"
|
||||
description = "Quad4 Proxynet"
|
||||
authors = [
|
||||
{name = "Quad4.io"}
|
||||
]
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
dependencies = [
|
||||
"rns (>=1.0.4,<2.0.0)",
|
||||
"cryptography (>=46.0.3,<47.0.0)",
|
||||
"pysocks (>=1.7.1,<2.0.0)"
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
proxynet = "quad4_proxynet.main:main"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
2
quad4_proxynet/__init__.py
Normal file
2
quad4_proxynet/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
# Reticulum Proxynet
|
||||
|
||||
306
quad4_proxynet/client.py
Normal file
306
quad4_proxynet/client.py
Normal file
@@ -0,0 +1,306 @@
|
||||
import socket
|
||||
import threading
|
||||
import RNS
|
||||
import RNS.vendor.umsgpack as umsgpack
|
||||
import time
|
||||
from .common import APP_NAME, load_or_create_identity
|
||||
|
||||
class ProxynetClient:
|
||||
def __init__(self, local_port=1080, identity_name="proxynet_client", preferred_servers=None):
|
||||
self.local_port = local_port
|
||||
self.identity = load_or_create_identity(identity_name)
|
||||
|
||||
if isinstance(preferred_servers, str):
|
||||
self.preferred_servers = [s.strip() for s in preferred_servers.split(",") if s.strip()]
|
||||
else:
|
||||
self.preferred_servers = preferred_servers or []
|
||||
|
||||
self.discovered_servers = set()
|
||||
self.lock = threading.Lock()
|
||||
self.udp_associations = {}
|
||||
|
||||
self.aspect_filter = RNS.Destination.expand_name(None, APP_NAME, "rproxy")
|
||||
|
||||
# Register for announcements
|
||||
RNS.Transport.register_announce_handler(self)
|
||||
|
||||
def start(self):
|
||||
server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
server_sock.bind(('127.0.0.1', self.local_port))
|
||||
server_sock.listen(100)
|
||||
RNS.log(f"SOCKS5 proxy listening on 127.0.0.1:{self.local_port}", RNS.LOG_NOTICE)
|
||||
|
||||
# Start a UDP listener for SOCKS5 UDP ASSOCIATE
|
||||
self.udp_relay_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
self.udp_relay_sock.bind(('127.0.0.1', 0))
|
||||
self.udp_relay_port = self.udp_relay_sock.getsockname()[1]
|
||||
threading.Thread(target=self.udp_relay_loop, daemon=True).start()
|
||||
|
||||
while True:
|
||||
client_sock, addr = server_sock.accept()
|
||||
threading.Thread(target=self.handle_socks_connection, args=(client_sock,), daemon=True).start()
|
||||
|
||||
def udp_relay_loop(self):
|
||||
while True:
|
||||
try:
|
||||
data, addr = self.udp_relay_sock.recvfrom(65535)
|
||||
# SOCKS5 UDP Request Header
|
||||
if data[2] != 0: # Fragmented packets not supported
|
||||
continue
|
||||
|
||||
header_pos = 4
|
||||
atyp = data[3]
|
||||
if atyp == 1: # IPv4
|
||||
host = socket.inet_ntoa(data[header_pos:header_pos+4])
|
||||
header_pos += 4
|
||||
elif atyp == 3: # Domain
|
||||
length = data[header_pos]
|
||||
host = data[header_pos+1:header_pos+1+length].decode()
|
||||
header_pos += 1 + length
|
||||
elif atyp == 4: # IPv6
|
||||
host = socket.inet_ntop(socket.AF_INET6, data[header_pos:header_pos+16])
|
||||
header_pos += 16
|
||||
else:
|
||||
continue
|
||||
|
||||
port = int.from_bytes(data[header_pos:header_pos+2], 'big')
|
||||
payload = data[header_pos+2:]
|
||||
|
||||
# Store association for return packets
|
||||
with self.lock:
|
||||
self.udp_associations[(host, port)] = addr
|
||||
|
||||
dest_hash = self.select_server()
|
||||
if dest_hash:
|
||||
link = self.establish_link(dest_hash)
|
||||
if link:
|
||||
msg = umsgpack.packb({
|
||||
'type': 'udp_packet',
|
||||
'host': host,
|
||||
'port': port,
|
||||
'payload': payload
|
||||
})
|
||||
RNS.Packet(link, msg).send()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"UDP relay error: {e}", RNS.LOG_DEBUG)
|
||||
|
||||
def received_announce(self, destination_hash, announced_identity, app_data):
|
||||
with self.lock:
|
||||
if destination_hash not in self.discovered_servers:
|
||||
self.discovered_servers.add(destination_hash)
|
||||
RNS.log(f"Discovered proxynet server: {RNS.prettyhexrep(destination_hash)}", RNS.LOG_NOTICE)
|
||||
|
||||
def handle_socks_connection(self, client_sock):
|
||||
try:
|
||||
# SOCKS5 Handshake
|
||||
version = client_sock.recv(1)
|
||||
if version != b'\x05':
|
||||
client_sock.close()
|
||||
return
|
||||
|
||||
nmethods = ord(client_sock.recv(1))
|
||||
methods = client_sock.recv(nmethods)
|
||||
|
||||
if b'\x00' not in methods:
|
||||
client_sock.sendall(b'\x05\xff')
|
||||
client_sock.close()
|
||||
return
|
||||
|
||||
client_sock.sendall(b'\x05\x00')
|
||||
|
||||
# Request
|
||||
header = client_sock.recv(4)
|
||||
if header[0] != 5:
|
||||
client_sock.close()
|
||||
return
|
||||
|
||||
cmd = header[1] # 1: CONNECT, 3: UDP ASSOCIATE
|
||||
atyp = header[3]
|
||||
|
||||
if cmd == 1: # CONNECT (TCP)
|
||||
if atyp == 1: # IPv4
|
||||
host = socket.inet_ntoa(client_sock.recv(4))
|
||||
elif atyp == 3: # Domain name
|
||||
domain_len = ord(client_sock.recv(1))
|
||||
host = client_sock.recv(domain_len).decode()
|
||||
elif atyp == 4: # IPv6
|
||||
host = socket.inet6_ntop(socket.AF_INET6, client_sock.recv(16))
|
||||
else:
|
||||
client_sock.close()
|
||||
return
|
||||
|
||||
port = int.from_bytes(client_sock.recv(2), 'big')
|
||||
RNS.log(f"SOCKS5 TCP request for {host}:{port}", RNS.LOG_NOTICE)
|
||||
self.handle_tcp_forward(client_sock, host, port)
|
||||
|
||||
elif cmd == 3: # UDP ASSOCIATE
|
||||
# The app tells us its address (usually ignored)
|
||||
# We tell the app our UDP relay address
|
||||
RNS.log(f"SOCKS5 UDP ASSOCIATE request", RNS.LOG_NOTICE)
|
||||
|
||||
# Skip the address provided by the app
|
||||
if atyp == 1: client_sock.recv(4)
|
||||
elif atyp == 3: client_sock.recv(ord(client_sock.recv(1)))
|
||||
elif atyp == 4: client_sock.recv(16)
|
||||
client_sock.recv(2) # Skip port
|
||||
|
||||
# Send response with our UDP relay port
|
||||
# BND.ADDR (127.0.0.1) and BND.PORT
|
||||
reply = b'\x05\x00\x00\x01' + socket.inet_aton('127.0.0.1') + self.udp_relay_port.to_bytes(2, 'big')
|
||||
client_sock.sendall(reply)
|
||||
|
||||
# We must keep the TCP connection open. When it closes, the UDP association ends.
|
||||
while True:
|
||||
if not client_sock.recv(1024):
|
||||
break
|
||||
client_sock.close()
|
||||
|
||||
else:
|
||||
client_sock.sendall(b'\x05\x07\x00\x01\x00\x00\x00\x00\x00\x00') # Command not supported
|
||||
client_sock.close()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"Error handling connection: {e}", RNS.LOG_ERROR)
|
||||
try:
|
||||
client_sock.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def handle_tcp_forward(self, client_sock, host, port):
|
||||
# Select server
|
||||
dest_hash = self.select_server()
|
||||
if not dest_hash:
|
||||
RNS.log("No servers available", RNS.LOG_ERROR)
|
||||
client_sock.sendall(b'\x05\x01\x00\x01\x00\x00\x00\x00\x00\x00')
|
||||
client_sock.close()
|
||||
return
|
||||
|
||||
# Establish Link
|
||||
link = self.establish_link(dest_hash)
|
||||
if not link:
|
||||
client_sock.sendall(b'\x05\x01\x00\x01\x00\x00\x00\x00\x00\x00')
|
||||
client_sock.close()
|
||||
return
|
||||
|
||||
response_received = threading.Event()
|
||||
success = [False]
|
||||
|
||||
def packet_received(message, packet):
|
||||
try:
|
||||
data = umsgpack.unpackb(message)
|
||||
if data.get('type') == 'response':
|
||||
success[0] = data.get('success')
|
||||
response_received.set()
|
||||
elif data.get('type') == 'data':
|
||||
client_sock.sendall(data.get('payload'))
|
||||
elif data.get('type') == 'udp_response':
|
||||
# Wrap the response in SOCKS5 UDP header and send to the app
|
||||
host = data.get('host')
|
||||
port = data.get('port')
|
||||
payload = data.get('payload')
|
||||
|
||||
with self.lock:
|
||||
app_addr = self.udp_associations.get((host, port))
|
||||
|
||||
if app_addr:
|
||||
# SOCKS5 UDP Header: RSV(0,0) FRAG(0) ATYP(1) BND.ADDR(4) BND.PORT(2)
|
||||
header = b'\x00\x00\x00\x01' + socket.inet_aton(host) + port.to_bytes(2, 'big')
|
||||
self.udp_relay_sock.sendto(header + payload, app_addr)
|
||||
except Exception as e:
|
||||
RNS.log(f"Client packet error: {e}", RNS.LOG_ERROR)
|
||||
|
||||
link.set_packet_callback(packet_received)
|
||||
|
||||
connect_msg = umsgpack.packb({
|
||||
'type': 'connect',
|
||||
'host': host,
|
||||
'port': port
|
||||
})
|
||||
RNS.Packet(link, connect_msg).send()
|
||||
|
||||
if not response_received.wait(timeout=30) or not success[0]:
|
||||
client_sock.sendall(b'\x05\x01\x00\x01\x00\x00\x00\x00\x00\x00')
|
||||
client_sock.close()
|
||||
link.teardown()
|
||||
return
|
||||
|
||||
client_sock.sendall(b'\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00')
|
||||
|
||||
# Relay data
|
||||
try:
|
||||
while True:
|
||||
data = client_sock.recv(16384)
|
||||
if not data:
|
||||
break
|
||||
msg = umsgpack.packb({'type': 'data', 'payload': data})
|
||||
RNS.Packet(link, msg).send()
|
||||
except Exception as e:
|
||||
RNS.log(f"Relay error: {e}", RNS.LOG_DEBUG)
|
||||
finally:
|
||||
link.teardown()
|
||||
client_sock.close()
|
||||
|
||||
def select_server(self):
|
||||
# 1. Check preferred servers
|
||||
for server_hex in self.preferred_servers:
|
||||
try:
|
||||
dest_hash = bytes.fromhex(server_hex)
|
||||
if RNS.Transport.has_path(dest_hash):
|
||||
return dest_hash
|
||||
except:
|
||||
continue
|
||||
|
||||
# 2. Check discovered servers
|
||||
with self.lock:
|
||||
for dest_hash in self.discovered_servers:
|
||||
if RNS.Transport.has_path(dest_hash):
|
||||
return dest_hash
|
||||
|
||||
# 3. Try to request path to preferred servers
|
||||
if self.preferred_servers:
|
||||
server_hex = self.preferred_servers[0]
|
||||
try:
|
||||
dest_hash = bytes.fromhex(server_hex)
|
||||
RNS.Transport.request_path(dest_hash)
|
||||
start = time.time()
|
||||
while not RNS.Transport.has_path(dest_hash) and time.time() - start < 5:
|
||||
time.sleep(0.5)
|
||||
if RNS.Transport.has_path(dest_hash):
|
||||
return dest_hash
|
||||
except:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
def establish_link(self, dest_hash):
|
||||
remote_identity = RNS.Identity.recall(dest_hash)
|
||||
if not remote_identity:
|
||||
RNS.log(f"Could not recall identity for {RNS.prettyhexrep(dest_hash)}", RNS.LOG_ERROR)
|
||||
return None
|
||||
|
||||
destination = RNS.Destination(
|
||||
remote_identity,
|
||||
RNS.Destination.OUT,
|
||||
RNS.Destination.SINGLE,
|
||||
APP_NAME,
|
||||
"rproxy"
|
||||
)
|
||||
|
||||
link = RNS.Link(destination)
|
||||
start = time.time()
|
||||
while link.status != RNS.Link.ACTIVE:
|
||||
time.sleep(0.1)
|
||||
if time.time() - start > 15:
|
||||
RNS.log("Link establishment timed out", RNS.LOG_ERROR)
|
||||
return None
|
||||
if link.status == RNS.Link.CLOSED:
|
||||
RNS.log("Link establishment failed", RNS.LOG_ERROR)
|
||||
return None
|
||||
|
||||
return link
|
||||
|
||||
def run_client(port=1080, identity_name="proxynet_client", preferred_servers=None):
|
||||
client = ProxynetClient(port, identity_name, preferred_servers)
|
||||
client.start()
|
||||
33
quad4_proxynet/common.py
Normal file
33
quad4_proxynet/common.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
import RNS
|
||||
|
||||
APP_NAME = "proxynet"
|
||||
DEFAULT_IDENTITY_NAME = "proxynet"
|
||||
|
||||
def get_identity_path(identity_name):
|
||||
config_path = os.path.expanduser("~/.reticulum")
|
||||
identity_path = os.path.join(config_path, "identities", f"{identity_name}")
|
||||
return identity_path
|
||||
|
||||
def load_or_create_identity(identity_name):
|
||||
identity_path = get_identity_path(identity_name)
|
||||
identity_dir = os.path.dirname(identity_path)
|
||||
|
||||
if not os.path.exists(identity_dir):
|
||||
os.makedirs(identity_dir)
|
||||
|
||||
if os.path.exists(identity_path):
|
||||
identity = RNS.Identity.from_file(identity_path)
|
||||
RNS.log(f"Loaded identity {identity_name} from {identity_path}", RNS.LOG_NOTICE)
|
||||
else:
|
||||
identity = RNS.Identity()
|
||||
identity.to_file(identity_path)
|
||||
RNS.log(f"Created new identity {identity_name} at {identity_path}", RNS.LOG_NOTICE)
|
||||
|
||||
return identity
|
||||
|
||||
def setup_reticulum(configpath=None, loglevel=RNS.LOG_NOTICE):
|
||||
reticulum = RNS.Reticulum(configpath)
|
||||
RNS.loglevel = loglevel
|
||||
return reticulum
|
||||
|
||||
44
quad4_proxynet/main.py
Normal file
44
quad4_proxynet/main.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import argparse
|
||||
import sys
|
||||
import RNS
|
||||
from .common import setup_reticulum
|
||||
from .server import run_server
|
||||
from .client import run_client
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Reticulum Proxynet")
|
||||
|
||||
group = parser.add_mutually_exclusive_group(required=True)
|
||||
group.add_argument("--server", action="store_true", help="Run in server mode")
|
||||
group.add_argument("--client", action="store_true", help="Run in client mode")
|
||||
|
||||
parser.add_argument("--port", type=int, default=1080, help="Local port for SOCKS5 proxy (client mode only)")
|
||||
parser.add_argument("--identity", type=str, default="proxynet", help="Identity name")
|
||||
parser.add_argument("--preferred", type=str, help="Preferred server destination hashes (comma-separated)")
|
||||
parser.add_argument("--config", type=str, help="Path to Reticulum config directory")
|
||||
parser.add_argument("-v", "--verbose", action="count", default=0, help="Increase verbosity")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.verbose == 1:
|
||||
loglevel = RNS.LOG_INFO
|
||||
elif args.verbose == 2:
|
||||
loglevel = RNS.LOG_VERBOSE
|
||||
elif args.verbose >= 3:
|
||||
loglevel = RNS.LOG_DEBUG
|
||||
else:
|
||||
loglevel = RNS.LOG_NOTICE
|
||||
|
||||
setup_reticulum(args.config, loglevel)
|
||||
|
||||
try:
|
||||
if args.server:
|
||||
run_server(args.identity)
|
||||
else:
|
||||
run_client(args.port, args.identity, args.preferred)
|
||||
except KeyboardInterrupt:
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
161
quad4_proxynet/server.py
Normal file
161
quad4_proxynet/server.py
Normal file
@@ -0,0 +1,161 @@
|
||||
import socket
|
||||
import threading
|
||||
import RNS
|
||||
import RNS.vendor.umsgpack as umsgpack
|
||||
from .common import APP_NAME, load_or_create_identity
|
||||
|
||||
class ProxynetServer:
|
||||
def __init__(self, identity_name="proxynet_server"):
|
||||
self.identity = load_or_create_identity(identity_name)
|
||||
self.destination = RNS.Destination(
|
||||
self.identity,
|
||||
RNS.Destination.IN,
|
||||
RNS.Destination.SINGLE,
|
||||
APP_NAME,
|
||||
"rproxy"
|
||||
)
|
||||
self.destination.set_link_established_callback(self.link_established)
|
||||
self.active_links = {}
|
||||
|
||||
def announce(self):
|
||||
self.destination.announce()
|
||||
RNS.log(f"Server announced: {RNS.prettyhexrep(self.destination.hash)}", RNS.LOG_NOTICE)
|
||||
|
||||
def link_established(self, link):
|
||||
RNS.log(f"Link established with {RNS.prettyhexrep(link.get_remote_identity().hash if link.get_remote_identity() else b'unknown')}", RNS.LOG_NOTICE)
|
||||
link.set_packet_callback(self.packet_received)
|
||||
link.set_link_closed_callback(self.link_closed)
|
||||
|
||||
def link_closed(self, link):
|
||||
RNS.log("Link closed", RNS.LOG_NOTICE)
|
||||
# Clean up any associated sockets
|
||||
if link in self.active_links:
|
||||
target_sock = self.active_links[link].get('target_sock')
|
||||
if target_sock:
|
||||
try:
|
||||
target_sock.close()
|
||||
except:
|
||||
pass
|
||||
del self.active_links[link]
|
||||
|
||||
def packet_received(self, message, packet):
|
||||
try:
|
||||
data = umsgpack.unpackb(message)
|
||||
msg_type = data.get('type')
|
||||
|
||||
if msg_type == 'connect':
|
||||
self.handle_connect(data, packet.link)
|
||||
elif msg_type == 'data':
|
||||
self.handle_data(data, packet.link)
|
||||
elif msg_type == 'udp_packet':
|
||||
self.handle_udp_packet(data, packet.link)
|
||||
elif msg_type == 'close':
|
||||
self.handle_close(packet.link)
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"Error processing packet: {e}", RNS.LOG_ERROR)
|
||||
|
||||
def handle_udp_packet(self, data, link):
|
||||
host = data.get('host')
|
||||
port = data.get('port')
|
||||
payload = data.get('payload')
|
||||
|
||||
try:
|
||||
|
||||
udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
udp_sock.settimeout(2.0)
|
||||
udp_sock.sendto(payload, (host, port))
|
||||
|
||||
# Try to receive a response
|
||||
try:
|
||||
response, addr = udp_sock.recvfrom(65535)
|
||||
# Send back to client
|
||||
resp_msg = umsgpack.packb({
|
||||
'type': 'udp_response',
|
||||
'host': addr[0],
|
||||
'port': addr[1],
|
||||
'payload': response
|
||||
})
|
||||
RNS.Packet(link, resp_msg).send()
|
||||
except socket.timeout:
|
||||
pass
|
||||
finally:
|
||||
udp_sock.close()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"UDP handling error: {e}", RNS.LOG_DEBUG)
|
||||
|
||||
def handle_connect(self, data, link):
|
||||
host = data.get('host')
|
||||
port = data.get('port')
|
||||
RNS.log(f"Connection request to {host}:{port}", RNS.LOG_INFO)
|
||||
|
||||
try:
|
||||
target_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
target_sock.settimeout(10)
|
||||
target_sock.connect((host, port))
|
||||
target_sock.settimeout(None)
|
||||
|
||||
self.active_links[link] = {
|
||||
'target_sock': target_sock,
|
||||
'active': True
|
||||
}
|
||||
|
||||
# Start a thread to read from the target socket and send back over Reticulum
|
||||
threading.Thread(target=self.target_reader, args=(link, target_sock), daemon=True).start()
|
||||
|
||||
# Send success response
|
||||
response = umsgpack.packb({'type': 'response', 'success': True})
|
||||
RNS.Packet(link, response).send()
|
||||
|
||||
except Exception as e:
|
||||
RNS.log(f"Failed to connect to {host}:{port}: {e}", RNS.LOG_ERROR)
|
||||
response = umsgpack.packb({'type': 'response', 'success': False, 'error': str(e)})
|
||||
RNS.Packet(link, response).send()
|
||||
link.teardown()
|
||||
|
||||
def handle_data(self, data, link):
|
||||
if link in self.active_links:
|
||||
target_sock = self.active_links[link]['target_sock']
|
||||
try:
|
||||
target_sock.sendall(data.get('payload'))
|
||||
except Exception as e:
|
||||
RNS.log(f"Error sending to target: {e}", RNS.LOG_ERROR)
|
||||
self.handle_close(link)
|
||||
|
||||
def handle_close(self, link):
|
||||
if link in self.active_links:
|
||||
target_sock = self.active_links[link]['target_sock']
|
||||
try:
|
||||
target_sock.close()
|
||||
except:
|
||||
pass
|
||||
self.active_links[link]['active'] = False
|
||||
del self.active_links[link]
|
||||
link.teardown()
|
||||
|
||||
def target_reader(self, link, target_sock):
|
||||
try:
|
||||
while True:
|
||||
data = target_sock.recv(16384)
|
||||
if not data:
|
||||
break
|
||||
|
||||
# Split data into chunks if necessary, though Reticulum Link handles some of this
|
||||
# For now, just send it. RNS.Link handles mtu.
|
||||
msg = umsgpack.packb({'type': 'data', 'payload': data})
|
||||
RNS.Packet(link, msg).send()
|
||||
except Exception as e:
|
||||
RNS.log(f"Target reader error: {e}", RNS.LOG_DEBUG)
|
||||
finally:
|
||||
self.handle_close(link)
|
||||
|
||||
def run_server(identity_name="proxynet_server"):
|
||||
server = ProxynetServer(identity_name)
|
||||
server.announce()
|
||||
|
||||
import time
|
||||
while True:
|
||||
time.sleep(600)
|
||||
server.announce()
|
||||
|
||||
Reference in New Issue
Block a user