xref: /haiku/src/tests/system/network/multicast/multisend.c (revision 5e54f6d4f9dd607ae2afcea4fe72f2f1763e4b5e)
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