1/*	$OpenBSD: mcsend.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
20#include <arpa/inet.h>
21#include <netinet/in.h>
22
23#include <err.h>
24#include <stdlib.h>
25#include <stdio.h>
26#include <string.h>
27#include <unistd.h>
28
29void __dead usage(void);
30
31void __dead
32usage(void)
33{
34	fprintf(stderr,
35"mcsend [-f file] [-g group] [-i ifaddr] [-m message] [-p port]\n"
36"    -f file         print message to log file, default stdout\n"
37"    -g group        multicast group, default 224.0.0.123\n"
38"    -i ifaddr       multicast interface address\n"
39"    -m message      message in payload, maximum 255 characters, default foo\n"
40"    -l loop         disable or enable loopback, 0 or 1\n"
41"    -p port         destination port number, default 12345\n"
42"    -t ttl          set multicast ttl\n");
43	exit(2);
44}
45
46int
47main(int argc, char *argv[])
48{
49	struct sockaddr_in sin;
50	FILE *log;
51	const char *errstr, *file, *group, *ifaddr, *msg;
52	size_t len;
53	ssize_t n;
54	int ch, s, loop, port, ttl;
55
56	log = stdout;
57	file = NULL;
58	group = "224.0.1.123";
59	ifaddr = NULL;
60	loop = -1;
61	msg = "foo";
62	port = 12345;
63	ttl = -1;
64	while ((ch = getopt(argc, argv, "f:g:i:l:m:p:t:")) != -1) {
65		switch (ch) {
66		case 'f':
67			file = optarg;
68			break;
69		case 'g':
70			group = optarg;
71			break;
72		case 'i':
73			ifaddr = optarg;
74			break;
75		case 'l':
76			loop = strtonum(optarg, 0, 1, &errstr);
77			if (errstr != NULL)
78				errx(1, "loop is %s: %s", errstr, optarg);
79			break;
80		case 'm':
81			msg = optarg;
82			break;
83		case 'p':
84			port = strtonum(optarg, 1, 0xffff, &errstr);
85			if (errstr != NULL)
86				errx(1, "port is %s: %s", errstr, optarg);
87			break;
88		case 't':
89			ttl = strtonum(optarg, 0, 255, &errstr);
90			if (errstr != NULL)
91				errx(1, "ttl is %s: %s", errstr, optarg);
92			break;
93		default:
94			usage();
95		}
96	}
97	argc -= optind;
98	argv += optind;
99	if (argc)
100		usage();
101
102	if (file != NULL) {
103		log = fopen(file, "w");
104		if (log == NULL)
105			err(1, "fopen %s", file);
106	}
107
108	s = socket(AF_INET, SOCK_DGRAM, 0);
109	if (s == -1)
110		err(1, "socket");
111	if (ifaddr != NULL) {
112		struct in_addr addr;
113
114		if (inet_pton(AF_INET, ifaddr, &addr) == -1)
115			err(1, "inet_pton %s", ifaddr);
116		if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &addr,
117		    sizeof(addr)) == -1)
118			err(1, "setsockopt IP_MULTICAST_IF %s", ifaddr);
119	}
120	if (loop != -1) {
121		unsigned char value = loop;
122
123		if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP, &value,
124		    sizeof(value)) == -1)
125			err(1, "setsockopt loop %d", loop);
126	}
127	if (ttl != -1) {
128		unsigned char value = ttl;
129
130		if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL, &value,
131		    sizeof(value)) == -1)
132			err(1, "setsockopt ttl %d", ttl);
133	}
134
135	memset(&sin, 0, sizeof(sin));
136	sin.sin_len = sizeof(sin);
137	sin.sin_family = AF_INET;
138	sin.sin_port = htons(port);
139	if (inet_pton(AF_INET, group, &sin.sin_addr) == -1)
140		err(1, "inet_pton %s", group);
141	if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) == -1)
142		err(1, "connect %s:%d", group, port);
143
144	len = strlen(msg);
145	if (len >= 255)
146		err(1, "message too long %zu", len);
147	n = send(s, msg, len, 0);
148	if (n == -1)
149		err(1, "send");
150	if ((size_t)n != len)
151		errx(1, "send %zd", n);
152	fprintf(log, ">>> %s\n", msg);
153	fflush(log);
154
155	return 0;
156}
157