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
main(int argc,char * argv[])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)
chat(const int sock)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
dump(const int sock,int out_fd,const int dump_packets)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
open_multicast_socket(const char * group,const char * port,const char * source)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
open_packet_file(void * buf,int n,const char * address)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
alarm_exit(int signaln)336 static void alarm_exit(int signaln)
337 {
338 v_printf(1, "Timeout.\n");
339 exit(0);
340 }
341
usage(void)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 */
err_sys(const char * fmt,...)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
err_quit(const char * fmt,...)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