commit 16260400ca85256a35f2f03f8eb1a29a95af7f63 Author: lhahn Date: Sat Aug 19 23:37:59 2023 +0200 Git initial commit diff --git a/led_blink.py b/led_blink.py new file mode 100755 index 0000000..d49cfa9 --- /dev/null +++ b/led_blink.py @@ -0,0 +1,6 @@ +from machine import Pin +import time +led = machine.Pin("LED", machine.Pin.OUT) +while True: + led.toggle() + time.sleep(1) diff --git a/pico_proxy.py b/pico_proxy.py new file mode 100755 index 0000000..3f24458 --- /dev/null +++ b/pico_proxy.py @@ -0,0 +1,320 @@ +import network +import socket +import _thread +from time import sleep +from collections import deque +from uasyncio import Event +import re + + +class MicroProxy: + wait_symbols = { + 0: "-", + 1: "\\", + 2: "|", + 3: "/" + } + def __init__( + self, n_threads_max = 2, buf_byte_size=1024, client_timeout=60): + self._n_threads = 0 + self._n_threads_max = n_threads_max + self._buf_byte_size = buf_byte_size + self._client_timeout = client_timeout + + self._thread_events = [] + self._job_queue = deque((), 1024) + self._threads_lock = _thread.allocate_lock() + self._max_lock = _thread.allocate_lock() + self._listener_event = Event() + self._is_listening = False + + def set_max_thread_count(self, n_threads_max): + self._max_lock.acquire() + self._n_threads_max = n_threads_max + if self._n_threads > self._n_threads_max: + self.rescale(self._n_threads_max) + self._max_lock.release() + + def max_thread_count(self): + return self._n_threads_max + + def thread_count(self): + return self._n_threads + + + def _set_listener(self): + # Check what kind of socket is needed to + # bind onto. + # Take the first possible socket and the + # required IP info for binding. + self._addr_listen = socket.getaddrinfo( + self._addr, self._port + )[0][-1] + + if hasattr(self, '_socket_listen') and self._socket_listen is not None: + self.stop() + self._socket_listen = socket.socket( + socket.AF_INET, + ( + socket.SOCK_STREAM + if self._proxy_type == "TCP" + else + socket.SOCK_DGRAM + ) + ) + self._socket_listen.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + def listen(self, addr, port, proxy_type="TCP", backlog=0): + if not self._is_listening: + self._addr = addr + self._port = port + self._proxy_type = "UDP" if not proxy_type == "TCP" else proxy_type + + self._set_listener() + + self._socket_listen.bind(self._addr_listen) + self._socket_listen.listen(backlog) + + self.rescale(self._n_threads_max) + #_thread.start_new_thread( + # self._listener_thread,(self._listener_event,) + #) + print(f"init done for serving on {self._addr_listen}") + self._is_listening = True + return self._listener_event + + def stop(self): + if self._is_listening: + self._listener_event.set() + ctr = 0 + while self._listener_event.is_set(): + print( + ( + "Waiting for listener thread to finish... " + f"{MicroProxy.wait_symbols[ctr%4]}\r" + ), + end="" + ) + ctr += 1 + sleep(0.5) + else: + print("Listener thread finished closing safely.") + self.rescale(0) + self._socket_listen.close() + del self._socket_listen + self._is_listening = False + + def rescale(self, n_threads): + new_n_threads = min( + ( + n_threads + if isinstance(n_threads, int) and max(-1,n_threads) >= 0 + else + self._n_threads + ), self._n_threads_max + ) + old_n_threads = self._n_threads + if self._n_threads < new_n_threads: + self._spin_up(new_n_threads - self._n_threads) + elif self._n_threads > new_n_threads: + self._spin_down(self._n_threads - new_n_threads) + if new_n_threads != old_n_threads: + print( + "Changed worker thread size " + f"from {old_n_threads} to {new_n_threads}." + ) + + def _spin_up(self, thread_cnt_new): + self._threads_lock.acquire() + + new_thread_events = [ + Event() + for _ in range(thread_cnt_new) + ] + self._thread_events.extend(new_thread_events) + + for idx, event in enumerate(new_thread_events): + _thread.start_new_thread( + self._worker_thread, ( + #thread_idx is 1 based; 0 is master. + event, self._n_threads+idx+1 + ) + ) + self._n_threads = len(self._thread_events) + + self._threads_lock.release() + + def _spin_down(self, thread_cnt_del): + self._threads_lock.acquire() + remaining = self._n_threads - thread_cnt_del + + thread_events_stop = self._thread_events[remaining:] + self._thread_events = self._thread_events[:remaining] + for event in thread_events_stop: + event.set() + + ctr = 0 + while any( + event.is_set() + for event in thread_events_stop + ): + print( + ( + "Wait for worker threads to finish... " + f"{MicroProxy.wait_symbols[ctr%4]}\r" + ), + end="" + ) + ctr += 1 + sleep(0.5) + else: + print("Worker threads finished spinning down safely.") + for idx in range(len(thread_events_stop)-1, -1, -1): + del thread_events_stop[idx] + del thread_events_stop + + self._threads_lock.release() + + + def _listener_thread(self, event): + print("listening started") + while not event.is_set(): + print("stuff todo?") + conn, addr = self._socket_listen.accept() + print(f"incoming connection from {addr}") + self._job_queue.append((addr, conn)) + # clear event to indicate it stopped at spindown task + event.clear() + + def _worker_thread(self, event, thread_id): + print(f"Worker thread {thread_id}: starting.") + socket_client_thread = socket.socket( + socket.AF_INET, + ( + socket.SOCK_STREAM + if self._proxy_type == "TCP" + else + socket.SOCK_DGRAM + ) + ) + socket_client_thread.settimeout(self._client_timeout) + while not event.is_set(): + if len(self._job_queue) > 0: + try: + addr, conn = self._job_queue.popleft() + except: + # Maybe another thread was faster inbetween. + # If so, simply continue + continue + print( + f"Worker thread {thread_id}:", + f"handling connection of '{addr}'" + ) + request = conn.recv(self._buf_byte_size) + protocol, host_domain, port = MicroProxy.proxy_forward_filter( + request.decode() + ) + print( + f"Worker thread {thread_id}: ", + f"{protocol}://{host_domain}:{port}" + ) + try: + client_addr = socket.getaddrinfo(host_domain, port)[0][-1] + socket_client_thread.connect(client_addr) + socket_client_thread.sendall(request) + has_response = False + while not has_response: + response = socket_client_thread.recv(self._buf_byte_size) + if len(response) > 0: + has_response = True + print("send response") + conn.send(response) + print("Done sending response") + except OSError as exc: + if exc.errno != 115: + print("OOOPS?!?!?!", exc) + else: + print( + f"Timeout ... increase by 2: {self._client_timeout}", + f"-> {self._client_timeout*2}" + ) + self._client_timeout = self._client_timeout * 2 + socket_client_thread.close() + conn.close() + print("finished closing") + else: + sleep(0.1) + print(f"Worker thread {thread_id}: finished safely and shutting down.") + # clear event to indicate it stopped at spindown task + event.clear() + + def proxy_forward_filter(request): + header = request.split('\n')[0] + url = header.split()[1] + port = 80 + protocol = None + has_port = False + has_protocol = False + + if url.startswith("http"): + protocol, host_part = url.split('://') + has_protocol = True + else: + host_part = url + + if ":" in host_part: + splitter = host_part.split(':') + host_domain = splitter[0] + port = int(splitter[1]) + has_port = True + elif "/" in host_part: + host_domain = host_part.split('/')[0] + + if not has_protocol and has_port: + if port == 443: + protocol = "https" + else: + protocol = "http" + if not has_port: + if protocol == "https": + port = 443 + else: + port = 80 + return (protocol, host_domain, port) + + +def connect_wlan(ssid, password): + wlan = network.WLAN(network.STA_IF) + wlan.active(True) + wlan.connect(ssid, password) + + max_wait = 10 + while max_wait > 0: + if wlan.status() < 0 or wlan.status() >= 3: + break + max_wait -= 1 + print( + "waiting for connection...", + f"{MicroProxy.wait_symbols[max_wait%4]}\r", + end="" + ) + sleep(1) + if wlan.status() != 3: + raise RuntimeError('network connection failed') + else: + status = wlan.ifconfig() + print(f'conntected, ip = {status[0]}') + return wlan + +def main(): + ssid = 'Lars-WLAN' + password = '0243LHBS18021909' + wlan = connect_wlan(ssid, password) + + mitm = MicroProxy(n_threads_max=1) + event = mitm.listen(addr='0.0.0.0', port=8080) + mitm._listener_thread(event) + + +if __name__ == "__main__": + main() diff --git a/picoproxy.py b/picoproxy.py new file mode 100755 index 0000000..5fc3b65 --- /dev/null +++ b/picoproxy.py @@ -0,0 +1,265 @@ +import _thread +import socket +import network +from select import select +from uasyncio import Event +from time import sleep +#from threading import Thread, Event + + +class PicoProxy: +### CLS FUNCTIONS ############################################################# + wait_symbols = "-\|/" + + def _get_socket(proxy_type="TCP"): + return socket.socket( + socket.AF_INET, + ( + socket.SOCK_STREAM + if proxy_type == "TCP" + else + socket.SOCK_DGRAM + ) + ) + def init_tunnle(request, sock_in, sock_out, host, port): + if request.startswith(b"CONNECT"): + try: + addr_info = socket.getaddrinfo(host,port)[0][-1] + sock_out.connect(addr_info) + sock_in.sendall(b"HTTP/1.1 200 established\r\n\r\n") + except Exception as e: + print("Cannot initiate proxy tunnel:", e) + + + def proxy_forward_filter(request): + #looks ugly, yes; but is able to run on Pico W micro-controller :D + header = request.split('\n')[0] + url = header.split()[1] + port = 80 + protocol = None + has_port = False + has_protocol = False + + if url.startswith("http"): + protocol, host_part = url.split('://') + has_protocol = True + else: + host_part = url + + if ":" in host_part: + splitter = host_part.split(':') + host_domain = splitter[0] + port = int(splitter[1]) + has_port = True + elif "/" in host_part: + host_domain = host_part.split('/')[0] + + if not has_protocol and has_port: + if port == 443: + protocol = "https" + else: + protocol = "http" + if not has_port: + if protocol == "https": + port = 443 + else: + port = 80 + return (protocol, host_domain, port) + + + +### OBJ FUNCTIONS ############################################################# + def __init__(self, buf_byte_size=4096, client_timeout=60): + self._buf_byte_size = buf_byte_size + self._client_timeout = client_timeout + + self._listener_event = Event() + + self._is_listening = False + self._incoming = [] + self._outgoing = [] + self._channel_map = {} + self._channel_init = {} + self._channel_from_client = [] + + + + def _set_listener(self): + # Check what kind of socket is needed to + # bind onto. + # Take the first possible socket and the + # required IP info for binding. + self._addr_listen = socket.getaddrinfo( + self._addr, self._port + )[0][-1] + + if hasattr(self, '_socket_listen') and self._socket_listen is not None: + self.stop() + self._socket_listen = PicoProxy._get_socket() + self._socket_listen.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + def listen(self, addr, port, proxy_type="TCP", backlog=0): + if not self._is_listening: + self._addr = addr + self._port = port + self._proxy_type = "UDP" if not proxy_type == "TCP" else proxy_type + + self._set_listener() + + self._socket_listen.bind(self._addr_listen) + self._socket_listen.listen(backlog) + + self._incoming.append(self._socket_listen) + + #self._listen_thread = Thread( + # target=self._listener_thread, args=(self._listener_event,) + #) + #self._listen_thread.start() + #_thread.start_new_thread( + # self._listener_thread, (self._listener_event,) + #) + print(f"init done for serving on {self._addr_listen}") + self._is_listening = True + self._listener_thread(self._listener_event) + + def stop(self): + if self._is_listening: + self._listener_event.set() + ctr = 0 + while not self._listener_event.is_set(): + print( + ( + "Waiting for listener thread to finish... " + f"{PicoProxy.wait_symbols[ctr%len(PicoProxy.wait_symbols)]}\r" + ), + end="" + ) + ctr += 1 + sleep(0.5) + else: + print("Listener thread finished closing safely.") + self._socket_listen.close() + self._is_listening = False + self._incoming.clear() + self._outgoing.clear() + self._channel_map.clear() + self._channel_init.clear() + self._channel_from_client.clear() + + def is_active(self): + return not self._listener_event.is_set() + + + def _listener_thread(self, event): + while self._incoming and not event.is_set(): + inrecv, outsend, excpt = select( + self._incoming, self._outgoing, self._incoming + ) + for sock in inrecv: + if sock is self._socket_listen: + self._handle_connection_incoming() + elif ( + id(sock) in self._channel_init and not self._channel_init[id(sock)] and + sock not in self._channel_from_client + ): + continue + else: + data = sock.recv(self._buf_byte_size) + if data: + self._handle_connection_receive(sock, data) + else: + self._handle_connection_close(sock) + event.clear() + + def _handle_connection_incoming(self): + conn, addr = self._socket_listen.accept() + conn.settimeout(self._client_timeout) + reverse_conn = PicoProxy._get_socket(self._proxy_type) + reverse_conn.settimeout(self._client_timeout) + + self._channel_from_client.append(conn) + + self._incoming.append(conn) + + self._channel_map[id(conn)] = reverse_conn + self._channel_map[id(reverse_conn)] = conn + + self._channel_init[id(conn)] = False + self._channel_init[id(reverse_conn)] = False + + def _handle_connection_receive(self, sock, data): + reverse_sock = self._channel_map[id(sock)] + if not self._channel_init[id(sock)] and not self._channel_init[id(reverse_sock)]: + protocol, host_domain, port = PicoProxy.proxy_forward_filter(data.decode()) + if protocol == "https" or port == 443: + PicoProxy.init_tunnle( + data, sock, reverse_sock, host_domain, port + ) + else: + addr_info = socket.getaddrinfo(host_domain,port)[0][-1] + reverse_sock.connect(addr_info) + #not a tunnel request, directly forward + reverse_sock.sendall(data) + self._incoming.append(reverse_sock) + self._channel_init[id(sock)] = True + self._channel_init[id(reverse_sock)] = True + else: + reverse_sock.sendall(data) + + def _handle_connection_close(self, sock): + reverse_sock = self._channel_map[id(sock)] + for s in (sock, reverse_sock): + if s in self._outgoing: + self._outgoing.remove(s) + if s in self._incoming: + self._incoming.remove(s) + if s in self._channel_from_client: + self._channel_from_client.remove(s) + s.close() + del self._channel_init[id(s)] + del self._channel_map[id(s)] + +def connect_wlan(ssid, password): + wlan = network.WLAN(network.STA_IF) + wlan.active(True) + wlan.connect(ssid, password) + + max_wait = 10 + while max_wait > 0: + if wlan.status() < 0 or wlan.status() >= 3: + break + max_wait -= 1 + print( + "waiting for connection...", + f"{PicoProxy.wait_symbols[max_wait%4]}\r", + end="" + ) + sleep(0.5) + if wlan.status() != 3: + raise RuntimeError('network connection failed') + else: + status = wlan.ifconfig() + print(f'conntected, ip = {status[0]}') + return wlan + +def main(): + ssid = 'WLAN-NAME' + password = 'WLAN-PASSWORD' + wlan = connect_wlan(ssid, password) + proxy = PicoProxy() + proxy.listen(addr='0.0.0.0', port=8080) + # ctr = 0 + # cnt = len(PicoProxy.wait_symbols) + # while proxy.is_active(): + # ctr = (ctr + 1)%cnt + # print( + # "Currently doing proxy stuff...", + # f"{PicoProxy.wait_symbols[ctr]}\r", + # end="" + # ) + # sleep(0.5) + + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/picoproxy_not_functional_recheck.py b/picoproxy_not_functional_recheck.py new file mode 100755 index 0000000..925001d --- /dev/null +++ b/picoproxy_not_functional_recheck.py @@ -0,0 +1,242 @@ +import socket +from time import sleep +from queue import Queue +from threading import Thread, Event +from select import select + + +class PicoProxy: +### CLS FUNCTIONS ############################################################# + wait_symbols = "-\|/" + + def _get_socket(proxy_type="TCP"): + return socket.socket( + socket.AF_INET, + ( + socket.SOCK_STREAM + if proxy_type == "TCP" + else + socket.SOCK_DGRAM + ) + ) + def init_tunnle(request, sock_in, sock_out, host, port): + if request.startswith(b"CONNECT"): + try: + sock_out.connect((host,port)) + sock_in.sendall(b"HTTP/1.1 200 established\r\n\r\n") + except Exception as e: + print("Cannot initiate HTTPS connection:", e) + + + def proxy_forward_filter(request): + #looks ugly, yes; but is able to run on Pico W micro-controller :D + header = request.split('\n')[0] + url = header.split()[1] + port = 80 + protocol = None + has_port = False + has_protocol = False + + if url.startswith("http"): + protocol, host_part = url.split('://') + has_protocol = True + else: + host_part = url + + if ":" in host_part: + splitter = host_part.split(':') + host_domain = splitter[0] + port = int(splitter[1]) + has_port = True + elif "/" in host_part: + host_domain = host_part.split('/')[0] + + if not has_protocol and has_port: + if port == 443: + protocol = "https" + else: + protocol = "http" + if not has_port: + if protocol == "https": + port = 443 + else: + port = 80 + return (protocol, host_domain, port) + + + +### OBJ FUNCTIONS ############################################################# + def __init__(self, buf_byte_size=4096, client_timeout=0.5): + self._buf_byte_size = buf_byte_size + self._client_timeout = client_timeout + + self._listener_event = Event() + + self._is_listening = False + self._incoming = [] + self._outgoing = [] + self._channel_msg_map = {} + self._channel_map = {} + self._channel_init = {} + self._channel_from_client = [] + + + + def _set_listener(self): + # Check what kind of socket is needed to + # bind onto. + # Take the first possible socket and the + # required IP info for binding. + self._addr_listen = socket.getaddrinfo( + self._addr, self._port + )[0][-1] + + if hasattr(self, '_socket_listen') and self._socket_listen is not None: + self.stop() + self._socket_listen = PicoProxy._get_socket() + self._socket_listen.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + def listen(self, addr, port, proxy_type="TCP", backlog=0): + if not self._is_listening: + self._addr = addr + self._port = port + self._proxy_type = "UDP" if not proxy_type == "TCP" else proxy_type + + self._set_listener() + + self._socket_listen.bind(self._addr_listen) + self._socket_listen.listen(backlog) + + self._incoming.append(self._socket_listen) + + self._listen_thread = Thread( + target=self._listener_thread, args=(self._listener_event,) + ) + self._listen_thread.start() + print(f"init done for serving on {self._addr_listen}") + self._is_listening = True + + def stop(self): + if self._is_listening: + self._listener_event.set() + ctr = 0 + while self._listen_thread.is_alive(): + print( + ( + "Waiting for listener thread to finish... " + f"{PicoProxy.wait_symbols[ctr%len(PicoProxy.wait_symbols)]}\r" + ), + end="" + ) + ctr += 1 + sleep(0.5) + else: + print("Listener thread finished closing safely.") + self._socket_listen.close() + self._is_listening = False + self._incoming.clear() + self._outgoing.clear() + self._channel_msg_map.clear() + self._channel_map.clear() + self._channel_init.clear() + + def join(self): + if self._is_listening: + self._listen_thread.join() + + + def _listener_thread(self, event): + while self._incoming and not event.is_set(): + inrecv, outsend, excpt = select( + self._incoming, self._outgoing, self._incoming + ) + for sock in inrecv: + if sock is self._socket_listen: + self._handle_connection_incoming() + elif ( + sock in self._channel_init and not self._channel_init[sock] and + sock not in self._channel_from_client + ): + continue + else: + data = sock.recv(self._buf_byte_size) + if data: + self._handle_connection_receive(sock, data) + else: + self._handle_connection_close(sock) +# for sock in outsend: +# self._handle_connection_send(sock) +# for sock in excpt: +# self._handle_connection_error(sock) + event.clear() + + def _handle_connection_incoming(self): + conn, addr = self._socket_listen.accept() + reverse_conn = PicoProxy._get_socket(self._proxy_type) + + self._channel_from_client.append(conn) + + self._incoming.append(conn) + + self._channel_map[conn] = reverse_conn + self._channel_map[reverse_conn] = conn + + self._channel_msg_map[conn] = Queue() + self._channel_msg_map[reverse_conn] = Queue() + + self._channel_init[conn] = False + self._channel_init[reverse_conn] = False + + def _handle_connection_receive(self, sock, data): + reverse_sock = self._channel_map[sock] + if not self._channel_init[sock] and not self._channel_init[reverse_sock]: + protocol, host_domain, port = PicoProxy.proxy_forward_filter(data.decode()) + if protocol == "https" or port == 443: + PicoProxy.init_tunnle( + data, sock, reverse_sock, host_domain, port + ) + else: + reverse_sock.connect((host_domain,port)) + #not a tunnel request, directly forward + reverse_sock.sendall(data) + self._incoming.append(reverse_sock) + self._channel_init[sock] = True + self._channel_init[reverse_sock] = True + else: + reverse_sock.sendall(data) +# self._channel_msg_map[reverse_sock].put(data) +# if reverse_sock not in self._outgoing: +# self._outgoing.append(reverse_sock) + + def _handle_connection_send(self, sock): + try: + msg = self._channel_msg_map[sock].get_nowait() + except Queue.Empty: + self._outgoing.remove(sock) + else: + sock.sendall(msg) + + def _handle_connection_close(self, sock): + reverse_sock = self._channel_map[sock] + for s in (sock, reverse_sock): + if s in self._outgoing: + self._outgoing.remove(s) + if s in self._incoming: + self._incoming.remove(s) + if s in self._channel_from_client: + self._channel_from_client.remove(s) + s.close() + del self._channel_msg_map[s] + del self._channel_init[s] + del self._channel_map[s] + + def _handle_connection_error(self, sock): + self._handle_connection_close(sock) + +def main(): + mitm = PicoProxy() + mitm.listen(addr='0.0.0.0', port=8080) + + +if __name__ == "__main__": + main() diff --git a/proxy.py b/proxy.py new file mode 100755 index 0000000..e3ca395 --- /dev/null +++ b/proxy.py @@ -0,0 +1,519 @@ +import socket +import ssl +from time import sleep +from queue import Queue +from threading import Thread, Event, Lock, get_native_id +from select import select + + + +class MicroProxy: +### CLS FUNCTIONS ############################################################# + wait_symbols = "-\|/" + + def _get_socket(proxy_type="TCP"): + return socket.socket( + socket.AF_INET, + ( + socket.SOCK_STREAM + if proxy_type == "TCP" + else + socket.SOCK_DGRAM + ) + ) + def init_tunnle(request, sock_in, sock_out, host, port): + if request.startswith(b"CONNECT"): + try: + sock_out.connect((host,port)) + sock_in.sendall(b"HTTP/1.1 200 established\r\n\r\n") + except Exception as e: + print("Cannot initiate proxy tunnel:", e) + + + def proxy_forward_filter(request): + #looks ugly, yes; but is able to run on Pico W micro-controller :D + header = request.split('\n')[0] + url = header.split()[1] + port = 80 + protocol = None + has_port = False + has_protocol = False + + if url.startswith("http"): + protocol, host_part = url.split('://') + has_protocol = True + else: + host_part = url + + if ":" in host_part: + splitter = host_part.split(':') + host_domain = splitter[0] + port = int(splitter[1]) + has_port = True + elif "/" in host_part: + host_domain = host_part.split('/')[0] + + if not has_protocol and has_port: + if port == 443: + protocol = "https" + else: + protocol = "http" + if not has_port: + if protocol == "https": + port = 443 + else: + port = 80 + return (protocol, host_domain, port) + + + +### OBJ FUNCTIONS ############################################################# + def __init__(self, buf_byte_size=4096, client_timeout=0.5): + self._buf_byte_size = buf_byte_size + self._client_timeout = client_timeout + + self._listener_event = Event() + + self._is_listening = False + self._incoming = [] + self._outgoing = [] + self._channel_map = {} + self._channel_init = {} + self._channel_from_client = [] + + + + def _set_listener(self): + # Check what kind of socket is needed to + # bind onto. + # Take the first possible socket and the + # required IP info for binding. + self._addr_listen = socket.getaddrinfo( + self._addr, self._port + )[0][-1] + + if hasattr(self, '_socket_listen') and self._socket_listen is not None: + self.stop() + self._socket_listen = MicroProxy._get_socket() + self._socket_listen.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + def listen(self, addr, port, proxy_type="TCP", backlog=0): + if not self._is_listening: + self._addr = addr + self._port = port + self._proxy_type = "UDP" if not proxy_type == "TCP" else proxy_type + + self._set_listener() + + self._socket_listen.bind(self._addr_listen) + self._socket_listen.listen(backlog) + + self._incoming.append(self._socket_listen) + + self._listen_thread = Thread( + target=self._listener_thread, args=(self._listener_event,) + ) + self._listen_thread.start() + print(f"init done for serving on {self._addr_listen}") + self._is_listening = True + + def stop(self): + if self._is_listening: + self._listener_event.set() + ctr = 0 + while self._listen_thread.is_alive(): + print( + ( + "Waiting for listener thread to finish... " + f"{MicroProxy.wait_symbols[ctr%len(MicroProxy.wait_symbols)]}\r" + ), + end="" + ) + ctr += 1 + sleep(0.5) + else: + print("Listener thread finished closing safely.") + self._socket_listen.close() + self._is_listening = False + self._incoming.clear() + self._outgoing.clear() + self._channel_map.clear() + self._channel_init.clear() + + def join(self): + if self._is_listening: + self._listen_thread.join() + + + def _listener_thread(self, event): + while self._incoming and not event.is_set(): + inrecv, outsend, excpt = select( + self._incoming, self._outgoing, self._incoming + ) + for sock in inrecv: + if sock is self._socket_listen: + self._handle_connection_incoming() + elif ( + sock in self._channel_init and not self._channel_init[sock] and + sock not in self._channel_from_client + ): + continue + else: + data = sock.recv(self._buf_byte_size) + if data: + self._handle_connection_receive(sock, data) + else: + self._handle_connection_close(sock) + event.clear() + + def _handle_connection_incoming(self): + conn, addr = self._socket_listen.accept() + conn.settimeout(self._client_timeout) + reverse_conn = MicroProxy._get_socket(self._proxy_type) + reverse_conn.settimeout(self._client_timeout) + + self._channel_from_client.append(conn) + + self._incoming.append(conn) + + self._channel_map[conn] = reverse_conn + self._channel_map[reverse_conn] = conn + + self._channel_init[conn] = False + self._channel_init[reverse_conn] = False + + def _handle_connection_receive(self, sock, data): + reverse_sock = self._channel_map[sock] + if not self._channel_init[sock] and not self._channel_init[reverse_sock]: + protocol, host_domain, port = MicroProxy.proxy_forward_filter(data.decode()) + if protocol == "https" or port == 443: + MicroProxy.init_tunnle( + data, sock, reverse_sock, host_domain, port + ) + else: + reverse_sock.connect((host_domain,port)) + #not a tunnel request, directly forward + reverse_sock.sendall(data) + self._incoming.append(reverse_sock) + self._channel_init[sock] = True + self._channel_init[reverse_sock] = True + else: + reverse_sock.sendall(data) + + def _handle_connection_close(self, sock): + reverse_sock = self._channel_map[sock] + for s in (sock, reverse_sock): + if s in self._outgoing: + self._outgoing.remove(s) + if s in self._incoming: + self._incoming.remove(s) + if s in self._channel_from_client: + self._channel_from_client.remove(s) + s.close() + del self._channel_init[s] + del self._channel_map[s] + +def main(): + mitm = MicroProxy() + mitm.listen(addr='0.0.0.0', port=8080) + + +if __name__ == "__main__": + main() + + + + + +class ThreadProxy: + wait_symbols = "-\|/" + def __init__( + self, n_threads_max = 2, buf_byte_size=4096, client_timeout=0.5): + self._n_threads = 0 + self._n_threads_max = n_threads_max + self._buf_byte_size = buf_byte_size + self._client_timeout = client_timeout + + self._thread_events = [] + self._threads = [] + self._job_queue = Queue() + self._threads_lock = Lock() + self._max_lock = Lock() + self._listener_event = Event() + self._is_listening = False + + def set_max_thread_count(self, n_threads_max): + self._max_lock.acquire() + self._n_threads_max = n_threads_max + if self._n_threads > self._n_threads_max: + self.rescale(self._n_threads_max) + self._max_lock.release() + + def max_thread_count(self): + return self._n_threads_max + + def thread_count(self): + return self._n_threads + + def _get_socket(self): + return socket.socket( + socket.AF_INET, + ( + socket.SOCK_STREAM + if self._proxy_type == "TCP" + else + socket.SOCK_DGRAM + ) + ) + + def _set_listener(self): + # Check what kind of socket is needed to + # bind onto. + # Take the first possible socket and the + # required IP info for binding. + self._addr_listen = socket.getaddrinfo( + self._addr, self._port + )[0][-1] + + if hasattr(self, '_socket_listen') and self._socket_listen is not None: + self.stop() + self._socket_listen = self._get_socket() + self._socket_listen.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + + def listen(self, addr, port, proxy_type="TCP", backlog=0): + if not self._is_listening: + self._addr = addr + self._port = port + self._proxy_type = "UDP" if not proxy_type == "TCP" else proxy_type + + self._set_listener() + + self._socket_listen.bind(self._addr_listen) + self._socket_listen.listen(backlog) + + self.rescale(self._n_threads_max) + self._listen_thread = Thread( + target=self._listener_thread, args=(self._listener_event,) + ) + self._listen_thread.start() + print(f"init done for serving on {self._addr_listen}") + self._is_listening = True + + def stop(self): + if self._is_listening: + self._listener_event.set() + ctr = 0 + while self._listen_thread.is_alive(): + print( + ( + "Waiting for listener thread to finish... " + f"{ThreadProxy.wait_symbols[ctr%len(ThreadProxy.wait_symbols)]}\r" + ), + end="" + ) + ctr += 1 + sleep(0.5) + else: + print("Listener thread finished closing safely.") + self.rescale(0) + self._socket_listen.close() + del self._listen_thread + self._is_listening = False + + def rescale(self, n_threads): + new_n_threads = min( + ( + n_threads + if isinstance(n_threads, int) and max(-1,n_threads) >= 0 + else + self._n_threads + ), self._n_threads_max + ) + old_n_threads = self._n_threads + if self._n_threads < new_n_threads: + self._spin_up(new_n_threads - self._n_threads) + elif self._n_threads > new_n_threads: + self._spin_down(self._n_threads - new_n_threads) + if new_n_threads != old_n_threads: + print( + "Changed worker thread size " + f"from {old_n_threads} to {new_n_threads}." + ) + + def join(self): + if self._is_listening: + self._listen_thread.join() + + def _spin_up(self, thread_cnt_new): + self._threads_lock.acquire() + + new_thread_events = [ + Event() + for _ in range(thread_cnt_new) + ] + self._thread_events.extend(new_thread_events) + + new_threads = [ + Thread(target=self._worker_thread, args=(event,)) + for event in new_thread_events + ] + self._threads.extend(new_threads) + self._n_threads = len(self._threads) + + for thread in new_threads: + thread.start() + + self._threads_lock.release() + + def _spin_down(self, thread_cnt_del): + self._threads_lock.acquire() + remaining = self._n_threads - thread_cnt_del + + thread_events_stop = self._thread_events[remaining:] + threads_stop = self._threads[remaining:] + self._thread_events = self._thread_events[:remaining] + self._threads = self._threads[:remaining] + for event in thread_events_stop: + event.set() + + ctr = 0 + while any( + thread.is_alive() + for thread in threads_stop + ): + print( + ( + "Wait for worker threads to finish... " + f"{ThreadProxy.wait_symbols[ctr%len(ThreadProxy.wait_symbols)]}\r" + ), + end="" + ) + ctr += 1 + sleep(0.5) + else: + print("Worker threads finished spinning down safely.") + for idx in range(len(thread_events_stop)-1, -1, -1): + del thread_events_stop[idx] + del threads_stop[idx] + del thread_events_stop + del threads_stop + + self._threads_lock.release() + + + def _listener_thread(self, event): + while not event.is_set(): + conn, addr = self._socket_listen.accept() + self._job_queue.put((addr, conn)) + # clear event to indicate it stopped at spindown task + event.clear() + + def init_tunnle(request, sock_in, sock_out, host, port): + if request.startswith(b"CONNECT"): + try: + sock_out.connect((host,port)) + sock_in.sendall(b"HTTP/1.1 200 established\r\n\r\n") + except Exception as e: + print("Cannot initiate HTTPS connection:", e) + return sock_out, sock_in + + def receive_data(sock, buf_byte): + data = b"" + is_complete = False + while not is_complete: + try: + part_data = sock.recv(buf_byte) + if len(part_data) > 0: + data += part_data + else: + is_complete = True + except socket.timeout: + is_complete = True + return data + + def _sendrecv(self, sock_in, sock_out): + init_data = ThreadProxy.receive_data(sock_in, self._buf_byte_size) + protocol, host_domain, port = ThreadProxy.proxy_forward_filter(init_data.decode()) + if protocol == "https" or port == 443: + sock_out, sock_in = ThreadProxy.init_tunnle(init_data, sock_in, sock_out, host_domain, port) + #initial request is CONNECT and handled by init_tunnle + is_init = False + else: + sock_out.connect((host_domain,port)) + is_init = True + is_last_request = len(init_data) == 0 + + while not is_last_request: + if is_init: + request = init_data + is_init = False + else: + request = ThreadProxy.receive_data(sock_in, self._buf_byte_size) + if len(request) > 0: + sock_out.sendall(request) + response = ThreadProxy.receive_data(sock_out, self._buf_byte_size) + if len(response) > 0: + sock_in.sendall(response) + else: + is_last_request = True + + def _worker_thread(self, event): + print(f"Worker Thread {get_native_id()}: Start working...") + while not event.is_set(): + if not self._job_queue.empty(): + addr, conn = self._job_queue.get() + + print(f"Worker Thread {get_native_id()}: Handle request of {addr}") + + conn.settimeout(self._client_timeout) + socket_client_thread = self._get_socket() + socket_client_thread.settimeout(self._client_timeout) + try: + self._sendrecv(conn, socket_client_thread) + except Exception as e: + print("ERROR occured in Thread: ", e) + conn.close() + socket_client_thread.close() + else: + sleep(0.1) + # clear event to indicate it stopped at spindown task + event.clear() + + def proxy_forward_filter(request): + header = request.split('\n')[0] + url = header.split()[1] + port = 80 + protocol = None + has_port = False + has_protocol = False + + if url.startswith("http"): + protocol, host_part = url.split('://') + has_protocol = True + else: + host_part = url + + if ":" in host_part: + splitter = host_part.split(':') + host_domain = splitter[0] + port = int(splitter[1]) + has_port = True + elif "/" in host_part: + host_domain = host_part.split('/')[0] + + if not has_protocol and has_port: + if port == 443: + protocol = "https" + else: + protocol = "http" + if not has_port: + if protocol == "https": + port = 443 + else: + port = 80 + return (protocol, host_domain, port) + +def main_alt_thread(): + mitm = MicroProxy(n_threads_max=20) + mitm.listen(addr='0.0.0.0', port=8080) + diff --git a/random_requests.py b/random_requests.py new file mode 100755 index 0000000..b0c31c7 --- /dev/null +++ b/random_requests.py @@ -0,0 +1,140 @@ +import requests +from datetime import datetime +from random import choices +from threading import Thread# +from time import sleep +from math import sqrt + +def do_request(url_list, count, time_delta, use_proxy=True): + global proxies + for url in choices(url_list, k=count): + kwargs = {'url':url,'proxies':proxies} if use_proxy else {'url':url} + start = datetime.now() + try: + response = requests.get(**kwargs) + except Exception as e: + print("Some error ...", e) + time_delta.append(-1) + continue + time_diff = datetime.now() - start + if response.status_code in (200, '200'): + time_delta.append(time_diff) + else: + time_delta.append(-1) + print(f"WARNING for URL '{url}'") + +def do_analysis(url_list, probe_size, thread_count, use_proxy=True): + thread_timedeltas = [ + [] + for _ in range(thread_count) + ] + + threads = [ + Thread(target=do_request, args=( + url_list, probe_size, time_delta, use_proxy + )) + for time_delta in thread_timedeltas + ] + for thread in threads: + thread.start() + + wait_string = "-\|/" + ctr = 0 + while any( + thread.is_alive() + for thread in threads + ): + print(f"Threads are still execution ... {wait_string[ctr%4]}\r", end="") + ctr += 1 + sleep(0.5) + print("Finished!", " "*100) + + for idx in range(len(threads)-1,-1,-1): + del threads[idx] + del threads + + return thread_timedeltas + + +def do_test(url_list, probe_size, thread_count, use_proxy): + thread_timedeltas = do_analysis(url_list, probe_size, thread_count, use_proxy) + time_deltas = [ + [ + time.total_seconds() + for time in time_delta + if time != -1 + ] + for time_delta in thread_timedeltas + ] + time_delta_values = [val for vals in time_deltas for val in vals] + ctr_deltas = len(time_delta_values) + min_delta = min(time_delta_values) + max_delta = max(time_delta_values) + avg_delta = sum(time_delta_values)/ctr_deltas + err_delta = (thread_count * probe_size) - ctr_deltas + std_delta = sqrt(sum( (delta-avg_delta)**2 for delta in time_delta_values)/(ctr_deltas-1)) + sort_delta = sorted(time_delta_values) + med_delta = sort_delta[(ctr_deltas-1)//2] + if len(time_delta_values) % 2 == 0: + med_delta = (med_delta + sort_delta[(ctr_deltas)//2])/2 + + result = (min_delta, med_delta, avg_delta, max_delta, std_delta, err_delta) + return result + + + +### INIT GET VALUES ########################################################### +proxy_file_mappings = { + #"rpi-compute": "rpi-4", + #"rpi-proxy2": "rpi-2", + "rpi-proxy0": "rpi-0", + "94.2.18.222": "rpi-pico" +} + + +random_links_file = "/home/lhahn/Projekte/pico-test/random_wiki_links.txt" +with open(random_links_file, "r") as file: + link_list = [ + line.rstrip() + for line in file + if len(line.rstrip()) > 0 + ] + +allfile = open("/home/lhahn/Projekte/pico-test/results_rpi-proxies.txt","a") +#allfile.write("rpi\tproxy\tprobesize\tthreads\tmin(s)\tmed(s)\tavg(s)\tmax(s)\tsd(s)\terr\n") +first_round = False +### ANALYSIS DONE IN SEVERAL CONFIGS ########################################## +for proxy,filename in proxy_file_mappings.items(): + proxies = { + 'http': f'http://{proxy}:8080', + 'https': f'http://{proxy}:8080' + } + with open(f"/home/lhahn/Projekte/pico-test/results_{filename}-proxy.txt", "w") as f: + f.write("proxy\tprobesize\tthreads\tmin(s)\tmed(s)\tavg(s)\tmax(s)\tsd(s)\terr\n") + for use_proxy in (False, True): + for probe_size in (10,25,50,100): + for thread_count in (1,5,10,20): + min_delta, med_delta, avg_delta, max_delta, std_delta, err_delta = do_test( + link_list, probe_size, thread_count, use_proxy + ) + data_str = ( + f"{use_proxy}\t{probe_size}\t{thread_count}\t{min_delta}\t{med_delta}" + f"\t{avg_delta}\t{max_delta}\t{std_delta}\t{err_delta}\n" + ) + result_str = ( + f"avg: {avg_delta}s, med:{med_delta} min: {min_delta}s, max: {max_delta}s, " + f"std: {std_delta}s | err: {err_delta}/{probe_size*thread_count} | " + f"thrds: {thread_count}, proxy: {use_proxy}, probe_size: {probe_size}, " + f"rpi: {filename}" + ) + print(result_str) + + f.write(data_str) + f.flush() + if use_proxy or (not use_proxy and first_round): + allfile.write( + f"{filename}\t{data_str}" + ) + allfile.flush() + first_round = False +allfile.close() diff --git a/random_wiki_links.txt b/random_wiki_links.txt new file mode 100755 index 0000000..3ed051a --- /dev/null +++ b/random_wiki_links.txt @@ -0,0 +1,51 @@ +https://en.wikipedia.org/wiki/Intertemporal_budget_constraint +https://en.wikipedia.org/wiki/Martin_Bell_(archaeologist) +https://en.wikipedia.org/wiki/Anna_Hannevik +https://en.wikipedia.org/wiki/Warm_Springs,_Montana +https://en.wikipedia.org/wiki/Automated_optical_inspection +https://en.wikipedia.org/wiki/Aunt_Sammy +https://en.wikipedia.org/wiki/1993_in_Norwegian_football +https://en.wikipedia.org/wiki/Michael_Paterson +https://en.wikipedia.org/wiki/Perfecto_de_Castro +https://en.wikipedia.org/wiki/Joco_(disambiguation) +https://en.wikipedia.org/wiki/TP_Ostrovia_Ostr%C3%B3w_Wielkopolski +https://en.wikipedia.org/wiki/Bautista,_Pangasinan +https://en.wikipedia.org/wiki/Pedro_Taborda +https://en.wikipedia.org/wiki/Samuel_Kettell +https://en.wikipedia.org/wiki/Trachysomus_wappesi +https://en.wikipedia.org/wiki/Mordellistena_louisianae +https://en.wikipedia.org/wiki/Peter_Wellings +https://en.wikipedia.org/wiki/COVID-19_pandemic_in_Taiwan +https://en.wikipedia.org/wiki/Prunus_mexicana +https://en.wikipedia.org/wiki/Matt_Foster +https://en.wikipedia.org/wiki/Lindsay_Vaughn +https://en.wikipedia.org/wiki/List_of_Indo-Guyanese_people +https://en.wikipedia.org/wiki/Krasnoufimsky_District +https://en.wikipedia.org/wiki/2015%E2%80%9316_SKNFA_Super_League +https://en.wikipedia.org/wiki/George_Richmond_Grose +https://en.wikipedia.org/wiki/Stawell_railway_station +https://en.wikipedia.org/wiki/Devonshire_Dock_Hall +https://en.wikipedia.org/wiki/Chakhar_Chamani +https://en.wikipedia.org/wiki/List_of_listed_buildings_in_Saline,_Fife +https://en.wikipedia.org/wiki/Chitala +https://en.wikipedia.org/wiki/Ouachita_Avenue_Historic_District +https://en.wikipedia.org/wiki/Vehicle_registration_plates_of_Guyana +https://en.wikipedia.org/wiki/Antonio_French +https://en.wikipedia.org/wiki/Odontocera_hirundipennis +https://en.wikipedia.org/wiki/Culver_Ridge +https://en.wikipedia.org/wiki/Eordaia +https://en.wikipedia.org/wiki/NER_Class_X +https://en.wikipedia.org/wiki/Darreh_Bang_Rugardengah +https://en.wikipedia.org/wiki/Pandin +https://en.wikipedia.org/wiki/Wells,_Maine +https://en.wikipedia.org/wiki/Cottonera_Lines +https://en.wikipedia.org/wiki/A_Pinch_of_Snuff_(novel) +https://en.wikipedia.org/wiki/B%C3%A0u_Chinh +https://en.wikipedia.org/wiki/Saint-Maurice-des-Champs +https://en.wikipedia.org/wiki/Cheer_Up,_Mr._Lee +https://en.wikipedia.org/wiki/Downtown_Owl_(film) +https://en.wikipedia.org/wiki/2015_Copa_Libertadores_second_stage +https://en.wikipedia.org/wiki/2014%E2%80%9315_Czech_First_League +https://en.wikipedia.org/wiki/Air_Surveillance_Wing_(Estonia) +https://en.wikipedia.org/wiki/Rice_County,_Kansas + diff --git a/readme.txt b/readme.txt new file mode 100755 index 0000000..e69de29 diff --git a/results_rpi-0-proxy.txt b/results_rpi-0-proxy.txt new file mode 100755 index 0000000..bceca82 --- /dev/null +++ b/results_rpi-0-proxy.txt @@ -0,0 +1,33 @@ +proxy probesize threads min(s) med(s) avg(s) max(s) sd(s) err +False 10 1 0.046536 0.061677 0.0752834 0.21199 0.04892309682711792 0 +False 10 5 0.044171 0.060025499999999996 0.06250002 0.109824 0.011338603568215217 0 +False 10 10 0.045522 0.062883 0.06292447999999998 0.109325 0.009743953590060481 0 +False 10 20 0.044138 0.06716050000000001 0.06684208000000003 0.115662 0.011931403712739834 0 +False 25 1 0.04307 0.060673 0.06206852 0.096587 0.013129487439602001 0 +False 25 5 0.042116 0.058163 0.06114224000000001 0.116519 0.011564586651748796 0 +False 25 10 0.041396 0.0629185 0.063441276 0.118276 0.01109555244447144 0 +False 25 20 0.044908 0.065925 0.06736639400000004 0.125421 0.01085174532284405 0 +False 50 1 0.044012 0.060331499999999996 0.0619153 0.110889 0.012387670116214883 0 +False 50 5 0.041966 0.0608965 0.06310128400000004 0.124835 0.012223720540438857 0 +False 50 10 0.043802 0.06290899999999999 0.06402033600000004 0.120987 0.011086957378782594 0 +False 50 20 0.043818 0.06528700000000001 0.06682387600000013 0.141692 0.012191870504603682 0 +False 100 1 0.043002 0.062425499999999995 0.06219341000000002 0.097122 0.010317976518497345 0 +False 100 5 0.042899 0.0624985 0.06292142799999999 0.122822 0.010043609624502273 0 +False 100 10 0.042548 0.0633605 0.06374070699999997 0.126233 0.010756968706513182 0 +False 100 20 0.043454 0.0654035 0.06684689049999999 0.138428 0.012347454857064014 0 +True 10 1 0.470139 0.9776290000000001 1.05756925 2.172159 0.5680755245047591 2 +True 10 5 0.487766 0.9305915 1.1440171 7.760909 1.0792800729053518 0 +True 10 10 0.408449 1.011827 1.720413727272727 38.687804 3.9771223157497753 1 +True 10 20 0.435667 1.4465919999999999 2.9104563349999997 34.889739 4.464876392627195 0 +True 25 1 0.263529 0.624963 0.6393519199999999 1.025937 0.1764719008413993 0 +True 25 5 0.425042 1.067067 1.176152248 3.247047 0.4593388904120203 0 +True 25 10 0.40333 0.8429534999999999 1.917743850806453 68.853489 5.773668797468752 2 +True 25 20 0.118881 1.1690755 1.9691684000000003 73.249322 4.440429642600693 10 +True 50 1 0.378436 0.6261215 0.6401591000000001 1.429998 0.16768655754628783 0 +True 50 5 0.140034 0.957948 1.189219919354839 12.061802 1.2709049157107513 2 +True 50 10 0.106731 1.118524 1.6789639919839705 17.305791 2.0091832750543768 1 +True 50 20 0.37752 1.4820259999999998 2.9884222323943637 66.253599 5.340589560842322 6 +True 100 1 0.279229 0.581473 0.5935158899999999 1.127414 0.13501656527876363 0 +True 100 5 0.399367 0.9562660000000001 1.1202544459999995 4.722667 0.5483744036240559 0 +True 100 10 0.355105 1.275188 1.8825838221105529 32.948261 2.172760972791116 5 +True 100 20 0.343612 1.344229 2.2484285411408367 73.176475 4.620196513875076 19 diff --git a/results_rpi-2-proxy.txt b/results_rpi-2-proxy.txt new file mode 100755 index 0000000..3abb332 --- /dev/null +++ b/results_rpi-2-proxy.txt @@ -0,0 +1,33 @@ +proxy probesize threads min(s) med(s) avg(s) max(s) sd(s) err +False 10 1 0.049546 0.0653085 0.07603079999999998 0.199493 0.04404371009147869 0 +False 10 5 0.045464 0.0650185 0.06628247999999998 0.114825 0.01411616572678723 0 +False 10 10 0.048 0.0643875 0.06524180999999998 0.126871 0.012394218534653466 0 +False 10 20 0.04621 0.06373799999999999 0.06512516000000002 0.117645 0.010342864468874545 0 +False 25 1 0.051174 0.065948 0.06405403999999999 0.076878 0.007594675741816324 0 +False 25 5 0.045273 0.061257 0.06421200799999999 0.126283 0.014068929745557028 0 +False 25 10 0.043393 0.063708 0.064355296 0.120371 0.011935442577065405 0 +False 25 20 0.044336 0.065109 0.066328148 0.138949 0.011817500297298467 0 +False 50 1 0.048006 0.0636265 0.0643729 0.12306 0.012412380209258162 0 +False 50 5 0.042417 0.06325800000000001 0.063484904 0.113597 0.011233843571473539 0 +False 50 10 0.042024 0.06276200000000001 0.06334417 0.122368 0.010571227908814576 0 +False 50 20 0.043562 0.06478600000000001 0.06637446299999993 0.139304 0.01129278563065512 0 +False 100 1 0.043471 0.062607 0.06453615 0.114254 0.013829495429181696 0 +False 100 5 0.041149 0.062817 0.06378914600000005 0.122499 0.011648234664113896 0 +False 100 10 0.041935 0.0627375 0.06360931000000002 0.123082 0.010711365194139458 0 +False 100 20 0.045126 0.065891 0.06705012200000006 0.14236 0.011524006370378684 0 +True 10 1 0.082593 0.091895 0.0933129 0.113011 0.010213139113144618 0 +True 10 5 0.071026 0.1061995 0.21161528000000004 1.135839 0.2810332008110858 0 +True 10 10 0.070867 0.129955 0.3081397900000001 1.973437 0.4357660019152921 0 +True 10 20 0.072841 0.1260475 0.4945304099999999 14.406155 1.4045274907784335 0 +True 25 1 0.080716 0.092062 0.09508203999999999 0.133946 0.013216435265355531 0 +True 25 5 0.069939 0.111354 0.20515491199999997 1.171152 0.2797495376408879 0 +True 25 10 0.076486 0.114143 0.26257808800000004 7.583448 0.7163466317882828 0 +True 25 20 0.080446 0.135388 0.573287568 54.520381 2.893917844544734 0 +True 50 1 0.073323 0.0933065 0.09513179999999999 0.134943 0.01286588577024766 0 +True 50 5 0.0684 0.10697 0.18744250799999992 1.352011 0.2634756590900604 0 +True 50 10 0.07642 0.13082500000000002 0.28653655600000016 3.32304 0.47890398305845555 0 +True 50 20 0.065947 0.14079599999999998 0.45855420799999974 20.562857 1.3681876872329788 0 +True 100 1 0.064478 0.0938725 0.09880818 0.188272 0.018865169940834498 0 +True 100 5 0.071009 0.1527185 0.22219954400000033 1.385164 0.23665895880128826 0 +True 100 10 0.066571 0.1772145 0.3431166930000004 15.582992 0.701055150887099 0 +True 100 20 0.076804 0.174723 0.5054862751375695 31.828925 1.4606876640694766 1 diff --git a/results_rpi-4-proxy.txt b/results_rpi-4-proxy.txt new file mode 100755 index 0000000..1d54839 --- /dev/null +++ b/results_rpi-4-proxy.txt @@ -0,0 +1,33 @@ +proxy probesize threads min(s) med(s) avg(s) max(s) sd(s) err +False 10 1 0.05258 0.063268 0.06401799999999999 0.082845 0.009716211447312625 0 +False 10 5 0.042688 0.0663445 0.06621266 0.116224 0.01346127034004425 0 +False 10 10 0.045348 0.062815 0.06432804999999997 0.116481 0.010837762825328888 0 +False 10 20 0.044482 0.064625 0.06609842000000006 0.131521 0.012003280662255818 0 +False 25 1 0.043608 0.060865 0.06101180000000001 0.076343 0.010558069567870825 0 +False 25 5 0.043438 0.06687 0.06542612000000002 0.120313 0.0115352178822215 0 +False 25 10 0.042775 0.064003 0.071858768 0.319419 0.04381362842286042 0 +False 25 20 0.044654 0.0655105 0.06718682199999998 0.133067 0.012855381613439963 0 +False 50 1 0.04533 0.064195 0.06420501999999999 0.08196 0.009165501412495064 0 +False 50 5 0.04235 0.0613085 0.06369152000000003 0.128451 0.01253812076808884 0 +False 50 10 0.042324 0.0637185 0.06425232800000001 0.120578 0.011820002484851676 0 +False 50 20 0.044236 0.0654525 0.0669509129999999 0.129377 0.01116333680572566 0 +False 100 1 0.044904 0.060164499999999996 0.06269716 0.121312 0.012338375918960151 0 +False 100 5 0.043138 0.0612435 0.06285398799999996 0.124611 0.011235806510604546 0 +False 100 10 0.042357 0.062998 0.06379597200000006 0.128426 0.011318821297236506 0 +False 100 20 0.042693 0.0660605 0.06729693049999994 0.140146 0.011620403794052865 0 +True 10 1 0.078209 0.088172 0.11936390000000001 0.397408 0.09805119093893987 0 +True 10 5 0.071983 0.0960995 0.17419723999999992 1.53029 0.29362031027777125 0 +True 10 10 0.07185 0.1047775 0.2683098399999999 2.788466 0.49892228717301645 0 +True 10 20 0.074289 0.1052255 0.8091272950000004 53.112468 4.383652551157289 0 +True 25 1 0.073915 0.085645 0.08659704000000001 0.12133 0.010799185364646724 0 +True 25 5 0.072272 0.10204 0.139369 1.367289 0.19369664094923308 0 +True 25 10 0.069689 0.11056350000000001 0.21563888399999986 4.465215 0.47754047078931156 0 +True 25 20 0.071833 0.12436050000000001 0.3836931559999997 15.472908 1.1340368640483225 0 +True 50 1 0.071639 0.0826635 0.08565465999999997 0.127944 0.012000784504305355 0 +True 50 5 0.063988 0.10142200000000001 0.1466366 3.160849 0.2453367643449244 0 +True 50 10 0.068776 0.1124955 0.2135674820000001 7.373921 0.5492528008569718 0 +True 50 20 0.057859 0.112275 0.3703267887887888 54.392447 2.33174245914143 1 +True 100 1 0.07133 0.08601500000000001 0.09002922000000008 0.171554 0.019164358265511765 0 +True 100 5 0.060425 0.10495 0.14636869800000002 1.21535 0.19144504160574558 0 +True 100 10 0.057269 0.117783 0.22751823699999982 7.286814 0.46211266535896617 0 +True 100 20 0.063147 0.124401 0.3515133696848417 55.451408 1.7760218566214225 1 diff --git a/results_rpi-pico-proxy.txt b/results_rpi-pico-proxy.txt new file mode 100755 index 0000000..d0c706e --- /dev/null +++ b/results_rpi-pico-proxy.txt @@ -0,0 +1,18 @@ +proxy probesize threads min(s) med(s) avg(s) max(s) sd(s) err +False 10 1 0.048808 0.07022300000000001 0.07672659999999998 0.164185 0.03200964279088413 0 +False 10 5 0.042816 0.062145 0.06268869999999999 0.11818 0.012464839753957398 0 +False 10 10 0.04377 0.06651099999999999 0.06536197000000003 0.106091 0.0103605000127524 0 +False 10 20 0.046672 0.06454299999999999 0.06551675500000004 0.12511 0.010680802788931892 0 +False 25 1 0.042986 0.064072 0.06375331999999999 0.087259 0.009216086957597567 0 +False 25 5 0.042791 0.063396 0.06423262400000002 0.12126 0.012092473756808273 0 +False 25 10 0.045328 0.06397 0.06519232400000004 0.122583 0.012130780239700979 0 +False 25 20 0.044618 0.06596650000000001 0.06687705000000001 0.130979 0.01163324345356875 0 +False 50 1 0.042629 0.06450349999999999 0.06401042 0.116855 0.013042292760579064 0 +False 50 5 0.043338 0.061895 0.063225352 0.122865 0.011386014738202752 0 +False 50 10 0.04348 0.063362 0.06381445799999994 0.124387 0.011777126692810643 0 +False 50 20 0.043025 0.06498 0.06623561 0.138012 0.011070506585606729 0 +False 100 1 0.04209 0.062305 0.06319001 0.111031 0.011515743188423282 0 +False 100 5 0.042515 0.063029 0.06356240399999996 0.124533 0.011557744523119872 0 +False 100 10 0.041643 0.063434 0.06410064999999998 0.296015 0.013086469614853368 0 +False 100 20 0.043326 0.06589149999999999 0.06913456299999998 0.199696 0.018131998587646737 0 +True 10 1 2.373214 3.485658 4.0446436 9.595809 2.21955089424551 0 diff --git a/results_rpi-proxies.txt b/results_rpi-proxies.txt new file mode 100755 index 0000000..afc1ffd --- /dev/null +++ b/results_rpi-proxies.txt @@ -0,0 +1,66 @@ +rpi proxy probesize threads min(s) med(s) avg(s) max(s) sd(s) err +PC False 10 1 0.047994 0.068465 0.0641226 0.076412 0.00997050435813332 0 +PC False 10 5 0.042907 0.0621605 0.06177637999999999 0.07904 0.00913705761795763 0 +PC False 10 10 0.045021 0.0625115 0.06314348000000002 0.114743 0.009697101832459577 0 +PC False 10 20 0.045928 0.064854 0.06615820500000001 0.131961 0.013274161851025135 0 +PC False 25 1 0.047917 0.067809 0.06485632 0.097195 0.010472119372091465 0 +PC False 25 5 0.042642 0.067011 0.064889376 0.092108 0.009833453011206843 0 +PC False 25 10 0.043023 0.0617315 0.06389453599999997 0.114211 0.010948782880437253 0 +PC False 25 20 0.045483 0.0658605 0.06693427800000008 0.114855 0.011041971881996515 0 +PC False 50 1 0.04415 0.06783349999999999 0.06756248000000001 0.116407 0.0145781397055695 0 +PC False 50 5 0.044018 0.061857499999999996 0.06300734399999999 0.120387 0.01044973487959764 0 +PC False 50 10 0.041109 0.0628705 0.06405227600000003 0.122661 0.011921940694352713 0 +PC False 50 20 0.042441 0.06539500000000001 0.06702196199999993 0.143467 0.012588986843280763 0 +PC False 100 1 0.043411 0.0599915 0.06288284999999999 0.123584 0.014168585276408107 0 +PC False 100 5 0.042401 0.0642625 0.06562582 0.294081 0.0227362529373435 0 +PC False 100 10 0.042718 0.06338099999999999 0.06376098500000005 0.124887 0.011260088078621472 0 +PC False 100 20 0.043766 0.066694 0.06886808450000007 0.296573 0.016201935733427503 0 +rpi-4 True 10 1 0.078209 0.088172 0.11936390000000001 0.397408 0.09805119093893987 0 +rpi-4 True 10 5 0.071983 0.0960995 0.17419723999999992 1.53029 0.29362031027777125 0 +rpi-4 True 10 10 0.07185 0.1047775 0.2683098399999999 2.788466 0.49892228717301645 0 +rpi-4 True 10 20 0.074289 0.1052255 0.8091272950000004 53.112468 4.383652551157289 0 +rpi-4 True 25 1 0.073915 0.085645 0.08659704000000001 0.12133 0.010799185364646724 0 +rpi-4 True 25 5 0.072272 0.10204 0.139369 1.367289 0.19369664094923308 0 +rpi-4 True 25 10 0.069689 0.11056350000000001 0.21563888399999986 4.465215 0.47754047078931156 0 +rpi-4 True 25 20 0.071833 0.12436050000000001 0.3836931559999997 15.472908 1.1340368640483225 0 +rpi-4 True 50 1 0.071639 0.0826635 0.08565465999999997 0.127944 0.012000784504305355 0 +rpi-4 True 50 5 0.063988 0.10142200000000001 0.1466366 3.160849 0.2453367643449244 0 +rpi-4 True 50 10 0.068776 0.1124955 0.2135674820000001 7.373921 0.5492528008569718 0 +rpi-4 True 50 20 0.057859 0.112275 0.3703267887887888 54.392447 2.33174245914143 1 +rpi-4 True 100 1 0.07133 0.08601500000000001 0.09002922000000008 0.171554 0.019164358265511765 0 +rpi-4 True 100 5 0.060425 0.10495 0.14636869800000002 1.21535 0.19144504160574558 0 +rpi-4 True 100 10 0.057269 0.117783 0.22751823699999982 7.286814 0.46211266535896617 0 +rpi-4 True 100 20 0.063147 0.124401 0.3515133696848417 55.451408 1.7760218566214225 1 +rpi-2 True 10 1 0.082593 0.091895 0.0933129 0.113011 0.010213139113144618 0 +rpi-2 True 10 5 0.071026 0.1061995 0.21161528000000004 1.135839 0.2810332008110858 0 +rpi-2 True 10 10 0.070867 0.129955 0.3081397900000001 1.973437 0.4357660019152921 0 +rpi-2 True 10 20 0.072841 0.1260475 0.4945304099999999 14.406155 1.4045274907784335 0 +rpi-2 True 25 1 0.080716 0.092062 0.09508203999999999 0.133946 0.013216435265355531 0 +rpi-2 True 25 5 0.069939 0.111354 0.20515491199999997 1.171152 0.2797495376408879 0 +rpi-2 True 25 10 0.076486 0.114143 0.26257808800000004 7.583448 0.7163466317882828 0 +rpi-2 True 25 20 0.080446 0.135388 0.573287568 54.520381 2.893917844544734 0 +rpi-2 True 50 1 0.073323 0.0933065 0.09513179999999999 0.134943 0.01286588577024766 0 +rpi-2 True 50 5 0.0684 0.10697 0.18744250799999992 1.352011 0.2634756590900604 0 +rpi-2 True 50 10 0.07642 0.13082500000000002 0.28653655600000016 3.32304 0.47890398305845555 0 +rpi-2 True 50 20 0.065947 0.14079599999999998 0.45855420799999974 20.562857 1.3681876872329788 0 +rpi-2 True 100 1 0.064478 0.0938725 0.09880818 0.188272 0.018865169940834498 0 +rpi-2 True 100 5 0.071009 0.1527185 0.22219954400000033 1.385164 0.23665895880128826 0 +rpi-2 True 100 10 0.066571 0.1772145 0.3431166930000004 15.582992 0.701055150887099 0 +rpi-2 True 100 20 0.076804 0.174723 0.5054862751375695 31.828925 1.4606876640694766 1 +rpi-0 True 10 1 0.470139 0.9776290000000001 1.05756925 2.172159 0.5680755245047591 2 +rpi-0 True 10 5 0.487766 0.9305915 1.1440171 7.760909 1.0792800729053518 0 +rpi-0 True 10 10 0.408449 1.011827 1.720413727272727 38.687804 3.9771223157497753 1 +rpi-0 True 10 20 0.435667 1.4465919999999999 2.9104563349999997 34.889739 4.464876392627195 0 +rpi-0 True 25 1 0.263529 0.624963 0.6393519199999999 1.025937 0.1764719008413993 0 +rpi-0 True 25 5 0.425042 1.067067 1.176152248 3.247047 0.4593388904120203 0 +rpi-0 True 25 10 0.40333 0.8429534999999999 1.917743850806453 68.853489 5.773668797468752 2 +rpi-0 True 25 20 0.118881 1.1690755 1.9691684000000003 73.249322 4.440429642600693 10 +rpi-0 True 50 1 0.378436 0.6261215 0.6401591000000001 1.429998 0.16768655754628783 0 +rpi-0 True 50 5 0.140034 0.957948 1.189219919354839 12.061802 1.2709049157107513 2 +rpi-0 True 50 10 0.106731 1.118524 1.6789639919839705 17.305791 2.0091832750543768 1 +rpi-0 True 50 20 0.37752 1.4820259999999998 2.9884222323943637 66.253599 5.340589560842322 6 +rpi-0 True 100 1 0.279229 0.581473 0.5935158899999999 1.127414 0.13501656527876363 0 +rpi-0 True 100 5 0.399367 0.9562660000000001 1.1202544459999995 4.722667 0.5483744036240559 0 +rpi-0 True 100 10 0.355105 1.275188 1.8825838221105529 32.948261 2.172760972791116 5 +rpi-0 True 100 20 0.343612 1.344229 2.2484285411408367 73.176475 4.620196513875076 19 +rpi-pico True 10 1 2.373214 3.485658 4.0446436 9.595809 2.21955089424551 0 diff --git a/simple_server.py b/simple_server.py new file mode 100755 index 0000000..88e845f --- /dev/null +++ b/simple_server.py @@ -0,0 +1,94 @@ +import time +import network +import socket +import machine + +from machine import Pin + +intled = machine.Pin("LED", machine.Pin.OUT) + +ssid = 'Lars-WLAN' +password = '0243LHBS18021909' + +wlan = network.WLAN(network.STA_IF) +wlan.active(True) +wlan.connect(ssid, password) + +html = """ + + Pico W +

Pico W

+

Hello World

+

+ Turn Light On +

+

+ Turn Light Off +

+
+ + +""" + +# Wait for connect or fail +max_wait = 10 +while max_wait > 0: + if wlan.status() < 0 or wlan.status() >= 3: + break + max_wait -= 1 + print('waiting for connection...') + time.sleep(1) + +# Handle connection error +if wlan.status() != 3: + raise RuntimeError('network connection failed') +else: + print('connected') + status = wlan.ifconfig() + print( 'ip = ' + status[0] ) + +# Open socket +addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] + +s = socket.socket() +s.bind(addr) +s.listen(1) + +print('listening on', addr) + +stateis = "" + +# Listen for connections +while True: + try: + cl, addr = s.accept() + print('client connected from', addr) + + request = cl.recv(1024) + print(request) + + request = str(request) + led_on = request.find('/light/on') + led_off = request.find('/light/off') + print( 'led on = ' + str(led_on)) + print( 'led off = ' + str(led_off)) + + if led_on == 6: + print("led on") + intled.value(1) + stateis = "LED is ON" + + if led_off == 6: + print("led off") + intled.value(0) + stateis = "LED is OFF" + + response = html + stateis + + cl.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') + cl.send(response) + cl.close() + + except OSError as e: + cl.close() + print('connection closed') diff --git a/wlan_scan.py b/wlan_scan.py new file mode 100755 index 0000000..34e6991 --- /dev/null +++ b/wlan_scan.py @@ -0,0 +1,106 @@ +import logging +import network +import binascii +import machine +from math import sqrt + + +class WlanSniffer: + def fibonacci_n(n): + fivesqrt = sqrt(5) + return int((((1+fivesqrt)/2)**n-((1-fivesqrt)/2)**n)/fivesqrt) + + def __init__(self, led_pin="LED", retry_limit=0, logger=None): + self.led = machine.Pin(led_pin, machine.Pin.OUT) + self.wlan = network.WLAN(network.STA_IF) + self.detected_wlans = {} + self.retry_limit = 1 if retry_limit == 0 else retry_limit + self.fibonacci = retry_limit == 0 + self.logger = logger if logger is not None else logging.getLogger() + + self.wlan.active(True) + + + def _update_retry_limit(self, round): + return ( + WlanSniffer.fibonacci_n(round) + if self.fibonacci + else + self.retry_limit + ) + + + def w_led_on(self): + self.led.value(1) + + + def w_led_off(self): + self.led.value(0) + + + def get_sniffed_wlans(self): + return dict(self.detected_wlans) + + + def sniff(self): + retry_limit = self.retry_limit + found_round = 0 + retry = 0 + + self.w_led_on() + + while retry != retry_limit: + self.logger.debug(f"Retry {retry}") + found_wlans = self.wlan.scan() + retry += 1 + detected = False + + for found_wlan in found_wlans: + ssid, bssid, channel, RSSI, security, hidden = found_wlan + ssid = ssid.decode('utf-8') + bssid = binascii.hexlify(bssid).decode() + if ssid not in self.detected_wlans.keys(): + self.detected_wlans[ssid] = { + "ssid": ssid, + "bssid": [bssid], + "channel": channel, + "rssi": RSSI, + "security": security, + "hidden": hidden, + "order_found": found_round + } + self.logger.info(f"NEW WLAN: {self.detected_wlans[ssid]}") + detected = True + elif bssid not in self.detected_wlans[ssid]['bssid']: + self.logger.info(f"NEW BSSID: {bssid} for WLAN '{ssid}'") + self.detected_wlans[ssid]['bssid'].append(bssid) + if detected: + found_round += 1 + old_retry_limit = retry_limit + retry_limit = self._update_retry_limit(found_round) + self.logger.debug(f"Set retry limit from {old_retry_limit} to {retry_limit}") + detected = False + retry = 0 + self.w_led_off() + + +def main(): + logging.basicConfig(level=logging.DEBUG) + log = logging.getLogger("WlanSniffer") + sniffer = WlanSniffer(logger=log) + sniffer.sniff() + wlans = sniffer.get_sniffed_wlans() + + with open("sniffed_wlans.txt", "w") as file: + for wlan in wlans.values(): + line = ", ".join( + f"{str(k)}:{str(v)}" + for k,v in wlan.items() + ) + log.info(f"Found WLAN: {line}") + print(f"Found WLAN: {line}") + file.write(f"{line}\n") + +if __name__ == "__main__": + main() +