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