How to upload a file to an sftp server, using libssh2 on Windows?

341 Views Asked by At

I try to upload a local file to a sftp server. This is all new for me. I worked with sockets before, but not with libssh2. I use the libssh2 library, but it seems to lock up when I call libssh2_session_startup. It stucks at

        rc = select(session->socket_fd + 1, readfd, writefd, NULL,
                    has_timeout ? &tv : NULL);

in _libssh2_wait_socket of session.c.

Here's the stacktrace: stacktrace

And here's my code:


    // RESOLVE IP
    addrinfo hints = {0};
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    addrinfo* pResult = NULL;
    int ret = getaddrinfo(toUtf8(sftpServer).c_str(), std::to_string(port).c_str(), &hints, &pResult);
    if(ret != 0) {
        std::cerr << "getaddrinfo error: " << ret << std::endl;
        WSACleanup();
        return -1;
    }

    // Log the IPs
    bool has_IPv4 = false;
    bool has_IPv6 = false;

    for(addrinfo* addr = pResult; addr != NULL; addr = addr->ai_next) {
        switch(addr->ai_family) {
        case AF_INET: { // IPv4
            has_IPv4 = true;
            std::cout << "IPv4 : " << addr_to_str(addr);
            break;
        }

        case AF_INET6: { // IPv6
            has_IPv6 = true;
            std::cout << "IPv6 : " << addr_to_str(addr);
            break;
        }
        }
    }

    // Connect to the HOSTNAME
    SOCKET sock = INVALID_SOCKET;

    if(has_IPv6) {
        // try IPv6 first...
        for(addrinfo* addr = pResult; addr != NULL; addr = addr->ai_next) {
            if(addr->ai_family != AF_INET6)
            { continue; }

            std::cout << "Connecting to IPv6 : " << addr_to_str(addr) << " | Port : " << port << std::endl;

            sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
            if(sock == INVALID_SOCKET) {
                ret = WSAGetLastError();
                std::cerr << "socket error: " << ret << std::endl;
                continue;
            }

            if(connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR) {
                ret = WSAGetLastError();
                std::cerr << "connect error: " << ret << std::endl;
                closesocket(sock);
                sock = INVALID_SOCKET;
                continue;
            }

            break;
        }
    }

    if((sock == INVALID_SOCKET) && (has_IPv4)) {
        // try IPv4 next...
        for(addrinfo* addr = pResult; addr != NULL; addr = addr->ai_next) {
            if(addr->ai_family != AF_INET)
            { continue; }

            std::cout << "Connecting to IPv4 : " << addr_to_str(addr) << " | Port : " << port << std::endl;

            sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
            if(sock == INVALID_SOCKET) {
                ret = WSAGetLastError();
                std::cerr << "socket error: " << ret << std::endl;
                continue;
            }

            if(connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR) {
                ret = WSAGetLastError();
                std::cerr << "connect error: " << ret << std::endl;
                closesocket(sock);
                sock = INVALID_SOCKET;
                continue;
            }

            break;
        }
    }

    freeaddrinfo(pResult);



    // Initialize libssh2
    int rc = libssh2_init(0);
    if(rc != 0) {
        std::cout << "Failed to initialize libssh2." << std::endl;
        return false;
    }

    // Create a new SSH session
    LIBSSH2_SESSION* session = libssh2_session_init();
    if(!session) {
        std::cout << "Failed to create SSH session." << std::endl;
        libssh2_exit();
        return false;
    }

    // Set blocking mode for the session
    libssh2_session_set_blocking(session, 1);

    libssh2_session_set_timeout(session, 10000);

    // Connect to the SFTP server
    rc = libssh2_session_startup(session, sock); // freezes here
    if(rc < 0) {
        char* last_error_msg=new char[256];
        int errlen = sizeof(last_error_msg);
        libssh2_session_last_error(session, &last_error_msg, &errlen, 1);

        std::cout << "Failed to establish SSH session." << std::endl;
        libssh2_session_disconnect(session, "Failed to establish SSH session.");
        libssh2_session_free(session);
        libssh2_exit();
        return false;
    }


    // Authenticate using password
    rc = libssh2_userauth_password(session, toUtf8(username).c_str(), toUtf8(password).c_str());
    if(rc != 0) {
        std::cout << "Failed to authenticate with password." << std::endl;
        libssh2_session_disconnect(session, "Failed to authenticate with password.");
        libssh2_session_free(session);
        libssh2_exit();
        return false;
    }

    // Open the local file for reading
    FILE* localFile = _wfopen(localFilePath.c_str(), L"rb");
    if(!localFile) {
        std::cout << "Failed to open local file." << std::endl;
        libssh2_session_disconnect(session, "Failed to open local file.");
        libssh2_session_free(session);
        libssh2_exit();
        return false;
    }

    // Get the file size
    fseek(localFile, 0, SEEK_END);
    int64_t fileSize = ftell(localFile);
    fseek(localFile, 0, SEEK_SET);

    // Open the remote file for writing
    LIBSSH2_SFTP* sftpSession = libssh2_sftp_init(session);
    if(!sftpSession) {
        std::cout << "Failed to initialize SFTP session." << std::endl;
        fclose(localFile);
        libssh2_session_disconnect(session, "Failed to initialize SFTP session.");
        libssh2_session_free(session);
        libssh2_exit();
        return false;
    }

    LIBSSH2_SFTP_HANDLE* sftpFile = libssh2_sftp_open(sftpSession, toUtf8(remoteFilePath).c_str(), LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, LIBSSH2_SFTP_S_IRUSR | LIBSSH2_SFTP_S_IWUSR | LIBSSH2_SFTP_S_IRGRP | LIBSSH2_SFTP_S_IROTH);
    if(!sftpFile) {
        std::cout << "Failed to open remote file." << std::endl;
        fclose(localFile);
        libssh2_sftp_shutdown(sftpSession);
        libssh2_session_disconnect(session, "Failed to open remote file.");
        libssh2_session_free(session);
        libssh2_exit();
        return false;
    }// Upload the file in chunks
    std::vector<char> buffer(32768); // 32KB buffer
    size_t bytesRead = 0;
    int64_t totalBytesUploaded = 0;
    while((bytesRead = fread(buffer.data(), 1, buffer.size(), localFile)) > 0) {
        ssize_t bytesWritten = libssh2_sftp_write(sftpFile, buffer.data(), bytesRead);
        if(bytesWritten < 0) {
            std::cout << "Failed to write to remote file." << std::endl;
            fclose(localFile);
            libssh2_sftp_close(sftpFile);
            libssh2_sftp_shutdown(sftpSession);
            libssh2_session_disconnect(session, "Failed to write to remote file.");
            libssh2_session_free(session);
            libssh2_exit();
            return false;
        }
        totalBytesUploaded += bytesWritten;
    }

    // Close the local and remote files
    fclose(localFile);
    libssh2_sftp_close(sftpFile);
    libssh2_sftp_shutdown(sftpSession);

    // Disconnect and free the session
    libssh2_session_disconnect(session, "Upload complete.");
    libssh2_session_free(session);

    // Cleanup libssh2
    libssh2_exit();


    std::cout << "File uploaded successfully. Total bytes uploaded: " << totalBytesUploaded << std::endl;

    return true;
}
0

There are 0 best solutions below