Git initial commit

This commit is contained in:
Lars Hahn 2023-08-19 23:37:59 +02:00
commit 16260400ca
15 changed files with 1926 additions and 0 deletions

6
led_blink.py Executable file
View File

@ -0,0 +1,6 @@
from machine import Pin
import time
led = machine.Pin("LED", machine.Pin.OUT)
while True:
led.toggle()
time.sleep(1)

320
pico_proxy.py Executable file
View File

@ -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()

265
picoproxy.py Executable file
View File

@ -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()

View File

@ -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()

519
proxy.py Executable file
View File

@ -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)

140
random_requests.py Executable file
View File

@ -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()

51
random_wiki_links.txt Executable file
View File

@ -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

0
readme.txt Executable file
View File

33
results_rpi-0-proxy.txt Executable file
View File

@ -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

33
results_rpi-2-proxy.txt Executable file
View File

@ -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

33
results_rpi-4-proxy.txt Executable file
View File

@ -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

18
results_rpi-pico-proxy.txt Executable file
View File

@ -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

66
results_rpi-proxies.txt Executable file
View File

@ -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

94
simple_server.py Executable file
View File

@ -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 = """<!DOCTYPE html>
<html>
<head> <title>Pico W</title> </head>
<body> <h1>Pico W</h1>
<p>Hello World</p>
<p>
<a href='/light/on'>Turn Light On</a>
</p>
<p>
<a href='/light/off'>Turn Light Off</a>
</p>
<br>
</body>
</html>
"""
# 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')

106
wlan_scan.py Executable file
View File

@ -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()