189ea101fSvgross /*
289ea101fSvgross  * Copyright (c) 2016 Vincent Gross <vincent.gross@kilob.yt>
389ea101fSvgross  *
489ea101fSvgross  * Permission to use, copy, modify, and distribute this software for any
589ea101fSvgross  * purpose with or without fee is hereby granted, provided that the above
689ea101fSvgross  * copyright notice and this permission notice appear in all copies.
789ea101fSvgross  *
889ea101fSvgross  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
989ea101fSvgross  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1089ea101fSvgross  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1189ea101fSvgross  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1289ea101fSvgross  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1389ea101fSvgross  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1489ea101fSvgross  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1589ea101fSvgross  */
1689ea101fSvgross 
1789ea101fSvgross #include <err.h>
1889ea101fSvgross #include <errno.h>
1989ea101fSvgross #include <getopt.h>
2089ea101fSvgross #include <netdb.h>
2189ea101fSvgross #include <signal.h>
2289ea101fSvgross #include <stdlib.h>
2389ea101fSvgross #include <string.h>
2489ea101fSvgross #include <stdio.h>
2589ea101fSvgross #include <unistd.h>
2689ea101fSvgross 
2789ea101fSvgross #include <sys/socket.h>
2889ea101fSvgross #include <sys/types.h>
2989ea101fSvgross #include <sys/wait.h>
3089ea101fSvgross 
3189ea101fSvgross #include <netinet/in.h>
3289ea101fSvgross 
3389ea101fSvgross #include <arpa/inet.h>
3489ea101fSvgross 
3589ea101fSvgross #define PORTNUM "23000"
3689ea101fSvgross 
3789ea101fSvgross int
main(int argc,char * argv[])3889ea101fSvgross main(int argc, char *argv[])
3989ea101fSvgross {
4089ea101fSvgross 	struct addrinfo		  hints;
4189ea101fSvgross 	struct addrinfo		 *in6ai;
4289ea101fSvgross 
4389ea101fSvgross 	struct sockaddr_in6	 *null_sin6 = NULL;
4489ea101fSvgross 	struct sockaddr_in6	**next_sin6_p = NULL;
4589ea101fSvgross 	struct sockaddr_in6	**first_sin6p = &null_sin6;
4689ea101fSvgross 	struct sockaddr_in6	**bind_sin6p = &null_sin6;
4789ea101fSvgross 	struct sockaddr_in6	**sendmsg_sin6p = &null_sin6;
4889ea101fSvgross 	struct sockaddr_in6	**setsockopt_sin6p = &null_sin6;
4989ea101fSvgross 	struct sockaddr_in6	**dst_sin6p = &null_sin6;
5089ea101fSvgross 
5189ea101fSvgross 	int			  ch, rc, wstat, expected = -1;
5289ea101fSvgross 	int			  first_sock;
53*5d84da69Svgross 	int			  reuse_addr = 0;
5489ea101fSvgross 	pid_t			  pid;
5589ea101fSvgross 
5689ea101fSvgross 	const char		 *numerr;
5789ea101fSvgross 	char			  adrbuf[40];
5889ea101fSvgross 	const char		 *adrp;
5989ea101fSvgross 
6089ea101fSvgross 
6189ea101fSvgross 	bzero(&hints, sizeof(hints));
6289ea101fSvgross 	hints.ai_family = AF_INET6;
6389ea101fSvgross 	hints.ai_socktype = SOCK_DGRAM;
6489ea101fSvgross 
6589ea101fSvgross 	do {
6689ea101fSvgross 		if (next_sin6_p == NULL)
6789ea101fSvgross 			next_sin6_p = malloc(sizeof(*next_sin6_p));
6889ea101fSvgross 		if (next_sin6_p == NULL)
6989ea101fSvgross 			err(2, "malloc()");
7089ea101fSvgross 		*next_sin6_p = NULL;
7189ea101fSvgross 		while ((ch = getopt(argc, argv, "dfbmoe:")) != -1) {
7289ea101fSvgross 			switch(ch) {
7389ea101fSvgross 			case 'd':
7489ea101fSvgross 				dst_sin6p = next_sin6_p;
7589ea101fSvgross 				break;
7689ea101fSvgross 			case 'f':
7789ea101fSvgross 				first_sin6p = next_sin6_p;
7889ea101fSvgross 				break;
7989ea101fSvgross 			case 'b':
8089ea101fSvgross 				bind_sin6p = next_sin6_p;
8189ea101fSvgross 				break;
8289ea101fSvgross 			case 'm':
8389ea101fSvgross 				sendmsg_sin6p = next_sin6_p;
8489ea101fSvgross 				break;
8589ea101fSvgross 			case 'o':
8689ea101fSvgross 				setsockopt_sin6p = next_sin6_p;
8789ea101fSvgross 				break;
8889ea101fSvgross 			case 'e':
8989ea101fSvgross 				expected = strtonum(optarg, 0, 255, &numerr);
9089ea101fSvgross 				if (numerr != NULL)
9189ea101fSvgross 					errx(2, "strtonum(%s): %s", optarg, numerr);
9289ea101fSvgross 				break;
9389ea101fSvgross 			}
9489ea101fSvgross 		}
9589ea101fSvgross 		if (optind < argc) {
9689ea101fSvgross 			rc = getaddrinfo(argv[optind], PORTNUM, &hints, &in6ai);
9789ea101fSvgross 			if (rc)
9889ea101fSvgross 				errx(2, "getaddrinfo(%s) = %d: %s",
9989ea101fSvgross 				    argv[0], rc, gai_strerror(rc));
10089ea101fSvgross 			*next_sin6_p = (struct sockaddr_in6 *)in6ai->ai_addr;
10189ea101fSvgross 			next_sin6_p = NULL;
10289ea101fSvgross 		}
10389ea101fSvgross 		optreset = 1; optind++;
10489ea101fSvgross 	} while (optind < argc);
10589ea101fSvgross 
10689ea101fSvgross 	if (*bind_sin6p == NULL)
10789ea101fSvgross 		errx(2, "bind_sin6p == NULL");
10889ea101fSvgross 
10989ea101fSvgross 	if (*dst_sin6p == NULL)
11089ea101fSvgross 		errx(2, "dst_sin6p == NULL");
11189ea101fSvgross 
11289ea101fSvgross 	if (expected < 0)
11389ea101fSvgross 		errx(2, "need expected");
11489ea101fSvgross 
115*5d84da69Svgross 	if (*first_sin6p) {
11689ea101fSvgross 		first_sock = udp6_first(*first_sin6p);
117*5d84da69Svgross 		reuse_addr = 1;
118*5d84da69Svgross 	}
11989ea101fSvgross 
12089ea101fSvgross 	pid = fork();
12189ea101fSvgross 	if (pid == 0) {
12289ea101fSvgross 		return udp6_override(*dst_sin6p, *bind_sin6p,
123*5d84da69Svgross 		    *setsockopt_sin6p, *sendmsg_sin6p, reuse_addr);
12489ea101fSvgross 	}
12589ea101fSvgross 	(void)wait(&wstat);
126*5d84da69Svgross 
127*5d84da69Svgross 	if (*first_sin6p)
12889ea101fSvgross 		close(first_sock);
12989ea101fSvgross 
13089ea101fSvgross 	if (! WIFEXITED(wstat))
13189ea101fSvgross 		errx(2, "error setting up override");
13289ea101fSvgross 
13389ea101fSvgross 	if (WEXITSTATUS(wstat) != expected)
13489ea101fSvgross 		errx(2, "expected %d, got %d", expected, WEXITSTATUS(wstat));
13589ea101fSvgross 
13689ea101fSvgross 	return EXIT_SUCCESS;
13789ea101fSvgross }
13889ea101fSvgross 
13989ea101fSvgross 
14089ea101fSvgross int
udp6_first(struct sockaddr_in6 * src)14189ea101fSvgross udp6_first(struct sockaddr_in6 *src)
14289ea101fSvgross {
14389ea101fSvgross 	int s_con;
14489ea101fSvgross 
14589ea101fSvgross 	s_con = socket(AF_INET6, SOCK_DGRAM, 0);
14689ea101fSvgross 	if (s_con == -1)
14789ea101fSvgross 		err(2, "udp6_bind: socket()");
14889ea101fSvgross 
14989ea101fSvgross 	if (bind(s_con, (struct sockaddr *)src, src->sin6_len))
15089ea101fSvgross 		err(2, "udp6_bind: bind()");
15189ea101fSvgross 
15289ea101fSvgross 	return s_con;
15389ea101fSvgross }
15489ea101fSvgross 
15589ea101fSvgross 
15689ea101fSvgross int
udp6_override(struct sockaddr_in6 * dst,struct sockaddr_in6 * src_bind,struct sockaddr_in6 * src_setsockopt,struct sockaddr_in6 * src_sendmsg,int reuse_addr)15789ea101fSvgross udp6_override(struct sockaddr_in6 *dst, struct sockaddr_in6 *src_bind,
158*5d84da69Svgross     struct sockaddr_in6 *src_setsockopt, struct sockaddr_in6 *src_sendmsg,
159*5d84da69Svgross     int reuse_addr)
16089ea101fSvgross {
16189ea101fSvgross 	int			 s, optval, error, saved_errno;
16289ea101fSvgross 	ssize_t			 send_rc;
16389ea101fSvgross 	struct msghdr		 msg;
16489ea101fSvgross 	struct iovec		 iov;
16589ea101fSvgross 	struct cmsghdr		*cmsg;
16689ea101fSvgross 	struct in6_pktinfo	*pi_sendmsg;
16789ea101fSvgross 	struct in6_pktinfo	 pi_setsockopt;
16889ea101fSvgross 	union {
16989ea101fSvgross 		struct cmsghdr	hdr;
17089ea101fSvgross 		unsigned char	buf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
17189ea101fSvgross 	} cmsgbuf;
17289ea101fSvgross 
17389ea101fSvgross 	bzero(&msg, sizeof(msg));
17489ea101fSvgross 	bzero(&cmsgbuf, sizeof(cmsgbuf));
17589ea101fSvgross 	bzero(&pi_setsockopt, sizeof(pi_setsockopt));
17689ea101fSvgross 
17789ea101fSvgross 	s = socket(AF_INET6, SOCK_DGRAM, 0);
17889ea101fSvgross 	if (s == -1) {
17989ea101fSvgross 		warn("udp6_override: socket()");
18089ea101fSvgross 		kill(getpid(), SIGTERM);
18189ea101fSvgross 	}
18289ea101fSvgross 
183*5d84da69Svgross 	if (reuse_addr) {
18489ea101fSvgross 		optval = 1;
18589ea101fSvgross 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(int))) {
18689ea101fSvgross 			warn("udp6_override: setsockopt(SO_REUSEADDR)");
18789ea101fSvgross 			kill(getpid(), SIGTERM);
18889ea101fSvgross 		}
189*5d84da69Svgross 	}
19089ea101fSvgross 
19189ea101fSvgross 	if (bind(s, (struct sockaddr *)src_bind, src_bind->sin6_len)) {
19289ea101fSvgross 		warn("udp6_override: bind()");
19389ea101fSvgross 		kill(getpid(), SIGTERM);
19489ea101fSvgross 	}
19589ea101fSvgross 
19689ea101fSvgross 	if (src_setsockopt != NULL) {
19789ea101fSvgross 		memcpy(&pi_setsockopt.ipi6_addr, &src_setsockopt->sin6_addr, sizeof(struct in6_addr));
19889ea101fSvgross 		if (setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pi_setsockopt, sizeof(pi_setsockopt))) {
19989ea101fSvgross 			warn("udp6_override: setsockopt(IPV6_PKTINFO)");
20089ea101fSvgross 			kill(getpid(), SIGTERM);
20189ea101fSvgross 		}
20289ea101fSvgross 	}
20389ea101fSvgross 
20489ea101fSvgross 	iov.iov_base = "payload";
20589ea101fSvgross 	iov.iov_len = 8;
20689ea101fSvgross 	msg.msg_name = dst;
20789ea101fSvgross 	msg.msg_namelen = dst->sin6_len;
20889ea101fSvgross 	msg.msg_iov = &iov;
20989ea101fSvgross 	msg.msg_iovlen = 1;
21089ea101fSvgross 
21189ea101fSvgross 	if (src_sendmsg) {
21289ea101fSvgross 		msg.msg_control = &cmsgbuf.buf;
21389ea101fSvgross 		msg.msg_controllen = sizeof(cmsgbuf.buf);
21489ea101fSvgross 		cmsg = CMSG_FIRSTHDR(&msg);
21589ea101fSvgross 		cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
21689ea101fSvgross 		cmsg->cmsg_level = IPPROTO_IPV6;
21789ea101fSvgross 		cmsg->cmsg_type = IPV6_PKTINFO;
21889ea101fSvgross 		pi_sendmsg = (struct in6_pktinfo *)CMSG_DATA(cmsg);
21989ea101fSvgross 		memcpy(&pi_sendmsg->ipi6_addr, &src_sendmsg->sin6_addr, sizeof(struct in6_addr));
22089ea101fSvgross 	}
22189ea101fSvgross 
22289ea101fSvgross 	send_rc = sendmsg(s, &msg, 0);
22389ea101fSvgross 	saved_errno = errno;
22489ea101fSvgross 
22589ea101fSvgross 	close(s);
22689ea101fSvgross 
22789ea101fSvgross 	if (send_rc == iov.iov_len)
22889ea101fSvgross 		return 0;
22989ea101fSvgross 	return saved_errno;
23089ea101fSvgross }
231