1/*
2 * Copyright 2003-2004 by Marco d'Itri <md@linux.it>
3 * This software is distributed under the terms of the GNU GPL. If we meet some
4 * day, and you think this stuff is worth it, you can buy me a beer in return.
5 *
6 */
7
8#include <stdio.h>
9#include <stdlib.h>
10#include <unistd.h>
11#include <stdarg.h>
12#include <string.h>		/* strerror */
13#include <errno.h>
14#include <string.h>
15#include <getopt.h>		/* getopt_long */
16#include <sys/types.h>
17#include <sys/socket.h>
18#include <netdb.h>
19#include <arpa/inet.h>
20#include <netinet/in.h>
21#include <time.h>
22#include <sys/utsname.h>
23
24int open_multicast_socket(const char *group, const char *port,
25	const char *source, const int ttl);
26static void err_sys(const char *fmt, ...);
27static void err_quit(const char *fmt, ...);
28static void usage(void);
29
30/* global variables */
31static struct option longopts[] = {
32    { "help",		no_argument,		NULL, 'h' },
33    { "input",		no_argument,		NULL, 'i' },
34    { "size",		required_argument,	NULL, 's' },
35    { "bind",		required_argument,	NULL, 'b' },
36    { "ttl",		required_argument,	NULL, 't' },
37    { NULL,		0,			NULL, 0   }
38};
39
40int main(int argc, char *argv[])
41{
42    int fd, len, ch;
43    int opt_input = 0;
44    int ttl = 128;
45    int sndbuf_size = 1280;
46    char *source = NULL;
47
48    while ((ch = getopt_long(argc, argv, "his:b:t:", longopts, 0)) > 0) {
49	switch (ch) {
50	case 's':
51	    sndbuf_size = atoi(optarg);
52	    break;
53	case 'i':
54	    opt_input = 1;
55	    break;
56	case 'b':
57	    source = optarg;
58	    break;
59	case 't':
60	    ttl = atoi(optarg);
61	    break;
62	default:
63	    usage();
64	}
65    }
66    argc -= optind;
67    argv += optind;
68
69    if (argc != 2)
70	usage();
71
72    fd = open_multicast_socket(argv[0], argv[1], source, ttl);
73
74    if (opt_input) {
75	char buf[sndbuf_size];
76
77	while ((len = read(STDIN_FILENO, buf, sizeof(buf))) > 0) {
78	    if (send(fd, buf, len, 0) < 0)
79		err_sys("send");
80	}
81    } else {
82	struct utsname utsname;
83	char buf[1024];
84
85	uname(&utsname);
86	snprintf(buf, sizeof(buf), "Hello from %s!\n", utsname.nodename);
87	len = strlen(buf);
88
89	while (1) {
90	    if (send(fd, buf, len, 0) < 0)
91		err_sys("send");
92
93	    sleep(1);
94	}
95    }
96
97    exit(0);
98}
99
100int open_multicast_socket(const char *group, const char *port,
101	const char *source, const int ttl)
102{
103    int fd;
104    int loop = 1;
105
106#ifdef AF_INET6
107    int err;
108    struct addrinfo hints, *res, *ai;
109    int level, sockopt_h, sockopt_l;
110
111    memset(&hints, 0, sizeof(hints));
112    hints.ai_family = AF_UNSPEC;
113    hints.ai_socktype = SOCK_DGRAM;
114
115    if ((err = getaddrinfo(group, port, &hints, &res)) != 0)
116	err_quit("getaddrinfo(%s, %s): %s", source, port, gai_strerror(err));
117    for (ai = res; ai; ai = ai->ai_next) {
118	if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
119	    continue;		/* ignore */
120	if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0)
121	    break;		/* success */
122	close(fd);
123    }
124
125    if (!ai)
126	err_sys("connect");
127
128    switch (ai->ai_family) {
129    case AF_INET:
130	level = IPPROTO_IP;
131	sockopt_h = IP_MULTICAST_TTL;
132	sockopt_l = IP_MULTICAST_LOOP;
133	break;
134    case AF_INET6:
135	level = IPPROTO_IPV6;
136	sockopt_h = IPV6_MULTICAST_HOPS;
137	sockopt_l = IPV6_MULTICAST_LOOP;
138	break;
139    default:
140	err_quit("FATAL: family %d is not known", ai->ai_family);
141    }
142
143    if (source) {
144	struct addrinfo shints, *sai;
145	int yes = 1;
146
147	memset(&shints, 0, sizeof(shints));
148	shints.ai_family = ai->ai_family;
149	shints.ai_protocol = ai->ai_protocol;
150
151	if ((err = getaddrinfo(source, port, &shints, &sai)) != 0)
152	    err_quit("getaddrinfo(%s, %s): %s", source, port,
153		    gai_strerror(err));
154
155	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
156	    err_sys("setsockopt(SO_REUSEADDR)");
157
158	if (bind(fd, sai->ai_addr, sai->ai_addrlen) < 0)
159	    err_sys("bind");
160
161	freeaddrinfo(sai);
162	/*
163	struct ip_addr ifaddr = inet_addr();
164	setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(struct in_addr));
165	setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifidx, sizeof(unsigned int));
166	man 7 netdevice
167	*/
168    }
169
170    freeaddrinfo(res);
171
172#else
173    struct sockaddr_in addr;
174    struct hostent *hostinfo;
175    const int level = IPPROTO_IP;
176    const int sockopt_h = IP_MULTICAST_TTL;
177    const int sockopt_l = IP_MULTICAST_LOOP;
178
179    if ((hostinfo = gethostbyname(group)) == NULL)
180	err_quit("Host %s not found.", group);
181
182    /* create what looks like an ordinary UDP socket */
183    if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
184	err_sys("socket");
185
186    /* set up destination address */
187    memset(&addr, 0, sizeof(addr));
188    addr.sin_family = AF_INET;
189    addr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
190    addr.sin_port = htons(atoi(port));
191
192    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
193	err_sys("connect");
194
195    if (source) { /* XXX */
196	int yes = 1;
197
198	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
199	    err_sys("setsockopt(SO_REUSEADDR)");
200    }
201#endif
202
203    if (setsockopt(fd, level, sockopt_h, &ttl, sizeof(ttl)) < 0)
204	err_sys("setsockopt(IP_MULTICAST_TTL)");
205
206    if (loop == 0)
207	if (setsockopt(fd, level, sockopt_l, &loop, sizeof(loop)) < 0)
208	    err_sys("setsockopt(IP_MULTICAST_LOOP)");
209
210    return fd;
211}
212
213
214static void usage(void)
215{
216    fprintf(stderr,
217"Usage: multisend [OPTIONS...] GROUP PORT\n"
218"\n"
219"  -b, --bind=ADDR    bind the socket to address ADDR\n"
220"  -t, --ttl=NUM      set the TTL to NUM\n"
221"  -h, --help         display this help and exit\n"
222"\n"
223"If --ttl is not specified, 128 is used.\n"
224);
225    exit(0);
226}
227
228/* Error routines */
229static void err_sys(const char *fmt, ...)
230{
231    va_list ap;
232
233    va_start(ap, fmt);
234    vfprintf(stderr, fmt, ap);
235    fprintf(stderr, ": %s\n", strerror(errno));
236    va_end(ap);
237    exit(1);
238}
239
240static void err_quit(const char *fmt, ...)
241{
242    va_list ap;
243
244    va_start(ap, fmt);
245    vfprintf(stderr, fmt, ap);
246    fputs("\n", stderr);
247    va_end(ap);
248    exit(1);
249}
250
251#if 0
252/* local network */
253#define HELLO_GROUP "224.0.0.3"
254/* ? */
255#define HELLO_GROUP "225.0.0.37"
256/* GLOP */
257#define HELLO_GROUP "233.49.235.42"
258/* fw */
259#define HELLO_GROUP "239.113.42.66"
260/* Organization-Local */
261#define HELLO_GROUP "239.192.42.66"
262/* Site-Local */
263#define HELLO_GROUP "239.252.42.66"
264/* Source Specific Multicast */
265#define HELLO_GROUP "232.1.1.1"
266/* node local */
267#define HELLO_ADDR "FF01::1111"
268#endif
269