Ejemplo n.º 1
0
int main(int argc, char **argv)
{
	int			ret = EX_OK;
	int			i;
	struct group		*grgid;
	struct passwd		*pwuid;
	timer_t			timerid_mld, timerid_pim;
	struct pollfd		fds[4];
	nfds_t			nfds = 0;
	struct sockaddr_storage	from, to;
	socklen_t		addrlen = sizeof(struct sockaddr_storage);
	unsigned int		from_ifindex;
	char			*buf;

	ret = parse_args(argc, argv);
	if (ret)
		return -ret;

	if (getuid()) {
		fprintf(stderr, "need to run as root\n");
		return EX_NOPERM;
	}

	if (!nofork) {
		pid_t pid = fork();

		if (pid < 0) {
			perror("fork()");
			return EX_OSERR;
		} else if (pid > 0)
			return EX_OK;

		if (setsid() < 0) {
			perror("setsid()");
			return EX_OSERR;
		}

		if (chdir("/") < 0) {
			perror("chdir(\"/\")");
			return EX_OSERR;
		}

		openlog(basename(argv[0]), LOG_PID, LOG_DAEMON);
	} else
		openlog(basename(argv[0]), LOG_PID | LOG_PERROR, LOG_DAEMON);
	setlogmask(LOG_UPTO(debug));

	logger(LOG_NOTICE, 0, "started");

	errno = 0;
	mroute4 = socket(AF_INET, SOCK_RAW, IPPROTO_IGMP);
	if (mroute4 >= 0) {
		if (pktinfo(mroute4) < 0) {
			close(mroute4);
			mroute4 = -1;
		} else {
			pim4 = pim_init(mroute4);
			if (pim4 < 0) {
				close(mroute4);
				mroute4 = -1;
			} else {
				add_poll(fds, &nfds, mroute4);
				add_poll(fds, &nfds, pim4);
			}
		}
	}
	if (mroute4 < 0)
		logger(LOG_WARNING, errno, "no IPv4 support");
	errno = 0;
	mroute6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6);
	if (mroute6 >= 0) {
		if (pktinfo(mroute6) < 0) {
			close(mroute6);
			mroute6 = -1;
		} else {
			pim6 = pim_init(mroute6);
			if (pim6 < 0) {
				close(mroute6);
				mroute6 = -1;
			} else {
				add_poll(fds, &nfds, mroute6);
				add_poll(fds, &nfds, pim6);
			}
		}
	}
	if (mroute6 < 0)
		logger(LOG_WARNING, errno, "no IPv6 support");

	if (mroute4 < 0 && mroute6 < 0) {
		logger(LOG_ERR, 0, "multicast routing unavailable");
		ret = -EX_OSERR;
		goto exit;
	}

	ret = route_init();
	if (ret)
		goto mroute;

	errno = 0;
	grgid = getgrnam(gid);
	if (grgid) {
		if (setgid(grgid->gr_gid))
			logger(LOG_WARNING, errno, "unable to drop group privileges");
	} else
		logger(LOG_WARNING, errno, "unable to find group '%s' to drop privileges to", gid);
	errno = 0;
	pwuid = getpwnam(uid);
	if (pwuid) {
		if (setuid(pwuid->pw_uid))
			logger(LOG_WARNING, errno, "unable to drop user privileges");
	} else
		logger(LOG_WARNING, errno, "unable to find user '%s' to drop privileges to", uid);

	ret = signals(&sig_handler);
	if (ret)
		goto route;

	ret = prime_timers(&timerid_mld, &timerid_pim);
	if (ret)
		goto signal;

	buf = malloc(SOCK_BUFLEN);
	if (buf == NULL) {
		logger(LOG_ERR, 0, "malloc()");
		ret = -EX_OSERR;
		goto timer;
	}

	while (running) {
		ret = poll(fds, nfds, -1);
		if (ret == -1) {
			if (errno == EINTR)
				continue;

			logger(LOG_ERR, errno, "poll()");
			ret = -EX_OSERR;
			running = 0;
			continue;
		}

		for (i = 0; i < nfds; i++) {
			/* TODO handle errors */
			assert(!(fds[i].revents & (POLLERR | POLLHUP)));

			/* either a non-event or there is something to read */
			assert(!fds[i].revents || fds[i].revents & POLLIN);

			if (!fds[i].revents)
				continue;

			if (fds[i].revents & POLLIN) {
				ret = recvfromto(fds[i].fd, buf, SOCK_BUFLEN, 0,
						(struct sockaddr *)&from,
						(struct sockaddr *)&to,
						&addrlen, &from_ifindex);
				if (ret == -1)
					continue;

				if (fds[i].fd == pim4 || fds[i].fd == pim6)
					pim_recv(fds[i].fd, buf, ret,
							&from, &to, addrlen,
							from_ifindex);
				else
					mld_recv(fds[i].fd, buf, ret,
							&from, &to, addrlen,
							from_ifindex);
			}
		}
	}

	free(buf);

timer:
	timer_delete(timerid_mld);
	timer_delete(timerid_pim);
signal:
	signals(SIG_IGN);
route:
	route_shutdown();
mroute:
	if (mroute4 > 0) {
		close(pim4);
		pim_shutdown(mroute4);
	}
	if (mroute6 > 0) {
		close(pim6);
		pim_shutdown(mroute6);
	}
exit:
	logger(LOG_NOTICE, 0, "exiting");

	closelog();

	assert(ret <= 0);

	return -ret;
}
Ejemplo n.º 2
0
/* NB: this will never set port# in 'to'!
 * _Only_ IP/IPv6 address part of 'to' is _maybe_ modified.
 * Typical usage is to preinit 'to' with "default" value
 * before calling recv_from_to(). */
ssize_t
recv_from_to(int fd, void *buf, size_t len, int flags,
		struct sockaddr *from, struct sockaddr *to,
		socklen_t sa_size)
{
#ifndef IP_PKTINFO
	return recvfrom(fd, buf, len, flags, from, &sa_size);
#else
	/* man recvmsg and man cmsg is needed to make sense of code below */
	struct iovec iov[1];
	union {
		char cmsg[CMSG_SPACE(sizeof(struct in_pktinfo))];
#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
		char cmsg6[CMSG_SPACE(sizeof(struct in6_pktinfo))];
#endif
	} u;
	struct cmsghdr *cmsgptr;
	struct msghdr msg;
	socklen_t recv_length;

	iov[0].iov_base = buf;
	iov[0].iov_len = len;

	memset(&msg, 0, sizeof(msg));
	msg.msg_name = (struct sockaddr *)from;
	msg.msg_namelen = sa_size;
	msg.msg_iov = iov;
	msg.msg_iovlen = 1;
	msg.msg_control = &u;
	msg.msg_controllen = sizeof(u);

	recv_length = recvmsg(fd, &msg, flags);
	if (recv_length < 0)
		return recv_length;

	/* Here we try to retrieve destination IP and memorize it */
	for (cmsgptr = CMSG_FIRSTHDR(&msg);
			cmsgptr != NULL;
			cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)
	) {
		if (cmsgptr->cmsg_level == IPPROTO_IP
		 && cmsgptr->cmsg_type == IP_PKTINFO
		) {
#define pktinfo(cmsgptr) ( (struct in_pktinfo*)(CMSG_DATA(cmsgptr)) )
			to->sa_family = AF_INET;
			((struct sockaddr_in*)to)->sin_addr = pktinfo(cmsgptr)->ipi_addr;
			/* ((struct sockaddr_in*)to)->sin_port = 123; */
#undef pktinfo
			break;
		}
#if ENABLE_FEATURE_IPV6 && defined(IPV6_PKTINFO)
		if (cmsgptr->cmsg_level == IPPROTO_IPV6
		 && cmsgptr->cmsg_type == IPV6_PKTINFO
		) {
#define pktinfo(cmsgptr) ( (struct in6_pktinfo*)(CMSG_DATA(cmsgptr)) )
			to->sa_family = AF_INET6;
			((struct sockaddr_in6*)to)->sin6_addr = pktinfo(cmsgptr)->ipi6_addr;
			/* ((struct sockaddr_in6*)to)->sin6_port = 123; */
#undef pktinfo
			break;
		}
#endif
	}
	return recv_length;
#endif
}