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