#ident	"@(#):netagg.c	1.2	03/09/26 22:06:27 ()"

/*
 * Copyright (c) by Greg A. Woods <woods@planix.com>
 * 
 * Years of publication:  2003
 *
 * Redistribution of this software in both source and binary forms, with
 * or without modification, is permitted provided that all of the
 * following conditions are met:
 * 
 * 1. Redistributions of source code, either alone or as part of a
 *    collective work, must retain this entire copyright notice, and the
 *    following disclaimer, without alteration, in each file that
 *    contains part of this software.
 * 
 * 2. Redistributions of this software in binary form, either alone or
 *    as part of a collective work, must reproduce this entire copyright
 *    notice, and the following disclaimer, without alteration, in
 *    either the documentation (as text files in electronic media, or in
 *    printed matter), and/or any original header files from this
 *    software as per the previous term, and/or other materials provided
 *    as part of the distribution.
 * 
 * 3. Collective works including this software must also include the
 *    following acknowledgement, either alone or as part of this entire
 *    copyright license, in any printed documentation accompanying a
 *    physical distribution (if there is printed documentation), and in
 *    a plain text file separate from the archive files (but perhaps
 *    along with other similar acknowledgments) on any electronic
 *    medium:
 * 
 * 	This product includes software developed by Greg A. Woods.
 * 
 * 4. The name of the author may NOT be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.  The use of the author's name strictly to meet the
 *    requirements of the previous terms is not to be considered
 *    promotion or endorsement under this term.
 * 
 * 5. Altered versions (derivative works) must be plainly marked as
 *    such, and must not be misrepresented as being the original
 *    software.  This copyright notice, and the following disclaimer,
 *    must not be removed from any derivative work and must not be
 *    changed in any way.
 * 
 * All other rights are reserved.
 * 
 * DISCLAIMER:
 * 
 * THIS SOFTWARE IS PROVIDED BY GREG A. WOODS ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/cdefs.h>

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MAX_AGG_NETS		1000

char *argv0 = "netagg";

extern int main __P((int, char **, char **));

/* ARGSUSED */
int
main(argc, argv, environ)
	int argc;				/* UNUSED */
	char *argv[];
	char *environ[];			/* UNUSED */
{
	char *networks[MAX_AGG_NETS];		/* XXX should be dynamic and realloc()ed as needed */
	char *nextline;
	char *ipaddr;
	size_t linelen;
	unsigned int i;
	FILE *sp;

	argv0 = (argv0 = strrchr(argv[0], '/')) ? argv0 + 1 : argv[0];
	for (i = 0; i < MAX_AGG_NETS; i++) {
		networks[i] = NULL;
	}

	/*
	 * read in netblock specs (in CIDR form)
	 */
	if (!(sp = popen("sort -n -k 2 -t /", "r"))) {
		fprintf(stderr, "%s: error opening pipe to sort command: %s\n", argv0, strerror(errno));
		exit(1);
		/* NOTREACHED */
	}
	while ((nextline = fgetln(sp, &linelen))) {
		in_addr_t net;
		in_addr_t mask;
		int bits;
		char *netspec;
		int matched;

		if (!(netspec = malloc(linelen))) {
			fprintf(stderr, "%s: malloc(%ld) failed: %s\n", argv0, (long) linelen, strerror(errno));
			exit(1);
			/* NOTREACHED */
		}
		strncpy(netspec, nextline, linelen);
		netspec[linelen - 1] = '\0';

		if ((bits = inet_net_pton(AF_INET, netspec, (void *) &net, sizeof(net))) == -1) {
			fprintf(stderr, "%s: [%s] is invalid: %s\n", argv0, netspec, strerror(errno));
			continue;
		}
		if (bits > 32 || bits < 0) {
			fprintf(stderr, "%s: [%s] has invalid number of bits: %d\n", argv0, netspec, bits);
			continue;
		}
		net = ntohl(net);
		mask = (bits == 0) ? 0 : (0xffffffffL << (32 - bits));
		if (mask && (net & ~mask)) {
			fprintf(stderr, "%s: warning: [%s] has non-zero bits in host part (0x%x).\n", argv0, netspec, net & ~mask);
		}
		matched = 0;
		i = 0;
		for (; networks[i] && i < MAX_AGG_NETS; i++) {
			in_addr_t dest;
			in_addr_t widermask;
			int widerbits;

			ipaddr = networks[i];
			dest = 0;
			if ((widerbits = inet_net_pton(AF_INET, ipaddr, (void *) &dest, sizeof(dest))) == -1) {
				/* XXX this "cannot" happen...  we've already done this once!  :-) */
				fprintf(stderr, "%s: [%s] is invalid: %s\n", argv0, netspec, strerror(errno));
				continue;
			}
			widermask = (widerbits == 0) ? 0 : (0xffffffffL << (32 - widerbits));
			dest = ntohl(dest);
			if ((dest & widermask) == (net & widermask)) {
#if 0
				fprintf(stderr, "%s: [%s] matched [%s].\n", argv0, netspec, ipaddr);
#endif
				matched++;
				break;
			}
		}
		if (!matched) {
			if (i >= MAX_AGG_NETS) {
				fprintf(stderr, "%s: too many nets!  Increase MAX_AGG_NETS (currently %d) and recompile\n", argv0, MAX_AGG_NETS);
				exit(1);
				/* NOTREACHED */
			}
			networks[i] = netspec;
		} else {
			free(netspec);
		}
	}
	if (pclose(sp) == -1) {
		fprintf(stderr, "%s: error closing pipe to sort command: %s\n", argv0, strerror(errno));
	}

	/*
	 * print the resulting list
	 */
	for (i = 0; networks[i] && i < MAX_AGG_NETS; i++) {
		printf("%s\n", networks[i]);
	}

	exit(0);
	/* NOTREACHED */
}
