UDP Multicast not receiving message - c

I am trying to make a simple UDP multicast example where a message is sent from one program and received from the other but right now the output is only:
Connected
Message Sent
and
bind
setup multicast
Can someone please tell me what I am missing so that I can receive the message successfully? Thank you!! Here are the codes in full:
int main(int argc, char *argv[])
{
int udp_socket_info;
struct sockaddr_in udp_server;
char* message="test";
//create socket
udp_socket_info = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket_info == -1) {
puts("Could not create socket");
}
//assign local values
udp_server.sin_addr.s_addr = inet_addr("225.0.0.37"); //multicast address
udp_server.sin_family = AF_INET;
udp_server.sin_port = htons( 1100 );
//checks connection
if (connect(udp_socket_info, (struct sockaddr *)&udp_server, sizeof(udp_server)) < 0) {
perror("Connection error");
}
puts("Connected");
//sends message
if( sendto(udp_socket_info , message , strlen(message) , 0, (struct sockaddr *)&udp_server, sizeof(udp_server)) < 0) {
perror("Send failed");
}
puts("Message Sent");
}
and the second program is
int main(int argc, char *argv[])
{
//initialize udp socket and structures
int udp_socket_info;
struct sockaddr_in udp_server;
struct sockaddr addr;
struct ip_mreq mreq;
socklen_t fromlen;
fromlen = sizeof addr;
char incoming_message[100];
//create udp socket
udp_socket_info = socket(AF_INET, SOCK_DGRAM, 0);
if (udp_socket_info == -1) {
puts("Could not create socket");
}
// set up
memset((char*)&udp_server,0,sizeof(udp_server));
udp_server.sin_family=AF_INET;
udp_server.sin_port = htons( 1100 );
udp_server.sin_addr.s_addr = inet_addr("192.168.0.100"); //local address
// bind
if (bind(udp_socket_info,(struct sockaddr *)&udp_server, sizeof(udp_server)) < 0) {
perror("bind error");
exit (1);
}
puts("bind");
// use setsockopt() to join multicast group
mreq.imr_multiaddr.s_addr=inet_addr("225.0.0.37"); //multicast address
mreq.imr_interface.s_addr= htonl(INADDR_ANY); //can use local address here too
if (setsockopt(udp_socket_info, IPPROTO_IP,IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
perror("setsockopt");
exit (1);
}
puts("setup multicast");
//Receive an incoming message
if( recvfrom(udp_socket_info, incoming_message , sizeof(incoming_message), 0, &addr, &fromlen) < 0) {
puts("Received failed");
exit (1);
}
puts("Message received");
puts(incoming_message);
}

You should bind the receiving socket to INADDR_ANY, not a local interface address. Otherwise you run the risk that the sender is out there via a different route and can't reach your socket. On some platforms you can bind it to the multicast address itself.
NB when you get an error it isn't sufficient to print a message of your own devising. The message must contain the errno, or the result of strerror(). For example, call perror().

Your receiver should not bind to a local address. It should instead bind to either INADDR_ANY or the multicast address you intend on joining. Binding to a local address breaks multicast on Linux systems.
Note that if you bind to a multicast address, this means you'll only receive packets for that multicast address. If you want to receive from multiple multicast addresses or if you also want to receive unicast packets then you need to bind to INADDR_ANY.
When joining a multicast group, using INADDR_ANY causes you to join the specified group on the default network interface. It's generally a good idea to explicitly specify an interface.
As EJP mentioned, you should always use perror to print error messages from any system or library call to ensure that a meaningful error message is printed.
Wireshark is an important tool for programs such as this. It helps you ensure that packets are going out and coming in the network interfaces you expect.
Also, if the sender and receive are on different network segments, you'll need to set the TTL via the IP_MULTICAST_TTL socket option. You also need to make sure that any routers between them are configured to pass multicast traffic.

Related

Reading UDP packets with several clients

I have an application installed locally (not developed by me), which broadcasts UDP packets every second.
Reading the packets from my application (developed in C++ in Windows) which also is locally installed, works fine.
WSADATA data;
WORD version = MAKEWORD(2, 2);
int wsOK = WSAStartup(version, &data);
SOCKET serverIn = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
sockaddr_in serverHint;
serverHint.sin_addr.S_un.S_addr = INADDR_ANY;
serverHint.sin_family = AF_INET;
serverHint.sin_port = htons(UDP_RECEIVE_PORT);
bind(serverIn, (sockaddr*)&serverHint, sizeof(serverHint));
sockaddr_in client;
int clientSize = sizeof(client);
int RECIEVE_BUFFER_SIZE = 65507;
char* recieveBuffer = new char[RECIEVE_BUFFER_SIZE];
while(updating)
{
int bytesIn = recvfrom(serverIn, recieveBuffer, RECIEVE_BUFFER_SIZE, 0, (sockaddr*)&client, &clientSize);
}
closesocket(serverIn);
WSACleanup();
But I recently noticed while I was testing some code, while my app was running, that the bind(...)
function returned an error code of 10048 (WSAEADDRINUSE). Hence, it seems the first client bound to listen for the UDP packets is the only one who can listen, and the other clients is unable to read the broadcasted UDP packets.
So then I added the SO_REUSEADDR option before calling the bind(...) function to be able to bind successfully to the socket:
BOOL bOptVal = TRUE;
int bOptLen = sizeof(BOOL);
setsockopt((SOCKET)serverIn, SOL_SOCKET, SO_REUSEADDR, (char*)&bOptVal, bOptLen);
That works, but the recvfrom(...) function then does not recieve any data at all! I guess it waits for the other client to close its socket.
Next solution is to initialize the socket with SOCK_RAW instead.
The above option SO_REUSEADDR is now not needed, and remove it:
SOCKET serverIn = socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
This works, I can read the data now! Though, Windows now requires the adminstrator rights for my application. Also I do recieve the UDP information in the data which I do not need.
Is there any better method to do this without requiring administrator rights, any possibility to discard the header information in the buffer?
Below is a little program I wrote to demonstrate that IPv4 UDP broadcast can and does work as expected under Windows (i.e. without requiring raw-sockets or Administrator privileges).
Run it with the command line argument "server" and it will send out one broadcast UDP packet per second.
Then also run several more instances of the same program, with no command line arguments, to receive the UDP packets and print a line of text to stdout whenever they do. The expected behavior should look like this:
As for why it's not working for you -- one possible guess is that your UDP-packet-sending program is actually sending out unicast UDP packets rather than broadcast. If that's the case, then I would expect that only one client program would receive packets (even if multiple clients are bound to the same port). A network trace tool like Wireshark might be able to help you determine if the UDP packets being sent are broadcast or unicast.
Anyway, here's the code:
#include <stdio.h>
#include <ws2tcpip.h>
#pragma comment(lib,"WS2_32")
static int BindUDPSocket(SOCKET sock, unsigned short port, bool allowPortSharing)
{
if (sock == INVALID_SOCKET) return -1;
if (allowPortSharing)
{
const BOOL trueValue = true;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *) &trueValue, sizeof(trueValue)) < 0) return -1;
}
struct sockaddr_in bindAddr; memset(&bindAddr, 0, sizeof(bindAddr));
bindAddr.sin_family = AF_INET;
bindAddr.sin_addr.s_addr = INADDR_ANY; // aka 0.0.0.0
bindAddr.sin_port = htons(port);
return bind(sock, (struct sockaddr *) &bindAddr, sizeof(bindAddr));
}
int main(int argc, char ** argv)
{
WSADATA data;
WORD version = MAKEWORD(2, 2);
(void) WSAStartup(version, &data);
const unsigned short TEST_PORT = 12345;
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock<0) {printf("socket() failed\n"); exit(10);}
if ((argc > 1)&&(strcmp(argv[1], "server") == 0))
{
if (BindUDPSocket(sock, 0, false)<0) {printf("BindUDPSocket() failed for server\n"); exit(10);}
const BOOL allowBroadcast = true;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char *) &allowBroadcast, sizeof(allowBroadcast)) < 0)
{
printf("setsockopt(SO_BROADCAST) failed\n");
exit(10);
}
const char buf[] = {0x01, 0x02, 0x03, 0x04}; // dummy data
struct sockaddr_in toAddr; memset(&toAddr, 0, sizeof(toAddr));
toAddr.sin_family = AF_INET;
toAddr.sin_addr.s_addr = INADDR_BROADCAST; // aka 255.255.255.255
toAddr.sin_port = htons(TEST_PORT);
printf("Sending outgoing broadcast UDP sockets on port %u, once per second\n", TEST_PORT);
while(true)
{
if (sendto(sock, buf, sizeof(buf), 0, (const sockaddr *) &toAddr, sizeof(toAddr)) == sizeof(buf))
{
printf("Sent %zu bytes of broadcast UDP data\n", sizeof(buf));
}
else printf("sendto() failed!\n");
Sleep(1000); // wait 1 second
}
}
else
{
if (BindUDPSocket(sock, TEST_PORT, true)<0) {printf("BindUDPSocket() failed for client\n"); exit(10);}
printf("Waiting to receive incoming broadcast UDP sockets on port %u\n", TEST_PORT);
while(true)
{
char buf[1024];
const int ret = recv(sock, buf, sizeof(buf), 0L);
printf("Received %i bytes of incoming UDP data\n", ret);
}
}
}

How to handle TCP client disconnect in C

I am trying to write a basic TCP server that streams serial data to a client. The server would connect to a serial device, read data from said device, and then transmit it as a byte stream to the client. Writing the TCP server is no problem. The issue is that the server will crash when a client disconnects. In other languages, like Python, I can simply wrap the write() statement in a try-catch block. The program will try to write to the socket, but if the client has disconnected then an exception will be thrown. In another project, this code snippet worked for me:
try:
client_socket.send(bytes(buf, encoding='utf8'))
except Exception as e:
logger.info("Client disconnected: %s", client_id)
I can handle client disconnects in my C code, but only by first reading from the socket and checking if the read is equal to 0. If it is, then my client has disconnected and I can carry on as usual. The problem with this solution is that my client has to ping back to the server after every write, which is less than ideal.
Does anyone know how to gracefully handle TCP client disconnects in C? My example code is shown below. Thank you!
// Define a TCP socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// Allow for the backlog of 100 connections to the socket
int backlog = 100;
// Supply a port to bind the TCP server to
short port = 9527;
// Set up server attributes
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
// Set the socket so that we can bind to the same port when we exit the program
int flag = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) {
perror("setsockopt fail");
}
// Bind the socket to the specified port
int res = bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (res < 0) {
perror("bind fail");
exit(1);
}
// Listen for incoming connections
if (listen(sockfd, backlog) == -1) {
perror("listen fail");
exit(1);
} else {
printf("Server listening on port\n", port);
}
for(;;) {
// Wait for incoming connection
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
if (-1 == connfd) {
perror("Could not accept incoming client");
continue;
}
//Resolving Client Address
char buff[INET_ADDRSTRLEN + 1] = {0};
inet_ntop(AF_INET, &cliaddr.sin_addr, buff, INET_ADDRSTRLEN);
uint16_t cli_port = ntohs(cliaddr.sin_port);
printf("connection from %s, port %d\n", buff, cli_port);
for(;;) {
// Read from serial device into variable here, then send
if(send(connfd, "Data...Data...Data\n", 19, 0) < 0) {
printf("Client disconnected...\n");
break;
}
}
}
Looks like a duplicate of this, this and this.
Long story short you can't detect the disconnection until you perform some write (or read) on that connection. More exactly, even if it seems there is no error returned by send, this is not a guarantee that this operation was really sent and received by the client. The reason is that the socket operations are buffered and the payload of send is just queued so that the kernel will dispatch it later on.
Depending on the context, the requirements and the assumptions you can do something more.
For example, if you are under the hypothesys that you will send periodic message at constant frequency, you can use select and a timeout approach to detect an anomaly.
In other words if you have not received anything in the last 3 minutes you assume that there is an issue.
As you can easily found, this and this are a good read on the topic.
Look at that for a far more detailed explanation and other ideas.
What you call the ping (intended as a message that is sent for every received packet) is more similar to what is usually known as an ACK.
You only need something like that (ACK/NACK) if you also want to be sure that the client received and processed that message.
Thanks to #emmanuaf, this is the solution that fits my project criteria. The thing that I was missing was the MSG_NOSIGNAL flag, referenced here.
I use Mashpoe's C Vector Library to create a new vector, which will hold all of my incoming client connections.
int* client_array = vector_create();
I then spawn a pthread that continually reads from a serial device, stores that data in a variable, and then sends it to each client in the client list
void* serve_clients(int *vargp) {
for(;;) {
// Perform a microsleep
sleep(0.1);
// Read from the Serial device
// Get the size of the client array vector
int client_vector_size = vector_size(vargp);
for(int i = 0 ; i < client_vector_size ; i++) {
// Make a reference to the socket
int* conn_fd = &vargp[i];
/*
In order to properly handle client disconnects, we supply a MSG_NOSIGNAL
flag to the send() call. That way, if the client disconnects, we will
be able to detect this, and properly remove them from the client list.
Referenced from: https://beej.us/guide/bgnet/html//index.html#sendman
*/
if (send(*conn_fd, "Reply from server\n", 18, MSG_NOSIGNAL) < 0) {
printf("Client disconnected...\n");
// Close the client connection
close(*conn_fd);
// Remove client socket from the vector
vector_remove(vargp, i);
// Decrement index and client_server_size by 1
i--;
client_vector_size--;
}
}
}
}
To spawn the pthread:
// Spawn the thread that serves clients
pthread_t serving_thread;
pthread_create(&serving_thread, NULL, serve_clients, client_array);
When a new connection comes in, I simply add the new connection to the client vector
while(1) {
// Wait for incoming connection
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
if (-1 == connfd) {
perror("Could not accept incoming client");
continue;
}
//Resolving Client Address
char buff[INET_ADDRSTRLEN + 1] = {0};
inet_ntop(AF_INET, &cliaddr.sin_addr, buff, INET_ADDRSTRLEN);
uint16_t cli_port = ntohs(cliaddr.sin_port);
printf("connection from %s:%d -- Connfd: %d\n", buff, cli_port, connfd);
// Add client to vector list
vector_add(&client_array, connfd);
}
In the end, we have a TCP server that can multiplex data to many clients, and handle when those clients disconnect.

socket binding with INADDR_ANY and specific interface in multicast with multiple interface

I'm trying to write multicast server. I've two questions:
How to send multicast announcements on multiple interfaces like ethernet/ wifi interface. Do I need to create multiple sockets for each interface and bind?
When bind socket with INADDR_ANY address, descriptor is ready to do I/O operation (using select call )but when I bind with specific interface address e.g ethernet/wifi then descriptor is not ready to perform any operation it is stuck at select api only.
So what is the difference between binding a socket with default address (INADDR_ANY) or specific interface address?
int sd = socket(AF_INET, SOCK_DGRAM, 0);
if (sd < 0) {
printf("scoket() failed");
return sd;
}
int r = -1;
int on = 1;
if ((r = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on))) < 0) {
printf("recv setsockopt(SO_REUSEADDR) failed");
return r;
}
// add membership to receiving socket
struct ip_mreq mreq;
memset(&mreq, 0, sizeof(struct ip_mreq));
mreq.imr_interface.s_addr = htonl(INADDR_ANY);
mreq.imr_multiaddr.s_addr = inet_addr(MDNS_ADDR);
if ((r = setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreq, sizeof(mreq))) < 0) {
printf("recv setsockopt(IP_ADD_MEM) failed");
return r;
}
// enable loopback in case someone else needs the data
if ((r = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &on, sizeof(on))) < 0) {
printf("recv setsockopt(IP_MULTICAST_LOOP) failed");
return r;
}
#ifdef IP_PKTINFO
if ((r = setsockopt(sd, SOL_IP, IP_PKTINFO, (char *) &on, sizeof(on))) < 0) {
printf("recv setsockopt(IP_PKTINFO) failed");
return r;
}
#endif
/* bind to an address */
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(MDNS_PORT);
//serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); /* receive multicast */
serveraddr.sin_addr.s_addr = inet_addr("192.168.10.23"); /* receive multicast */
if ((r = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0) {
printf("recv bind()");
}
In thread waiting for descriptors to ready for I/O (basically all are read file descriptors)
FD_ZERO(&sockfd_set);
FD_SET(svr->sockfd, &sockfd_set);
FD_SET(svr->notify_pipe[0], &sockfd_set);
printf("before select\n");
select(max_fd + 1, &sockfd_set, NULL, NULL, NULL);
printf("after select\n");
When socket id bind with INADD_ANY address select will returns and I'm able to read with file descriptor but when bind with specific interface then select never returns coz there is no file descriptors available to read.
How to send multicast announcements on multiple interfaces like ethernet/ wifi interface. Do I need to create multiple sockets for each interface and bind?
No. Loop over the network interfaces and call setsockopt() with an appropriate join parameters for each interface in turn. You only need one socket.
When bind socket with INADDR_ANY address, descriptor is ready to do I/O operation (using select call)
That's not correct. Your code only checks for readability.
but when I bind with specific interface address e.g ethernet/wifi then descriptor is not ready to perform any operation
Again that's not correct. You're only checking for readability, not 'any operation'. All this means is that no multicasts are coming in via the address you bound to.
it is stuck at select api only. So what is the difference between binding a socket with default address (INADDR_ANY) or specific interface address?
It determines the IP address you send and receive via.
Binding a socket with a specific address is a generic way to lock it down to a device. This method is a bit old-fashion, since addresses can be changed on a device after your program has started up. In general I would recommend to bind to INADDR_ANY.
You can change the output multicast device on a socket whenever you want.
Options to consider:
man 7 ip :
IP_MULTICAST_ALL - deliver to all devices (default 1)
IP_MULTICAST_IF - I'm pretty sure this only sets the output device
man 7 socket :
SO_BINDTODEVICE
Just play a little with the options and see what the result is

C sockets send UDP and process ICMP reply from router

I'm trying to send a UDP packet to a router with a time to live of 1, to then receive an ICMP time exceeded reply. So far I'm able to send the packet, but when my program gets to the recv part of the execution, it just hangs. I have an error check for recvfrom, but it doesn't even get to that. My computer is receiving the request. I know this because I run Wireshark when I run the program and I filter for ICMP requests. Every time I run the program, I receive the reply. What am I doing wrong with recvfrom?
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <errno.h>
#define UNSPEC_PROTO 0
int main(int argc, const char *argv[])
{
if (argc != 2) {
printf("usage: routetracer <ip address or hostname>\n");
return -1;
}
struct addrinfo hints; //params for ret val of getaddrinfo
struct addrinfo* ret; //return value of getaddrinfo
struct sockaddr* reply_addr;
char ipv4[INET_ADDRSTRLEN];
char* msg = "THE PORT IS OVER 9000!!!!";
int status = 0;
int ttl = 0;
int src_sock = 0;
int recv_sock = 0;
socklen_t reply_addr_len = sizeof(struct sockaddr);
const char* dest_port = "9001";
int icmp_msg_len = 100;
char icmp_msg[icmp_msg_len];
//define what we want from getaddrinfo
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET; //IPv4
hints.ai_socktype = SOCK_DGRAM; //UDP packets
//call getaddrinfo to fill ret, w/ error chk
if ((status = getaddrinfo(argv[1], dest_port, &hints, &ret)) != 0) {
printf("getaddrinfo: %s\n", gai_strerror(status));
return -1;
}
//extract IPv4 address from ret
struct sockaddr_in* ip = (struct sockaddr_in *)ret->ai_addr;
//convert address from pure numbers to something easier to read
inet_ntop(ret->ai_family, &(ip->sin_addr), ipv4, INET_ADDRSTRLEN);
//kindly inform the user of which hostname they are connecting to
printf("Route for: %s\n", ipv4);
//create a socket for our machine
if ((src_sock = socket(ret->ai_family, ret->ai_socktype,
ret->ai_protocol)) < 0) {
fprintf(stderr, "Error creating host socket: %s\n", strerror(errno));
return -1;
}
//create a socket to recv icmp packet from hops
if ((recv_sock = socket(AF_INET, SOCK_DGRAM, UNSPEC_PROTO)) < 0){
fprintf(stderr, "Error creating recv socket: %s\n", strerror(errno));
}
/*
* We go from hop to hop by incrementing the time to live in the IP header
* for each hop we visit until we reach the destination IP address (which we
* already have). Time to live decrements for every hop, so once it reaches
* zero we report the IP address of the node we are connected to.
*/
//while(test_ip != dest_ip)
//time_to_live++
//send_to(dest_addr)
//receive icmp error message
//get src addr of error msg from ip header of icmp
//test_ip = src addr
/*
while (last_hop == 0) {
ttl++;
setsockopt(sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
sendto(sock, msg, strlen(msg), 0, (struct sockaddr *)ip, sizeof(ip));
}
*/
ttl = 1;
if (!(setsockopt(src_sock, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)))) {
printf("TTL set successfully\n");
} else {
printf("Error setting TTL: %s\n", strerror(errno));
}
if ((sendto(src_sock, msg, strlen(msg), 0, ret->ai_addr,
ret->ai_addrlen)) > 0) {
printf("msg sent successfully\n");
} else {
fprintf(stderr, "Error sending msg: %s\n", strerror(errno));
}
if ((recvfrom(recv_sock, icmp_msg, icmp_msg_len, 0, reply_addr,
&reply_addr_len)) != -1) {
/* PROCESS THE INFORMATION */
printf("Packet received\n");
} else {
fprintf(stderr, "Error receiving packet: %s\n", strerror(errno));
}
return 0;
}
Normally, UDP pretty much ignores ICMP errors, so if you want to see them, you need to open a raw socket to receive all ICMP packets and look for ones relevant to your socket.
On Linux, at least, an alternative is to set the IP_RECVERR socket option. If you do that, you can do a recvmsg with the MSG_ERRQUEUE flag set to get any ICMP (or other) errors associated with your socket. This has the advantage of not requiring elevated privileges or a second socket.
In some implementations of sockets, UDP socket has to be connected to receive errors.
So, you need to add connect call and then use send/recv functions.
I've confirmed this on FreeBSD. At least one source clearly states that:
http://www.softlab.ntua.gr/facilities/documentation/unix/unix-socket-faq/unix-socket-faq-5.html (see 5.3 Does doing a connect() call affect the receive behaviourof the socket?)
P.S. Note, however, that you won't receive exact ICMP error message that way. You'll only get some error code, without many details (if any).
Check the options when you are opening your sockets.
See How to sniff all ICMP packets using RAW sockets.
See How to receive ICMP request in C with raw sockets.
You may also want to change the socket options to be non-blocking and use the select() function to determine if there is something to read or not.
For examples on using the select() function see the following.
Blocking recvfrom with select system call.
Unexepcted results with select and recvfrom.
First of all, your code has undefined behavior, because reply_addr is uninitialised. You should fix that first:
struct sockaddr_in reply_addr;
...then:
recvfrom(recv_sock, icmp_msg, icmp_msg_len, 0, (struct sockaddr*)&reply_addr,
&reply_addr_len);
Finally, you need to use raw sockets, not datagram sockets, to receive ICMP packets:
recv_sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

Retrieve ip address of client who got a connection reset

I have this code which gets the ip of the client when the client closes or loses the connection.
char buffer[80];
ssize_t bread;
struct sockaddr_in peer;
socklen_t peer_len;
peer_len = sizeof(peer);
memset(&buffer, 0, sizeof(buffer));
bread = read(connectlist[listnum], buffer, 80);
if (bread < 0)
{
if(getpeername(connectlist[listnum],(struct sockaddr *) &peer, &peer_len) == -1){
perror("getpeername() failed");
}
printf("Connection Reset From IP: %s\n", inet_ntoa(peer.sin_addr));
_Print_To_File(inet_ntoa(peer.sin_addr));
close(connectlist[listnum]);
close(connectlist[listnum]);
connectlist[listnum] = 0;
}
if(bread == 0)
{
if(getpeername(connectlist[listnum],(struct sockaddr *) &peer, &peer_len) == -1){
perror("getpeername() failed");
}
printf("Connection Closed From IP: %s\n", inet_ntoa(peer.sin_addr));
_Print_To_File(inet_ntoa(peer.sin_addr));
close(connectlist[listnum]);
connectlist[listnum] = 0;
}
I can get the ip of the client when Connection Closed but When Connection Reset I don't get the ip of the client. I get 0.0.0.0 on connection reset. How can i fix this. thanks,
getpeername() works only for connected sockets. Once the socket disconnected you'll get ENOTCONN error when invoking it. This is why getpeername() is sometimes used as a check whether the socket has/is connected.
You might like to use the struct sockaddr returned by the call to accept() done prior to read()ing.
Verbatim from man accept:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
[...]
The argument addr is a pointer to a sockaddr structure. This
structure is filled in with the address of the peer socket, as known
to the communications layer. The exact format of the address returned
addr is determined by
the socket's address family (see socket(2) and the respective protocol man pages). When addr is NULL, nothing is filled in; in this
case, addrlen is not used, and should also be NULL.

Resources