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 <errno.h> 14#include <string.h> 15#include <getopt.h> /* getopt_long */ 16#include <sys/types.h> 17#include <sys/socket.h> 18#include <netdb.h> 19#include <arpa/inet.h> 20#include <netinet/in.h> 21#include <time.h> 22#include <sys/utsname.h> 23 24int open_multicast_socket(const char *group, const char *port, 25 const char *source, const int ttl); 26static void err_sys(const char *fmt, ...); 27static void err_quit(const char *fmt, ...); 28static void usage(void); 29 30/* global variables */ 31static struct option longopts[] = { 32 { "help", no_argument, NULL, 'h' }, 33 { "input", no_argument, NULL, 'i' }, 34 { "size", required_argument, NULL, 's' }, 35 { "bind", required_argument, NULL, 'b' }, 36 { "ttl", required_argument, NULL, 't' }, 37 { NULL, 0, NULL, 0 } 38}; 39 40int main(int argc, char *argv[]) 41{ 42 int fd, len, ch; 43 int opt_input = 0; 44 int ttl = 128; 45 int sndbuf_size = 1280; 46 char *source = NULL; 47 48 while ((ch = getopt_long(argc, argv, "his:b:t:", longopts, 0)) > 0) { 49 switch (ch) { 50 case 's': 51 sndbuf_size = atoi(optarg); 52 break; 53 case 'i': 54 opt_input = 1; 55 break; 56 case 'b': 57 source = optarg; 58 break; 59 case 't': 60 ttl = atoi(optarg); 61 break; 62 default: 63 usage(); 64 } 65 } 66 argc -= optind; 67 argv += optind; 68 69 if (argc != 2) 70 usage(); 71 72 fd = open_multicast_socket(argv[0], argv[1], source, ttl); 73 74 if (opt_input) { 75 char buf[sndbuf_size]; 76 77 while ((len = read(STDIN_FILENO, buf, sizeof(buf))) > 0) { 78 if (send(fd, buf, len, 0) < 0) 79 err_sys("send"); 80 } 81 } else { 82 struct utsname utsname; 83 char buf[1024]; 84 85 uname(&utsname); 86 snprintf(buf, sizeof(buf), "Hello from %s!\n", utsname.nodename); 87 len = strlen(buf); 88 89 while (1) { 90 if (send(fd, buf, len, 0) < 0) 91 err_sys("send"); 92 93 sleep(1); 94 } 95 } 96 97 exit(0); 98} 99 100int open_multicast_socket(const char *group, const char *port, 101 const char *source, const int ttl) 102{ 103 int fd; 104 int loop = 1; 105 106#ifdef AF_INET6 107 int err; 108 struct addrinfo hints, *res, *ai; 109 int level, sockopt_h, sockopt_l; 110 111 memset(&hints, 0, sizeof(hints)); 112 hints.ai_family = AF_UNSPEC; 113 hints.ai_socktype = SOCK_DGRAM; 114 115 if ((err = getaddrinfo(group, port, &hints, &res)) != 0) 116 err_quit("getaddrinfo(%s, %s): %s", source, port, gai_strerror(err)); 117 for (ai = res; ai; ai = ai->ai_next) { 118 if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) < 0) 119 continue; /* ignore */ 120 if (connect(fd, ai->ai_addr, ai->ai_addrlen) == 0) 121 break; /* success */ 122 close(fd); 123 } 124 125 if (!ai) 126 err_sys("connect"); 127 128 switch (ai->ai_family) { 129 case AF_INET: 130 level = IPPROTO_IP; 131 sockopt_h = IP_MULTICAST_TTL; 132 sockopt_l = IP_MULTICAST_LOOP; 133 break; 134 case AF_INET6: 135 level = IPPROTO_IPV6; 136 sockopt_h = IPV6_MULTICAST_HOPS; 137 sockopt_l = IPV6_MULTICAST_LOOP; 138 break; 139 default: 140 err_quit("FATAL: family %d is not known", ai->ai_family); 141 } 142 143 if (source) { 144 struct addrinfo shints, *sai; 145 int yes = 1; 146 147 memset(&shints, 0, sizeof(shints)); 148 shints.ai_family = ai->ai_family; 149 shints.ai_protocol = ai->ai_protocol; 150 151 if ((err = getaddrinfo(source, port, &shints, &sai)) != 0) 152 err_quit("getaddrinfo(%s, %s): %s", source, port, 153 gai_strerror(err)); 154 155 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) 156 err_sys("setsockopt(SO_REUSEADDR)"); 157 158 if (bind(fd, sai->ai_addr, sai->ai_addrlen) < 0) 159 err_sys("bind"); 160 161 freeaddrinfo(sai); 162 /* 163 struct ip_addr ifaddr = inet_addr(); 164 setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &ifaddr, sizeof(struct in_addr)); 165 setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifidx, sizeof(unsigned int)); 166 man 7 netdevice 167 */ 168 } 169 170 freeaddrinfo(res); 171 172#else 173 struct sockaddr_in addr; 174 struct hostent *hostinfo; 175 const int level = IPPROTO_IP; 176 const int sockopt_h = IP_MULTICAST_TTL; 177 const int sockopt_l = IP_MULTICAST_LOOP; 178 179 if ((hostinfo = gethostbyname(group)) == NULL) 180 err_quit("Host %s not found.", group); 181 182 /* create what looks like an ordinary UDP socket */ 183 if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)) < 0) 184 err_sys("socket"); 185 186 /* set up destination address */ 187 memset(&addr, 0, sizeof(addr)); 188 addr.sin_family = AF_INET; 189 addr.sin_addr = *(struct in_addr *) hostinfo->h_addr; 190 addr.sin_port = htons(atoi(port)); 191 192 if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) 193 err_sys("connect"); 194 195 if (source) { /* XXX */ 196 int yes = 1; 197 198 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) 199 err_sys("setsockopt(SO_REUSEADDR)"); 200 } 201#endif 202 203 if (setsockopt(fd, level, sockopt_h, &ttl, sizeof(ttl)) < 0) 204 err_sys("setsockopt(IP_MULTICAST_TTL)"); 205 206 if (loop == 0) 207 if (setsockopt(fd, level, sockopt_l, &loop, sizeof(loop)) < 0) 208 err_sys("setsockopt(IP_MULTICAST_LOOP)"); 209 210 return fd; 211} 212 213 214static void usage(void) 215{ 216 fprintf(stderr, 217"Usage: multisend [OPTIONS...] GROUP PORT\n" 218"\n" 219" -b, --bind=ADDR bind the socket to address ADDR\n" 220" -t, --ttl=NUM set the TTL to NUM\n" 221" -h, --help display this help and exit\n" 222"\n" 223"If --ttl is not specified, 128 is used.\n" 224); 225 exit(0); 226} 227 228/* Error routines */ 229static void err_sys(const char *fmt, ...) 230{ 231 va_list ap; 232 233 va_start(ap, fmt); 234 vfprintf(stderr, fmt, ap); 235 fprintf(stderr, ": %s\n", strerror(errno)); 236 va_end(ap); 237 exit(1); 238} 239 240static void err_quit(const char *fmt, ...) 241{ 242 va_list ap; 243 244 va_start(ap, fmt); 245 vfprintf(stderr, fmt, ap); 246 fputs("\n", stderr); 247 va_end(ap); 248 exit(1); 249} 250 251#if 0 252/* local network */ 253#define HELLO_GROUP "224.0.0.3" 254/* ? */ 255#define HELLO_GROUP "225.0.0.37" 256/* GLOP */ 257#define HELLO_GROUP "233.49.235.42" 258/* fw */ 259#define HELLO_GROUP "239.113.42.66" 260/* Organization-Local */ 261#define HELLO_GROUP "239.192.42.66" 262/* Site-Local */ 263#define HELLO_GROUP "239.252.42.66" 264/* Source Specific Multicast */ 265#define HELLO_GROUP "232.1.1.1" 266/* node local */ 267#define HELLO_ADDR "FF01::1111" 268#endif 269