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