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