#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#define INET6
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/uio.h>
#include <netinet/ip6.h>

extern struct in6_addr in6addr_any;

struct sockaddr_in6 sin6;

main(argc, argv)
	int             argc;
	char          **argv;
{
	unsigned int	hops = 0;
	int             s, cc, ch;
	char            buf[1024];
	struct in6_addr addr6, src6;
	struct {
		struct cmsghdr c_cm;
		struct in6_pktinfo c_pi;
	} c_cmsg;
#define pi	c_cmsg.c_pi
#define src6	pi.ipi6_addr
#define ifi	pi.ipi6_ifindex
	struct msghdr msg;
	struct iovec iov[1];
	int usemsg = 0;
	int ispkt = 0;
	extern char	*optarg;
	extern int	optind;

	bzero (pi, sizeof (pi));
	addr6 = in6addr_any;
	if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
		perror("socket");
		exit(1);
	}
	while ((ch = getopt(argc, argv, "gs:h:t:i:")) != -1)
		switch(ch) {
		    case 'g':
			usemsg = 1;
			break;
		    case 'h':
		    case 't':
			hops = atoi(optarg);
			break;
		    case 's':
			if (!inet_pton(AF_INET6, optarg, &src6)) {
				fprintf(stderr, "invalid src addr %s\n",
					optarg);
			}
			ispkt = 1;
			break;
		    case 'i':
			if (sscanf(optarg, "%u\0", &ifi) != 1) {
				ifi = if_nametoindex(optarg);
				if (ifi == 0)
					fprintf(stderr,
						"unknown %s interface name\n",
						optarg);
			}
			ispkt = 1;
			break;
		}
	argc -= optind;
	argv += optind;
	if (argc != 1) {
		fprintf(stderr, "groupe manque\n");
		exit(-1);
	}
	if (hops > 0)
		if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
			       &hops, sizeof(hops)) < 0) {
			perror("setsockopt IPV6_MULTICAST_HOPS");
			return (0);
		}
	if (ispkt && !usemsg)
		if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO,
			    (char *)&pi, sizeof pi) < 0) {
			perror("setsockopt(IPV6_PKTINFO)");       
			return (0);
		}
	if (usemsg) {
		msg.msg_name = (caddr_t)&sin6;
		msg.msg_namelen = sizeof(sin6);
		msg.msg_iov = &iov[0];
		msg.msg_iovlen = 1;
		msg.msg_control = NULL;
		msg.msg_controllen = 0;
		if (ispkt) {
			msg.msg_control = (caddr_t)&c_cmsg;
			msg.msg_controllen = sizeof c_cmsg;
			c_cmsg.c_cm.cmsg_len =
				sizeof c_cmsg.c_cm + sizeof c_cmsg.c_pi;
			c_cmsg.c_cm.cmsg_level = IPPROTO_IPV6;
			c_cmsg.c_cm.cmsg_type = IPV6_PKTINFO;
		}
		iov[0].iov_base = buf;
	}

	(void) inet_pton(AF_INET6, *argv, &addr6);

#ifdef SIN6_LEN
	sin6.sin6_len = sizeof(sin6);
#endif
	sin6.sin6_family = AF_INET6;
	sin6.sin6_addr = addr6;
	sin6.sin6_port = htons(54321);

	for (;;) {
		cc = read(0, buf, 1024);
		if (cc < 0) {
			perror("read file");
			return (cc);
		} else if (cc > 0) {
		    if (usemsg) {
			iov[0].iov_len = cc;
			if (sendmsg(s, &msg, 0) < 0)
				perror("sendmsg");
		    } else {
			if (sendto(s, buf, cc, 0,
				   (struct sockaddr *)&sin6, sizeof(sin6)) < 0)
				perror("sendto");
		    }
			fprintf(stderr, "-%d->\n", cc);
		} else {
			fprintf(stderr, ".\n", cc);
			return (0);
		}
	}
}
