xref: /openbsd/regress/sys/netinet6/mcast6/mc6recv.c (revision d415bd75)
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