Skip to content

Instantly share code, notes, and snippets.

@gtbX
Forked from hostilefork/listener.c
Last active October 13, 2023 18:20
Show Gist options
  • Save gtbX/f83daa63d918c4e1667a51a53ab41577 to your computer and use it in GitHub Desktop.
Save gtbX/f83daa63d918c4e1667a51a53ab41577 to your computer and use it in GitHub Desktop.
Simple listener and sender for UDP multicast
//
// Simple listener.c program for UDP multicast
//
// Adapted from:
// http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html
//
// Changes:
// * Compiles for Windows as well as Linux
// * Takes the port and group on the command line
//
#ifdef _WIN32
#include <Winsock2.h> // before Windows.h, else Winsock 1 conflict
#include <Ws2tcpip.h> // needed for ip_mreq definition for multicast
#include <iphlpapi.h> // needed for GetAdaptersAddresses()
#include <Windows.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <errno.h>
#include <time.h>
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define MSGBUFSIZE 256
int main(int argc, char *argv[])
{
if (argc != 3) {
printf("Command line args should be multicast group and port\n");
printf("(e.g. for SSDP, `listener 239.255.255.250 1900`)\n");
return 1;
}
char* group = argv[1]; // e.g. 239.255.255.250 for SSDP
int port = atoi(argv[2]); // 0 if error, which is an invalid port
#ifdef _WIN32
//
// Initialize Windows Socket API with given VERSION.
//
WSADATA wsaData;
if (WSAStartup(0x0101, &wsaData)) {
perror("WSAStartup");
return 1;
}
#endif
// parse the group address into the appropriate sockaddr
//
struct addrinfo* info;
int gai = getaddrinfo(group, NULL, NULL, &info);
if (gai != 0) {
fprintf(stderr, "getaddrinfo %s\n", gai_strerror(gai));
return gai;
}
// create what looks like an ordinary UDP socket
//
int fd = socket(info->ai_family, SOCK_DGRAM, 0);
if (fd < 0) {
perror("socket");
return 1;
}
// allow multiple sockets to use the same PORT number
//
u_int yes = 1;
if (
setsockopt(
fd, SOL_SOCKET, SO_REUSEADDR, (char*) &yes, sizeof(yes)
) < 0
){
perror("Reusing ADDR failed");
return 1;
}
// set up destination address
//
struct sockaddr_storage addr;
socklen_t socklen;
memset(&addr, 0, sizeof(addr));
addr.ss_family = info->ai_family;
if (addr.ss_family == AF_INET) {
struct sockaddr_in* inaddr = (struct sockaddr_in*)&addr;
inaddr->sin_addr.s_addr = htonl(INADDR_ANY); // differs from sender
inaddr->sin_port = htons(port);
socklen = sizeof(struct sockaddr_in);
} else if (addr.ss_family == AF_INET6) {
struct sockaddr_in6* inaddr6 = (struct sockaddr_in6*)&addr;
inaddr6->sin6_addr = in6addr_any; // differs from sender
inaddr6->sin6_port = htons(port);
socklen = sizeof(struct sockaddr_in6);
}
// bind to receive address
//
if (bind(fd, (struct sockaddr*) &addr, socklen) < 0) {
perror("bind");
return 1;
}
// iterate through all network interfaces, and join the group on all of them.
// joining the group using only the wildcard address will only request messages from the interface of the default route.
//
#ifdef _WIN32
char ipaddrs[16*1024];
ULONG ipaddrs_len = sizeof(ipaddrs);
ULONG err = GetAdaptersAddresses(info->ai_family, GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME, NULL, ipaddrs, &ipaddrs_len);
if (err != ERROR_SUCCESS) {
fprintf(stderr, "GetAdapterAddresses: %ul\n", err);
return err;
}
for (PIP_ADAPTER_ADDRESSES ipa = (PIP_ADAPTER_ADDRESSES)ipaddrs; ipa != NULL; ipa = ipa->Next) {
if (ipa->OperStatus != IfOperStatusUp)
continue; // interface must be up
if (ipa->FirstUnicastAddress == NULL)
continue; // interface must have an address
#else
struct ifaddrs* ifaddr;
if (getifaddrs(&ifaddr) != 0) {
perror("getifaddrs");
return 1;
}
for (struct ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL)
continue; // interface must have an address
if (ifa->ifa_addr->sa_family != info->ai_family)
continue; // address must be same family as group address
#endif
// use setsockopt() to request that the kernel join a multicast group
//
if (info->ai_family == AF_INET) {
struct ip_mreq mreq;
struct sockaddr_in* g_addr = (struct sockaddr_in*)info->ai_addr;
mreq.imr_multiaddr.s_addr = g_addr->sin_addr.s_addr;
struct sockaddr_in* if_addr = (struct sockaddr_in*)
#ifdef _WIN32
ipa->FirstUnicastAddress->Address.lpSockaddr;
#else
ifa->ifa_addr;
#endif
mreq.imr_interface.s_addr = if_addr->sin_addr.s_addr;
if (
setsockopt(
fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*) &mreq, sizeof(mreq)
) < 0
){
if (errno == EADDRINUSE)
continue;
perror("setsockopt(IP_ADD_MEMBERSHIP)");
return 1;
}
} else if (info->ai_family == AF_INET6) {
struct ipv6_mreq mreq;
struct sockaddr_in6* g_addr = (struct sockaddr_in6*)info->ai_addr;
mreq.ipv6mr_multiaddr = g_addr->sin6_addr;
mreq.ipv6mr_interface = // IPv6 requires interface ID instead of address
#ifdef _WIN32
ipa->IfIndex;
#else
if_nametoindex(ifa->ifa_name);
#endif
if (
setsockopt(
fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char*) &mreq, sizeof(mreq)
) < 0
){
if (errno == EADDRINUSE)
continue;
perror("setsockopt(IPV6_JOIN_GROUP)");
return 1;
}
}
}
#ifndef _WIN32
freeifaddrs(ifaddr);
#endif
freeaddrinfo(info);
// now just enter a read-print loop
//
while (1) {
char hostbuf[64];
char portbuf[16];
char msgbuf[MSGBUFSIZE];
socklen_t addrlen = sizeof(addr);
int nbytes = recvfrom(
fd,
msgbuf,
MSGBUFSIZE,
0,
(struct sockaddr *) &addr,
&addrlen
);
if (nbytes < 0) {
perror("recvfrom");
return 1;
}
msgbuf[nbytes] = '\0';
gai = getnameinfo((struct sockaddr*)&addr, addrlen, hostbuf, sizeof(hostbuf), portbuf, sizeof(portbuf), NI_NUMERICHOST | NI_NUMERICSERV | NI_DGRAM);
if (gai != 0) {
fprintf(stderr, "getnameinfo %s\n", gai_strerror(gai));
return gai;
}
printf("From [%s]:%s received: %s\n", hostbuf, portbuf, msgbuf);
}
//
// Program never actually gets here due to infinite loop that has to be
// canceled, but since people on the internet wind up using examples
// they find at random in their own code it's good to show what shutting
// down cleanly would look like.
//
#ifdef _WIN32
WSACleanup();
#endif
return 0;
}
//
// Simple sender.c program for UDP
//
// Adapted from:
// http://ntrg.cs.tcd.ie/undergrad/4ba2/multicast/antony/example.html
//
// Changes:
// * Compiles for Windows as well as Linux
// * Takes the port and group on the command line
//
// Note that what this program does should be equivalent to NETCAT:
//
// echo "Hello World" | nc -u 239.255.255.250 1900
#ifdef _WIN32
#include <Winsock2.h> // before Windows.h, else Winsock 1 conflict
#include <Ws2tcpip.h> // needed for ip_mreq definition for multicast
#include <Windows.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h> // for sleep()
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
if (argc < 3) {
printf("Command line args should be multicast group and port, with optional source address\n");
printf("(e.g. for SSDP, `sender 239.255.255.250 1900`)\n");
return 1;
}
char* group = argv[1]; // e.g. 239.255.255.250 for SSDP
int port = atoi(argv[2]); // 0 if error, which is an invalid port
// !!! If test requires, make these configurable via args
//
const int delay_secs = 1;
const char *message = "Hello, World!";
#ifdef _WIN32
//
// Initialize Windows Socket API with given VERSION.
//
WSADATA wsaData;
if (WSAStartup(0x0101, &wsaData)) {
perror("WSAStartup");
return 1;
}
#endif
// parse the group address into the appropriate sockaddr
//
struct addrinfo* info;
int gai = getaddrinfo(group, NULL, NULL, &info);
if (gai != 0) {
fprintf(stderr, "getaddrinfo(%s): %s\n", group, gai_strerror(gai));
return gai;
}
// create what looks like an ordinary UDP socket
//
int fd = socket(info->ai_family, SOCK_DGRAM, 0);
if (fd < 0) {
perror("socket");
return 1;
}
if (argc > 3) {
// parse source address from command line
char* source = argv[3];
struct addrinfo* source_info;
gai = getaddrinfo(source, NULL, NULL, &source_info);
if (gai != 0) {
fprintf(stderr, "getaddrinfo(%s): %s\n", source, gai_strerror(gai));
return gai;
}
if (source_info->ai_family != info->ai_family) {
fprintf(stderr, "Group (%s) and source (%s) IPs must be the same type\n", group, source);
return 1;
}
if (bind(fd, source_info->ai_addr, source_info->ai_addrlen) != 0) {
perror("bind");
return 1;
}
freeaddrinfo(source_info);
}
// set up destination address
//
struct sockaddr* addr = info->ai_addr;
socklen_t addrlen = info->ai_addrlen;
if (addr->sa_family == AF_INET) {
((struct sockaddr_in*)addr)->sin_port = htons(port);
} else if (addr->sa_family == AF_INET6) {
((struct sockaddr_in6*)addr)->sin6_port = htons(port);
}
// now just sendto() our destination!
//
while (1) {
char ch = 0;
int nbytes = sendto(
fd,
message,
strlen(message),
0,
addr,
addrlen
);
if (nbytes < 0) {
perror("sendto");
return 1;
}
#ifdef _WIN32
Sleep(delay_secs * 1000); // Windows Sleep is milliseconds
#else
sleep(delay_secs); // Unix sleep is seconds
#endif
}
//
// Program never actually gets here due to infinite loop that has to be
// canceled, but since people on the internet wind up using examples
// they find at random in their own code it's good to show what shutting
// down cleanly would look like.
//
freeaddrinfo(info);
#ifdef _WIN32
WSACleanup();
#endif
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment