A third question in the saga: How to correctly implement select to correctly get data from stdin and recv(). I recommend reading this and the other question it links to understand the situation.
Basically, I tried my luck at implementing select()
myself. My code:
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
using namespace std;
int main (int argc, char** argv) {
if (argv[1] == NULL) {
cout << "\033[31mTARGET NOT SPECIFIED - TERMINATING...\033[0m\n";
return -1;
}
if (argv[2] == NULL) {
cout << "\033[31mPORT NOT SPECIFIED - TERMINATING...\033[0m\n";
return -2;
}
string target = argv[1];
int port = atoi(argv[2]);
cout << "GENERATING SOCKET...\n";
int chatter = socket(AF_INET, SOCK_STREAM, 0);
if (chatter == -1) {
cout << "\033[31mSOCKET GENERATION FAILURE - TERMINATING...\033[0m\n";
return -3;
}
cout << "\033[32mSUCCESSFULLY GENERATED SOCKET\033[0m\n";
struct sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, target.c_str(), &hint.sin_addr);
struct timeval tv;
tv.tv_usec = 0.0;
tv.tv_sec = 5;
int recval;
cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";
int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
if (connection_status == -1) {
cout << "\033[31mCONNECTION FAILURE - TERMINATING...\033[0m\n";
return -4;
}
cout << "\033[32mCONNECTED TO HOST\033[0m\n";
char buf[4096] = {0};
string msg;
while (true) {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(chatter, &rfds);
getline(cin, msg);
msg+"\r\n";
int sendmsg = send(chatter, msg.c_str(), msg.size()+1, 0);
if (sendmsg == -1) {
cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
return -5;
}
recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
switch(recval) {
case(0):
cout << "\033[31mTIMEOUT\033[0m\n";
break;
case(-1):
cout << "\033[31mERROR\033[0m\n";
break;
default:
if (recv(chatter, buf, 4096, 0) < 0) {
cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
return -6;
} else {
cout << recv(chatter, buf, 4096, 0) << "\n";
cout << buf << "\n";
}
break;
}
}
close(chatter);
return 0;
}
I keep getting TIMEOUT
when trying the program on scanme.nmap.org
and my HTTP server. What am I doing wrong?
At this point, after fixing something a user in the first question pointed out, I know that there isn't an issue with how I'm sending data. Just an issue with the way the program handles getting data from getline()
/recv()
.
EDIT: NEW, IMPROVED, WORKING CODE THANKS TO ANSWERER
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
using namespace std;
int main (int argc, char** argv) {
if (argv[1] == NULL) {
cout << "\033[31mTARGET NOT SPECIFIED - TERMINATING...\033[0m\n";
return -1;
}
if (argv[2] == NULL) {
cout << "\033[31mPORT NOT SPECIFIED - TERMINATING...\033[0m\n";
return -2;
}
string target = argv[1];
int port = atoi(argv[2]);
cout << "GENERATING SOCKET...\n";
int chatter = socket(AF_INET, SOCK_STREAM, 0);
if (chatter == -1) {
cout << "\033[31mSOCKET GENERATION FAILURE - TERMINATING...\033[0m\n";
return -3;
}
cout << "\033[32mSUCCESSFULLY GENERATED SOCKET\033[0m\n";
struct sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, target.c_str(), &hint.sin_addr);
int recval;
cout << "CONNECTING TO " << target << " AT PORT " << port << "...\n";
int connection_status = connect(chatter, (sockaddr*)&hint, sizeof(hint));
if (connection_status == -1) {
cout << "\033[31mCONNECTION FAILURE - TERMINATING...\033[0m\n";
return -4;
}
cout << "\033[32mCONNECTED TO HOST\033[0m\n";
char buf[4096] = {0};
string msg;
while (true) {
struct timeval tv;
tv.tv_usec = 0.0;
tv.tv_sec = 5;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(chatter, &rfds);
getline(cin, msg);
msg += "\r\n";
const char *pMsg = msg.c_str();
size_t msgSize = msg.size();
do {
int numSent = send(chatter, pMsg, msgSize, 0);
if (numSent == -1) {
cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
close(chatter);
return -5;
}
pMsg += numSent;
msgSize -= numSent;
} while (msgSize > 0);
recval = select(chatter + 1, &rfds, NULL, NULL, &tv);
switch(recval) {
case(0):
cout << "\033[31mTIMEOUT\033[0m\n";
break;
case(-1):
cout << "\033[31mERROR\033[0m\n";
break;
default:
int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
close(chatter);
return -6;
}
else if (numRead == 0) {
cout << "\033[31mDISCONNECTED - TERMINATING...\033[0m\n";
close(chatter);
break;
} else {
cout << numRead << "\n";
cout.write(buf, numRead);
cout << "\n";
}
break;
}
}
close(chatter);
return 0;
}
On some platforms, select()
alters the passed timeval
to indicate how much time is remaining. So this is likely the cause of your timeout errors, as you are setting the timeval
only once and it will eventually fall to 0. You need to reset your tv
variable every time you call select()
, so move that inside your while
loop.
Also, you have 2 calls to recv()
where you should be using only 1 call. You are ignoring the bytes received by the 1st recv()
, and if the server happens to send less then 4096 bytes then there won't be any data left for the next call to select()
to detect, unless the connection is disconnected.
Change this:
if (recv(chatter, buf, 4096, 0) < 0) {
cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
return -6;
} else {
cout << recv(chatter, buf, 4096, 0) << "\n";
cout << buf << "\n";
}
To this:
int numRead = recv(chatter, buf, 4096, 0);
if (numRead < 0) {
cout << "\033[31mFAILURE TO RECEIVE MESSAGE - TERMINATING...\033[0m\n";
return -6;
}
else if (numRead == 0) {
cout << "\033[32mHOST DISCONNECTED\033[0m\n";
break;
} else {
cout << numRead << "\n";
cout.write(buf, numRead);
cout << "\n";
}
Also, msg+"\r\n";
is a no-op, you probably meant to use msg += "\r\n";
instead.
And, you should not be including the msg
's null terminator when calling send()
. And you are not accounting for the possibility that send()
may not be able to send the whole data in one go. You need to call send()
in a loop instead, eg:
const char *pMsg = msg.c_str();
size_t msgSize = msg.size();
do {
int numSent = send(chatter, pMsg, msgSize, 0);
if (numSent == -1) {
cout << "\033[31mMESSAGE SENDING FAILURE - TERMINATING...\033[0m\n";
return -5;
}
pMsg += numSent;
msgSize -= numSent;
}
while (msgSize > 0);