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
24 int open_multicast_socket(const char *group, const char *port,
25 const char *source, const int ttl);
26 static void err_sys(const char *fmt, ...);
27 static void err_quit(const char *fmt, ...);
28 static void usage(void);
29
30 /* global variables */
31 static 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
main(int argc,char * argv[])40 int 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
open_multicast_socket(const char * group,const char * port,const char * source,const int ttl)100 int 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
usage(void)214 static 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 */
err_sys(const char * fmt,...)229 static 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
err_quit(const char * fmt,...)240 static 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