import socket
import threading
from ldap3.protocol.rfc4511 import LDAPMessage, BindRequest, BindResponse, SearchRequest
from pyasn1.codec.ber import decoder, encoder

class LDAPProxyServer:
	def __init__(self):
		self.host = '0.0.0.0'
		self.port = 3389
		self.target_host = 'practicum.cs.msu.su'
		self.target_port = 389
		self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
		self.modified_uri = "ldap://practicum.cs.msu.su:389"
		self.modified_bindpw = 'wH=8L9k!4%Ry'

	def start(self):
		try:
			self.server.bind((self.host, self.port))
			self.server.listen(5)
			print(f"artos_server at {self.host}:{self.port}")
			
			while True:
				client_socket, address = self.server.accept()
				print(f"Connected: {address}")
				client_handler = threading.Thread(
					target=self.handle_client,
					args=(client_socket, address)
				)
				client_handler.start()
				
		except Exception as e:
			print(f"Error: {e}")
		finally:
			self.server.close()
	def handle_client(self, client_socket, address):
		target_socket = None
		try:
			target_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
			target_socket.connect((self.target_host, self.target_port))
			print(f"Tunnel: {address} - {self.target_host}:{self.target_port}")

			def forward(source, dest, modify):
				first = True
				try:
					while True:
						data = source.recv(8192)
						if not data:
							break
						if modify:
							first = False
							data = self.modify(data)
						dest.send(data)
				except Exception as e:
					print(f"Error: {e}")

			client_to_target = threading.Thread(
				target=forward, args=(client_socket, target_socket, True)
			)
			target_to_client = threading.Thread(
				target=forward, args=(target_socket, client_socket, False)
			)
			
			client_to_target.daemon = True
			target_to_client.daemon = True
			client_to_target.start()
			target_to_client.start()
			
			client_to_target.join()
			target_to_client.join()

		except Exception as e:
			print(f"Error: {e}")
		finally:
			if target_socket:
				target_socket.close()
			client_socket.close()
			print(f"Closed: {address}")

	def modify(self, data):
		try:
			decoded, _ = decoder.decode(data, asn1Spec=LDAPMessage())
		except:
			return data
		if not decoded:
			print(decoded)
			print(_)
			#if 'bindRequest' in decoded['protocolOp']:
			decoded['protocolOp']['bindRequest']['authentication']['simple'] = self.modified_bindpw.encode()
			#decoded['protocolOp']['searchRequest']['attributes']['uri'] = self.modified_uri.encode()
			modified_data = encoder.encode(decoded)
			#decoded, _ = decoder.decode(data, asn1Spec=LDAPMessage())
			print(decoded)
			print(_)
			return modified_data
		return data

if __name__ == "__main__":
	server = LDAPProxyServer()
	server.start()
