asyncproxy 1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- asyncproxy-1.0/LICENSE +22 -0
- asyncproxy-1.0/MANIFEST.in +2 -0
- asyncproxy-1.0/PKG-INFO +213 -0
- asyncproxy-1.0/README.md +191 -0
- asyncproxy-1.0/asyncproxy.egg-info/PKG-INFO +213 -0
- asyncproxy-1.0/asyncproxy.egg-info/SOURCES.txt +19 -0
- asyncproxy-1.0/asyncproxy.egg-info/dependency_links.txt +1 -0
- asyncproxy-1.0/asyncproxy.egg-info/top_level.txt +2 -0
- asyncproxy-1.0/python/AsyncProxy.py +177 -0
- asyncproxy-1.0/python/Forwarder.py +205 -0
- asyncproxy-1.0/python/ForwarderFast.py +70 -0
- asyncproxy-1.0/python/TCPProxy.py +202 -0
- asyncproxy-1.0/python/env.py +24 -0
- asyncproxy-1.0/setup.cfg +4 -0
- asyncproxy-1.0/setup.py +75 -0
- asyncproxy-1.0/src/Symbol.map +14 -0
- asyncproxy-1.0/src/asp_iostats.h +10 -0
- asyncproxy-1.0/src/asp_sock.c +61 -0
- asyncproxy-1.0/src/asp_sock.h +24 -0
- asyncproxy-1.0/src/asyncproxy.c +663 -0
- asyncproxy-1.0/src/asyncproxy.h +56 -0
asyncproxy-1.0/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2010-2025 Sippy Software, Inc. All rights reserved.
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
4
|
+
are permitted provided that the following conditions are met:
|
|
5
|
+
|
|
6
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
7
|
+
list of conditions and the following disclaimer.
|
|
8
|
+
|
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
10
|
+
this list of conditions and the following disclaimer in the documentation and/or
|
|
11
|
+
other materials provided with the distribution.
|
|
12
|
+
|
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
15
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
16
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
17
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
18
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
19
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
20
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
21
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
22
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
asyncproxy-1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: asyncproxy
|
|
3
|
+
Version: 1.0
|
|
4
|
+
Summary: Background TCP proxy for async IO
|
|
5
|
+
Home-page: https://github.com/sippy/libasyncproxy.git
|
|
6
|
+
Author: Maksym Sobolyev
|
|
7
|
+
Author-email: sobomax@sippysoft.com
|
|
8
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
9
|
+
Classifier: Operating System :: POSIX
|
|
10
|
+
Classifier: Programming Language :: C
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: summary
|
|
22
|
+
|
|
23
|
+
# libasyncproxy
|
|
24
|
+
|
|
25
|
+
## Introduction
|
|
26
|
+
|
|
27
|
+
The libasyncproxy is a fairy simple C library and a respective python wrapper,
|
|
28
|
+
which allows splicing two sockets, pipes and in general file descriptors to
|
|
29
|
+
relay bidirectional data in/out in a background using a worker thread (one per
|
|
30
|
+
connection at the moment).
|
|
31
|
+
|
|
32
|
+
Unlike system-wide facilities that might be offering similar functionality,
|
|
33
|
+
this library provides more control and flexibility. Allowing to connect
|
|
34
|
+
different kinds of underlying objects (i.e. plain file to a socket, device to
|
|
35
|
+
a pipe etc).
|
|
36
|
+
|
|
37
|
+
It also privides mechanism for the python code to supply a handler(s) to
|
|
38
|
+
monitor, record and/or alter the data being transmitted.
|
|
39
|
+
|
|
40
|
+
Last but not least, the C library can be used directly from a low-level code
|
|
41
|
+
for the same effect.
|
|
42
|
+
|
|
43
|
+
## History
|
|
44
|
+
|
|
45
|
+
The code was created to allow Python code implementing application-layer proxy
|
|
46
|
+
to manage session routing and connection, while handling all transfers outside
|
|
47
|
+
of confinments of the slow Python and its GIL.
|
|
48
|
+
|
|
49
|
+
## Interfaces
|
|
50
|
+
|
|
51
|
+
AsyncProxy: the lowest-level interface, dealing with raw sockets, wrapper for
|
|
52
|
+
libasyncproxy.
|
|
53
|
+
|
|
54
|
+
ForwarderFast: super-set of AsyncProxy with some utility methods.
|
|
55
|
+
|
|
56
|
+
Forwarder: same API and functionality as ForwarderFast, but without using
|
|
57
|
+
AsyncProxy C module (i.e. python thread doing i/o). Mostly for backward
|
|
58
|
+
compatibility when we need to break library API.
|
|
59
|
+
|
|
60
|
+
TCPProxy: set of high-level classes to accept and manage inbound connections
|
|
61
|
+
and initiate/tear-down outbound as needed, connecting them using forwarders
|
|
62
|
+
once established. Will use ForwarderFast if available, falling back to the
|
|
63
|
+
Forwarder if that fails to load or initialize.
|
|
64
|
+
|
|
65
|
+
## Use Cases
|
|
66
|
+
|
|
67
|
+
We use this library to allow applications to be redirected to one of several
|
|
68
|
+
available DB replicas and re-routed instantly if the configuration changes.
|
|
69
|
+
|
|
70
|
+
## Build and Install Python module from source code:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
git clone https://github.com/sippy/libasyncproxy.git
|
|
74
|
+
pip install libasyncproxy/
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
### asyncproxy -- `AsyncProxy2FD` Example
|
|
80
|
+
|
|
81
|
+
This example shows how to set up a bidirectional relay between two socket pairs using `AsyncProxy2FD`. Data sent on one end is forwarded to the other, and vice versa.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import socket
|
|
85
|
+
from asyncproxy.AsyncProxy import AsyncProxy2FD
|
|
86
|
+
|
|
87
|
+
# 1. Create two socket pairs:
|
|
88
|
+
# - (client_socket, proxy_in): client writes to `proxy_in`
|
|
89
|
+
# - (proxy_out, server_socket): proxy writes to `proxy_out`, server reads
|
|
90
|
+
client_socket, proxy_in = socket.socketpair()
|
|
91
|
+
proxy_out, server_socket = socket.socketpair()
|
|
92
|
+
|
|
93
|
+
# 2. Initialize and start the proxy:
|
|
94
|
+
proxy = AsyncProxy2FD(proxy_in.fileno(), proxy_out.fileno())
|
|
95
|
+
proxy.start()
|
|
96
|
+
|
|
97
|
+
# 3. Send from client → server:
|
|
98
|
+
client_msg = b"Hello from Client!"
|
|
99
|
+
client_socket.sendall(client_msg)
|
|
100
|
+
print("Client sent:", client_msg.decode())
|
|
101
|
+
|
|
102
|
+
server_recv = server_socket.recv(1024)
|
|
103
|
+
print("Server received:", server_recv.decode())
|
|
104
|
+
|
|
105
|
+
# 4. Send from server → client:
|
|
106
|
+
server_msg = b"Hello from Server!"
|
|
107
|
+
server_socket.sendall(server_msg)
|
|
108
|
+
print("Server sent:", server_msg.decode())
|
|
109
|
+
|
|
110
|
+
client_recv = client_socket.recv(1024)
|
|
111
|
+
print("Client received:", client_recv.decode())
|
|
112
|
+
|
|
113
|
+
# 5. Shutdown and cleanup:
|
|
114
|
+
proxy.join(shutdown=True)
|
|
115
|
+
for sock in (client_socket, proxy_in, proxy_out, server_socket):
|
|
116
|
+
sock.close()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### asyncproxy -- `TCPProxy` Example
|
|
120
|
+
|
|
121
|
+
This example shows how to set up a TCP proxy accepting connections on
|
|
122
|
+
`localhost:8080` and forwarding it to `www.google.com:80`.
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
import socket
|
|
126
|
+
from time import sleep
|
|
127
|
+
from asyncproxy.TCPProxy import TCPProxy
|
|
128
|
+
|
|
129
|
+
# 1. Initialize and start the proxy:
|
|
130
|
+
# - Listen on local port 8080
|
|
131
|
+
# - Forward all traffic to www.google.com:80
|
|
132
|
+
proxy = TCPProxy(port=8080, newhost='www.google.com', newport=80)
|
|
133
|
+
proxy.start()
|
|
134
|
+
print("TCPProxy running on:", proxy.sock.getsockname())
|
|
135
|
+
|
|
136
|
+
# 2. Connect via the proxy and send HTTP requests twice
|
|
137
|
+
for _ in (1, 2):
|
|
138
|
+
with socket.create_connection(('127.0.0.1', 8080)) as s:
|
|
139
|
+
print("Connected to www.google.com via TCPProxy.")
|
|
140
|
+
s.sendall(b"GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n")
|
|
141
|
+
resp = s.recv(256)
|
|
142
|
+
print("Response received from proxy:")
|
|
143
|
+
print(resp.decode('utf-8', errors='replace'))
|
|
144
|
+
|
|
145
|
+
# 3. Shutdown the proxy cleanly
|
|
146
|
+
proxy.shutdown()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### asyncproxy -- Advanced `AsyncProxy2FD` Example
|
|
150
|
+
|
|
151
|
+
This example shows how to subclass `AsyncProxy2FD` to inspect and modify data in transit using custom `in2out` and `out2in` hooks.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import socket
|
|
155
|
+
from ctypes import string_at, memmove
|
|
156
|
+
from asyncproxy.AsyncProxy import AsyncProxy2FD
|
|
157
|
+
|
|
158
|
+
class NosyProxy(AsyncProxy2FD):
|
|
159
|
+
def in2out(self, res_p):
|
|
160
|
+
# Unpack the struct
|
|
161
|
+
tr = res_p.contents
|
|
162
|
+
ptr, length = tr.buf, tr.len
|
|
163
|
+
|
|
164
|
+
# Read original bytes, transform, and write back
|
|
165
|
+
original = string_at(ptr, length)
|
|
166
|
+
length -= 1
|
|
167
|
+
transformed = original.upper()[:length]
|
|
168
|
+
memmove(ptr, transformed, length)
|
|
169
|
+
tr.len = length
|
|
170
|
+
|
|
171
|
+
print("in2out hook:", original, "→", transformed)
|
|
172
|
+
|
|
173
|
+
def out2in(self, res_p):
|
|
174
|
+
tr = res_p.contents
|
|
175
|
+
ptr, length = tr.buf, tr.len
|
|
176
|
+
|
|
177
|
+
original = string_at(ptr, length)
|
|
178
|
+
length -= 1
|
|
179
|
+
transformed = original[::-1][1:]
|
|
180
|
+
memmove(ptr, transformed, length)
|
|
181
|
+
tr.len = length
|
|
182
|
+
|
|
183
|
+
print("out2in hook:", original, "→", transformed)
|
|
184
|
+
|
|
185
|
+
# 1. Create two socket pairs for bidirectional flow
|
|
186
|
+
client_socket, proxy_in = socket.socketpair()
|
|
187
|
+
proxy_out, server_socket = socket.socketpair()
|
|
188
|
+
|
|
189
|
+
# 2. Initialize and start the custom proxy
|
|
190
|
+
proxy = NosyProxy(proxy_in.fileno(), proxy_out.fileno())
|
|
191
|
+
proxy.start()
|
|
192
|
+
|
|
193
|
+
# 3. Client → Server (uppercase transformation)
|
|
194
|
+
client_msg = b"Hello from Client!"
|
|
195
|
+
client_socket.sendall(client_msg)
|
|
196
|
+
print("Client sent:", client_msg.decode())
|
|
197
|
+
|
|
198
|
+
srv_recv = server_socket.recv(1024)
|
|
199
|
+
print("Server received:", srv_recv.decode())
|
|
200
|
+
|
|
201
|
+
# 4. Server → Client (reverse transformation)
|
|
202
|
+
server_msg = b"Hello from Server!"
|
|
203
|
+
server_socket.sendall(server_msg)
|
|
204
|
+
print("Server sent:", server_msg.decode())
|
|
205
|
+
|
|
206
|
+
cli_recv = client_socket.recv(1024)
|
|
207
|
+
print("Client received:", cli_recv.decode())
|
|
208
|
+
|
|
209
|
+
# 5. Shutdown and cleanup
|
|
210
|
+
proxy.join(shutdown=True)
|
|
211
|
+
for sock in (client_socket, proxy_in, proxy_out, server_socket):
|
|
212
|
+
sock.close()
|
|
213
|
+
```
|
asyncproxy-1.0/README.md
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# libasyncproxy
|
|
2
|
+
|
|
3
|
+
## Introduction
|
|
4
|
+
|
|
5
|
+
The libasyncproxy is a fairy simple C library and a respective python wrapper,
|
|
6
|
+
which allows splicing two sockets, pipes and in general file descriptors to
|
|
7
|
+
relay bidirectional data in/out in a background using a worker thread (one per
|
|
8
|
+
connection at the moment).
|
|
9
|
+
|
|
10
|
+
Unlike system-wide facilities that might be offering similar functionality,
|
|
11
|
+
this library provides more control and flexibility. Allowing to connect
|
|
12
|
+
different kinds of underlying objects (i.e. plain file to a socket, device to
|
|
13
|
+
a pipe etc).
|
|
14
|
+
|
|
15
|
+
It also privides mechanism for the python code to supply a handler(s) to
|
|
16
|
+
monitor, record and/or alter the data being transmitted.
|
|
17
|
+
|
|
18
|
+
Last but not least, the C library can be used directly from a low-level code
|
|
19
|
+
for the same effect.
|
|
20
|
+
|
|
21
|
+
## History
|
|
22
|
+
|
|
23
|
+
The code was created to allow Python code implementing application-layer proxy
|
|
24
|
+
to manage session routing and connection, while handling all transfers outside
|
|
25
|
+
of confinments of the slow Python and its GIL.
|
|
26
|
+
|
|
27
|
+
## Interfaces
|
|
28
|
+
|
|
29
|
+
AsyncProxy: the lowest-level interface, dealing with raw sockets, wrapper for
|
|
30
|
+
libasyncproxy.
|
|
31
|
+
|
|
32
|
+
ForwarderFast: super-set of AsyncProxy with some utility methods.
|
|
33
|
+
|
|
34
|
+
Forwarder: same API and functionality as ForwarderFast, but without using
|
|
35
|
+
AsyncProxy C module (i.e. python thread doing i/o). Mostly for backward
|
|
36
|
+
compatibility when we need to break library API.
|
|
37
|
+
|
|
38
|
+
TCPProxy: set of high-level classes to accept and manage inbound connections
|
|
39
|
+
and initiate/tear-down outbound as needed, connecting them using forwarders
|
|
40
|
+
once established. Will use ForwarderFast if available, falling back to the
|
|
41
|
+
Forwarder if that fails to load or initialize.
|
|
42
|
+
|
|
43
|
+
## Use Cases
|
|
44
|
+
|
|
45
|
+
We use this library to allow applications to be redirected to one of several
|
|
46
|
+
available DB replicas and re-routed instantly if the configuration changes.
|
|
47
|
+
|
|
48
|
+
## Build and Install Python module from source code:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
git clone https://github.com/sippy/libasyncproxy.git
|
|
52
|
+
pip install libasyncproxy/
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
### asyncproxy -- `AsyncProxy2FD` Example
|
|
58
|
+
|
|
59
|
+
This example shows how to set up a bidirectional relay between two socket pairs using `AsyncProxy2FD`. Data sent on one end is forwarded to the other, and vice versa.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import socket
|
|
63
|
+
from asyncproxy.AsyncProxy import AsyncProxy2FD
|
|
64
|
+
|
|
65
|
+
# 1. Create two socket pairs:
|
|
66
|
+
# - (client_socket, proxy_in): client writes to `proxy_in`
|
|
67
|
+
# - (proxy_out, server_socket): proxy writes to `proxy_out`, server reads
|
|
68
|
+
client_socket, proxy_in = socket.socketpair()
|
|
69
|
+
proxy_out, server_socket = socket.socketpair()
|
|
70
|
+
|
|
71
|
+
# 2. Initialize and start the proxy:
|
|
72
|
+
proxy = AsyncProxy2FD(proxy_in.fileno(), proxy_out.fileno())
|
|
73
|
+
proxy.start()
|
|
74
|
+
|
|
75
|
+
# 3. Send from client → server:
|
|
76
|
+
client_msg = b"Hello from Client!"
|
|
77
|
+
client_socket.sendall(client_msg)
|
|
78
|
+
print("Client sent:", client_msg.decode())
|
|
79
|
+
|
|
80
|
+
server_recv = server_socket.recv(1024)
|
|
81
|
+
print("Server received:", server_recv.decode())
|
|
82
|
+
|
|
83
|
+
# 4. Send from server → client:
|
|
84
|
+
server_msg = b"Hello from Server!"
|
|
85
|
+
server_socket.sendall(server_msg)
|
|
86
|
+
print("Server sent:", server_msg.decode())
|
|
87
|
+
|
|
88
|
+
client_recv = client_socket.recv(1024)
|
|
89
|
+
print("Client received:", client_recv.decode())
|
|
90
|
+
|
|
91
|
+
# 5. Shutdown and cleanup:
|
|
92
|
+
proxy.join(shutdown=True)
|
|
93
|
+
for sock in (client_socket, proxy_in, proxy_out, server_socket):
|
|
94
|
+
sock.close()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### asyncproxy -- `TCPProxy` Example
|
|
98
|
+
|
|
99
|
+
This example shows how to set up a TCP proxy accepting connections on
|
|
100
|
+
`localhost:8080` and forwarding it to `www.google.com:80`.
|
|
101
|
+
|
|
102
|
+
```python
|
|
103
|
+
import socket
|
|
104
|
+
from time import sleep
|
|
105
|
+
from asyncproxy.TCPProxy import TCPProxy
|
|
106
|
+
|
|
107
|
+
# 1. Initialize and start the proxy:
|
|
108
|
+
# - Listen on local port 8080
|
|
109
|
+
# - Forward all traffic to www.google.com:80
|
|
110
|
+
proxy = TCPProxy(port=8080, newhost='www.google.com', newport=80)
|
|
111
|
+
proxy.start()
|
|
112
|
+
print("TCPProxy running on:", proxy.sock.getsockname())
|
|
113
|
+
|
|
114
|
+
# 2. Connect via the proxy and send HTTP requests twice
|
|
115
|
+
for _ in (1, 2):
|
|
116
|
+
with socket.create_connection(('127.0.0.1', 8080)) as s:
|
|
117
|
+
print("Connected to www.google.com via TCPProxy.")
|
|
118
|
+
s.sendall(b"GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n")
|
|
119
|
+
resp = s.recv(256)
|
|
120
|
+
print("Response received from proxy:")
|
|
121
|
+
print(resp.decode('utf-8', errors='replace'))
|
|
122
|
+
|
|
123
|
+
# 3. Shutdown the proxy cleanly
|
|
124
|
+
proxy.shutdown()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### asyncproxy -- Advanced `AsyncProxy2FD` Example
|
|
128
|
+
|
|
129
|
+
This example shows how to subclass `AsyncProxy2FD` to inspect and modify data in transit using custom `in2out` and `out2in` hooks.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
import socket
|
|
133
|
+
from ctypes import string_at, memmove
|
|
134
|
+
from asyncproxy.AsyncProxy import AsyncProxy2FD
|
|
135
|
+
|
|
136
|
+
class NosyProxy(AsyncProxy2FD):
|
|
137
|
+
def in2out(self, res_p):
|
|
138
|
+
# Unpack the struct
|
|
139
|
+
tr = res_p.contents
|
|
140
|
+
ptr, length = tr.buf, tr.len
|
|
141
|
+
|
|
142
|
+
# Read original bytes, transform, and write back
|
|
143
|
+
original = string_at(ptr, length)
|
|
144
|
+
length -= 1
|
|
145
|
+
transformed = original.upper()[:length]
|
|
146
|
+
memmove(ptr, transformed, length)
|
|
147
|
+
tr.len = length
|
|
148
|
+
|
|
149
|
+
print("in2out hook:", original, "→", transformed)
|
|
150
|
+
|
|
151
|
+
def out2in(self, res_p):
|
|
152
|
+
tr = res_p.contents
|
|
153
|
+
ptr, length = tr.buf, tr.len
|
|
154
|
+
|
|
155
|
+
original = string_at(ptr, length)
|
|
156
|
+
length -= 1
|
|
157
|
+
transformed = original[::-1][1:]
|
|
158
|
+
memmove(ptr, transformed, length)
|
|
159
|
+
tr.len = length
|
|
160
|
+
|
|
161
|
+
print("out2in hook:", original, "→", transformed)
|
|
162
|
+
|
|
163
|
+
# 1. Create two socket pairs for bidirectional flow
|
|
164
|
+
client_socket, proxy_in = socket.socketpair()
|
|
165
|
+
proxy_out, server_socket = socket.socketpair()
|
|
166
|
+
|
|
167
|
+
# 2. Initialize and start the custom proxy
|
|
168
|
+
proxy = NosyProxy(proxy_in.fileno(), proxy_out.fileno())
|
|
169
|
+
proxy.start()
|
|
170
|
+
|
|
171
|
+
# 3. Client → Server (uppercase transformation)
|
|
172
|
+
client_msg = b"Hello from Client!"
|
|
173
|
+
client_socket.sendall(client_msg)
|
|
174
|
+
print("Client sent:", client_msg.decode())
|
|
175
|
+
|
|
176
|
+
srv_recv = server_socket.recv(1024)
|
|
177
|
+
print("Server received:", srv_recv.decode())
|
|
178
|
+
|
|
179
|
+
# 4. Server → Client (reverse transformation)
|
|
180
|
+
server_msg = b"Hello from Server!"
|
|
181
|
+
server_socket.sendall(server_msg)
|
|
182
|
+
print("Server sent:", server_msg.decode())
|
|
183
|
+
|
|
184
|
+
cli_recv = client_socket.recv(1024)
|
|
185
|
+
print("Client received:", cli_recv.decode())
|
|
186
|
+
|
|
187
|
+
# 5. Shutdown and cleanup
|
|
188
|
+
proxy.join(shutdown=True)
|
|
189
|
+
for sock in (client_socket, proxy_in, proxy_out, server_socket):
|
|
190
|
+
sock.close()
|
|
191
|
+
```
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: asyncproxy
|
|
3
|
+
Version: 1.0
|
|
4
|
+
Summary: Background TCP proxy for async IO
|
|
5
|
+
Home-page: https://github.com/sippy/libasyncproxy.git
|
|
6
|
+
Author: Maksym Sobolyev
|
|
7
|
+
Author-email: sobomax@sippysoft.com
|
|
8
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
9
|
+
Classifier: Operating System :: POSIX
|
|
10
|
+
Classifier: Programming Language :: C
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Dynamic: author
|
|
15
|
+
Dynamic: author-email
|
|
16
|
+
Dynamic: classifier
|
|
17
|
+
Dynamic: description
|
|
18
|
+
Dynamic: description-content-type
|
|
19
|
+
Dynamic: home-page
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
Dynamic: summary
|
|
22
|
+
|
|
23
|
+
# libasyncproxy
|
|
24
|
+
|
|
25
|
+
## Introduction
|
|
26
|
+
|
|
27
|
+
The libasyncproxy is a fairy simple C library and a respective python wrapper,
|
|
28
|
+
which allows splicing two sockets, pipes and in general file descriptors to
|
|
29
|
+
relay bidirectional data in/out in a background using a worker thread (one per
|
|
30
|
+
connection at the moment).
|
|
31
|
+
|
|
32
|
+
Unlike system-wide facilities that might be offering similar functionality,
|
|
33
|
+
this library provides more control and flexibility. Allowing to connect
|
|
34
|
+
different kinds of underlying objects (i.e. plain file to a socket, device to
|
|
35
|
+
a pipe etc).
|
|
36
|
+
|
|
37
|
+
It also privides mechanism for the python code to supply a handler(s) to
|
|
38
|
+
monitor, record and/or alter the data being transmitted.
|
|
39
|
+
|
|
40
|
+
Last but not least, the C library can be used directly from a low-level code
|
|
41
|
+
for the same effect.
|
|
42
|
+
|
|
43
|
+
## History
|
|
44
|
+
|
|
45
|
+
The code was created to allow Python code implementing application-layer proxy
|
|
46
|
+
to manage session routing and connection, while handling all transfers outside
|
|
47
|
+
of confinments of the slow Python and its GIL.
|
|
48
|
+
|
|
49
|
+
## Interfaces
|
|
50
|
+
|
|
51
|
+
AsyncProxy: the lowest-level interface, dealing with raw sockets, wrapper for
|
|
52
|
+
libasyncproxy.
|
|
53
|
+
|
|
54
|
+
ForwarderFast: super-set of AsyncProxy with some utility methods.
|
|
55
|
+
|
|
56
|
+
Forwarder: same API and functionality as ForwarderFast, but without using
|
|
57
|
+
AsyncProxy C module (i.e. python thread doing i/o). Mostly for backward
|
|
58
|
+
compatibility when we need to break library API.
|
|
59
|
+
|
|
60
|
+
TCPProxy: set of high-level classes to accept and manage inbound connections
|
|
61
|
+
and initiate/tear-down outbound as needed, connecting them using forwarders
|
|
62
|
+
once established. Will use ForwarderFast if available, falling back to the
|
|
63
|
+
Forwarder if that fails to load or initialize.
|
|
64
|
+
|
|
65
|
+
## Use Cases
|
|
66
|
+
|
|
67
|
+
We use this library to allow applications to be redirected to one of several
|
|
68
|
+
available DB replicas and re-routed instantly if the configuration changes.
|
|
69
|
+
|
|
70
|
+
## Build and Install Python module from source code:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
git clone https://github.com/sippy/libasyncproxy.git
|
|
74
|
+
pip install libasyncproxy/
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Usage
|
|
78
|
+
|
|
79
|
+
### asyncproxy -- `AsyncProxy2FD` Example
|
|
80
|
+
|
|
81
|
+
This example shows how to set up a bidirectional relay between two socket pairs using `AsyncProxy2FD`. Data sent on one end is forwarded to the other, and vice versa.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import socket
|
|
85
|
+
from asyncproxy.AsyncProxy import AsyncProxy2FD
|
|
86
|
+
|
|
87
|
+
# 1. Create two socket pairs:
|
|
88
|
+
# - (client_socket, proxy_in): client writes to `proxy_in`
|
|
89
|
+
# - (proxy_out, server_socket): proxy writes to `proxy_out`, server reads
|
|
90
|
+
client_socket, proxy_in = socket.socketpair()
|
|
91
|
+
proxy_out, server_socket = socket.socketpair()
|
|
92
|
+
|
|
93
|
+
# 2. Initialize and start the proxy:
|
|
94
|
+
proxy = AsyncProxy2FD(proxy_in.fileno(), proxy_out.fileno())
|
|
95
|
+
proxy.start()
|
|
96
|
+
|
|
97
|
+
# 3. Send from client → server:
|
|
98
|
+
client_msg = b"Hello from Client!"
|
|
99
|
+
client_socket.sendall(client_msg)
|
|
100
|
+
print("Client sent:", client_msg.decode())
|
|
101
|
+
|
|
102
|
+
server_recv = server_socket.recv(1024)
|
|
103
|
+
print("Server received:", server_recv.decode())
|
|
104
|
+
|
|
105
|
+
# 4. Send from server → client:
|
|
106
|
+
server_msg = b"Hello from Server!"
|
|
107
|
+
server_socket.sendall(server_msg)
|
|
108
|
+
print("Server sent:", server_msg.decode())
|
|
109
|
+
|
|
110
|
+
client_recv = client_socket.recv(1024)
|
|
111
|
+
print("Client received:", client_recv.decode())
|
|
112
|
+
|
|
113
|
+
# 5. Shutdown and cleanup:
|
|
114
|
+
proxy.join(shutdown=True)
|
|
115
|
+
for sock in (client_socket, proxy_in, proxy_out, server_socket):
|
|
116
|
+
sock.close()
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### asyncproxy -- `TCPProxy` Example
|
|
120
|
+
|
|
121
|
+
This example shows how to set up a TCP proxy accepting connections on
|
|
122
|
+
`localhost:8080` and forwarding it to `www.google.com:80`.
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
import socket
|
|
126
|
+
from time import sleep
|
|
127
|
+
from asyncproxy.TCPProxy import TCPProxy
|
|
128
|
+
|
|
129
|
+
# 1. Initialize and start the proxy:
|
|
130
|
+
# - Listen on local port 8080
|
|
131
|
+
# - Forward all traffic to www.google.com:80
|
|
132
|
+
proxy = TCPProxy(port=8080, newhost='www.google.com', newport=80)
|
|
133
|
+
proxy.start()
|
|
134
|
+
print("TCPProxy running on:", proxy.sock.getsockname())
|
|
135
|
+
|
|
136
|
+
# 2. Connect via the proxy and send HTTP requests twice
|
|
137
|
+
for _ in (1, 2):
|
|
138
|
+
with socket.create_connection(('127.0.0.1', 8080)) as s:
|
|
139
|
+
print("Connected to www.google.com via TCPProxy.")
|
|
140
|
+
s.sendall(b"GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n")
|
|
141
|
+
resp = s.recv(256)
|
|
142
|
+
print("Response received from proxy:")
|
|
143
|
+
print(resp.decode('utf-8', errors='replace'))
|
|
144
|
+
|
|
145
|
+
# 3. Shutdown the proxy cleanly
|
|
146
|
+
proxy.shutdown()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### asyncproxy -- Advanced `AsyncProxy2FD` Example
|
|
150
|
+
|
|
151
|
+
This example shows how to subclass `AsyncProxy2FD` to inspect and modify data in transit using custom `in2out` and `out2in` hooks.
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
import socket
|
|
155
|
+
from ctypes import string_at, memmove
|
|
156
|
+
from asyncproxy.AsyncProxy import AsyncProxy2FD
|
|
157
|
+
|
|
158
|
+
class NosyProxy(AsyncProxy2FD):
|
|
159
|
+
def in2out(self, res_p):
|
|
160
|
+
# Unpack the struct
|
|
161
|
+
tr = res_p.contents
|
|
162
|
+
ptr, length = tr.buf, tr.len
|
|
163
|
+
|
|
164
|
+
# Read original bytes, transform, and write back
|
|
165
|
+
original = string_at(ptr, length)
|
|
166
|
+
length -= 1
|
|
167
|
+
transformed = original.upper()[:length]
|
|
168
|
+
memmove(ptr, transformed, length)
|
|
169
|
+
tr.len = length
|
|
170
|
+
|
|
171
|
+
print("in2out hook:", original, "→", transformed)
|
|
172
|
+
|
|
173
|
+
def out2in(self, res_p):
|
|
174
|
+
tr = res_p.contents
|
|
175
|
+
ptr, length = tr.buf, tr.len
|
|
176
|
+
|
|
177
|
+
original = string_at(ptr, length)
|
|
178
|
+
length -= 1
|
|
179
|
+
transformed = original[::-1][1:]
|
|
180
|
+
memmove(ptr, transformed, length)
|
|
181
|
+
tr.len = length
|
|
182
|
+
|
|
183
|
+
print("out2in hook:", original, "→", transformed)
|
|
184
|
+
|
|
185
|
+
# 1. Create two socket pairs for bidirectional flow
|
|
186
|
+
client_socket, proxy_in = socket.socketpair()
|
|
187
|
+
proxy_out, server_socket = socket.socketpair()
|
|
188
|
+
|
|
189
|
+
# 2. Initialize and start the custom proxy
|
|
190
|
+
proxy = NosyProxy(proxy_in.fileno(), proxy_out.fileno())
|
|
191
|
+
proxy.start()
|
|
192
|
+
|
|
193
|
+
# 3. Client → Server (uppercase transformation)
|
|
194
|
+
client_msg = b"Hello from Client!"
|
|
195
|
+
client_socket.sendall(client_msg)
|
|
196
|
+
print("Client sent:", client_msg.decode())
|
|
197
|
+
|
|
198
|
+
srv_recv = server_socket.recv(1024)
|
|
199
|
+
print("Server received:", srv_recv.decode())
|
|
200
|
+
|
|
201
|
+
# 4. Server → Client (reverse transformation)
|
|
202
|
+
server_msg = b"Hello from Server!"
|
|
203
|
+
server_socket.sendall(server_msg)
|
|
204
|
+
print("Server sent:", server_msg.decode())
|
|
205
|
+
|
|
206
|
+
cli_recv = client_socket.recv(1024)
|
|
207
|
+
print("Client received:", cli_recv.decode())
|
|
208
|
+
|
|
209
|
+
# 5. Shutdown and cleanup
|
|
210
|
+
proxy.join(shutdown=True)
|
|
211
|
+
for sock in (client_socket, proxy_in, proxy_out, server_socket):
|
|
212
|
+
sock.close()
|
|
213
|
+
```
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
MANIFEST.in
|
|
3
|
+
README.md
|
|
4
|
+
setup.py
|
|
5
|
+
asyncproxy.egg-info/PKG-INFO
|
|
6
|
+
asyncproxy.egg-info/SOURCES.txt
|
|
7
|
+
asyncproxy.egg-info/dependency_links.txt
|
|
8
|
+
asyncproxy.egg-info/top_level.txt
|
|
9
|
+
python/AsyncProxy.py
|
|
10
|
+
python/Forwarder.py
|
|
11
|
+
python/ForwarderFast.py
|
|
12
|
+
python/TCPProxy.py
|
|
13
|
+
python/env.py
|
|
14
|
+
src/Symbol.map
|
|
15
|
+
src/asp_iostats.h
|
|
16
|
+
src/asp_sock.c
|
|
17
|
+
src/asp_sock.h
|
|
18
|
+
src/asyncproxy.c
|
|
19
|
+
src/asyncproxy.h
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|