* Copyright 2003-2004 by Marco d'Itri <md@linux.it>
* This software is distributed under the terms of the GNU GPL. If we meet some
* day, and you think this stuff is worth it, you can buy me a beer in return.
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <time.h>
#include <sys/utsname.h>
int open_multicast_socket(const char *group, const char *port,
const char *source, const int ttl);
static void err_sys(const char *fmt, ...);
static void err_quit(const char *fmt, ...);
static void usage(void);
static struct option longopts[] = {
{ "help", no_argument, NULL, 'h' },
{ "input", no_argument, NULL, 'i' },
{ "size", required_argument, NULL, 's' },
{ "bind", required_argument, NULL, 'b' },
{ "ttl", required_argument, NULL, 't' },
{ NULL, 0, NULL, 0 }
};
int main(int argc, char *argv[])
{
int fd, len, ch;
int opt_input = 0;
int ttl = 128;
int sndbuf_size = 1280;
char *source = NULL;
while ((ch = getopt_long(argc, argv, "his:b:t:", longopts, 0)) > 0) {
switch (ch) {
case 's':
sndbuf_size = atoi(optarg);
break;
case 'i':
opt_input = 1;
break;
case 'b':
source = optarg;
break;
case 't':
ttl = atoi(optarg);
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 2)
usage();
fd = open_multicast_socket(argv[0], argv[1], source, ttl);
if (opt_input) {
char buf[sndbuf_size];
while ((len = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
if (send(fd, buf, len, 0) < 0)
err_sys("send");
}
} else {
struct utsname utsname;
char buf[1024];
uname(&utsname);
snprintf(buf, sizeof(buf), "Hello from %s!\n", utsname.nodename);
len = strlen(buf);
while (1) {
if (send(fd, buf, len, 0) < 0)
err_sys("send");
sleep(1);
}
}
exit(0);
}
int open_multicast_socket(const char *group, const char *port,
const char *source, const int ttl)
{
int fd;
int loop = 1;
#ifdef AF_INET6
int err;
struct addrinfo hints, *res, *ai;
int level, sockopt_h, sockopt_l;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
if ((err = getaddrinfo(group, port, &hints, &res)) != 0)
err_quit("getaddrinfo(%s, %s): %s", source, port, gai_strerror(err));
for (ai = res; ai; ai = ai->ai_next) {
if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
continue;
if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
break;
close(fd);
}
if (!ai)
err_sys("connect");
switch (ai->ai_family) {
case AF_INET:
level = IPPROTO_IP;
sockopt_h = IP_MULTICAST_TTL;
sockopt_l = IP_MULTICAST_LOOP;
break;
case AF_INET6:
level = IPPROTO_IPV6;
sockopt_h = IPV6_MULTICAST_HOPS;
sockopt_l = IPV6_MULTICAST_LOOP;
break;
default:
err_quit("FATAL: family %d is not known", ai->ai_family);
}
if (source) {
struct addrinfo shints, *sai;
int yes = 1;
memset(&shints, 0, sizeof(shints));
shints.ai_family = ai->ai_family;
shints.ai_protocol = ai->ai_protocol;
if ((err = getaddrinfo(source, port, &shints, &sai)) != 0)
err_quit("getaddrinfo(%s, %s): %s", source, port,
gai_strerror(err));
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
err_sys("setsockopt(SO_REUSEADDR)");
if (bind(fd, sai->ai_addr, sai->ai_addrlen) < 0)
err_sys("bind");
freeaddrinfo(sai);
struct ip_addr ifaddr = inet_addr();
setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(struct in_addr));
setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifidx, sizeof(unsigned int));
man 7 netdevice
*/
}
freeaddrinfo(res);
#else
struct sockaddr_in addr;
struct hostent *hostinfo;
const int level = IPPROTO_IP;
const int sockopt_h = IP_MULTICAST_TTL;
const int sockopt_l = IP_MULTICAST_LOOP;
if ((hostinfo = gethostbyname(group)) == NULL)
err_quit("Host %s not found.", group);
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
err_sys("socket");
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
addr.sin_port = htons(atoi(port));
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
err_sys("connect");
if (source) {
int yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
err_sys("setsockopt(SO_REUSEADDR)");
}
#endif
if (setsockopt(fd, level, sockopt_h, &ttl, sizeof(ttl)) < 0)
err_sys("setsockopt(IP_MULTICAST_TTL)");
if (loop == 0)
if (setsockopt(fd, level, sockopt_l, &loop, sizeof(loop)) < 0)
err_sys("setsockopt(IP_MULTICAST_LOOP)");
return fd;
}
static void usage(void)
{
fprintf(stderr,
"Usage: multisend [OPTIONS...] GROUP PORT\n"
"\n"
" -b, --bind=ADDR bind the socket to address ADDR\n"
" -t, --ttl=NUM set the TTL to NUM\n"
" -h, --help display this help and exit\n"
"\n"
"If --ttl is not specified, 128 is used.\n"
);
exit(0);
}
static void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": %s\n", strerror(errno));
va_end(ap);
exit(1);
}
static void err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
fputs("\n", stderr);
va_end(ap);
exit(1);
}
#if 0
#define HELLO_GROUP "224.0.0.3"
#define HELLO_GROUP "225.0.0.37"
#define HELLO_GROUP "233.49.235.42"
#define HELLO_GROUP "239.113.42.66"
#define HELLO_GROUP "239.192.42.66"
#define HELLO_GROUP "239.252.42.66"
#define HELLO_GROUP "232.1.1.1"
#define HELLO_ADDR "FF01::1111"
#endif