Ping Function fails after 1020 tries - c++

I have a program that needs to constantly ping a device
However this ping function fails exactlly after 1020 tries
int ping(string target)
{
int s, i, cc, packlen, datalen = DEFDATALEN;
struct hostent *hp;
struct sockaddr_in to, from;
//struct protoent *proto;
struct ip *ip;
u_char *packet, outpack[MAXPACKET];
char hnamebuf[MAXHOSTNAMELEN];
string hostname;
struct icmp *icp;
int ret, fromlen, hlen;
fd_set rfds;
struct timeval tv;
int retval;
struct timeval start, end;
int /*start_t, */end_t;
bool cont = true;
to.sin_family = AF_INET;
// try to convert as dotted decimal address, else if that fails assume it's a hostname
to.sin_addr.s_addr = inet_addr(target.c_str());
if (to.sin_addr.s_addr != (u_int)-1)
hostname = target;
else
{
hp = gethostbyname(target.c_str());
if (!hp)
{
cerr << "unknown host "<< target << endl;
return -1;
}
to.sin_family = hp->h_addrtype;
bcopy(hp->h_addr, (caddr_t)&to.sin_addr, hp->h_length);
strncpy(hnamebuf, hp->h_name, sizeof(hnamebuf) - 1);
hostname = hnamebuf;
}
packlen = datalen + MAXIPLEN + MAXICMPLEN;
if ( (packet = (u_char *)malloc((u_int)packlen)) == NULL)
{
cerr << "malloc error\n";
return -1;
}
/*
if ( (proto = getprotobyname("icmp")) == NULL)
{
cerr << "unknown protocol icmp" << endl;
return -1;
}
*/
if ( (s = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
{
perror("socket"); /* probably not running as superuser */
return -1;
}
icp = (struct icmp *)outpack;
icp->icmp_type = ICMP_ECHO;
icp->icmp_code = 0;
icp->icmp_cksum = 0;
icp->icmp_seq = 12345; /* seq and id must be reflected */
icp->icmp_id = getpid();
cc = datalen + ICMP_MINLEN;
icp->icmp_cksum = in_cksum((unsigned short *)icp,cc);
gettimeofday(&start, NULL);
i = sendto(s, (char *)outpack, cc, 0, (struct sockaddr*)&to, (socklen_t)sizeof(struct sockaddr_in));
if (i < 0 || i != cc)
{
if (i < 0)
perror("sendto error");
cout << "wrote " << hostname << " " << cc << " chars, ret= " << i << endl;
}
// Watch stdin (fd 0) to see when it has input.
FD_ZERO(&rfds);
FD_SET(s, &rfds);
// Wait up to one seconds.
tv.tv_sec = 1;
tv.tv_usec = 0;
while(cont)
{
retval = select(s+1, &rfds, NULL, NULL, &tv);
if (retval == -1)
{
perror("select()");
return -1;
}
else if (retval)
{
fromlen = sizeof(sockaddr_in);
if ( (ret = recvfrom(s, (char *)packet, packlen, 0,(struct sockaddr *)&from, (socklen_t*)&fromlen)) < 0)
{
perror("recvfrom error");
return -1;
}
// Check the IP header
ip = (struct ip *)((char*)packet);
hlen = sizeof( struct ip );
if (ret < (hlen + ICMP_MINLEN))
{
cerr << "packet too short (" << ret << " bytes) from " << hostname << endl;;
return -1;
}
// Now the ICMP part
icp = (struct icmp *)(packet + hlen);
if (icp->icmp_type == ICMP_ECHOREPLY)
{
cout << "Recv: echo reply"<< endl;
if (icp->icmp_seq != 12345)
{
cout << "received sequence # " << icp->icmp_seq << endl;
continue;
}
if (icp->icmp_id != getpid())
{
cout << "received id " << icp->icmp_id << endl;
continue;
}
cont = false;
}
else
{
cout << "Recv: not an echo reply" << endl;
continue;
}
gettimeofday(&end, NULL);
end_t = 1000000*(end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec);
if(end_t < 1)
end_t = 1;
cout << "Elapsed time = " << end_t << " usec" << endl;
return end_t;
}
else
{
cout << "No data within one seconds.\n";
return 0;
}
}
return 0;
}
If i run the ping function twice it fails at exactly half 510. And the pattern is exact. Here is the output after the 1020 mark
socket: Too many open files
ping returned -1
Im pretty bad at network programming but i dont even know where to begin for the above code.
Am i missing a close somewhere?
The ping function is taken from: http://www.linuxforums.org/forum/networking/60389-implementing-ping-c.html
Edit: Running Ubuntu 10.10, same issue on Fedora 13
Thanks

Yes, you need to close the socket with close(s); before exiting the function.

When allocating resources you need to make sure to free them.
As unwind pointed out, you have to close sockets.
But you also need to free the allocated memory (malloc, line 40-ish).
You should consider using memcpy instead of bcopy.

Agree with "unwind".
Note that you run a function, not a program. Hence your process is still alive, and all the memory/resource leaks are not automatically freed.
As a general rule, if you see in the code a call to a socket function without the corresponding closesocket - this must always scratch your eyes!

Related

Select() implemented wrong in program, getting timeouts from server

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);

How to enable TCP_NODELAY option in both Server and Client?

I am implementing a communication system (tx, rx) using TCP, in windows 10. My problem is when tx sends a message to rx, the message is received with delay. When tx sends several messages, the rx starts receiving only after several messages are sent. My guess is that tx waits until its buffer gets full and then starts sending messages altogether (in my platform buffer length is 512).
As shown in the bellow picture, before receiving is started, this error appears:
ERROR receiving TCP, error #: 10014
I tried to solve this problem by enabling the TCP_NODELAY option so that the messages are being sent immediately (which was not successful). However, my knowledge of TCP is shallow and I am not sure if I am doing it correctly.
Here are my questions:
Does the delayed sending (or receiving) is related to the TCP_NODELAY option of the TCP?
If yes, am I enabling the TCP_NODELAY correctly as follows:
int yes = 1;
int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int)); // 1 - on, 0 - off
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY";
Am I putting the TCP_NODELAY option in the right place in both server and client codes?
Here are my codes for client:
size_t IPTunnel::ipTunnelSend_tcp(size_t process) {
size_t remaining = process;
size_t result{ 0 };
size_t sent{ 0 };
while (remaining > 0) {
result = send(clientSocket[0], &(ip_tunnel_buffer[0]) + sent, (int) remaining, 0);
if (result >= 0) {
remaining -= result;
sent += result;
}
else {
if (getLogValue()) {
if ( result == 0) std::cerr << "No data send through TCP" << std::endl;
else std::cerr << "ERROR sending TCP, error #: " << WSAGetLastError() << std::endl;
}
break;
}
}
return sent;
}
and:
bool IPTunnel::client_tcp() {
// STEP 1 - Initialize Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0)
{
std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
return false;
}
// STEP 2 - Create a socket
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
int resultt = setsockopt(clientSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes,
sizeof(int));
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY";
clientSocket.push_back(s);
if (clientSocket[0] == INVALID_SOCKET)
{
std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
// STEP 3 - Connect to the server
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons((u_short)localMachinePort);
inet_pton(AF_INET, remoteMachineIpAddress.c_str(), &hint.sin_addr);
int connResult{ -2 };
while (connResult != 0 || numberOfTrials == 0) {
connResult = connect(clientSocket[0], (sockaddr*)& hint, sizeof(hint));
if (connResult == SOCKET_ERROR)
{
std::cerr << "Can't connect to server, Err #" << WSAGetLastError() << std::endl;
std::cerr << "Waiting " << timeIntervalSeconds << " seconds." << std::endl;
Sleep(timeIntervalSeconds * 1000);
}
if (--numberOfTrials == 0) {
std::cerr << "Reached maximum number of attempts." << std::endl;
}
}
std::cerr << "Connected!\n";
return true;
}
Here are my codes for server:
size_t IPTunnel::ipTunnelRecv_tcp(size_t space) {
char* recv_buffer = &ip_tunnel_buffer[0];
size_t remaining{ 0 };
if (outputSignals[0]->getValueType() == (signal_value_type::t_message))
{
remaining = ip_tunnel_buffer.size();
}
else
{
remaining = space * outputSignals[0]->valueTypeSizeOf();
}
size_t received{ 0 };
while (remaining > 0) {
auto aux{ 0 };
aux = recv(serverSocket[1], recv_buffer + received, (int) remaining, 0);
if (aux > 0) {
received += aux;
remaining -= received;
}
else {
if (getLogValue()) {
if (aux == 0) std::cerr << "No data received through TCP" << std::endl;
else std::cerr << "ERROR receiving TCP, error #: " << WSAGetLastError() << std::endl;
}
break;
}
}
return received;
}
and:
bool IPTunnel::server_tcp() {
// STEP 1 - Initialize Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0)
{
std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
return false;
}
// STEP 2 - Create a socket
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serverSocket.push_back(s);
if (serverSocket[0] == INVALID_SOCKET)
{
std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
// STEP 3 - Bind the socket
sockaddr_in hint;
hint.sin_family = AF_INET; // AF_INET=2, IPv4
inet_pton(AF_INET, localMachineIpAddress.data(), &hint.sin_addr.S_un.S_addr);
hint.sin_port = ntohs((u_short)localMachinePort);
char ipAddress[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &hint.sin_addr.S_un.S_addr, ipAddress, INET_ADDRSTRLEN);
std::cerr << "The TCP server is running on IP address: " << ipAddress;
std::cerr << " Port: " << htons(hint.sin_port) << std::endl;
if (bind(serverSocket[0], (sockaddr*)& hint, sizeof(hint)) < 0) {
std::cerr << "Bind failed with error code #" << WSAGetLastError() << std::endl;
return false;
}
// STEP 4 - Listen on the socket for a client
if (listen(serverSocket[0], SOMAXCONN) == -1) {
std::cerr << "Listen error!" << std::endl;
return false;
}
// STEP 5 - Accept a connection from a client
sockaddr_in client;
int clientSize = sizeof(client);
s = accept(serverSocket[0], (sockaddr*) &client, &clientSize);
serverSocket.push_back(s);
// Provides information
char host[NI_MAXHOST];
char service[NI_MAXSERV];
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXSERV);
if (getnameinfo((sockaddr*)& client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
{
std::cerr << host << " connected on port " << service << std::endl;
}
else
{
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::cerr << host << " connected on port " << ntohs(client.sin_port) << std::endl;
}
int yes = 1;
int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int)); // 1 - on, 0 - off
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY_Server";
return true;
}
Here are the codes for sending the message (tx):
bool LoadFromCommandWindow::runBlock(void) {
int space = outputSignals[0]->space();
if (!space) return false;
if (flag && flag1)
{
std::getline(std::cin, plainData);
}
if (plainData.length() == 0)
{
flag = false;
return false;
}
else
{
data = plainData.substr(k, paddedDataLength);
if (data.length() != paddedDataLength) paddedData = padTo(data, paddedDataLength, '\0');
else paddedData = data;
outputSignals[0]->bufferPut((std::byte*) paddedData.c_str(), paddedDataLength); \\ This line puts the message into buffer
k += data.length();
if (k != plainData.length()) flag1 = false;
else
{
flag1 = true;
k = 0;
}
}
return flag;
}
std::string LoadFromCommandWindow::padTo(std::string str, const size_t num, const char paddingChar = '\0')
{
if (num > str.size())
str.insert(str.size(), num - str.size(), paddingChar);
return str;
}
Here are the codes for receiving the message (rx):
bool PrintData::runBlock(void) {
int ready = inputSignals[0]->ready();
int space = outputSignals[0]->space();
int process = std::min(ready, space);
if (process == 0) return false;
inputSignals[0]->bufferGet((std::byte*)decryptedData, decryptedDataLength);
decryptedDataLength = unpad(decryptedDataLength, decryptedData, '\0');
for (size_t i = 0; i < decryptedDataLength; i++)
{
std::cout << decryptedData[i];
}
std::cout << std::endl;
outputSignals[0]->bufferPut((std::byte*)decryptedData, decryptedDataLength);
decryptedDataLength = 496;
return true;
}
Thank you!
socket should be
int data_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
it should be enough , but it depends
bulletproof stuff is to use whole buffer transfers with buffer size on system maximum
https://stackoverflow.com/a/64726689/7172363

How to send multicast data over UDP - IPv6 using global address

I want to send and receive data over UDP using IPv6 multicast address(ff02::1). I am able to send data to multicast address but it's using link local address(fe80: instead of unicast(global) address. How can I send data to link local address instead of unicast(global) address?
Moreover, if program is bind to any addressgroupSock.sin6_addr = in6addr_any; bind(sd, (sockaddr*)&groupSock, sizeof groupSock)); it receives the same data that's being transmitted(loop back). Is there any way to avoid reception of data that's transmitted by host itself.
Any help/support in this regard would be highly appreciated.
using namespace std;
int main(int argc, char const *argv[]) {
struct sockaddr_in6 groupSock;
int sd = -1;
char databuf[10];
int datalen = sizeof databuf;
/* Create a datagram socket on which to send/receive. */
if((sd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
perror("Opening datagram socket error");
return 1;
} else {
cout << "Opening the datagram socket...OK." << endl;;
}
/* Enable SO_REUSEADDR to allow multiple instances of this */
/* application to receive copies of the multicast datagrams. */
int reuse = 1;
if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof reuse) < 0) {
perror("Setting SO_REUSEADDR error");
close(sd);
return 1;
} else {
cout << "Setting SO_REUSEADDR...OK." << endl;
}
/* Initialize the group sockaddr structure with a */
memset((char *) &groupSock, 0, sizeof groupSock);
groupSock.sin6_family = AF_INET6;
// address of the group
inet_pton(AF_INET6, "ff02::01", &groupSock.sin6_addr);
groupSock.sin6_port = htons(13400);
/* Set local interface for outbound multicast datagrams. */
/* The IP address specified must be associated with a local, */
/* multicast capable interface. */
int ifindex = if_nametoindex ("vx0");
cout << "ifindex is " << ifindex << endl;
if(setsockopt(sd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof ifindex)) {
perror("Setting local interface error");
return 1;
} else {
cout << "Setting the local interface...OK" << endl;
}
struct ifreq ifr;
if_indextoname(ifindex , ifr.ifr_name);
if(setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr))){
perror("Setting SO_BINDTODEVICE interface error");
return 1;
} else {
cout << "Setting the SO_BINDTODEVICE interface...OK" << endl;
}
// choice is 0 for sending and 1 for receiving
int choice;
if (argc < 2) {
cout << "missing argv[1]" << endl;
return 1;
}
sscanf (argv[1], "%d", &choice);
// if sending
if (choice == 0) {
memset(databuf, 'a', datalen);
databuf[sizeof databuf - 1] = '\0';
if (sendto(sd, databuf, datalen, 0, (sockaddr*)&groupSock, sizeof groupSock) < 0) {
cout << "Error in send" << endl;
} else {
cout << "Send okay!" << endl;
}
}
// if receiving
else if (choice == 1) {
groupSock.sin6_addr = in6addr_any;
if(bind(sd, (sockaddr*)&groupSock, sizeof groupSock)) {
perror("Binding datagram socket error");
close(sd);
return 1;
} else {
cout << "Binding datagram socket...OK." << endl;
}
/* Join the multicast group ff0e::/16 on the local */
/* interface. Note that this IP_ADD_MEMBERSHIP option must be */
/* called for each local interface over which the multicast */
/* datagrams are to be received. */
struct ipv6_mreq group;
inet_pton (AF_INET6, "ff02::01", &group.ipv6mr_multiaddr.s6_addr);
group.ipv6mr_interface = ifindex;
if(setsockopt(sd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *)&group, sizeof group) < 0) {
perror("Adding multicast group error");
close(sd);
return 1;
} else {
cout << "Adding multicast group...OK." << endl;
}
if (read(sd, databuf, datalen) < 0) {
perror("Error in read");
} else {
databuf[sizeof databuf - 1] = '\0';// just for safety
cout << "Read Okay" << endl;
cout << "Message is : " << databuf << endl;
}
}
return 0;
}

Empty buffer after successful recv

I'm writing a server on Windows in C++ and I'm facing a strange behavior using recv().
I wrote this function:
bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int left, res;
FD_ZERO(&readset);
FD_SET(s, &readset);
left = size;
std::cout << "-----called readN to read " << size << " byte" << std::endl;
while (left > 0) {
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
res = recv(s, buffer, left, 0);
if (res == 0) {//connection closed by client
return false;
}
left -= res;
std::cout << "\treceived " << res << " left " << left << std::endl;
if (left != 0) {
buffer += res;
}
}
else if (res == 0) { //timer expired
return false;
}
else { //socket error
return false;
}
}
std::cout << "\t" << buffer << std::endl;
return true;
}
And I call it like this:
std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size_);
if (readN(sck, size_, buffer.get())) {
std::cout << "----read message----" << std::endl;
std::cout <<"\t"<< buffer.get()<< std::endl;
}
The problem is that even if recv() returns a positive number, the buffer is still empty. What am I missing?
I see a few problems in your code.
you are not resetting the readset variable each time you call select(). select() modifies the variable. For a single-socket case, this is not too bad, but you should get in the habit of resetting the variable each time.
you are not checking for errors returned by recv(). You assume any non-graceful-disconnect is success, but that is not always true.
at the end of readN() before returning true, you are outputting the buffer parameter to std::cout, however buffer will be pointing at the END of the data, not the BEGINNING, since it was advanced by the reading loop. This is likely where your confusion about an "empty buffer" is coming from. readN() itself should not even be outputting the data at all, since you do that after readN() exits, otherwise you end up with redundant output messages.
if readN() returns true, you are passing the final buffer to std::cout using an operator<< that expects a null terminated char string, but your buffer is not guaranteed to be null-terminated.
Try something more like this instead:
bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
FD_ZERO(&readset);
FD_SET(s, &readset);
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
res = WSAGetLastError();
if (res == WSAEWOULDBLOCK) {
continue; //call select() again
}
return false; //socket error
}
if (res == 0) {
return false; //connection closed by client
}
buffer += res;
size -= res;
std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}
/*
else if (res == 0) {
return false; //timer expired
}
else {
return false; //socket error
}
*/
else {
return false; //timer expired or socket error
}
}
return true;
}
std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size_);
if (readN(sck, size_, buffer.get())) {
std::cout << "----read message----" << std::endl;
std::cout << "\t";
std::cout.write(buffer.get(), size_);
std::cout << std::endl;
}
With that said, I would suggest an alternative implementation of readN(), depending on whether you are using a blocking or non-blocking socket.
If blocking, use setsockopt(SO_RCVTIMEO) instead of select(). If recv() fails with a timeout, WSAGetLastError() will report WSAETIMEDOUT:
sck = socket(...);
DWORD timeout = MAXWAIT * 1000;
setsockopt(sck, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
bool readN(SOCKET s, int size, char* buffer){
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
/*
res = WSAGetLastError();
if (res == WSAETIMEDOUT) {
return false; //timer expired
}
else {
return false; //socket error
}
*/
return false; //timer expired or socket error
}
if (res == 0) {
return false; //connection closed by client
}
buffer += res;
size -= res;
std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}
return true;
}
If non-blocking, don't call select() unless recv() asks you to call it:
bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
res = WSAGetLastError();
if (res != WSAEWOULDBLOCK) {
return false; //socket error
}
FD_ZERO(&readset);
FD_SET(s, &readset);
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
continue; //call recv() again
}
/*
else if (res == 0) {
return false; //timer expired
}
else {
return false; //socket error
}
*/
return false; //timer expired or socket error
}
if (res == 0) {
return false; //connection closed by client
}
buffer += res;
size -= res;
std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}
return true;
}
At the end of the readN() there is
std::cout << "\t" << buffer << std::endl;
The problem is that the buffer points now to buffer + size in respect to the original value of buffer. The value has been modified by
buffer += res;
This should output the buffer,
std::cout << "\t" << (buffer - size) << std::endl;
After experimenting readN() with the following main(), it seems that readN() works if the socket is not invalid handle (text/binary data sent by ncat). If the socket is a invalid handle, the function returns quickly.
#include <iostream>
#include <memory>
#include <string.h>
#ifdef _WIN64
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <errno.h>
#define MAXWAIT 5000
bool readN(SOCKET fd, int size, char *buffer)
{
fd_set readset;
struct timeval tv;
int left, res;
FD_ZERO(&readset);
FD_SET(fd, &readset);
left = size;
std::cout << "-----called readN to read " << size << " byte" << std::endl;
while (left > 0) {
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(fd + 1, &readset, NULL, NULL, &tv);
if (res > 0) {
res = recv(fd, buffer, left, 0);
if (res == 0) { //connection closed by client
return false;
}
left -= res;
std::cout << "\treceived " << res << " left " << left << std::endl;
buffer += res;
} else if (res == 0) { //timer expired
std::cout << "\ttimer expired" << std::endl;
return false;
} else { //socket error
std::cout << "\tsocket error " << WSAGetLastError() << std::endl;
return false;
}
}
std::cout << "Print the buffer now\n" << (buffer - size) << std::endl;
return true;
}
int main(void)
{
int err;
SOCKET cfd = 0;
SOCKET afd = 0;
struct sockaddr_in addr;
socklen_t clen;
struct sockaddr_in caddr;
#ifdef _WIN64
WORD ver = 0x202;
WSADATA wsa_data;
memset(&wsa_data, 0, sizeof(wsa_data));
std::cout << "WSAStartup" << std::endl;
err = WSAStartup(ver, &wsa_data);
if (err < 0) goto error_exit;
#endif
memset(&addr, 0, sizeof(addr));
memset(&caddr, 0, sizeof(caddr));
std::cout << "socket" << std::endl;
afd = socket(AF_INET, SOCK_STREAM, 0);
if (afd < 0) goto error_exit;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(1234);
std::cout << "bind" << std::endl;
err = bind(afd, (struct sockaddr *)&addr, sizeof(addr));
if (err < 0) goto error_exit;
std::cout << "listen" << std::endl;
listen(afd, 5);
clen = sizeof(caddr);
std::cout << "accept" << std::endl;
cfd = accept(afd, (struct sockaddr *) &caddr, &clen);
if (cfd == INVALID_SOCKET) goto error_exit;
{
int size_ = 1024;
std::unique_ptr<char[]> buffer2 = std::make_unique<char[]>(size_);
std::cout << "readN" << std::endl;
if (readN(cfd, 1024, buffer2.get())) {
std::cout << "----read message----" << std::endl;
std::cout <<"\t"<< buffer2.get() << std::endl;
}
}
return 0;
error_exit:
std::cout << "Error!" << std::endl;
std::cout << "\tsocket error " << WSAGetLastError() << std::endl;
return 1;
}

Reading ICMP reply with select in Linux using C++

I am sending an ICMP request using C++ and raw sockets to a router and after that I want to read the ICMP reply. My problem is, select() is not receiving the replay and times-out all the time. I am not getting any error (errno is returning success). The router is sending the ICMP reply, because i can see the response using Wireshark.
http://i.imgur.com/0Wra1.png
wireshark screenshot
For testing my program I am using Ubuntu 12.10 running on VirtualBox 4.2.6 and GN3 for a virtual network.
My source code:
char buffer[IP_MAXPACKET]; // for the received ICMP reply
struct iphdr *ipRec; // ICMP header
timeval tv; // timeout
fd_set mySet; // descriptor set
...
tv.tv_sec = 3; // default time-out 3s
tv.tv_usec = 0;
int retval; // select
...
do {
FD_ZERO(&mySet);
FD_SET(mysocket, &mySet);
retval = select(mysocket+1, &mySet, NULL, NULL, &tv);
cout << "Errno after select:" << strerror(errno) << endl;
if(retval == -1) {
cerr << "select error" << endl;
break;
}
else if (retval) {
if((length = recvfrom(mysocket, buffer, MAX, 0, result->ai_addr, &(result->ai_addrlen))) == -1) {
cerr << "Error: while receiving data." << endl;
}
else {
cout << "good" << endl;
ipRec = (struct iphdr*) buffer;
icmpRec = (struct icmphdr*) (buffer + ipRec->ihl * 4);
cout << "the packet." << " PID: " << ntohs(icmpRec->un.echo.id) << " Seq: " << ntohs(icmpRec->un.echo.sequence) << endl;
if ((icmpRec->type == ICMP_ECHOREPLY) && (ntohs(icmpRec->un.echo.id) == pid) && (ntohs(icmpRec->un.echo.sequence) == (seq - 1))) {
minBuff = lengthBuff;
}
}
} else {
// getting here all the time = select times-out and reads no data
cout << "mysocket:" << mysocket << endl;
cout << "retval:" << retval << endl;
maxBuff = lengthBuff;
break;
}
} while (!((icmpRec->type == ICMP_ECHOREPLY) && (ntohs(icmpRec->un.echo.id) == pid) && (ntohs(icmpRec->un.echo.sequence) == (seq - 1))));
if (packet)
delete(packet);
...
Thank you for any help.
Fixed the problem. Found the solution in a different thread: ICMP packets are not being sent C. Changing IPPROTO_RAW to IPPROTO_ICMP fixed it. Now is select reading the ICMP reply packet.