From 9cb37654fc1d62503685625b4aaca7e5507baa23 Mon Sep 17 00:00:00 2001 From: devoalda Date: Mon, 20 Mar 2023 20:49:26 +0800 Subject: [PATCH] Added SSL for Client and server. LIST still buggy. --- SSL/certificate.pem | 33 +++++++++++++++++++++ SSL/privatekey.pem | 52 +++++++++++++++++++++++++++++++++ client.py | 70 ++++++++++++++++++++++++++++++++++----------- config/config.ini | 7 +++++ server.py | 62 +++++++++++++++++++++++++-------------- 5 files changed, 185 insertions(+), 39 deletions(-) create mode 100644 SSL/certificate.pem create mode 100644 SSL/privatekey.pem create mode 100644 config/config.ini diff --git a/SSL/certificate.pem b/SSL/certificate.pem new file mode 100644 index 0000000..57f84ce --- /dev/null +++ b/SSL/certificate.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +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 +-----END CERTIFICATE----- diff --git a/SSL/privatekey.pem b/SSL/privatekey.pem new file mode 100644 index 0000000..01c6fc0 --- /dev/null +++ b/SSL/privatekey.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +jsryOfVmeGQ4HMfMdrYsCMTG4QBDo84= +-----END PRIVATE KEY----- diff --git a/client.py b/client.py index 341d3ed..5b32044 100644 --- a/client.py +++ b/client.py @@ -1,19 +1,34 @@ import os -import socket +from socket import * +import configparser +import ssl +import sys + CLIENT_FILE = "clientfile" BUFFER_SIZE = 1024 +# TODO: Buggy after some time, Not everything is transmitted def handle_list(conn, args): + # send command over + conn.sendall("LIST\r\n".encode()) + num_of_files_received = 0 - num_of_files = int(conn.recv(BUFFER_SIZE).decode('utf-8')) - print("Total files in directory: "+ str(num_of_files)) + num_of_files = 0 + # Receive response from server + # Decode bytes to string + response = conn.recv(BUFFER_SIZE).decode('utf-8').strip() + try: + num_of_files = int(response) + except ValueError: + print(response) + return + while num_of_files_received < num_of_files: - file_info = conn.recv(BUFFER_SIZE).decode('utf-8') - print(file_info) + # Print response + print(conn.recv(BUFFER_SIZE).decode('utf-8').strip()) num_of_files_received += 1 - response = conn.recv(BUFFER_SIZE).decode('utf-8') - print(response) + def handle_quit(conn, args): conn.sendall("QUIT\r\n".encode()) @@ -24,6 +39,7 @@ def handle_quit(conn, args): conn.close() return True + def handle_DWLD(conn, args): # send command over filename = args @@ -41,6 +57,7 @@ def handle_DWLD(conn, args): print(response) os.chdir("../") + def handle_UPLD(conn, args): filename = args os.chdir(os.path.abspath(CLIENT_FILE)) @@ -50,15 +67,15 @@ def handle_UPLD(conn, args): bytes_sent = 0 file_size = os.path.getsize(filename) conn.sendall(f"UPLD {filename} {file_size}\r".encode()) - #does file exist at the server? + # does file exist at the server? serverExist = conn.recv(BUFFER_SIZE).decode('utf-8') - #if server already has the file + # if server already has the file if serverExist == 'T': - #do you want to overwrite? + # do you want to overwrite? print(conn.recv(BUFFER_SIZE).decode('utf-8')) overwrite = input() conn.sendall(overwrite.encode('utf-8')) - #yes, overwrite + # yes, overwrite if overwrite.upper() == 'Y': with open(filename, "rb") as f: while bytes_sent < file_size: @@ -68,12 +85,12 @@ def handle_UPLD(conn, args): response = conn.recv(BUFFER_SIZE).decode().strip() print(response) os.chdir("../") - #no, dont overwrite + # no, dont overwrite elif overwrite.upper() == 'N': response = conn.recv(BUFFER_SIZE).decode().strip() print(response) os.chdir("../") - #server doesnt have the file + # server doesnt have the file elif serverExist == 'F': print(conn.recv(BUFFER_SIZE).decode('utf-8')) with open(filename, "rb") as f: @@ -85,11 +102,13 @@ def handle_UPLD(conn, args): print(response) os.chdir("../") + def handle_HELP(conn, args): conn.sendall(f"HELP\r".encode()) response = conn.recv(BUFFER_SIZE).decode('utf-8') print(response) + def user_input(): # Get user input user_input = input("> ").strip() @@ -103,8 +122,12 @@ def user_input(): return command, args -def ftp_cient(host, port): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +def ftp_client(host, port, cert): + context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + context.load_verify_locations(cert) + context.check_hostname = False + + sock = context.wrap_socket(socket(AF_INET, SOCK_STREAM), server_hostname=host) sock.connect((host, port)) response = sock.recv(BUFFER_SIZE).decode().strip() @@ -119,7 +142,7 @@ def ftp_cient(host, port): command, args = user_input, "" if command.upper() == "LIST": - sock.sendall("LIST\r\n".encode()) + # sock.sendall("LIST\r\n".encode()) handle_list(sock, args) elif command.upper() == "QUIT": handle_quit(sock, args) @@ -146,4 +169,17 @@ def ftp_cient(host, port): if __name__ == "__main__": # FTP Client should be able to define IP and port - ftp_cient("127.0.0.1", 2001) + config = configparser.ConfigParser() + config.read('./config/config.ini') + ip = config['FTPSERVER']['ip'] + port = int(config['FTPSERVER']['port']) + cert = config['SSL']['cert'] + + try: + ftp_client(ip, port, cert) + except KeyboardInterrupt: + print("Client interrupted") + try: + sys.exit(0) + except SystemExit: + os._exit(0) diff --git a/config/config.ini b/config/config.ini new file mode 100644 index 0000000..703ddb7 --- /dev/null +++ b/config/config.ini @@ -0,0 +1,7 @@ +[FTPSERVER] +ip = 127.0.0.1 +port = 5000 + +[SSL] +cert = ./SSL/certificate.pem +key = ./SSL/privatekey.pem \ No newline at end of file diff --git a/server.py b/server.py index 98f069a..40c46df 100644 --- a/server.py +++ b/server.py @@ -5,17 +5,19 @@ import sys import threading import struct import time +import configparser BUFFER_SIZE = 1024 SERVER_FILE = "serverfile" +# TODO: Buggy after some time, Not everything is transmitted def handle_list(conn, args): #conn.send(b'150 Opening ASCII mode data connection for file list.\n') #home_directory = os.path.expanduser('~') os.chdir(os.path.abspath(SERVER_FILE)) files = os.listdir(os.getcwd()) #send number of files over - conn.sendall(str(len(files)).encode('utf-8')) + conn.sendall(str(len(files)).encode('utf-8') +b'\r\n') for file in files: file_info = os.stat(file) @@ -29,7 +31,6 @@ def handle_list(conn, args): if not files: conn.sendall(b'226 No files in directory.\n') os.chdir("../") - conn.sendall(b'226 Transfer complete.\n') def handle_upload(conn, args): filename = args[1] @@ -161,22 +162,28 @@ def handle_connection(conn): conn.sendall(b'220 Welcome to the FTP server.\n') while True: - - data = conn.recv(BUFFER_SIZE).decode() - print(f'Server received: {data}') - if not data: - break - args = data.split() - command = args[0] - print(f'Command: {command}') - if command in commands: - if commands == handle_quit: - conn.sendall(b'Closing Connection') - conn.close() + try: + data = conn.recv(BUFFER_SIZE).decode() + print(f'Server received: {data}') + if not data: break - commands[command](conn, args) - else: - conn.sendall(b'Invalid command.\n') + args = data.split() + command = args[0] + print(f'Command: {command}') + if command in commands: + if commands == handle_quit: + conn.sendall(b'Closing Connection') + conn.close() + break + commands[command](conn, args) + else: + conn.sendall(b'Invalid command.\n') + except ConnectionResetError: + print('Connection closed by client') + break + except OSError as e: + print(e) + break # For FTP client testing #handle_pwd(conn, []) @@ -192,12 +199,23 @@ def main(): # print("Usage: python3 server.py ") # return - #context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - #context.load_cert_chain("certificate.pem", "privatekey.pem") + config = configparser.ConfigParser() + config.read('./config/config.ini') - port = 2001 - #server = context.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_side=True) - server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + # TODO: Need to allow user to define port through command line as well + port = int(config['FTPSERVER']['port']) + cert = config['SSL']['cert'] + key = config['SSL']['key'] + + # print(cert, key) + + context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) + # context.load_cert_chain("./SSL/certificate.pem", "./SSL/privatekey.pem") + context.load_cert_chain(cert,key) + + #port = 2001 + server = context.wrap_socket(socket.socket(socket.AF_INET, socket.SOCK_STREAM), server_side=True) + # server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(('', port)) server.listen(1) print("Listening on port", port)