xref: /haiku/src/tests/system/network/multicast/multicat.c (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 <signal.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <getopt.h>		/* getopt_long */
17 #include <sys/types.h>
18 #include <sys/select.h>
19 #include <sys/socket.h>
20 #include <sys/stat.h>
21 #include <netdb.h>
22 #include <arpa/inet.h>
23 #include <netinet/in.h>
24 
25 #define RECV_BUF_SIZE	64 * 1024
26 
27 /* SSM and protocol-independent API from RFC3678 */
28 #ifndef IP_ADD_SOURCE_MEMBERSHIP
29 # define IP_ADD_SOURCE_MEMBERSHIP 39
30 struct ip_mreq_source {
31     struct in_addr imr_multiaddr;	/* multicast group */
32     struct in_addr imr_sourceaddr;	/* source */
33     struct in_addr imr_interface;	/* local IP address of the interface */
34 };
35 #endif /* IP_ADD_SOURCE_MEMBERSHIP */
36 
37 #ifndef MCAST_JOIN_GROUP
38 # define MCAST_JOIN_GROUP 42
39 # define MCAST_JOIN_SOURCE_GROUP 46
40 struct group_req {
41     uint32_t                gr_interface;	/* interface index */
42     struct sockaddr_storage gr_group;		/* group address */
43 };
44 struct group_source_req {
45     uint32_t                gsr_interface;	/* interface index */
46     struct sockaddr_storage gsr_group;		/* group address */
47     struct sockaddr_storage gsr_source;		/* source address */
48 };
49 #endif
50 
51 
52 #define v_printf(level, x...) \
53     if (verbose >= level) \
54 	fprintf(stderr, x)
55 
56 /* prototypes*/
57 //void chat(const int sock, const struct sockaddr_in6 saddr);
58 void chat(const int sock);
59 void dump(const int sock, int out_fd, const int dump_packets);
60 static int open_multicast_socket(const char *group, const char *port,
61 	const char *source);
62 static int open_packet_file(void *buf, int n, const char *addr);
63 static void alarm_exit(int signaln);
64 static void usage(void);
65 static void err_quit(const char *, ...);
66 static void err_sys(const char *, ...);
67 
68 /* global variables */
69 static struct option longopts[] = {
70     { "help",		no_argument,		NULL, 'h' },
71     { "verbose",	no_argument,		NULL, 'v' },
72     { "timeout",	required_argument,	NULL, 't' },
73     { "output",		required_argument,	NULL, 'o' },
74     { "source",		required_argument,	NULL, 's' },
75     { NULL,		0,			NULL, 0   }
76 };
77 
78 static int verbose = 0;
79 
80 int main(int argc, char *argv[])
81 {
82     int timeout = 0;
83     int out_fd = STDOUT_FILENO;
84     int dump_packets = 0;
85     int ch, sock;
86     char *source = NULL;
87 
88     while ((ch = getopt_long(argc, argv, "hvo:pt:s:", longopts, 0)) > 0) {
89 	switch (ch) {
90 	case 'o':
91 	    out_fd = open(optarg, O_CREAT | O_TRUNC | O_WRONLY, 0666);
92 	    if (out_fd < 0)
93 		err_sys("open");
94 	    break;
95 	case 'p':
96 	    dump_packets = 1;
97 	    break;
98 	case 't':
99 	    timeout = atoi(optarg);
100 	    break;
101 	case 's':
102 	    source = optarg;
103 	    break;
104 	case 'v':
105 	    verbose++;
106 	    break;
107 	default:
108 	    usage();
109 	}
110     }
111     argc -= optind;
112     argv += optind;
113 
114     if (argc != 2)
115 	usage();
116 
117     sock = open_multicast_socket(argv[0], argv[1], source);
118 
119     if (timeout) {
120 	signal(SIGALRM, alarm_exit);
121 	alarm(timeout);
122     }
123 
124     chat(sock);
125     dump(sock, out_fd, dump_packets);
126     exit(0);
127 }
128 
129 //void chat(const int sock, const struct sockaddr_in6 saddr)
130 void chat(const int sock)
131 {
132     fd_set in;
133 
134     while (1) {
135 	int res;
136 
137 	FD_ZERO(&in);
138 
139 	FD_SET(0, &in);
140 	FD_SET(sock, &in);
141 
142 	res = select(sock + 1, &in, 0, 0, 0);
143 	if (res < 0)
144 	    break;
145 
146 	if (FD_ISSET(0, &in)) {
147 	    char buffer[8192];
148 	    int red = read(0, buffer, sizeof(buffer) - 1);
149 	    buffer[red] = 0;
150 #if 0
151 	    if (sendto(sock, buffer, red + 1, 0, (struct sockaddr *) &saddr,
152 			sizeof(saddr)) < 0)
153 #else
154 	    if (send(sock, buffer, red + 1, 0) < 0)
155 #endif
156 		err_sys("send");
157 	}
158 	if (FD_ISSET(sock, &in)) {
159 	    struct sockaddr_in6 from;
160 	    socklen_t fromlen = sizeof(from);
161 	    char taddr[64];
162 	    char buffer[8192];
163 
164 	    int red = recvfrom(sock, buffer, sizeof(buffer), 0,
165 		    (struct sockaddr *) &from, &fromlen);
166 	    buffer[red] = 0;
167 
168 	    inet_ntop(AF_INET6, &from.sin6_addr, taddr, sizeof(taddr));
169 
170 	    printf("<%s> %s\n", taddr, buffer);
171 	}
172     }
173 }
174 
175 void dump(const int sock, int out_fd, const int dump_packets) {
176     int n;
177     struct sockaddr_storage sraddr;
178     struct sockaddr *raddr = (struct sockaddr *)&sraddr;
179     socklen_t rlen = sizeof(struct sockaddr_storage);
180     char buf[RECV_BUF_SIZE];
181 
182     while ((n = recvfrom(sock, buf, sizeof(buf), 0, raddr, &rlen)) > 0) {
183 	char address[NI_MAXHOST], port[NI_MAXSERV];
184 
185 	if (verbose >= 2) {
186 	    if (getnameinfo(raddr, rlen, address, sizeof(address), port,
187 			sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV) < 0)
188 		err_sys("getnameinfo");
189 	    printf("Received %d bytes from [%s]:%s.\n", n, address, port);
190 	}
191 	if (dump_packets)
192 	    out_fd = open_packet_file(buf, n, address);
193 	if (write(out_fd, buf, n) != n)
194 	    err_sys("write");
195 	if (dump_packets)
196 	    if (close(out_fd) < 0)
197 		err_sys("close");
198     }
199     if (n < 0)
200 	err_sys("recvfrom");
201 
202     if (!dump_packets && out_fd != STDOUT_FILENO)
203 	if (close(out_fd) < 0)
204 	    err_sys("close");
205 
206     v_printf(1, "End of stream.\n");
207 }
208 
209 static int open_multicast_socket(const char *group, const char *port,
210 	const char *source)
211 {
212     int fd;
213     int yes = 1;
214 #ifdef AF_INET6
215     int err;
216     struct addrinfo hints, *res, *ai;
217     int level;
218     struct group_req gr;
219     struct group_source_req gsr;
220 
221     memset(&hints, 0, sizeof(hints));
222     hints.ai_family = AF_UNSPEC;
223     hints.ai_socktype = SOCK_DGRAM;
224 
225     if ((err = getaddrinfo(group, port, &hints, &res)) != 0)
226 	err_quit("getaddrinfo(%s, %s): %s", source, port, gai_strerror(err));
227     for (ai = res; ai; ai = ai->ai_next) {
228 	if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0)
229 	    continue;		/* ignore */
230 	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
231 	    err_sys("setsockopt(SO_REUSEADDR)");
232 	if (bind(fd, ai->ai_addr, ai->ai_addrlen) == 0)
233 	    break;		/* success */
234 	close(fd);
235     }
236 
237     if (connect(fd, ai->ai_addr, ai->ai_addrlen) < 0)
238 	err_sys("connect");
239 
240     if (!ai)
241 	err_sys("bind");
242 
243     switch (ai->ai_family) {
244     case AF_INET:
245 	level = IPPROTO_IP;
246 	break;
247     case AF_INET6:
248 	level = IPPROTO_IPV6;
249 	break;
250     default:
251 	err_quit("FATAL: family %d is not known", ai->ai_family);
252     }
253 
254     if (source) {
255 	struct addrinfo shints, *sai;
256 
257 	memset(&shints, 0, sizeof(shints));
258 	shints.ai_family = ai->ai_family;
259 	shints.ai_protocol = ai->ai_protocol;
260 
261 	if ((err = getaddrinfo(source, port, &shints, &sai)) != 0)
262 	    err_quit("getaddrinfo(%s, %s): %s", source, port,
263 		    gai_strerror(err));
264 
265 	memcpy(&gsr.gsr_group, ai->ai_addr, ai->ai_addrlen);
266 	memcpy(&gsr.gsr_source, sai->ai_addr, sai->ai_addrlen);
267 	gsr.gsr_interface = 0;
268 	if (setsockopt(fd, level, MCAST_JOIN_SOURCE_GROUP, &gsr,
269 		    sizeof(gsr)) < 0)
270 	    err_sys("setsockopt(MCAST_JOIN_SOURCE_GROUP)");
271 	freeaddrinfo(sai);
272     } else {
273 	memcpy(&gr.gr_group, ai->ai_addr, ai->ai_addrlen);
274 	gr.gr_interface = 0;
275 	if (setsockopt(fd, level, MCAST_JOIN_GROUP, &gr, sizeof(gr)) < 0)
276 	    err_sys("setsockopt(MCAST_JOIN_GROUP)");
277     }
278 
279     freeaddrinfo(res);
280 #else
281     struct hostent *hostinfo;
282     struct sockaddr_in saddr;
283     struct ip_mreq mr;
284     struct ip_mreq_source mrs;
285 
286     if ((hostinfo = gethostbyname(group)) == NULL)
287 	err_quit("Host %s not found.", group);
288 
289     if (!IN_MULTICAST(ntohl(*hostinfo->h_addr)))
290 	err_quit("%s is not a multicast address", group);
291 
292     if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0)
293 	err_sys("socket");
294 
295     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
296 	err_sys("setsockopt(SO_REUSEADDR)");
297 
298     memset(&saddr, 0, sizeof(saddr));
299     saddr.sin_family = AF_INET;
300     saddr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
301     saddr.sin_port = htons(atoi(port));
302     if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
303 	err_sys("bind");
304 
305     if (source) {
306 	mrs.imr_multiaddr = saddr.sin_addr;
307 	mrs.imr_sourceaddr.s_addr = inet_addr(source);
308 	mrs.imr_interface.s_addr = htonl(INADDR_ANY);
309 	if (setsockopt(fd, IPPROTO_IP, IP_ADD_SOURCE_MEMBERSHIP, &mrs,
310 		    sizeof(mrs)) < 0)
311 	    err_sys("setsockopt(IP_ADD_SOURCE_MEMBERSHIP)");
312     } else {
313 	mr.imr_multiaddr = saddr.sin_addr;
314 	mr.imr_interface.s_addr = htonl(INADDR_ANY);
315 	if (setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mr, sizeof(mr))<0)
316 	    err_sys("setsockopt(IP_ADD_MEMBERSHIP)");
317     }
318 #endif
319 
320     return fd;
321 }
322 
323 static int open_packet_file(void *buf, int n, const char *address)
324 {
325     static unsigned long int counter = 0;
326     char filename[1024];
327     int fd;
328 
329     snprintf(filename, sizeof(filename), "pkt-%lu-%s", counter++, address);
330     fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0666);
331     if (fd < 0)
332 	err_sys("open");
333     return fd;
334 }
335 
336 static void alarm_exit(int signaln)
337 {
338     v_printf(1, "Timeout.\n");
339     exit(0);
340 }
341 
342 static void usage(void)
343 {
344     fprintf(stderr,
345 "Usage: multicat [OPTIONS...] GROUP PORT\n"
346 "\n"
347 "  -o, --output=FILE  write the stream to FILE\n"
348 "  -p                 write every packet to a new file\n"
349 "  -t, --timeout=NUM  the program will exit after NUM seconds\n"
350 "  -s, --source=ADDR  join the SSM source group ADDR\n"
351 "  -v, --verbose      tell me more. Use multiple times for more details\n"
352 "  -h, --help         display this help and exit\n"
353 "\n"
354 "If --output is not specified, standard output is used.\n"
355 );
356     exit(0);
357 }
358 
359 /* Error routines */
360 static void err_sys(const char *fmt, ...)
361 {
362     va_list ap;
363 
364     va_start(ap, fmt);
365     vfprintf(stderr, fmt, ap);
366     fprintf(stderr, ": %s\n", strerror(errno));
367     va_end(ap);
368     exit(1);
369 }
370 
371 static void err_quit(const char *fmt, ...)
372 {
373     va_list ap;
374 
375     va_start(ap, fmt);
376     vfprintf(stderr, fmt, ap);
377     fputs("\n", stderr);
378     va_end(ap);
379     exit(1);
380 }
381 
382