I'm using libssh and I want to get some output from an executed command. It works for the most part, but I'm getting unwanted characters in the output. What am I doing wrong?
Example output for the command "test -f "/path/to/file" && echo found || echo not found"
not found
t foun
I want "not found", but not the line below it -- "t foun"
Where I think the problem is:
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
output.append(buffer, sizeof(buffer));
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
Here's my function.
std::string exec_command(ssh_session session, const std::string& command)
{
ssh_channel channel;
int rc;
char* buffer;
std::string output;
int nbytes;
channel = ssh_channel_new(ssh_session);
if (channel == NULL)
return "Error";
rc = ssh_channel_open_session(channel);
if (rc != SSH_OK)
{
ssh_channel_free(channel);
return "Not Ok";
}
rc = ssh_channel_request_exec(channel, command.c_str());
if (rc != SSH_OK)
{
ssh_channel_close(channel);
ssh_channel_free(channel);
return "Not Ok";
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
output.append(buffer, sizeof(buffer));
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
if (nbytes < 0)
{
ssh_channel_close(channel);
ssh_channel_free(channel);
return "Error";
}
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return output;
}
sizeof(buffer) means sizeof(char*) which is probably 4 bytes. In ssh_channel_read third parameter(count) is the limit of your buffer. Not the number of elements loaded in your buffer. You get it as a return value. So first, you need to allocate some memory for your buffer, lets say it 256 bytes:
const int BUFFER_SIZE = 256;
char buffer[BUFFER_SIZE];
Now you can pass buffer size as parameter and fill the buffer:
nbytes = ssh_channel_read(channel, buffer, BUFFER_SIZE, 0);
while (nbytes > 0)
{
output.append(buffer, nbytes);
nbytes = ssh_channel_read(channel, buffer, BUFFER_SIZE, 0);
}
And you need to append as much as you read which is nbytes.
Related
I have this function which if you connect to a system with ssh, you can call it to execute your given command on that system.
std::string sshconnection::exec_ssh_command(ssh_session session, char *command) {
string receive = "";
int rc, nbytes;
char buffer[256];
ssh_channel channel = ssh_channel_new(session);
if( channel == NULL )
return NULL;
rc = ssh_channel_open_session(channel);
if( rc != SSH_OK ) {
ssh_channel_free(channel);
return NULL;
}
rc = ssh_channel_request_exec(channel, command);
if( rc != SSH_OK ) {
ssh_channel_close(channel);
ssh_channel_free(channel);
cout << "Error";
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
if (write(1, buffer, nbytes) != (unsigned int) nbytes)
{
ssh_channel_close(channel);
ssh_channel_free(channel);
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
if( nbytes < 0 )
return NULL;
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return receive;
}
this function works great. I just don't understand that part which is about to write from buffer into a file descriptor=1 . we haven't filled receive anywhere but it is the return value. if we call this function like below:
s = exec_ssh_command(my_ssh_session, "cat /proc/stat" );
the s won't have any value, but if we do this:
std::cout<<s;
this will print s value. and of course we can't save s in a file. can someone explain to me how is this happening?
EDIT:function to connect to ssh:
int sshconnection::sshConnection()
{
if( my_ssh_session == NULL ) {
cout << "Error creating ssh session" << endl;
return 1;
}
ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, "yourip");
ssh_options_set(my_ssh_session, SSH_OPTIONS_USER, "username");
int rc = ssh_connect(my_ssh_session);
if( rc != SSH_OK ) {
cout << "Error with connecting" << endl;
ssh_free(my_ssh_session);
return -1;
}
rc = ssh_userauth_password(my_ssh_session, NULL, "yourpassword");
if( rc != SSH_AUTH_SUCCESS) {
cout << "Error with authorization " << ssh_get_error(my_ssh_session) << endl;
ssh_disconnect(my_ssh_session);
ssh_free(my_ssh_session);
return -1;
}
// ssh_disconnect(my_ssh_session);
// ssh_free(my_ssh_session);
}
I know this is old, but I had the same issue. I came up with the following solution.
Use std::string::append like so receive.append(buffer, nbytes).
std::string sshconnection::exec_ssh_command(ssh_session session, char *command) {
string receive = "";
int rc, nbytes;
char buffer[256];
ssh_channel channel = ssh_channel_new(session);
if( channel == NULL )
return NULL;
rc = ssh_channel_open_session(channel);
if( rc != SSH_OK ) {
ssh_channel_free(channel);
return NULL;
}
rc = ssh_channel_request_exec(channel, command);
if( rc != SSH_OK ) {
ssh_channel_close(channel);
ssh_channel_free(channel);
cout << "Error";
return NULL;
}
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
while (nbytes > 0)
{
receive.append(buffer, nbytes);
nbytes = ssh_channel_read(channel, buffer, sizeof(buffer), 0);
}
if( nbytes < 0 )
return NULL;
ssh_channel_send_eof(channel);
ssh_channel_close(channel);
ssh_channel_free(channel);
return receive;
}
I am using fread function to read file, which I am sending via TCP. I found out, that fread doesn't read the whole file, if the file is binary. I tried everything what i found on the internet, but nothing helped. My code is:
#define BUFSIZE 1024
char buf[BUFSIZE];
FILE *file = fopen(soubor,"rb"); //I do a check which i won't write here
size_t bytes_loaded = 0;
while (!feof(file))
{
bytes_loaded = fread(buf,1,BUFSIZE,file);
if(bytes_loaded != BUFSIZE)
{
if(!feof(file))
{
for(int i = 0; i < 100;i++)
{
fseek(file,-strlen(buf),SEEK_CUR);
bytes_loaded = fread(buf,1,BUFSIZE,file);
if(bytes_loaded == BUFSIZE)
{
break;
}
else if(i == 99)
{
fprintf(stderr,"C could't read the file\n");
fclose(file);
close(client_socket);
return 1;
}
}
}
}
bytestx = send(client_socket, buf, BUFSIZE, 0);
if (bytestx < 0)
perror("ERROR in sendto");
bzero(buf, BUFSIZE);
bytes_loaded = 0;
}
Am I doing something wrong? For example that fread check...
Your whole fread() error handling is wrong, get rid of it (using strlen() on a binary buffer is wrong anyway).
In fact, you shouldn't be using feof() to control your loop. Simply call fread() in a loop until it returns < 1 on EOF or error (use feof() and ferror() to differentiate). And when it returns > 0, you need to pass that value to send instead of passing BUFSIZE.
Try something more like this:
#define BUFSIZE 1024
char buf[BUFSIZE], *pbuf;
FILE *file = fopen(soubor, "rb");
...
size_t bytes_loaded;
do
{
bytes_loaded = fread(buf, 1, BUFSIZE, file);
if (bytes_loaded < 1)
{
if ((!feof(file)) && ferror(file))
fprintf(stderr, "Couldn't read the file\n");
break;
}
pbuf = buf;
do
{
bytestx = send(client_socket, pbuf, bytes_loaded, 0);
if (bytestx < 0)
{
perror("ERROR in send");
break;
}
pbuf += bytestx;
bytes_loaded -= bytestx;
}
while (bytes_loaded > 0);
}
while (bytes_loaded == 0);
fclose(file);
...
If you are just shifting bytes from the file to the socket then you can just keep looping on the return value from std::fread which tells you how many bytes you read and then send exactly that many bytes to your send() command.
Something like this (untested) code:
if(FILE* fp = std::fopen(soubor, "rb"))
{
char buf[1024];
std::size_t bytesrx;
while((bytesrx = std::fread(0, 1, sizeof(buf), fp)) > 0)
{
int bytestx;
if((bytestx = send(client_socket, buf, bytesrx, 0) < 0))
{
// socket error
std::cout << "socket error: " << std::strerror(errno) << '\n';
return EXIT_FAILURE;
}
}
if(bytesrx < 0)
{
// file error
std::cout << "file error: " << std::strerror(errno) << '\n';
return EXIT_FAILURE;
}
}
else
{
// error opening file
}
I have an remote big file, and only need to read the last bytes of it.
How can I do this?
I got this for the whole file, but I don't see where I can only write the last bytes without reading it all.
channel = libssh2_scp_recv2(session, remotefile.c_str(), &fileinfo);
if (!channel) {
fprintf(stderr, "Unable to open a session: %d\n",
libssh2_session_last_errno(session));
return;
}
FILE* pFile = fopen (filename.c_str(), "wb");
while(got < fileinfo.st_size) {
char mem[1024];
int amount=sizeof(mem);
if((fileinfo.st_size -got) < amount) {
amount = (int)(fileinfo.st_size -got);
}
rc = libssh2_channel_read(channel, mem, amount);
if(rc > 0) {
fwrite (mem , sizeof(char), rc, pFile);
//write(1, mem, rc);
}
else if(rc < 0) {
fprintf(stderr, "libssh2_channel_read() failed: %d\n", rc);
break;
}
got += rc;
}
fclose (pFile);
libssh2_channel_free(channel);
Ok I managed using libssh2_sftp_open instead with libssh2_sftp_seek64
Thanks
I am trying to upload files to the server, but they always come corrupted, the program is very simple, only serves to transfer files between client and server.
I need assistance in this code, for fix the problem.
and improve it.
SERVER
int Socket_Manip::FILE_UPLOAD() {
//get file size.
iResult = recv(ClientSocket, GotFileSize, LENGTH, 0);
if (iResult == 0)
{
closesocket(ClientSocket);
WSACleanup();
return 1;
}
else if (iResult < 0) {
closesocket(ClientSocket);
WSACleanup();
return 1;
}
//start manip download
long FileSize = atoi(GotFileSize);
long SizeCheck = 0;
char* mfcc;
FILE *fp = fopen("C::\\Users\\Server\\Downloads\\transfer.zip", "wb");
if (fp == NULL)
{
closesocket(ClientSocket);
WSACleanup();
return 1;
}
if (FileSize > 1499) {
mfcc = (char*)malloc(1500);
while (FileSize > SizeCheck){
int Received = recv(ClientSocket, mfcc, 1500, 0);
if (Received == 0)
{
break;
}
else if (Received < 0) {
closesocket(ClientSocket);
WSACleanup();
return 1;
}
SizeCheck += Received;
fwrite(mfcc, 1, Received, fp);
fflush(fp);
}
} else
{
mfcc = (char*)malloc(FileSize + 1);
int Received = recv(ClientSocket, mfcc, FileSize, 0);
fwrite(mfcc, 1, Received, fp);
fflush(fp);
}
fclose(fp);
free(mfcc);
}
CLIENT SENDER
int File_Transfer_Manip() {
FILE *File;
char *Buffer;
unsigned long Size;
File = fopen("C:\\Users\\r4minux\\Downloads\\upload.zip", "rb");
if (!File)
{
printf("Error file\n");
return 1;
}
fseek(File, 0, SEEK_END);
Size = ftell(File);
fseek(File, 0, SEEK_SET);
Buffer = new char[Size];
fread(Buffer, Size, 1, File);
char cSize[MAX_PATH];
sprintf(cSize, "%i", Size);
fclose(File);
iResult = send(ConnectSocket, cSize, MAX_PATH, 0); // File size
if (iResult == SOCKET_ERROR) {
printf("send erroR: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
getchar();
return 1;
}
int Offset = 0;
while (Offset < Size)
{
int Amount = send(ConnectSocket, Buffer + Offset, Size - Offset, 0);
if (Amount <= 0)
{
std::cout << "Error: " << WSAGetLastError() << std::endl;
break;
}
else
{
Offset += Amount;
}
}
// cleanup
free(Buffer);
}
TCP is a byte stream. It has no concept of message boundaries. You are not making sure that send() is actually sending everything you give it, or that recv() is reading everything you ask it to. They CAN return fewer bytes. You have to account for that.
Try this instead:
SERVER:
int readBytes(SOCKET s, void *buffer, int buflen)
{
int total = 0;
char *pbuf = (char*) buffer;
while (buflen > 0)
{
int iResult = recv(s, pbuf, buflen, 0);
if (iResult < 0)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// optionally use select() to wait for the
// socket to have more bytes to read before
// calling recv() again...
continue;
}
printf("recv error: %d\n", WSAGetLastError());
return SOCKET_ERROR;
}
else if (iResult == 0)
{
printf("disconnected\n");
return 0;
}
else
{
pbuf += iResult;
buflen -= iResult;
total += iResult;
}
}
return total;
}
int Socket_Manip::FILE_UPLOAD()
{
//start download
FILE *fp = fopen("C::\\Users\\Server\\Downloads\\transfer.zip", "wb");
if (fp == NULL)
{
printf("Error creating file\n");
closesocket(ClientSocket);
WSACleanup();
return 1;
}
//get file size.
unsigned long FileSize;
int iResult = readBytes(ClientSocket, &FileSize, sizeof(FileSize));
if (iResult <= 0)
{
fclose(fp);
closesocket(ClientSocket);
WSACleanup();
return 1;
}
FileSize = ntohl(FileSize);
char mfcc[1024];
while (FileSize > 0)
{
int Received = readBytes(ClientSocket, mfcc, min(sizeof(mfcc), FileSize));
if (Received <= 0)
{
fclose(fp);
closesocket(ClientSocket);
WSACleanup();
return 1;
}
if (fwrite(mfcc, 1, Received, fp) != Received)
{
printf("Error writing file\n");
fclose(fp);
closesocket(ClientSocket);
WSACleanup();
return 1;
}
FileSize -= Received;
}
fflush(fp);
fclose(fp);
return 0;
}
CLIENT
int sendBytes(SOCKET s, void *buffer, int buflen)
{
int total = 0;
char *pbuf = (char*) buffer;
while (buflen > 0)
{
int iResult = send(s, pbuf, buflen, 0);
if (iResult < 0)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// optionally use select() to wait for the
// socket to have more space to write before
// calling send() again...
continue;
}
printf("send error: %d\n", WSAGetLastError());
return SOCKET_ERROR;
}
else if (iResult == 0)
{
printf("disconnected\n");
return 0;
}
else
{
pbuf += iResult;
buflen -= iResult;
total += iResult;
}
}
return total;
}
int File_Transfer_Manip()
{
char Buffer[1024];
FILE *fp = fopen("C:\\Users\\r4minux\\Downloads\\upload.zip", "rb");
if (!fp)
{
printf("Error opening file\n");
return 1;
}
fseek(fp, 0, SEEK_END);
unsigned long FileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
unsigned long tmpFileSize = htonl(FileSize);
int iResult = sendBytes(ConnectSocket, &tmpFileSize, sizeof(tmpFileSize));
if (iResult <= 0)
{
fclose(fp);
closesocket(ConnectSocket);
WSACleanup();
getchar();
return 1;
}
while (FileSize > 0)
{
long Size = fread(Buffer, 1, min(sizeof(Buffer), FileSize), fp);
if (Size <= 0)
{
printf("Error reading file\n");
fclose(fp);
closesocket(ConnectSocket);
WSACleanup();
getchar();
return 1;
}
if (sendBytes(ConnectSocket, Buffer, Size) != Size)
{
fclose(fp);
closesocket(ConnectSocket);
WSACleanup();
getchar();
return 1;
}
FileSize -= Size;
}
fclose(fp);
return 0;
}
I will rephrase the whole question here so that it is answerable.
I am able to copy binary file perfectly in the same machine not using sockets but just making a simple copy function. Trying to implement this code for copying onto a TCP/IP connection but can't get it to work.
FILE *filehandle = fopen("imagefile.jpg", "rb");
FILE *dest =fopen("imagecopy.jpg", "wb"); // copied image file
fseek(filehandle, 0, SEEK_END);
unsigned long filesize = ftell(filehandle);
char *buffer = (char*)malloc(sizeof(char)*filesize);
rewind(filehandle);
int bytesread = fread(buffer, sizeof(char), filesize, filehandle);
for( int i=0; i<filesize; i++ )
{
fputc(buffer[i], filehandle); // copies all the contents to dest
}
The code above works perfectly for copying an image file in the computer but when implemented to copy on server, it is difficult to go about it.
I am trying to send an image file from a server to a client both which have been made manually in C. The length of the file to be sent by the server is only known to the server when it's sending the file so the buffer is dynamically generated in the server, something like this:
SERVER
fseek(filehandle, 0, SEEK_END);
long filesize = ftell(filehandle); // file could be 11000bytes
char *buffer = (char*)malloc(sizeof(char)*filesize); // char buffer with 11000 bytes to store the data from the file.
// then I call the send() function
rewind(filehandle); // go back to beginning
send(clientsocket, buffer, filesize, 0); // this is being sent perfectly, no errors because in the actual code, I am checking for errors
CLIENT
// here is where I don't understand how to dynamically allocate the 11000 bytes to store the data in a client buffer
// the filesize is not necessarily going to be 11000 so need to dynamically allocate
// I did the following:
#define BUFSIZE 10
FILE *filehandle = fopen("imagefile.jpg", "wb"); // image file created by client
char *buffer = (char*)malloc(sizeof(char)*BUFSIZE);
int bytesread = recv(buffer, 1, strlen(buffer), 0);
if( bytesread > 0 )
{
printf("Bytes read: %d\n", bytesread); // bytes read is 5
printf("Buffer: %s\n", buffer); // but buffer shows all the binary text like it normally would
// when I try to store buffer in a file, it doesn't put full buffer because only 5 characters are written
for( int i=0; i<bytesread; i++ )
{
fputc(buffer[i], filehandle); // this doesn't create full image
}
}
How can I dynamically allocate the 11000 bytes sent by the server?
You need to loop both the sending and receiving. Neither send() nor recv() are guaranteed to send/read as many bytes as you requested.
You also should send the file size before the file data so the receiver knows how many bytes to expect and when to stop reading.
Try something more like this:
SERVER
bool senddata(SOCKET sock, void *buf, int buflen)
{
unsigned char *pbuf = (unsigned char *) buf;
while (buflen > 0)
{
int num = send(sock, pbuf, buflen, 0);
if (num == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// optional: use select() to check for timeout to fail the send
continue;
}
return false;
}
pbuf += num;
buflen -= num;
}
return true;
}
bool sendlong(SOCKET sock, long value)
{
value = htonl(value);
return senddata(sock, &value, sizeof(value));
}
bool sendfile(SOCKET sock, FILE *f)
{
fseek(f, 0, SEEK_END);
long filesize = ftell(f);
rewind(f);
if (filesize == EOF)
return false;
if (!sendlong(sock, filesize))
return false;
if (filesize > 0)
{
char buffer[1024];
do
{
size_t num = min(filesize, sizeof(buffer));
num = fread(buffer, 1, num, f);
if (num < 1)
return false;
if (!senddata(sock, buffer, num, 0))
return false;
filesize -= num;
}
while (filesize > 0);
}
return true;
}
FILE *filehandle = fopen("imagefile.jpg", "rb");
if (filehandle != NULL)
{
sendfile(clientsocket, filehandle);
fclose(filehandle);
}
CLIENT
bool readdata(SOCKET sock, void *buf, int buflen)
{
unsigned char *pbuf = (unsigned char *) buf;
while (buflen > 0)
{
int num = recv(sock, pbuf, buflen, 0);
if (num == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// optional: use select() to check for timeout to fail the read
continue;
}
return false;
}
else if (num == 0)
return false;
pbuf += num;
buflen -= num;
}
return true;
}
bool readlong(SOCKET sock, long *value)
{
if (!readdata(sock, value, sizeof(value)))
return false;
*value = ntohl(*value);
return true;
}
bool readfile(SOCKET sock, FILE *f)
{
long filesize;
if (!readlong(sock, &filesize))
return false;
if (filesize > 0)
{
char buffer[1024];
do
{
int num = min(filesize, sizeof(buffer));
if (!readdata(sock, buffer, num))
return false;
int offset = 0;
do
{
size_t written = fwrite(&buffer[offset], 1, num-offset, f);
if (written < 1)
return false;
offset += written;
}
while (offset < num);
filesize -= num;
}
while (filesize > 0);
}
return true;
}
FILE *filehandle = fopen("imagefile.jpg", "wb");
if (filehandle != NULL)
{
bool ok = readfile(clientsocket, filehandle);
fclose(filehandle);
if (ok)
{
// use file as needed...
}
else
remove("imagefile.jpg");
}
We could avoid the header that contains the image size, but we just read to the end of the sent data. About the buffer size, we could use a fixed number such as 10 * 1024, when we received some data from the server, we just save it into a file according to the actual received data length.
// please open a file ...
FILE * fp;
// ...
const int LENGTH = 10 * 1024;
int len = 0;
char * buffer = (char *)malloc(LENGTH);
while ((len = recv(socket, buffer, LENGTH, 0)) > 0) {
fwrite(buffer, 1, len, fp);
}
free(buffer);
// close the file
#T.C: I guess we cannot allocate a buffer according to the size sent from the server in case the image is too large to save inside the client's memory. Not mention the server is fake, and intended to make any attack.