-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrelay.py
More file actions
117 lines (100 loc) · 3.39 KB
/
relay.py
File metadata and controls
117 lines (100 loc) · 3.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
"""Port forwarder with graceful exit.
Run the example as
python portforwarder.py :8080 gevent.org:80
Then direct your browser to http://localhost:8080 or do "telnet localhost 8080".
When the portforwarder receives TERM or INT signal (type Ctrl-C),
it closes the listening socket and waits for all existing
connections to finish. The existing connections will remain unaffected.
The program will exit once the last connection has been closed.
"""
import sys
import signal
import gevent
import socket
from gevent.server import StreamServer
from gevent.socket import create_connection, gethostbyname
import logging
import logging.config
LOGGING = {
'version': 1,
'formatters': {
'standard': {
'format': '%(asctime)s|%(levelname)s|%(process)d|%(filename)s:%(lineno)d|%(module)s.%(funcName)s|%(message)s',
},
},
'handlers': {
'main': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'log',
'maxBytes': 1024*1024*50,
'formatter': 'standard',
'backupCount': 10,
}
},
'loggers': {
'main': {
'level': 'INFO',
'handlers': ['main']
},
},
}
logging.config.dictConfig(LOGGING)
logger = logging.getLogger('main')
class PortForwarder(StreamServer):
def __init__(self, listener, dest, **kwargs):
StreamServer.__init__(self, listener, **kwargs)
self.dest = dest
def handle(self, source, address):
logger.info('%s:%s accepted', *address[:2])
try:
dest = create_connection(self.dest)
except IOError as ex:
logger.error('%s:%s failed to connect to %s:%s: %s', address[0], address[1], self.dest[0], self.dest[1], ex)
return
forwarders = (gevent.spawn(forward, source, dest),
gevent.spawn(forward, dest, source))
# if we return from this method, the stream will be closed out
# from under us, so wait for our children
gevent.joinall(forwarders)
def close(self):
if self.closed:
sys.exit('Multiple exit signals received - aborting.')
else:
logger.info('Closing listener socket')
StreamServer.close(self)
def forward(source, dest):
#source_address = '%s:%s' % source.getpeername()[:2]
#dest_address = '%s:%s' % dest.getpeername()[:2]
try:
while True:
data = source.recv(4096)
#logger.debug('%s->%s: %r', source_address, dest_address, data)
if not data:
break
dest.sendall(data)
except Exception as e:
pass
finally:
source.close()
dest.close()
def parse_address(address):
try:
hostname, port = address.rsplit(':', 1)
port = int(port)
except ValueError:
sys.exit('Expected HOST:PORT: %r' % address)
return gethostbyname(hostname), port
def main():
args = sys.argv[1:]
if len(args) != 2:
sys.exit('Usage: %s source-address destination-address' % __file__)
source = args[0]
dest = parse_address(args[1])
server = PortForwarder(source, dest)
logger.info('Starting port forwarder %s:%s -> %s:%s', *(server.address[:2] + dest))
gevent.signal(signal.SIGTERM, server.close)
gevent.signal(signal.SIGINT, server.close)
server.start()
gevent.wait()
if __name__ == '__main__':
main()