93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
|
import socket
|
||
|
import select
|
||
|
import threading
|
||
|
import paramiko
|
||
|
|
||
|
class ParamikoConnection(paramiko.ServerInterface):
|
||
|
def __init__(self, server):
|
||
|
self.event = threading.Event()
|
||
|
self.server = server
|
||
|
|
||
|
def check_channel_request(self, kind, chanid):
|
||
|
print("check_channel_request", kind, chanid)
|
||
|
if kind == 'session':
|
||
|
return paramiko.OPEN_SUCCEEDED
|
||
|
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||
|
|
||
|
def check_auth_password(self, username, password):
|
||
|
if self.server.on_ssh_login:
|
||
|
return self.server.on_ssh_login(username, password)
|
||
|
|
||
|
print("check_auth_password", username, password)
|
||
|
return paramiko.AUTH_SUCCESSFUL
|
||
|
|
||
|
class SSHServer:
|
||
|
|
||
|
def __init__(self, addr='0.0.0.0', port=22):
|
||
|
self.server_address = addr
|
||
|
self.listen_port = port
|
||
|
self.listen_socket = None
|
||
|
self.listen_thread = None
|
||
|
self.client_sockets = []
|
||
|
self.transports = []
|
||
|
self.verbose = True
|
||
|
self.on_message = None
|
||
|
self.is_running = True
|
||
|
self.private_key = None
|
||
|
|
||
|
# hooks
|
||
|
self.on_ssh_login = None
|
||
|
|
||
|
|
||
|
def load_private_key_from_file(self, path):
|
||
|
with open(path, "r") as f:
|
||
|
self.private_key = paramiko.RSAKey.from_private_key(f)
|
||
|
|
||
|
def start(self):
|
||
|
|
||
|
if self.private_key is None:
|
||
|
self.private_key = paramiko.RSAKey.generate(2048)
|
||
|
|
||
|
self.listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||
|
self.listen_socket.setblocking(False)
|
||
|
self.listen_socket.bind((self.server_address, self.listen_port))
|
||
|
self.listen_socket.listen()
|
||
|
if self.verbose:
|
||
|
print(f"SSH server running on: {self.server_address}:{self.listen_port}")
|
||
|
try:
|
||
|
while self.is_running:
|
||
|
try:
|
||
|
client_socket, client_address = self.listen_socket.accept()
|
||
|
if self.verbose:
|
||
|
print("Incoming connection:", client_address)
|
||
|
self.client_sockets.append(client_socket)
|
||
|
transport = paramiko.Transport(client_socket)
|
||
|
transport.add_server_key(self.private_key)
|
||
|
paramiko_connection = ParamikoConnection(self)
|
||
|
transport.start_server(server=paramiko_connection)
|
||
|
self.transports.append(transport)
|
||
|
# for client_sock in self.client_sockets:
|
||
|
|
||
|
except BlockingIOError:
|
||
|
pass
|
||
|
# handle_client(client_socket, client_address)
|
||
|
finally:
|
||
|
self.listen_socket.close()
|
||
|
|
||
|
def start_background(self):
|
||
|
self.listen_thread = threading.Thread(target=self.start)
|
||
|
self.listen_thread.start()
|
||
|
return self.listen_thread
|
||
|
|
||
|
def close(self):
|
||
|
if self.listen_socket:
|
||
|
self.listen_socket.close()
|
||
|
for sock in self.client_sockets:
|
||
|
sock.close()
|
||
|
|
||
|
def stop(self):
|
||
|
self.is_running = False
|
||
|
self.close()
|
||
|
if self.listen_thread:
|
||
|
self.listen_thread.join()
|