1*5a9255e7Sbluhm /* $OpenBSD: mc6recv.c,v 1.2 2021/07/06 11:50:34 bluhm Exp $ */
24a94906dSbluhm /*
34a94906dSbluhm * Copyright (c) 2019 Alexander Bluhm <bluhm@openbsd.org>
44a94906dSbluhm *
54a94906dSbluhm * Permission to use, copy, modify, and distribute this software for any
64a94906dSbluhm * purpose with or without fee is hereby granted, provided that the above
74a94906dSbluhm * copyright notice and this permission notice appear in all copies.
84a94906dSbluhm *
94a94906dSbluhm * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
104a94906dSbluhm * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
114a94906dSbluhm * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
124a94906dSbluhm * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
134a94906dSbluhm * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
144a94906dSbluhm * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
154a94906dSbluhm * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
164a94906dSbluhm */
174a94906dSbluhm
184a94906dSbluhm #include <sys/socket.h>
194a94906dSbluhm #include <sys/wait.h>
204a94906dSbluhm
214a94906dSbluhm #include <arpa/inet.h>
224a94906dSbluhm #include <net/if.h>
234a94906dSbluhm #include <netinet/in.h>
244a94906dSbluhm
254a94906dSbluhm #include <err.h>
264a94906dSbluhm #include <limits.h>
274a94906dSbluhm #include <signal.h>
284a94906dSbluhm #include <stdlib.h>
294a94906dSbluhm #include <stdio.h>
304a94906dSbluhm #include <string.h>
314a94906dSbluhm #include <unistd.h>
324a94906dSbluhm
334a94906dSbluhm void __dead usage(void);
344a94906dSbluhm void sigexit(int);
354a94906dSbluhm
364a94906dSbluhm void __dead
usage(void)374a94906dSbluhm usage(void)
384a94906dSbluhm {
394a94906dSbluhm fprintf(stderr,
404a94906dSbluhm "mc6recv [-f file] [-g group] [-i ifname] [-n timeout] [-p port] [-r timeout]\n"
414a94906dSbluhm " [mc6send ...]\n"
424a94906dSbluhm " -f file print message to log file, default stdout\n"
434a94906dSbluhm " -g group multicast group, default 224.0.0.123\n"
444a94906dSbluhm " -i ifname multicast interface name\n"
454a94906dSbluhm " -n timeout expect not to receive any message until timeout\n"
464a94906dSbluhm " -p port destination port number, default 12345\n"
474a94906dSbluhm " -r timeout receive timeout in seconds\n"
484a94906dSbluhm " mc6send ... after setting up receive, fork and exec send command\n");
494a94906dSbluhm exit(2);
504a94906dSbluhm }
514a94906dSbluhm
524a94906dSbluhm int
main(int argc,char * argv[])534a94906dSbluhm main(int argc, char *argv[])
544a94906dSbluhm {
554a94906dSbluhm struct sockaddr_in6 sin6;
564a94906dSbluhm struct ipv6_mreq mreq6;
574a94906dSbluhm FILE *log;
584a94906dSbluhm const char *errstr, *file, *group, *ifname;
594a94906dSbluhm char msg[256];
604a94906dSbluhm ssize_t n;
614a94906dSbluhm int ch, s, norecv, port, status;
624a94906dSbluhm unsigned int timeout;
634a94906dSbluhm pid_t pid;
644a94906dSbluhm
654a94906dSbluhm log = stdout;
664a94906dSbluhm file = NULL;
674a94906dSbluhm group = "ff04::123";
684a94906dSbluhm ifname = "lo0";
694a94906dSbluhm norecv = 0;
704a94906dSbluhm port = 12345;
714a94906dSbluhm timeout = 0;
724a94906dSbluhm while ((ch = getopt(argc, argv, "f:g:i:n:p:r:")) != -1) {
734a94906dSbluhm switch (ch) {
744a94906dSbluhm case 'f':
754a94906dSbluhm file = optarg;
764a94906dSbluhm break;
774a94906dSbluhm case 'g':
784a94906dSbluhm group = optarg;
794a94906dSbluhm break;
804a94906dSbluhm case 'i':
814a94906dSbluhm ifname = optarg;
824a94906dSbluhm break;
834a94906dSbluhm case 'n':
844a94906dSbluhm norecv = 1;
854a94906dSbluhm timeout = strtonum(optarg, 1, INT_MAX, &errstr);
864a94906dSbluhm if (errstr != NULL)
874a94906dSbluhm errx(1, "no timeout is %s: %s", errstr, optarg);
884a94906dSbluhm break;
894a94906dSbluhm case 'p':
904a94906dSbluhm port = strtonum(optarg, 1, 0xffff, &errstr);
914a94906dSbluhm if (errstr != NULL)
924a94906dSbluhm errx(1, "port is %s: %s", errstr, optarg);
934a94906dSbluhm break;
944a94906dSbluhm case 'r':
954a94906dSbluhm timeout = strtonum(optarg, 1, INT_MAX, &errstr);
964a94906dSbluhm if (errstr != NULL)
974a94906dSbluhm errx(1, "timeout is %s: %s", errstr, optarg);
984a94906dSbluhm break;
994a94906dSbluhm default:
1004a94906dSbluhm usage();
1014a94906dSbluhm }
1024a94906dSbluhm }
1034a94906dSbluhm argc -= optind;
1044a94906dSbluhm argv += optind;
1054a94906dSbluhm
1064a94906dSbluhm if (file != NULL) {
1074a94906dSbluhm log = fopen(file, "w");
1084a94906dSbluhm if (log == NULL)
1094a94906dSbluhm err(1, "fopen %s", file);
1104a94906dSbluhm }
1114a94906dSbluhm
1124a94906dSbluhm s = socket(AF_INET6, SOCK_DGRAM, 0);
1134a94906dSbluhm if (s == -1)
1144a94906dSbluhm err(1, "socket");
1154a94906dSbluhm if (inet_pton(AF_INET6, group, &mreq6.ipv6mr_multiaddr) == -1)
1164a94906dSbluhm err(1, "inet_pton %s", group);
1174a94906dSbluhm mreq6.ipv6mr_interface = if_nametoindex(ifname);
1184a94906dSbluhm if (mreq6.ipv6mr_interface == 0)
1194a94906dSbluhm err(1, "if_nametoindex %s", ifname);
1204a94906dSbluhm if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
1214a94906dSbluhm sizeof(mreq6)) == -1)
1224a94906dSbluhm err(1, "setsockopt IPV6_JOIN_GROUP %s %s", group, ifname);
1234a94906dSbluhm
1244a94906dSbluhm memset(&sin6, 0, sizeof(sin6));
1254a94906dSbluhm sin6.sin6_len = sizeof(sin6);
1264a94906dSbluhm sin6.sin6_family = AF_INET6;
1274a94906dSbluhm sin6.sin6_port = htons(port);
1284a94906dSbluhm if (inet_pton(AF_INET6, group, &sin6.sin6_addr) == -1)
1294a94906dSbluhm err(1, "inet_pton %s", group);
1304a94906dSbluhm if (IN6_IS_ADDR_LINKLOCAL(&sin6.sin6_addr) ||
1314a94906dSbluhm IN6_IS_ADDR_MC_LINKLOCAL(&sin6.sin6_addr) ||
1324a94906dSbluhm IN6_IS_ADDR_MC_INTFACELOCAL(&sin6.sin6_addr)) {
1334a94906dSbluhm sin6.sin6_scope_id = mreq6.ipv6mr_interface;
1344a94906dSbluhm }
1354a94906dSbluhm if (bind(s, (struct sockaddr *)&sin6, sizeof(sin6)) == -1)
1364a94906dSbluhm err(1, "bind [%s]:%d", group, port);
1374a94906dSbluhm
1384a94906dSbluhm if (argc) {
1394a94906dSbluhm pid = fork();
1404a94906dSbluhm switch (pid) {
1414a94906dSbluhm case -1:
1424a94906dSbluhm err(1, "fork");
1434a94906dSbluhm case 0:
1444a94906dSbluhm execvp(argv[0], argv);
1454a94906dSbluhm err(1, "exec %s", argv[0]);
1464a94906dSbluhm }
1474a94906dSbluhm }
1484a94906dSbluhm if (timeout) {
1494a94906dSbluhm if (norecv) {
1504a94906dSbluhm if (signal(SIGALRM, sigexit) == SIG_ERR)
1514a94906dSbluhm err(1, "signal SIGALRM");
1524a94906dSbluhm }
153*5a9255e7Sbluhm alarm(timeout);
1544a94906dSbluhm }
1554a94906dSbluhm n = recv(s, msg, sizeof(msg) - 1, 0);
1564a94906dSbluhm if (n == -1)
1574a94906dSbluhm err(1, "recv");
1584a94906dSbluhm msg[n] = '\0';
1594a94906dSbluhm fprintf(log, "<<< %s\n", msg);
1604a94906dSbluhm fflush(log);
1614a94906dSbluhm
1624a94906dSbluhm if (norecv)
1634a94906dSbluhm errx(1, "received %s", msg);
1644a94906dSbluhm
1654a94906dSbluhm if (argc) {
1664a94906dSbluhm if (waitpid(pid, &status, 0) == -1)
1674a94906dSbluhm err(1, "waitpid %d", pid);
1684a94906dSbluhm if (status)
1694a94906dSbluhm errx(1, "%s %d", argv[0], status);
1704a94906dSbluhm }
1714a94906dSbluhm
1724a94906dSbluhm return 0;
1734a94906dSbluhm }
1744a94906dSbluhm
1754a94906dSbluhm void
sigexit(int sig)1764a94906dSbluhm sigexit(int sig)
1774a94906dSbluhm {
1784a94906dSbluhm _exit(0);
1794a94906dSbluhm }
180