I’m trying to implement a simple FTP client using winsock. I’m having problems trying to download a file. Here’s the code I’m using at the moment:
bool FTPHandler::downloadFile(const char * remoteFilePath, const char * filePath) {
if (!isConnected()) {
setErrorMsg("Not connected, imposible to upload file...");
return false;
}
if (usePasiveMode) {
this->pasivePort = makeConectionPasive();
if (this->pasivePort == -1) {
//error msg will be setted by makeConectionPasive()
return false;
}
} else {
setErrorMsg("Unable to upload file not in pasive mode :S");
return false;
}
char * fileName = new char[500];
getFileName(remoteFilePath,fileName);
// Default name and path := current directory and same name as remote.
if (filePath == NULL) {
filePath = fileName;
}
if (!setDirectory(remoteFilePath)) {
return false;
}
char msg[OTHER_BUF_SIZE];
char serverMsg[SERVER_BUF_SIZE];
sprintf(msg,"%s%s\n",RETR_MSG,fileName);
send(sock, msg, strlen(msg), 0);
SOCKET passSocket;
SOCKADDR_IN passServer;
passSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (passSocket == INVALID_SOCKET) {
WSACleanup();
sprintf(errorMsg,"Error trying to create socket (WSA error code: %d)",WSAGetLastError());
return false;
}
passServer.sin_family = PF_INET;
passServer.sin_port = htons(this->pasivePort);
passServer.sin_addr = *((struct in_addr *)gethostbyname(this->host)->h_addr);
memset(server.sin_zero,0,8);
int errorCode = connect(passSocket, (LPSOCKADDR) &passServer, sizeof(struct sockaddr));
int tries = 0;
while (errorCode == SOCKET_ERROR) {
tries++;
if (tries >= MAX_TRIES) {
closesocket(passSocket);
sprintf(errorMsg,"Error trying to create socket");
WSACleanup();
return false;
}
}
char * buffer = (char *) malloc(CHUNK_SIZE);
ofstream f(filePath);
Sleep(WAIT_TIME);
while (int readBytes = ***recv(passSocket, buffer, CHUNK_SIZE, 0)***>0) {
buffer[readBytes] = '\0';
f.write(buffer,readBytes);
}
f.close();
Sleep(WAIT_TIME);
recv(sock, serverMsg, OTHER_BUF_SIZE, 0);
if (!startWith(serverMsg, FILE_STATUS_OKEY_CODE)) {
sprintf(errorMsg,"Bad response: %s",serverMsg);
return false;
}
return true;
}
That last recv() returns 1 byte several times, and then the method ends and the file that should be around 1Kb is just 23 bytes.
Why isn’t recv reading the hole file?
There are all kinds of logic holes and incorrect/missing error handling in this code. You really need to clean up this code in general.
You are passing the wrong
sizeof()value toconnect(), and not handling an error correctly ifconnect()fails (your retry loop is useless). You need to usesizeof(sockaddr_in)orsizeof(passServer)instead ofsizeof(sockaddr). You are also not initializingpassServercorrectly.You are not checking
recv()for errors. And in the off-chance thatrecv()actually readCHUCK_SIZEnumber of bytes then you have a buffer overflow that will corrupt memory when you write the null byte into the buffer (which you do not need to do) because you are writing it past the boundaries of the buffer.If
connect()fails, orrecv()fails with any error other than a server-side initiated disconnect, you are not telling the server to abort the transfer.Once you tell the server to go into Passive mode, you need to connect to the IP/Port (not just the Port) that the server tells you, before you then send your
RETRcommand.Don’t forget to send the server a
TYPEcommand so it knows what format to send the file bytes in, such asTYPE Afor ASCII text andTYPE Ifor binary data. If you try to transfer a file in the wrong format, you can corrupt the data. FTP’s defaultTYPEis ASCII, not Binary.And lastly, since you clearly do not seem to know how to program sockets effectively, I suggest you use the FTP portions of the WinInet library instead of WinSock directly, such as the
FtpGetFile()function. Let WinInet handle the details of transferring FTP files for you.