0

am getting this error 8:51:19 AM | Trace @8F596749-638656982249418103 Error downloading or archiving file: EOF o...rred in violation of protocol (_ssl.c:2706), when am sending request to the az function am using explicit ftp over tls encryption, this is my code: import azure.functions as func

import ftplib
import json
import base64
import io
import ssl

def main(req: func.HttpRequest) -> func.HttpResponse:
    try:
        params = req.get_json()
        host = params.get('host')
        port = params.get('port', 21)
        username = params.get('username')
        password = params.get('password')
        remote_dir = params.get('remote_dir')
        action = params.get('action', 'list')
        filename = params.get('filename')
        file_content = params.get('file_content')
        archive = params.get('archive', 'no')

        print(f"Received request: Action={action}, Host={host}, Port={port}, Username={username}, Remote Dir={remote_dir}, Archive={archive}")

        if action == 'list':
            files = list_ftp_files(host, port, username, password, remote_dir)
            if files:
                print(f"Successfully listed {len(files)} files")
                return func.HttpResponse(json.dumps({"success": True, "files": files}), mimetype="application/json")
            else:
                print("Failed to retrieve files")
                return func.HttpResponse(json.dumps({"success": False, "error": "Failed to retrieve files"}), mimetype="application/json")
        
        elif action == 'download':
            if not filename:
                print("Download requested but filename not provided")
                return func.HttpResponse(json.dumps({"success": False, "error": "Filename not provided"}), mimetype="application/json")
            
            print(f"Attempting to download file: {filename}")
            try:
                file_content, archived = download_ftp_file(host, port, username, password, remote_dir, filename, archive.lower() == 'yes')
                if file_content:
                    encoded_content = base64.b64encode(file_content).decode('utf-8')
                    print(f"Successfully downloaded file: {filename}, Size: {len(file_content)} bytes, Archived: {archived}")
                    return func.HttpResponse(json.dumps({
                        "success": True, 
                        "filename": filename, 
                        "content": encoded_content,
                        "size": len(file_content),
                        "archived": archived
                    }), mimetype="application/json")
                else:
                    print(f"Failed to download file: {filename}")
                    return func.HttpResponse(json.dumps({"success": False, "error": f"Failed to download file {filename}"}), mimetype="application/json")
            except Exception as e:
                print(f"Error during file download: {str(e)}")
                return func.HttpResponse(json.dumps({"success": False, "error": f"Error downloading file {filename}: {str(e)}"}), mimetype="application/json")
        
        elif action == 'upload':
            if not filename or not file_content:
                print("Upload requested but filename or file content not provided")
                return func.HttpResponse(json.dumps({"success": False, "error": "Filename and file content are required for upload"}), mimetype="application/json")
            
            print(f"Attempting to upload file: {filename}")
            try:
                success = upload_ftp_file(host, port, username, password, remote_dir, filename, file_content)
                if success:
                    print(f"Successfully uploaded file: {filename}")
                    return func.HttpResponse(json.dumps({"success": True, "message": f"File {filename} uploaded successfully"}), mimetype="application/json")
                else:
                    print(f"Failed to upload file: {filename}")
                    return func.HttpResponse(json.dumps({"success": False, "error": f"Failed to upload file {filename}"}), mimetype="application/json")
            except Exception as e:
                print(f"Error during file upload: {str(e)}")
                return func.HttpResponse(json.dumps({"success": False, "error": f"Error uploading file {filename}: {str(e)}"}), mimetype="application/json")
        
        else:
            print(f"Invalid action requested: {action}")
            return func.HttpResponse(json.dumps({"success": False, "error": "Invalid action"}), mimetype="application/json")

    except Exception as e:
        print(f"Unexpected error in main function: {str(e)}")
        return func.HttpResponse(json.dumps({"success": False, "error": f"An unexpected error occurred: {str(e)}"}), mimetype="application/json")


def connect_ftp(host, port, username, password):
    try:
        # Create a context that doesn't verify certificates
        context = ssl.SSLContext(ssl.PROTOCOL_TLS)
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE

        # Establishing a secure FTP_TLS connection with custom SSL context
        ftp = ftplib.FTP_TLS()
        ftp.context = context  # Assign the modified context to FTP_TLS
        ftp.connect(host, port, timeout=60)
        ftp.login(username, password)
        ftp.prot_p()  # Enable data connection encryption
        ftp.set_pasv(True)  # Use passive mode for secure connections
        return ftp
    except Exception as e:
        print(f"FTP connection error: {e}")
    return None


def list_ftp_files(host, port, username, password, remote_dir):
    ftp = connect_ftp(host, port, username, password)
    if not ftp:
        print("Failed to establish FTP connection")
        return None

    try:
        print(f"Changing to directory: {remote_dir}")
        ftp.cwd(remote_dir)
        print("Successfully changed directory")

        print("Listing files...")
        files = ftp.nlst()
        print(f"Files retrieved: {files}")
        return files
    except Exception as e:
        print(f"Error listing files: {str(e)}")
    finally:
        if ftp:
            try:
                ftp.quit()
                print("FTP connection closed")
            except:
                print("Error closing FTP connection")
    return None


def download_ftp_file(host, port, username, password, remote_dir, filename, archive):
    ftp = connect_ftp(host, port, username, password)
    if not ftp:
        print("Failed to establish FTP connection for download")
        return None, False

    try:
        print(f"Changing to directory: {remote_dir}")
        ftp.cwd(remote_dir)
        print("Successfully changed directory")

        print(f"Initiating download of {filename}")
        file_content = io.BytesIO()
        ftp.retrbinary(f"RETR {filename}", file_content.write)
        print(f"Download completed. File size: {len(file_content.getvalue())} bytes")
        
        archived = False
        if archive:
            archive_dir = "Archive"
            try:
                ftp.cwd(archive_dir)
            except ftplib.error_perm:
                ftp.mkd(archive_dir)
            ftp.cwd('..')

            print(f"Moving file to Archive: {filename}")
            ftp.rename(filename, f"{archive_dir}/{filename}")
            print(f"File {filename} moved to Archive successfully")
            archived = True
        
        return file_content.getvalue(), archived
    except Exception as e:
        print(f"Error downloading or archiving file: {str(e)}")
        raise
    finally:
        if ftp:
            try:
                ftp.quit()
                print("FTP connection closed")
            except:
                print("Error closing FTP connection")


def upload_ftp_file(host, port, username, password, remote_dir, filename, file_content):
    ftp = connect_ftp(host, port, username, password)
    if not ftp:
        print("Failed to establish FTP connection for upload")
        return False

    try:
        print(f"Changing to directory: {remote_dir}")
        ftp.cwd(remote_dir)
        print("Successfully changed directory")

        print(f"Initiating upload of {filename}")
        ftp.storbinary(f"STOR {filename}", io.BytesIO(base64.b64decode(file_content)))
        print(f"Upload completed.")
        return True
    except Exception as e:
        print(f"Error uploading file: {str(e)}")
        raise
    finally:
        if ftp:
            try:
                ftp.quit()
                print("FTP connection closed")
            except:
                print("Error closing FTP connection")

am getting this error 8:51:19 AM | Trace @8F596749-638656982249418103 Error downloading or archiving file: EOF o...rred in violation of protocol (_ssl.c:2706), when am sending request to the az function am using explicit ftp over tls encryption,

3
  • The error "EOF occurred in violation of protocol" occurs during secure FTP operations. Have you checked the SSL/TLS connection? Commented Oct 28, 2024 at 8:12
  • yes i already Error closing FTP connection 4:49:44 PM | Trace Error listing files: EOF occurred in violation of protocol (_ssl.c:2706) 4:49:44 PM | Trace Listing files... 4:49:44 PM | Trace Successfully changed directory @DasariKamali Commented Oct 28, 2024 at 15:57
  • Are you still facing an issue! @zeddrakstar Commented Oct 30, 2024 at 3:34

1 Answer 1

0

Error downloading or archiving file: EOF occurred in violation of protocol:-

According to the Microsoft Q&A, the EOF error protocol issue occurred when the SSL connection is abruptly closed, and it might be due to either network side issue or a server-side issue. Check below to resolve the issue.

Firstly, you can try upgrading the python related packages and also check that the latest python has been installed and compatible with the system

Also, sometimes the issue might be due to the socket needs the ssl to be wrapped automatically as there is an implicit FTPS connections existed. Refer SO by @hynekcer for the similar information.

As mentioned in the Python socket connections document, you can try setting the SSL context as context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) which has specifically existed for client-side connections and might be helpful to avoid the conflict here.

try:
        # Create a context that doesn't verify certificates
        context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
        context.check_hostname = False
        context.verify_mode = ssl.CERT_NONE

        # Establishing a secure FTP_TLS connection with custom SSL context
        ftp = ftplib.FTP_TLS()
        ftp.context = context  # Assign the modified context to FTP_TLS
        ftp.connect(host, port, timeout=60)
        ftp.login(username, password)
        ftp.prot_p()  # Enable data connection encryption
        ftp.set_pasv(True)  # Use passive mode for secure connections
        return ftp
    except Exception as e:
        print(f"FTP connection error: {e}")
    return None

Also check in the portal >> function app that the outbound security rules virtual network is allowing traffic to the FTP server( TCP ports 21).

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.