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 24 int open_multicast_socket(const char *group, const char *port, 25 const char *source, const int ttl); 26 static void err_sys(const char *fmt, ...); 27 static void err_quit(const char *fmt, ...); 28 static void usage(void); 29 30 /* global variables */ 31 static 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 40 int 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 100 int 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 214 static 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 */ 229 static 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 240 static 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