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.
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;
}
