xref: /openbsd/regress/sys/netinet/mcast/mcrecv.c (revision 274d7c50)
1 /*	$OpenBSD: mcrecv.c,v 1.2 2019/09/05 02:44:36 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 <netinet/in.h>
23 
24 #include <err.h>
25 #include <limits.h>
26 #include <signal.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 void __dead usage(void);
33 void sigexit(int);
34 
35 void __dead
36 usage(void)
37 {
38 	fprintf(stderr,
39 "mcrecv [-f file] [-g group] [-i ifaddr] [-n timeout] [-p port] [-r timeout]\n"
40 "    [mcsend ...]\n"
41 "    -f file         print message to log file, default stdout\n"
42 "    -g group        multicast group, default 224.0.0.123\n"
43 "    -i ifaddr       multicast interface address\n"
44 "    -n timeout      expect not to receive any message until timeout\n"
45 "    -p port         destination port number, default 12345\n"
46 "    -r timeout      receive timeout in seconds\n"
47 "    mcsend ...      after setting up receive, fork and exec send command\n");
48 	exit(2);
49 }
50 
51 int
52 main(int argc, char *argv[])
53 {
54 	struct sockaddr_in sin;
55 	struct ip_mreq mreq;
56 	FILE *log;
57 	const char *errstr, *file, *group, *ifaddr;
58 	char msg[256];
59 	ssize_t n;
60 	int ch, s, norecv, port, status;
61 	unsigned int timeout;
62 	pid_t pid;
63 
64 	log = stdout;
65 	file = NULL;
66 	group = "224.0.1.123";
67 	ifaddr = "0.0.0.0";
68 	norecv = 0;
69 	port = 12345;
70 	timeout = 0;
71 	while ((ch = getopt(argc, argv, "f:g:i:n:p:r:")) != -1) {
72 		switch (ch) {
73 		case 'f':
74 			file = optarg;
75 			break;
76 		case 'g':
77 			group = optarg;
78 			break;
79 		case 'i':
80 			ifaddr = optarg;
81 			break;
82 		case 'n':
83 			norecv = 1;
84 			timeout = strtonum(optarg, 1, INT_MAX, &errstr);
85 			if (errstr != NULL)
86 				errx(1, "no timeout is %s: %s", errstr, optarg);
87 			break;
88 		case 'p':
89 			port = strtonum(optarg, 1, 0xffff, &errstr);
90 			if (errstr != NULL)
91 				errx(1, "port is %s: %s", errstr, optarg);
92 			break;
93 		case 'r':
94 			timeout = strtonum(optarg, 1, INT_MAX, &errstr);
95 			if (errstr != NULL)
96 				errx(1, "timeout is %s: %s", errstr, optarg);
97 			break;
98 		default:
99 			usage();
100 		}
101 	}
102 	argc -= optind;
103 	argv += optind;
104 
105 	if (file != NULL) {
106 		log = fopen(file, "w");
107 		if (log == NULL)
108 			err(1, "fopen %s", file);
109 	}
110 
111 	s = socket(AF_INET, SOCK_DGRAM, 0);
112 	if (s == -1)
113 		err(1, "socket");
114 	if (inet_pton(AF_INET, group, &mreq.imr_multiaddr) == -1)
115 		err(1, "inet_pton %s", group);
116 	if (inet_pton(AF_INET, ifaddr, &mreq.imr_interface) == -1)
117 		err(1, "inet_pton %s", ifaddr);
118 	if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
119 	    sizeof(mreq)) == -1)
120 		err(1, "setsockopt IP_ADD_MEMBERSHIP %s %s", group, ifaddr);
121 
122 	memset(&sin, 0, sizeof(sin));
123 	sin.sin_len = sizeof(sin);
124 	sin.sin_family = AF_INET;
125 	sin.sin_port = htons(port);
126 	if (inet_pton(AF_INET, group, &sin.sin_addr) == -1)
127 		err(1, "inet_pton %s", group);
128 	if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
129 		err(1, "bind %s:%d", group, port);
130 
131 	if (argc) {
132 		pid = fork();
133 		switch (pid) {
134 		case -1:
135 			err(1, "fork");
136 		case 0:
137 			execvp(argv[0], argv);
138 			err(1, "exec %s", argv[0]);
139 		}
140 	}
141 	if (timeout) {
142 		if (norecv) {
143 			if (signal(SIGALRM, sigexit) == SIG_ERR)
144 				err(1, "signal SIGALRM");
145 		}
146 		if (alarm(timeout) == (unsigned  int)-1)
147 			err(1, "alarm %u", timeout);
148 	}
149 	n = recv(s, msg, sizeof(msg) - 1, 0);
150 	if (n == -1)
151 		err(1, "recv");
152 	msg[n] = '\0';
153 	fprintf(log, "<<< %s\n", msg);
154 	fflush(log);
155 
156 	if (norecv)
157 		errx(1, "received %s", msg);
158 
159 	if (argc) {
160 		if (waitpid(pid, &status, 0) == -1)
161 			err(1, "waitpid %d", pid);
162 		if (status)
163 			errx(1, "%s %d", argv[0], status);
164 	}
165 
166 	return 0;
167 }
168 
169 void
170 sigexit(int sig)
171 {
172 	_exit(0);
173 }
174