Skip to content
Permalink
main
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
Latest commit 1214bda Oct 22, 2024 History
0 contributors

Users who have contributed to this file

import _thread as thread
import re
import socket
import sys
# Constants
ADDR = "localhost"
PORT = 50009
MAX_CONNS = 10
# Enable print logging for debugging
_LOGGING = False
def printLog(message: str) -> None:
"""Print the message if logging is enabled
:param message: The message to print
:type message: str
"""
if _LOGGING:
print(message)
def find_matching_words(query: str) -> list:
"""Find matching partial strings in words from the wordlist.txt file based on the query
:param query: The query partial string to search for
:type query: str
:return: List of matching words
:rtype: list
"""
# Loop through every character and add a "\" to escape special characters
query = "".join(f"\\{character}" if character in "[](){}.*+^$|\\" else character for character in query)
# Replace ? with . to match any character and add .* at the start and end to match any characters before and after the query
pattern = f".*{query.replace("?", ".")}.*"
with open("wordlist.txt", "r") as file:
pattern = f".*{query.replace("?", ".")}.*"
# Use regex to match the pattern with a word
matching_words = [
word.strip() for word in file if re.match(pattern, word.strip())
]
return matching_words
def handle_client(connection: socket.socket, clientAddress: tuple) -> None:
printLog(f"Connected by {clientAddress}")
while True:
encodedData = b""
while True:
# Receive data in chunks of 1024 bytes
encodedDataChunk = connection.recv(1024)
if not encodedDataChunk:
break
encodedData += encodedDataChunk
# Check if the data received is incomplete
if len(encodedDataChunk) < 1024 and encodedDataChunk[-3:] != b"END":
printLog("Data received is incomplete, exiting...")
connection.close()
return
# Data send is complete once the END tag is received
if encodedDataChunk[-3:] == b"END":
break
try:
data = encodedData.decode()
except UnicodeDecodeError:
printLog("Data is not in unicode format")
# Status code 400 indicates a unicode error
connection.send("400:Data is not in unicode format:END".encode())
# Should never happen, but can continue listening for more queries from the client
continue
printLog(f"Data Received: {data}")
# Status code 201 indicates a client disconnection
if data.startswith("201"):
printLog("Client disconnected")
connection.close()
return
if not data.startswith("200"):
printLog(f"Unexpected query: {data}")
# Status code 401 indicates an unexpected query
connection.send("401:Unexpected query:END".encode())
# Can continue listening for more queries from the client
continue
# Extract the query from in between the Status Code and END tags
query = data.split(":")[1]
if not query:
printLog("Empty query")
# Status code 402 indicates an empty query
connection.send("402:Empty query:END".encode())
# Can continue listening for more queries from the client
continue
print(f"Client Query: {query}")
# Check if the query contains only letters and symbols no numbers
if any(character.isnumeric() for character in query):
printLog("Invalid query")
# Status code 403 indicates an invalid query
connection.send("403:Invalid query:END".encode())
# Can continue listening for more queries from the client
continue
# Find the matching words based on the query
matchingWords = find_matching_words(query)
wordCount = len(matchingWords)
if not matchingWords:
printLog("No matching words found")
# Create the response with the matching words, status code 100 indicates a successful response
response = f"100:{wordCount}:{','.join(matchingWords)}:END"
connection.send(response.encode())
printLog(f"Sent response: {response}")
# Continue listening for more queries from the client
def run_server() -> None:
"""Run the server to listen for incoming connections and respond to queries from clients"""
try:
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.bind((ADDR, PORT))
serverSocket.listen(MAX_CONNS)
except socket.error as e:
print(f"Socket Error: {e}")
return
print(
f"Server started at address {ADDR} on port {PORT} with {MAX_CONNS} max connection(s)"
)
while True:
printLog("Waiting for connection...")
connection, clientAddress = serverSocket.accept()
# Handle the client connection in a separate function for easier implementation of multi-threading
thread.start_new_thread(handle_client, (connection, clientAddress))
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == "-debug":
_LOGGING = True
run_server()