recvcfrom() and sendto() ip address to be used - c++

Actually, I want to create an application in C such that 2 people can chat with each other. Let us assume they know their IP (Actually, I think I am making the mistake here. I get my IPs from www.whatismyip.com).
void recv_data(char *from, unsigned short int Port, char *data, int data_length)
{
WSADATA wsaData;
SOCKET RecvSocket;
sockaddr_in RecvAddr;
char RecvBuf[data_length];
sockaddr_in SenderAddr;
int SenderAddrSize = sizeof (SenderAddr);
WSAStartup(MAKEWORD(2, 2), &wsaData);
RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(Port);
RecvAddr.sin_addr.s_addr = inet_addr(from);
bind(RecvSocket, (SOCKADDR *) & RecvAddr, sizeof (RecvAddr));
recvfrom(RecvSocket, RecvBuf, data_length, 0, (SOCKADDR *) & SenderAddr, &SenderAddrSize);
int i;
for(i=0;i<=data_length-1;i++)
*(data+i)=RecvBuf[i];
WSACleanup();
}
The above is a function to receive what the other person is sending. It works great when "127.0.0.1" is the value of from but when my ip (117.193.52.176) is used, something else appears. Could anyone tell me where I am wrong ?

The address you passing to "bind" is likely wrong. Just use the IP of INADDR_ANY (0) for the call to bind. I suspect 117.193.52.176 is likely your external IP address outside of your home NAT. Your PC's real IP address is 192.168.1.2 or something like that. Type "ipconfig /all" from the command line. In any case, just bind to INADDR_ANY so you don't have to know your real IP address.
Other issues with this code:
Not checking return values from socket APIs
Don't call WSAStartup and WSACleanup for every recvfrom call. Just call WSAStartup once in your app, and don't worry about calling WSACleanup.
I'm not entirely sure if the line "char RecvBuf[data_length];" will compile. (Dynamically length static buffer on the stack? Maybe it's a new compiler feature).
Don't create a new socket for every recvfrom call. Create it once and bind to it, then use it for all subsequent send/recv calls.
5.. A more fundamnetal design problem. Unless both you and person you are communicating with are directly connected to the Internet (not NAT and no firewall), sending and receiving UDP packets will be difficult. Read the article on hole-punching here.
In any case, here's a cleaner version of your code:
int g_fWinsockInit = 0;
void initWinsock()
{
WSADATA wsaData = {};
if(!g_fWinsockInit)
{
WSAStartup(MAKEWORD(2,2), &wsaData);
g_fWinsockInit = 1;
}
}
void recv_data(char *from, unsigned short int Port, char *data, int data_length)
{
SOCKET RecvSocket;
sockaddr_in RecvAddr = {}; // zero-init, this will implicitly set s_addr to INADDR_ANY (0)
sockaddr_in SenderAddr = {}; // zero-init
int SenderAddrSize = sizeof(SendAddr);
int ret;
initWinsock();
RecvSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (RecvSocket == INVALID_SOCK)
{
printf("Error - socket failed (err = %x)\n", WSAGetLastError());
return;
}
RecvAddr.sin_family = AF_INET;
RecvAddr.sin_port = htons(Port);
ret = bind(RecvSocket, (SOCKADDR *) & RecvAddr, sizeof (RecvAddr));
if (ret < 0)
{
printf("bind failed (error = %x)\n", WSAGetLastError());
return;
}
ret = recvfrom(RecvSocket, data, data_length, 0, (SOCKADDR *) &SenderAddr, &SenderAddrSize);
if (ret < 0)
{
printf("recvfrom failed (error = %x)\n", WSAGetLastError());
}
else
{
printf("received %d bytes\n");
}
}

Related

RAW ICMP socket: recvfrom() not recieving any data

The following code is a program designed to send ICMP echo requests and receive replies.
/*
Forgive my lack of error handling :)
*/
SOCKET ASOCKET = INVALID_SOCKET;
struct sockaddr saddr;
struct sockaddr_in *to = (struct sockaddr_in *) &saddr;
struct sockaddr_in from;
int fromsize = sizeof(from);
std::string ip = "[arbitrary ip address]";
struct ICMP {
USHORT type;
USHORT code;
USHORT cksum;
USHORT id;
USHORT seq;
}*_ICMP;
char sendBuffer[sizeof(struct ICMP)];
char recvBuffer[256];
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
memset(&saddr, NULL, sizeof(saddr));
ASOCKET = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
// Configure timeout
DWORD timeoutmilsec = 3000;
setsockopt(ASOCKET, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeoutmilsec, sizeof(timeoutmilsec));
to->sin_family = AF_INET;
inet_pton(AF_INET, ip.c_str(), &(to->sin_addr));
_ICMP = new ICMP();
_ICMP->type = 8;
_ICMP->code = 0;
_ICMP->cksum = 0;
_ICMP->id = rand();
_ICMP->seq++;
// I have omitted my declaration of checksum() for simplicity
_ICMP->cksum = checksum((u_short *)_ICMP, sizeof(struct ICMP));
memcpy(sendBuffer, _ICMP, sizeof(struct ICMP));
if (sendto(ASOCKET, sendBuffer, sizeof(sendBuffer), NULL, (sockaddr *)&saddr, sizeof(saddr)) == SOCKET_ERROR)
{
printf("sendto() failed with error: %u\n", WSAGetLastError());
return false;
}
if (recvfrom(ASOCKET, recvBuffer, sizeof(recvBuffer), NULL, (sockaddr *)&from, &fromsize) == SOCKET_ERROR)
{
if (WSAGetLastError() == TIMEOUTERROR)
{
printf("Timed out\n\n");
return false;
}
printf("recvfrom() failed with error: %u\n", WSAGetLastError());
return false;
}
My issue is that my recvfrom() call does not receive any data and returns TIMEOUTERROR (10060) despite the fact that the ping has been replied to (Wireshark captures the request and reply being sent). sendto() works but recvfrom() behaves weirdly and I can't figure out what the problem is.
What I find interesting is recvfrom() will receive data only when the gateway tells me that a host is unreachable; it won't if the host is reachable and has responded to the ping.
The problem lies in struct ICMP.
type and code of ICMP should be unsigned char.
Header of ICMP should be 8-byte, but size of struct ICMP is 10 bytes.
So it should be changed to:
struct ICMP {
unsigned char type;
unsigned char code;
USHORT cksum;
USHORT id;
USHORT seq;
}*_ICMP;
It turns out the whole time it was my firewall blocking the responses. The only error in my code was the size of my ICMP struct (mentioned by cshu).
Thanks for all the help everyone.

Reading from UDP socket over WiFi always timeout

I have a piece of code that send a UDP broadcast to scan for device on our local network. It works fine when im plugged via ethernet, but it doesnt when im connected via WiFi.
Is there something different to do to connect in UDP when using WiFi?
You can find the code im using below. When using WiFi, select always return 0
struct sockaddr_in addr;
//Create socket
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
{
perror("socket");
exit(1);
}
/* set up destination address */
memset((char *)&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(48620);
addr.sin_addr.s_addr = inet_addr("192.168.3.255");
//TRYING TO BIND, NOT WORKING
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
int a = WSAGetLastError(); //ERROR 10049
perror("bind"); //Says NO ERROR
}
//allow broadcast
int broadcast = 1;
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&broadcast, sizeof(broadcast)) == -1)
exit(1);
if (sendto(fd, (const char *)&request, sizeof(request), 0, (struct sockaddr *) &addr, sizeof(addr)) < 0)
{
perror("sendto");
exit(1);
}
do
{
FD_ZERO(&rdFs);
FD_SET(fd, &rdFs);
lTimeout.tv_sec = 1;
lTimeout.tv_usec = 000000;
lSelRet = select(fd, (fd_set*)&rdFs, NULL, NULL, &lTimeout);
if (lSelRet > 0 && FD_ISSET(fd, &rdFs))
{
addrFromSize = sizeof(addrFrom);
lResult = recvfrom(fd, bufferIn, sizeof(bufferIn), 0, (struct sockaddr *) &addrFrom, &addrFromSize);
//Treat result
}
} while (lSelRet > 0);
Note : Even using WiFi, i can estalbish a TCP connection and communicate with the device, its just the UDP broadcast that doesnt work
Note2: currently testing on windows, but I will port it to Linux after
Edit : added the SO_BROADCAST as advised by Remy
Finally got it working, it was a code issue, not a router issue.
The issue was a misuse of the bind function, I needed to use my IP and not the broadcast IP.
/* set up destination address */
memset((char *)&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(48620);
addr.sin_addr.s_addr = inet_addr("192.168.3.134"); //<== Windows : My IP, not the broadcast IP
addr.sin_addr.s_addr = INADDR_ANY; //Linux
if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
{
perror("bind");
}
EDIT : strangely enough, in windows you must bind to the ip sending the request, and on linux you must bind to INADDR_ANY.

How to receive a message from the server in a client? (UDP)

I'm trying to receive a message from the server in my client, and although I don't get any compiling errors, my buffer won't take what the server is sending. I've tried changing the parameters in recvfrom in the client to correlate to the parameters used in the client's sendto but the same thing happens, my memset buffer remains empty. I've also tried just sending a simple null terminated char array of size two to test it, and the same result occurs.
Server:
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
struct sockaddr_storage their_addr;
char buf[MAXBUFLEN];
socklen_t addr_len;
char s[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // set to AF_INET to force IPv4
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, MYPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1)
perror("listener: socket");
continue;
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("listener: bind");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "listener: failed to bind socket\n");
return 2;
}
freeaddrinfo(servinfo);
while(1){
addr_len = sizeof their_addr;
if ((numbytes = recvfrom(sockfd, buf, MAXBUFLEN-1 , 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1) {
perror("recvfrom");
exit(1);
}
buf[numbytes] = '\0';
string toRespond = theMove(buf, AG);
char * sendBack = new char[toRespond.size() + 1];
std::copy(toRespond.begin(), toRespond.end(), sendBack);
sendBack[toRespond.size()] = '\0';
sendto(sockfd, testing, strlen(testing), 0, (struct sockaddr *)&their_addr, addr_len);
}
Client:
int sockfd;
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage src_addr;
socklen_t src_addr_len = sizeof(src_addr);
int rv;
int numbytes;
if (argc != 3) {
fprintf(stderr,"usage: talker hostname message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVERPORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return 2;
}
char * chatBuff = (char*)malloc(sizeof(char)*512);
while(1){
scanf("%s", chatBuff);
if ((numbytes = sendto(sockfd, chatBuff, strlen(chatBuff), 0,
p->ai_addr, p->ai_addrlen)) == -1) {
perror("talker: sendto");
exit(1);
}
memset(chatBuff, '\0', sizeof(chatBuff));
if (recvfrom(sockfd, chatBuff, strlen(chatBuff), 0, (struct sockaddr*)&src_addr, &src_addr_len) == -1)
{
puts("throw computer out the stacks");
}
puts(chatBuff);
freeaddrinfo(servinfo);
printf("talker: sent %d bytes to %s\n", numbytes, argv[1]);
memset(chatBuff, '\0', sizeof(chatBuff));
}
memset(chatBuff, '\0', sizeof(chatBuff));
While not actually incorrect, this initializing of the entire buffer is cargo-cult nonsense when you intend to load it in the next line with a call that retuns the number of bytes loaded - that return would allow you to ensure a null-terminated string by setting one byte only. The only thing you must remember is that you must leave enough space for the null, either by oversizing the buffer or reducing the read length requested.
if (recvfrom(sockfd, chatBuff, strlen(chatBuff), 0, (struct sockaddr*)&src_addr, &src_addr_len) == -1)
In the (unnecessary and wasteful) line above, you use 'sizeof(chatBuff)' as the buffer size but then here, inexplicably, you shove in 'strlen(chatBuff)' - a RUNTIME CALL that returns the size of a null-terminated char array. Since you just set that array to all null, it returns zero, so your recvfrom() will always return with a 'buffer too small' error unless you receive an empty datagram.
So:
int bytesRec=recvfrom(sockfd, chatBuff, sizeof(chatBuff)-1, 0, (struct sockaddr*)&src_addr, &src_addr_len);
if(bytesRec<1) puts("throw computer out the stacks")
else chatBuff[bytesRec]=0;
In your server code, you are passing some unknown buffer named testing as the buffer to send, but you should be passing the sendBack buffer instead:
sendto(sockfd, sendBack, strlen(sendBack), 0, (struct sockaddr *)&their_addr, addr_len);
For that matter, you can eliminate sendBack and send the data from toRespond directly instead:
sendto(sockfd, toRespond.c_str(), toResponse.size(), 0, (struct sockaddr *)&their_addr, addr_len);
In both your server and client code, you are using AF_UNSPEC when calling getaddrinfo(). That is OK in a server, but generally not OK in a client. Imagine what happens if the server binds to an IPv6 address, but the client creates an IPv4 socket, or vice versa. Obvious mismatch, communication will not be possible (unless the server creates a dual-stack IPv6 socket that can accept both IPv4 and IPv6 packets). So in your client code, you should not use AF_UNSPEC. Use either AF_INET or AF_INET6, depending on what the server is actually bound to. If you must use AF_UNSPEC on the client side, then you need to call sendto() and recvfrom() for every possible server IP address until you receive a response from one of them.
Lastly, in your client code, your call to recvfrom() is assuming the response data will be no more than the same size as the data sent with sendto(). Is that actually the case? You did not show what theMove() does to the data the server receives, or how it generates a response. At the very least, you should replace strlen() with 512 when calling recvfrom(). Your client code is also assuming that the server's response will be null-terminated, but the server is not sending a null terminator in the data it echoes. So you need to terminate the buffer that you are passing to puts().

Writing Client and Server, UDP

I'm trying to write a simple client and server using UDP.
I can send data from the client to the server, and the server recieves it well, but when I send the data from the server to the client, it doesnt work at all...
(It doesnt detect anything, and is stuck on recvfrom function..
Here is my Server code :
SOCKET ServerOn()
{
SOCKET ListenSocket;
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
{
exit(0);
}
// Create a SOCKET for listening for
// incoming connection requests.
ListenSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (ListenSocket == INVALID_SOCKET)
{
WSACleanup();
exit(1);
}
// The sockaddr_in structure specifies the address family,
// IP address, and port for the socket that is being bound.
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("0.0.0.0");
service.sin_port = htons(2583);
if (bind(ListenSocket,(SOCKADDR *) & service, sizeof (service)) == SOCKET_ERROR)
{
closesocket(ListenSocket);
WSACleanup();
exit(2);
}
return ListenSocket;
}
In this function, I'm initializing the server on port 2583.
Here is my other code in the server :
int size = sizeof(service);
char *data = new char[500];
recvfrom(s,data, 500, NULL, (SOCKADDR*)&service, &size); // Getting a new connection
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("10.0.0.1");
service.sin_port = htons(2583);
int addrSize = sizeof(service);
if (sendto(s, "123", 3, NULL, (struct sockaddr*)&service, addrSize) != 3)
printf("%d", WSAGetLastError()); // Print error if did not send successfully
"10.0.0.1" is the IP of the Client (I made sure it is)...
I didnt found a way to get the IP automaticly from the socket, so I'm just putting it right away for now...
Here is my client code :
SOCKET ConnectToServer()
{
//----------------------
// Initialize Winsock
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != NO_ERROR)
{
return NULL;
}
//----------------------
// Create a SOCKET for connecting to server
SOCKET ConnectSocket;
ConnectSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (ConnectSocket == INVALID_SOCKET)
{
WSACleanup();
return NULL;
}
//----------------------
// The sockaddr_in structure specifies the address family,
// IP address, and port of the server to be connected to.
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr(Default_IP.c_str()); // IP
clientService.sin_port = htons(Default_Port); // Port
//----------------------
// Connect to server.
iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService)); // Connecting
while (iResult == SOCKET_ERROR) // ERROR, could not connect. keep trying
{
iResult = connect(ConnectSocket, (SOCKADDR *) & clientService, sizeof (clientService)); // Connecting
}
return ConnectSocket;
}
In this code, I'm connecting to the client.
and here is the rest of the client code :
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr("10.0.0.1");
service.sin_port = htons(2583);
s = ConnectToServer();
sendto(s, "123", 3, NULL, (struct sockaddr*)&service, addrSize);
while(true)
{
result = recvfrom(s, (char*)waveIn, NUMPTS * sizeof(short int), NULL, (struct sockaddr *)&service, &addrSize);
if (result > 0)
{
std::cout << "New Data!" << std::endl;
}
else
printf("%d\n", WSAGetLastError());
}
the sendto function in the client does work, and the server recieves it, though when the server tries to send data back to the client, it recieves nothing, and it is stuck on the recvfrom function.
What am I doing wrong?
P.S - I'm running both client and server from the same computer, which means both has the same IP adress ("10.0.0.1"), but it allways worked for me when using sockets with TCP, so I've did it here too.
though, I did try use this code with 2 different computers, and I still had the same error...
Thanks!
When the server calls recvfrom(), it reports the IP:Port that the data came from. You need to send your reply back to that same IP:Port, eg:
sockaddr_in service;
int size = sizeof(service);
char data[500];
int len = recvfrom(s, data, 500, NULL, (SOCKADDR*)&service, &size);
if (len == -1)
printf("recv failed: %d", WSAGetLastError()); // Print error if did not recv successfully
else
{
if (sendto(s, "123", 3, NULL, (struct sockaddr*)&service, size) == -1)
printf("send failed: %d", WSAGetLastError()); // Print error if did not send successfully
}

WSAGetLastError returns WSAENOTSOCK - Cause?

I have something like this in my code
WSADATA wsadata;
int error = WSAStartup(0x0202, &wsadata);
SOCKADDR_IN target; //Socket address information
target.sin_family = AF_INET; // address family Internet
target.sin_port = htons (5005);
target.sin_addr.s_addr = inet_addr ("127.0.0.1");
clntSocket = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP); //Create socket
if (::connect(clntSocket, (SOCKADDR *)&target, sizeof(target)) == SOCKET_ERROR)
{
//Could not connect
__debugbreak();
}
else
{
//Connected - Now receive data
do
{
char my_stream[800];
iResult = recv(clntSocket,my_stream,sizeof(my_stream),0);
if(iResult<0)
{
int a = WSAGetLastError();
//Receives 10038 - WSAENOTSOCK
}
} while( iResult > 0 );
}
And I would sometimes (occasionally) get 10038. Which states that
Socket operation on nonsocket.
An operation was attempted on something that is not a socket. Either the socket handle parameter did not reference a valid socket,or for select, a member of an fd_set was not valid.
Am I doing something wrong while setting up the socket ? Any suggestions on how to fix the problem ?
Either:
You never opened the socket.
You have corrupted the handle value.
You have closed the socket and then continued to use it.