FTP server file transfer - c++

I am uncertain about a few things regarding ftp file transfer. I am writing an ftp server and I am trying to figure out how to make the file tranfer work correctly. So far it works somehow but I have certain doubts. Here is my file transfer function (only retrieve so far):
void RETRCommand(int & clie_sock, int & c_data_sock, char buffer[]){
ifstream file; //clie_sock is used for commands and c_data_sock for data transfer
char *file_name, packet[PACKET_SIZE]; //packet size is 2040
int packet_len, pre_pos = 0, file_end;
file_name = new char[strlen(buffer + 5)];
strcpy(file_name, buffer + 5);
sprintf(buffer, "150 Opening BINARY mode data connection for file transfer\r\n");
if (send(clie_sock, buffer, strlen(buffer), 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
file_name[strlen(file_name) - 2] = '\0';
file.open(file_name, ios::in | ios::binary);
if (file.is_open()) {
file.seekg(0, file.end);
file_end = (int) file.tellg();
file.seekg(0, file.beg);
while(file.good()){
pre_pos = file.tellg();
file.read(packet, PACKET_SIZE);
if ((int) file.tellg() == -1)
packet_len = file_end - pre_pos;
else
packet_len = PACKET_SIZE;
if (send(c_data_sock, packet, packet_len, 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent some data" << endl;
}
}
else {
sprintf(buffer, "550 Requested action not taken. File unavailable\r\n", packet);
if (send(clie_sock, buffer, packet_len + 2, 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
delete(file_name);
return;
}
sprintf(buffer, "226 Transfer complete\r\n");
if (send(clie_sock, buffer, strlen(buffer), 0) == -1) {
perror("Error while writing ");
close(clie_sock);
exit(1);
}
cout << "sent: " << buffer << endl;
close(c_data_sock);
delete(file_name);
}
So one problem is the data transfer itself. I am not exactly sure how it is supposed to work. Now it works like this: the server sends all the data to c_data_sock, closes this socket and then the client starts doing something. Shouldn't the client recieve the data while the server is sending them? And the other problem is the abor command. How am I supposed to recieve the abor command? I tried recv with flag set to MSG_OOB but then I get an error saying "Invalid argument". I would be glad if someone could give me a hint or an example of how to do it right as I don't seem to be able to figure it out myself.
Thanks,
John

Ftp use two connections. First - is command connection, in your case it is clie_sock. 'ABOR' command should be received though it. You going to receive it the same way you received 'RETR' command.
To receive file client establishes data connection with your server ( c_data_sock socket ). It will not be opened till client connects, so this is the answer to your second question. You cannot start client after server executes this function. First client sends 'retr' command to your command socket. Then your sever waits new connection from client ( after sending him data ip and port ). Then client connects ( now you have your c_data_sock ready ) and sends all the data to that socket, which are in turn received by the client.
You probably need to read more about networking in general if you feel you don't understand it. I prefer this one: http://beej.us/guide/bgnet/
Also you have a memory leak here, after you allocate an array with the
file_name = new char[strlen(buffer + 5)];
you need to delete it using
delete [] file_name;
Otherwise file_name will be treated as a simple pointer, not an array, so most of array memory will be kept by your application which is bad especially when creating server.

Related

Send integer from TCP socket in C to Qt TCP socket

I'm currently trying to implement basic client-server file transfer program using TCP sockets. Client is being written in C++/Qt and the server in C.
I encountered great difficulty when trying to send the file size from server to client (integer value). Below are the code samples.
C server:
if(fileExists == '1')
{
fp = fopen(filename, "r");
fseek(fp, 0L, SEEK_END);
fileSize = ftell(fp);
printf("%d\n", fileSize);
send(client_socket, &fileSize, sizeof(fileSize), 0);
}
Qt Client:
void Client::receiveFile(QString filename)
{
qint32 fileSize;
clientSocket->waitForReadyRead(1000);
clientSocket->read(fileSize);
qDebug() << fileSize;
}
The problem is that C calculates the file size properly as 40435408 and sends it over to the client which says the size is 32767. It's obvious that the problem lies on the client side. I tried to figure out the problem for almost whole day and failed.
I realize that this is some simple and stupid mistake I made and I apologize for asking such dumb question. I'm a complete begginner. Can anyone help?
When you call clientSocket->read(fileSize);, you are in fact calling QByteArray QIODevice::read(qint64 maxSize). Which means you are reading up to an undefined (as fileSize is not initialized) amount of bytes from the TCP socket and discard it immediatly, as you don't use the return value.
I think you are trying to use qint64 QIODevice::read(char *data, qint64 maxSize), so your code should look like this:
qint32 fileSize = 0;
clientSocket->waitForReadyRead(1000);
if (clientSocket->bytesAvailable() >= sizeof(fileSize)) {
clientSocket->read(&fileSize, sizeof(fileSize));
} else {
qWarning() < "Only received" << clientSocket->bytesAvailable() << "bytes: " << clientSocket->readAll().toHex(' ');
}
qDebug() << fileSize;
Note that I would not use this code in any software that is more than a proof of concept.

Does TCPdump strip any headers when receiving packets?

So i am attempting to send an already constructed packet over a RAW socket interface (these are packets that have been previously captured and i want to resend them without changing the packet integrity) and am using TCPdump to check that the packets are going over correctly (surprise they are not).
The packets are physically being sent but are always 24 bytes short of what my "sent" returns.
In wireshark my eth headers seem to be erased as my source and dest MAC addresses are "00:00:00:00:00
sock setup is as follows
sock = socket(AF_PACKET,SOCK_RAW,IPPROTO_RAW);
if(sock==-1)
{
qDebug() << "sock error";
}
int reuse = 1;
if(setsockopt(sock, IPPROTO_RAW, IP_HDRINCL, (char *)&reuse, sizeof(reuse)) < 0)
{
qDebug() << "error setting reuse"
}
else
{
"setting reuse"
}
struct sockaddr_ll sll;
struct ifreq ifr;
bzero(&sll, sizeof(sll));
bzero(&ifr, sizeof(ifr));
sll.sll_family = AF_PACKET;
sll.sll_ifindex = ifr.ifr_ifindex;
sll.sll_protocol = htons(IPPROTO_RAW);
sll.sll_halen = ETH_ALEN;
strncpy((char*)ifr.ifr_ifrn.ifrn_name,interface.toUtf8.constData(),IFNAMSIZ);
if(ioctl(sock,SIOCGIFINDEX,&ifr) == -1)
{
qDebug() << "error getting interface name";
}
strncpy((char*)ifr.ifr_ifrn.ifrn_name,interface.toUtf8.constData(),IFNAMSIZ);
if(ioctl(sock,SIOCGIFHWADDR,&ifr) == -1)
{
qDebug() << "error getting interface name";
}
if(bind(sock,(struct sockaddr *)&sll,sizeof(sll))==-1)
{
qDebug() << "error binding sock";
}
after this im using
int size = write(sock,(const void*)&packet,hdr.caplen);
i've tried sendto in the past but it would always reconfigure things so this was my next solution which also isnt working as i would like.
I'm not the most savy with TCP/IP stuff so any help would be greatly appreciated!
okay so after just trying a bunch of different stuff i landed on what seems to be my solution.
i created a second pointer that will point to the top of the packet and send that instead.
(char *)sendingPacket;
struct ethhdr *ethh = (struct ethhdr*)packet;
sendingPacket = (char*) ethh;
i don't really understand why this works but sending the other packet doesn't so if anyone has insight please share!

Network Programming Issue - buffer will only send once to the server

I am trying to send a file to a server using socket programming. My server and client are able to connect to each other successfully however I am expecting the while loop below to go through the entire file and add it to the server. The issue I am having is that it only send the first chunk and not the rest.
On the client side I have the following:
memset(szbuffer, 0, sizeof(szbuffer)); //Initialize the buffer to zero
int file_block_size;
while ((file_block_size = fread(szbuffer, sizeof(char), 256, file)) > 0){
if (send(s, szbuffer, file_block_size, 0) < 0){
throw "Error: failed to send file";
exit(1);
} //Loop while there is still contents in the file
memset(szbuffer, 0, sizeof(szbuffer)); //Reset the buffer to zero
}
On the server side I have the following:
while (1)
{
FD_SET(s, &readfds); //always check the listener
if (!(outfds = select(infds, &readfds, NULL, NULL, tp))) {}
else if (outfds == SOCKET_ERROR) throw "failure in Select";
else if (FD_ISSET(s, &readfds)) cout << "got a connection request" << endl;
//Found a connection request, try to accept.
if ((s1 = accept(s, &ca.generic, &calen)) == INVALID_SOCKET)
throw "Couldn't accept connection\n";
//Connection request accepted.
cout << "accepted connection from " << inet_ntoa(ca.ca_in.sin_addr) << ":"
<< hex << htons(ca.ca_in.sin_port) << endl;
//Fill in szbuffer from accepted request.
while (szbuffer > 0){
if ((ibytesrecv = recv(s1, szbuffer, 256, 0)) == SOCKET_ERROR)
throw "Receive error in server program\n";
//Print reciept of successful message.
cout << "This is the message from client: " << szbuffer << endl;
File.open("test.txt", ofstream::out | ofstream::app);
File << szbuffer;
File.close();
//Send to Client the received message (echo it back).
ibufferlen = strlen(szbuffer);
if ((ibytessent = send(s1, szbuffer, ibufferlen, 0)) == SOCKET_ERROR)
throw "error in send in server program\n";
else cout << "Echo message:" << szbuffer << endl;
}
}//wait loop
} //try loop
The code above is the setup for the connection between the client and server which works great. It is in a constant while loop waiting to receive new requests. The issue is with my buffer. Once I send the first buffer over, the next one doesn't seem to go through. Does anyone know what I can do to set the server to receive more than just one buffer? I've tried a while loop but did not get any luck.
Your code that sends the file from the server appears to send consecutive sections of the file correctly.
Your code that appears to have the intention of receiving the file from the client performs the following steps:
1) Wait for and accept a socket.
2) Read up to 256 bytes from the socket.
3) Write those bytes back to the socket.
At this point the code appears to go back to waiting for another connection, and keeping the original connection open, and, at least based on the code you posted, obviously leaking the file descriptor.
So, the issues seems to be that the client and the server disagreeing on what should happen. The client tries to send the entire file, and doesn't read from the socket. The server reads the first 256 bytes from the socket, and writes it back to the client.
Of course, its entirely possible that portions of the code not shown implement some of the missing pieces, but there's definitely a disconnect here between what the sending side is doing, and what the receiving side is doing.
buffer will only send once to the server
No, your server is only reading once from the client. You have to loop, just like the sending loop does.

recv() socket loop never end C

A am writing a client-server program in C. It sends a directory name and receives a list of files as answer. The problem I have is that it gets stuck in an infinite loop.
If I send only one directory name it works, but if I send a list of directories it never ends and outputs nothing.
Server
while(recv(sock, name, BUFSIZE, 0) > 0){
if ((fddir=opendir(name)) == NULL){
send(sock, strerror(errno), strlen(strerror(errno)), 0);
close(sock);
return 1;
}
send(sock, name, strlen(name), 0);
send(sock, ":", strlen(":"), 0);
send(sock, "\n", strlen("\n"), 0);
while ((dirbuf = readdir(fddir)) != NULL){
buf[0] = '\0';
strcat(buf, dirbuf->d_name);
strcat(buf, "\t");
send(sock, buf, BUFSIZE, 0);
}
}
Client
for (int i=1;i<3;i++){
send(sock, argv[i], strlen(path), 0);
while(recv(sock, buf, BUFSIZE, 0) > 0)
printf("%s", buf);
}
The server waits until all directory names are received, and then the client wait until server send all files in it. How do I trace where the program gets stuck?
TCP is not message based, so you have no way of knowing where the boundaries between two client send() calls is when you call recv() on the server. Thus when you send multiple names back-to-back it is possible for the server to receive them all in a single recv() (or however many bytes you allocated for BUFSIZE). This is probably mangling your directory names, causing opendir to fail. This would be more obvious to you if you were checking for errors from send and recv and Captain Obvlious describes in another answer.
You need to check the calls to recv for errors. It returns 0 if the connection was disconnected and -1 on an error. You are only checking for values > 0 which will not work. The example below shows how to approach checking the errors.
while(true)
{
const int result = recv(sock, buf, BUFSIZE, 0);l
if(result == -1)
{
std::cout << "Error: " << errno << std::endl;
break;
}
else if(result == 0)
{
std::cout << "Disconnected" << std::endl;
break;
}
// process the data here. No errors
}
You should also be checking the value returned by send as it works in the same way.

Why am I getting a bad file descriptor error?

I am trying to write a short program that acts like a client, like telnet.
I receive from the user input like so: www.google.com 80 (the port number) and /index.html
However, I get some errors. When I write some debug information, it says that I have a bad file descriptor and Read Failed on file descriptor 100 messagesize = 0.
struct hostent * pHostInfo;
struct sockaddr_in Address;
long nHostAddress;
char pBuffer[BUFFER_SIZE];
unsigned nReadAmount;
int nHostPort = atoi(port);
vector<char *> headerLines;
char buffer[MAX_MSG_SZ];
char contentType[MAX_MSG_SZ];
int hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (hSocket == SOCKET_ERROR)
{
cout << "\nCould Not Make a Socket!\n" << endl;
return 1;
}
pHostInfo = gethostbyname(strHostName);
memcpy(&nHostAddress,pHostInfo -> h_addr, pHostInfo -> h_length);
Address.sin_addr.s_addr = nHostAddress;
Address.sin_port=htons(nHostPort);
Address.sin_family = AF_INET;
if (connect(hSocket, (struct sockaddr *)&Address, sizeof(Address)) == SOCKET_ERROR)
{
cout << "Could not connect to the host!" << endl;
exit(1);
}
char get[] = "GET ";
char http[] = " HTTP/1.0\r\nHost: ";
char end[] = "\r\n\r\n";
strcat(get, URI);
strcat(get, http);
strcat(get, strHostName);
strcat(get, end);
strcpy(pBuffer, get);
int len = strlen(pBuffer);
send(hSocket, pBuffer, len, 0);
nReadAmount = recv(hSocket,pBuffer, BUFFER_SIZE, 0);
//Parsing of data here, then close socket
if (close(hSocket) == SOCKET_ERROR)
{
cout << "Could not close the socket!" << endl;
exit(1);
}
Thanks!
You have a buffer overrun in your code. You're attempting to allocate a bunch of character arrays together into a buffer than is only 5 bytes long:
char get[] = "GET "; // 'get' is 5 bytes long
char http[] = " HTTP/1.0\r\nHost: ";
char end[] = "\r\n\r\n";
strcat(get, URI); // BOOM!
strcat(get, http);
strcat(get, strHostName);
strcat(get, end);
This is likely overwriting your local variable hSocket, resulting in the "bad file descriptor error".
Since you're using C++ (you have a vector in your code), just use a std::string instead of C arrays of characters so that you don't have to worry about memory management.
Another cause of 'Bad file descriptor' can be encountered when trying to write into a location onto which you do not have write permission. For example, when trying to download content with wget in Windows environment:
Cannot write to 'ftp.org.com/parent/child/index.html' (Bad file descriptor).
Another case may be the target out file name includes characters the filesystem won't allow. For instance, suppose you are running wget command on a Linux computer and the target path is an external hard drive formatted with NTFS. The Linux Ext4 filesystem will allow for characters such as '?', but the NTFS filesystem will not. The default behavior for wget is to name the target folder as the specified url. If the url contains a special character like ':', NTFS will not accept that character, resulting in the error "Bad file descriptor". You can disable this default behavior in wget with the --no-host-directories parameter.
Another cause of 'bad file descriptor' can be accidentally trying to write() to a read-only descriptor. I was stuck on that for a while because I was spending all my time looking for a second close() or buffer overrun.