1 /* $OpenBSD: mc6recv.c,v 1.2 2021/07/06 11:50:34 bluhm Exp $ */ 2 /* 3 * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/socket.h> 19 #include <sys/wait.h> 20 21 #include <arpa/inet.h> 22 #include <net/if.h> 23 #include <netinet/in.h> 24 25 #include <err.h> 26 #include <limits.h> 27 #include <signal.h> 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 void __dead usage(void); 34 void sigexit(int); 35 36 void __dead 37 usage(void) 38 { 39 fprintf(stderr, 40 "mc6recv [-f file] [-g group] [-i ifname] [-n timeout] [-p port] [-r timeout]\n" 41 " [mc6send ...]\n" 42 " -f file print message to log file, default stdout\n" 43 " -g group multicast group, default 224.0.0.123\n" 44 " -i ifname multicast interface name\n" 45 " -n timeout expect not to receive any message until timeout\n" 46 " -p port destination port number, default 12345\n" 47 " -r timeout receive timeout in seconds\n" 48 " mc6send ... after setting up receive, fork and exec send command\n"); 49 exit(2); 50 } 51 52 int 53 main(int argc, char *argv[]) 54 { 55 struct sockaddr_in6 sin6; 56 struct ipv6_mreq mreq6; 57 FILE *log; 58 const char *errstr, *file, *group, *ifname; 59 char msg[256]; 60 ssize_t n; 61 int ch, s, norecv, port, status; 62 unsigned int timeout; 63 pid_t pid; 64 65 log = stdout; 66 file = NULL; 67 group = "ff04::123"; 68 ifname = "lo0"; 69 norecv = 0; 70 port = 12345; 71 timeout = 0; 72 while ((ch = getopt(argc, argv, "f:g:i:n:p:r:")) != -1) { 73 switch (ch) { 74 case 'f': 75 file = optarg; 76 break; 77 case 'g': 78 group = optarg; 79 break; 80 case 'i': 81 ifname = optarg; 82 break; 83 case 'n': 84 norecv = 1; 85 timeout = strtonum(optarg, 1, INT_MAX, &errstr); 86 if (errstr != NULL) 87 errx(1, "no timeout is %s: %s", errstr, optarg); 88 break; 89 case 'p': 90 port = strtonum(optarg, 1, 0xffff, &errstr); 91 if (errstr != NULL) 92 errx(1, "port is %s: %s", errstr, optarg); 93 break; 94 case 'r': 95 timeout = strtonum(optarg, 1, INT_MAX, &errstr); 96 if (errstr != NULL) 97 errx(1, "timeout is %s: %s", errstr, optarg); 98 break; 99 default: 100 usage(); 101 } 102 } 103 argc -= optind; 104 argv += optind; 105 106 if (file != NULL) { 107 log = fopen(file, "w"); 108 if (log == NULL) 109 err(1, "fopen %s", file); 110 } 111 112 s = socket(AF_INET6, SOCK_DGRAM, 0); 113 if (s == -1) 114 err(1, "socket"); 115 if (inet_pton(AF_INET6, group, &mreq6.ipv6mr_multiaddr) == -1) 116 err(1, "inet_pton %s", group); 117 mreq6.ipv6mr_interface = if_nametoindex(ifname); 118 if (mreq6.ipv6mr_interface == 0) 119 err(1, "if_nametoindex %s", ifname); 120 if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6, 121 sizeof(mreq6)) == -1) 122 err(1, "setsockopt IPV6_JOIN_GROUP %s %s", group, ifname); 123 124 memset(&sin6, 0, sizeof(sin6)); 125 sin6.sin6_len = sizeof(sin6); 126 sin6.sin6_family = AF_INET6; 127 sin6.sin6_port = htons(port); 128 if (inet_pton(AF_INET6, group, &sin6.sin6_addr) == -1) 129 err(1, "inet_pton %s", group); 130 if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) || 131 IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr) || 132 IN6_IS_ADDR_MC_INTFACELOCAL(&sin6.sin6_addr)) { 133 sin6.sin6_scope_id = mreq6.ipv6mr_interface; 134 } 135 if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) == -1) 136 err(1, "bind [%s]:%d", group, port); 137 138 if (argc) { 139 pid = fork(); 140 switch (pid) { 141 case -1: 142 err(1, "fork"); 143 case 0: 144 execvp(argv[0], argv); 145 err(1, "exec %s", argv[0]); 146 } 147 } 148 if (timeout) { 149 if (norecv) { 150 if (signal(SIGALRM, sigexit) == SIG_ERR) 151 err(1, "signal SIGALRM"); 152 } 153 alarm(timeout); 154 } 155 n = recv(s, msg, sizeof(msg) - 1, 0); 156 if (n == -1) 157 err(1, "recv"); 158 msg[n] = '\0'; 159 fprintf(log, "<<< %s\n", msg); 160 fflush(log); 161 162 if (norecv) 163 errx(1, "received %s", msg); 164 165 if (argc) { 166 if (waitpid(pid, &status, 0) == -1) 167 err(1, "waitpid %d", pid); 168 if (status) 169 errx(1, "%s %d", argv[0], status); 170 } 171 172 return 0; 173 } 174 175 void 176 sigexit(int sig) 177 { 178 _exit(0); 179 } 180