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
usage(void)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
main(int argc,char * argv[])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
sigexit(int sig)176 sigexit(int sig)
177 {
178 _exit(0);
179 }
180