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