Exemple #1
0
// Returns AF_MAX (with errno set) if error, AF_UNSPEC if no more addrs (socket closed)
struct IPAddr nextAddr(struct AddrFilter const filter, struct MonitorState * const state){
	// NLMSG_OK checks length first, so safe to call with state->nlh == NULL iff
	// state->nlmsg_len < (int) sizeof(struct nlmsghdr)
	if (NLMSG_OK(state->nlh, state->nlmsg_len) && (state->nlh->nlmsg_type != NLMSG_DONE)){
		struct nlmsghdr * nlh = state->nlh;
		state->nlh = NLMSG_NEXT(state->nlh, state->nlmsg_len);
		switch(nlh->nlmsg_type){
		case NLMSG_ERROR:
			errno = -((struct nlmsgerr *) NLMSG_DATA(nlh))->error;
			struct IPAddr addr = {.af = AF_MAX};
			return addr;
		case RTM_NEWADDR: {
			struct ifaddrmsg * ifa = (struct ifaddrmsg *) NLMSG_DATA(nlh);
			if (!filterIfAddrMsg(*ifa, filter))
				return nextAddr(filter, state);
			{
			struct rtattr * rth;
			size_t rtmsg_len;
			for (rth = IFA_RTA(ifa), rtmsg_len = IFA_PAYLOAD(nlh);
			     RTA_OK(rth, rtmsg_len); RTA_NEXT(rth, rtmsg_len)){
				if (rth->rta_type != IFA_ADDRESS)
					continue;
				// family checked in filterIfAddrMsg, so always valid.
				struct IPAddr addr = {.af = ifa->ifa_family};
				switch (ifa->ifa_family) {
				case AF_INET:
					addr.ipv4 = *((struct in_addr *) RTA_DATA(rth));
					break;
				case AF_INET6:
					addr.ipv6 = *((struct in6_addr *) RTA_DATA(rth));
					break;
				}
				if (addrIsPrivate(addr) && !filter.allow_private)
					return nextAddr(filter, state);
				else
					return addr;
			}
			}
			// Recieved RTM_NEWADDR without any address.
			errno = EBADMSG;
			struct IPAddr addr = {.af = AF_MAX};
			return addr;
		}
		default:
			return nextAddr(filter, state);
		}
	} else {
		state->nlmsg_len = nextMessage(filter, state->socket, &state->buf, &state->buf_len);
		if (state->nlmsg_len == 0) {
			// Socket closed by kernel
			struct IPAddr addr = {.af = AF_UNSPEC};
			return addr;
		} else if (state->nlmsg_len < 0) {
			// Socket error
			struct IPAddr addr = {.af = AF_MAX};
			return addr;
		} else  {
Exemple #2
0
int main(int const argc, char** argv) {
	struct AddrFilter filter = {.allow_private = false};
	int (* addr_processor)(struct IPAddr);

	// Deal with options

	const char short_opts[] = "vVh46pa";
	struct option long_opts[] = {
		{"allow-private", no_argument, 0, 'p'},
		{"process-all", no_argument, 0, 'a'},
		{"verbose", no_argument, 0, 'v'},
		{"version", no_argument, 0, 'V'},
		{"help", no_argument, 0, 'h'},
	};
	bool verbosity = 0;
	bool process_all = false;
	int opt_index = 0;
	char opt;

	while ((opt = getopt_long(argc, argv, short_opts, long_opts, &opt_index)) != -1) {
		switch (opt) {
		case 'a':
			process_all = true;
			break;
		case 'v':
			verbosity = 1;
			break;
		case 'V':
			puts(version);
			return EXIT_SUCCESS;
		case '4':
			addFilterAf(&filter, AF_INET);
			break;
		case '6':
			addFilterAf(&filter, AF_INET6);
			break;
		case 'p':
			filter.allow_private = true;
			break;
		case 'h':
			printUsage();
			return EXIT_SUCCESS;
		default:
			printUsage();
			return EXIT_USAGE;
		}
	}

	// Listen for all changes if none specified.
	if (filter.num_af == 0){
		addFilterAf(&filter, AF_INET);
		addFilterAf(&filter, AF_INET6);
	}

	switch ((argc - optind)){
	case 1:
		addr_processor = printAddr;
		break;
	case 2:
		setUrl(argv[optind + 1]);
		addr_processor = webUpdate;
		break;
	default:
		puts("Usage:\n");
		printUsage();
		return EXIT_USAGE;
	}

	char const * const iface_name = argv[optind];
	filter.iface = if_nametoindex(iface_name);
	if (!filter.iface) {
		fprintf(stderr, "Error resolving interface %s: %s\n",
			iface_name, strerror(errno));
		return EXIT_FAILURE;
	}

	if (verbosity){
		puts("Running in verbose mode.");
		printf("Listening on interfaces: %s (#%d)\n", iface_name, filter.iface);
		fputs("Listening for address changes in:", stdout);
		if (checkFilterAf(filter, AF_INET))
			printf(" IPv4");
		if (checkFilterAf(filter, AF_INET6))
			printf(" IPv6");
		puts("");
	}

	// Prepare monitoring, cleanup necessary if exiting after this point.
	struct MonitorState state;
	if (!initState(filter, &state, 1024)){
		perror("Couldn't set up for monitoring");
		return EXIT_FAILURE;
	}

	// Main loop

	pid_t child = -1;
	do {
		struct IPAddr new_addr = nextAddr(filter, &state);
		if (child != -1){
			// Make sure kill isn't called on first loop.
			if (!process_all)
				kill(child, termsig);

			int status;
			if (waitpid(child, &status, 0) == -1){
				perror("Error waiting for child");
				break;
			}
			if (!childOK(status))
				break;
		}

		if (new_addr.af == AF_MAX){
			perror("An error occurred while waiting for a new IP");
			break;
		} else if (new_addr.af == AF_UNSPEC) {
			fputs("Netlink socket closed by kernel.", stderr);
			break;
		}

		// TODO: Could use exec
		child = fork();
		if (child == -1){
			perror("Could not fork to process new address.");
			break;
		} else if (!child){
			close(state.socket); // Make sure to set CLOEXEC if changing to exec.
			return addr_processor(new_addr);
		}
	} while (true);

	close(state.socket);
	free(state.buf);
	return EXIT_FAILURE;
}