/*	$NetBSD: mapper.c,v 1.28 2019/02/03 03:19:31 mrg Exp $	*/

/* Mapper for connections between MRouteD multicast routers.
 * Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
 */

/*
 * Copyright (c) 1992, 2001 Xerox Corporation.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither name of the Xerox, PARC, nor the names of its contributors may be used
 * to endorse or promote products derived from this software 
 * without specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``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 XEROX CORPORATION OR CONTRIBUTORS
 * 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 <ctype.h>
#include <string.h>
#include <netdb.h>
#include <sys/time.h>
#include <poll.h>
#include "defs.h"
#include <arpa/inet.h>
#include <stdarg.h>

#define DEFAULT_TIMEOUT	2	/* How long to wait before retrying requests */
#define DEFAULT_RETRIES 1	/* How many times to ask each router */


/* All IP addresses are stored in the data structure in NET order. */

typedef struct neighbor {
    struct neighbor    *next;
    u_int32_t		addr;		/* IP address in NET order */
    u_char		metric;		/* TTL cost of forwarding */
    u_char		threshold;	/* TTL threshold to forward */
    u_short		flags;		/* flags on connection */
#define NF_PRESENT 0x8000	/* True if flags are meaningful */
} Neighbor;

typedef struct interface {
    struct interface *next;
    u_int32_t	addr;		/* IP address of the interface in NET order */
    Neighbor   *neighbors;	/* List of neighbors' IP addresses */
} Interface;

typedef struct node {
    u_int32_t	addr;		/* IP address of this entry in NET order */
    u_int32_t	version;	/* which mrouted version is running */
    int		tries;		/* How many requests sent?  -1 for aliases */
    union {
	struct node *alias;		/* If alias, to what? */
	struct interface *interfaces;	/* Else, neighbor data */
    } u;
    struct node *left, *right;
} Node;


Node   *routers = 0;
u_int32_t	our_addr, target_addr = 0;		/* in NET order */
int	debug = 0;
int	retries = DEFAULT_RETRIES;
int	timeout = DEFAULT_TIMEOUT;
int	show_names = TRUE;
vifi_t  numvifs;		/* to keep loader happy */
				/* (see COPY_TABLES macro called in kern.c) */

Node *			find_node(u_int32_t addr, Node **ptr);
Interface *		find_interface(u_int32_t addr, Node *node);
Neighbor *		find_neighbor(u_int32_t addr, Node *node);
void			ask(u_int32_t dst);
void			ask2(u_int32_t dst);
int			retry_requests(Node *node);
char *			inet_name(u_int32_t addr);
void			print_map(Node *node);
char *			graph_name(u_int32_t addr, char *buf, size_t);
void			graph_edges(Node *node);
void			elide_aliases(Node *node);
void			graph_map(void);
int			get_number(int *var, int deflt, char ***pargv,
				   int *pargc);
u_int32_t		host_addr(char *name);

void logit(int severity, int syserr, const char *format, ...)
	__attribute__((__format__(__printf__, 3, 4)));

Node *find_node(u_int32_t addr, Node **ptr)
{
    Node *n = *ptr;

    if (!n) {
	*ptr = n = (Node *) malloc(sizeof(Node));
	n->addr = addr;
	n->version = 0;
	n->tries = 0;
	n->u.interfaces = 0;
	n->left = n->right = 0;
	return n;
    } else if (addr == n->addr)
	return n;
    else if (addr < n->addr)
	return find_node(addr, &(n->left));
    else
	return find_node(addr, &(n->right));
}


Interface *find_interface(u_int32_t addr, Node *node)
{
    Interface *ifc;

    for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
	if (ifc->addr == addr)
	    return ifc;

    ifc = (Interface *) malloc(sizeof(Interface));
    ifc->addr = addr;
    ifc->next = node->u.interfaces;
    node->u.interfaces = ifc;
    ifc->neighbors = 0;

    return ifc;
}


Neighbor *find_neighbor(u_int32_t addr, Node *node)
{
    Interface *ifc;

    for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
	Neighbor *nb;

	for (nb = ifc->neighbors; nb; nb = nb->next)
	    if (nb->addr == addr)
		return nb;
    }

    return 0;
}


/*
 * Log errors and other messages to stderr, according to the severity of the
 * message and the current debug level.  For errors of severity LOG_ERR or
 * worse, terminate the program.
 */
void
logit(int severity, int syserr, const char *format, ...)
{
    va_list ap;
    char    fmt[100];

    switch (debug) {
	case 0: if (severity > LOG_WARNING) return;
		/* FALLTHROUGH */
	case 1: if (severity > LOG_NOTICE ) return;
		/* FALLTHROUGH */
	case 2: if (severity > LOG_INFO   ) return;
		/* FALLTHROUGH */
	default:
	    fmt[0] = '\0';
	    if (severity == LOG_WARNING)
		strlcat(fmt, "warning - ", sizeof(fmt));
	    strlcat(fmt, format, sizeof(fmt));
	    format = fmt;
	    va_start(ap, format);
	    vfprintf(stderr, format, ap);
	    va_end(ap);
	    if (syserr == 0)
		fprintf(stderr, "\n");
	    else
		fprintf(stderr, ": %s\n", strerror(syserr));
    }

    if (severity <= LOG_ERR)
	exit(1);
}


/*
 * Send a neighbors-list request.
 */
void ask(u_int32_t dst)
{
    send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS,
		htonl(MROUTED_LEVEL), 0);
}

void ask2(u_int32_t dst)
{
    send_igmp(our_addr, dst, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2,
		htonl(MROUTED_LEVEL), 0);
}


/*
 * Process an incoming group membership report.
 */
void accept_group_report(u_int32_t src, u_int32_t dst, u_int32_t group, int r_type)
{
    logit(LOG_INFO, 0, "ignoring IGMP group membership report from %s to %s",
	inet_fmt(src), inet_fmt(dst));
}


/*
 * Process an incoming neighbor probe message.
 */
void accept_probe(u_int32_t src, u_int32_t dst, char *p, int datalen,
		  u_int32_t level)
{
    logit(LOG_INFO, 0, "ignoring DVMRP probe from %s to %s",
	inet_fmt(src), inet_fmt(dst));
}


/*
 * Process an incoming route report message.
 */
void accept_report(u_int32_t src, u_int32_t dst, char *p, int datalen,
		   u_int32_t level)
{
    logit(LOG_INFO, 0, "ignoring DVMRP routing report from %s to %s",
	inet_fmt(src), inet_fmt(dst));
}


/*
 * Process an incoming neighbor-list request message.
 */
void accept_neighbor_request(u_int32_t src, u_int32_t dst)
{
    if (src != our_addr)
	logit(LOG_INFO, 0,
	    "ignoring spurious DVMRP neighbor request from %s to %s",
	    inet_fmt(src), inet_fmt(dst));
}

void accept_neighbor_request2(u_int32_t src, u_int32_t dst)
{
    if (src != our_addr)
	logit(LOG_INFO, 0,
	    "ignoring spurious DVMRP neighbor request2 from %s to %s",
	    inet_fmt(src), inet_fmt(dst));
}


/*
 * Process an incoming neighbor-list message.
 */
void accept_neighbors(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
		      u_int32_t level)
{
    Node       *node = find_node(src, &routers);

    if (node->tries == 0)	/* Never heard of 'em; must have hit them at */
	node->tries = 1;	/* least once, though...*/
    else if (node->tries == -1)	/* follow alias link */
	node = node->u.alias;

#define GET_ADDR(a) (a = ((u_int32_t)*p++ << 24), a += ((u_int32_t)*p++ << 16),\
		     a += ((u_int32_t)*p++ << 8), a += *p++)

    /* if node is running a recent mrouted, ask for additional info */
    if (level != 0) {
	node->version = level;
	node->tries = 1;
	ask2(src);
	return;
    }

    if (debug > 3) {
	int i;

	fprintf(stderr, "    datalen = %d\n", datalen);
	for (i = 0; i < datalen; i++) {
	    if ((i & 0xF) == 0)
		fprintf(stderr, "   ");
	    fprintf(stderr, " %02x", p[i]);
	    if ((i & 0xF) == 0xF)
		fprintf(stderr, "\n");
	}
	if ((datalen & 0xF) != 0xF)
	    fprintf(stderr, "\n");
    }

    while (datalen > 0) {	/* loop through interfaces */
	u_int32_t		ifc_addr;
	u_char		metric, threshold, ncount;
	Node   	       *ifc_node;
	Interface      *ifc;
	Neighbor       *old_neighbors;

	if (datalen < 4 + 3) {
	    logit(LOG_WARNING, 0, "received truncated interface record from %s",
		inet_fmt(src));
	    return;
	}

	GET_ADDR(ifc_addr);
	ifc_addr = htonl(ifc_addr);
	metric = *p++;
	threshold = *p++;
	ncount = *p++;
	datalen -= 4 + 3;

	/* Fix up any alias information */
	ifc_node = find_node(ifc_addr, &routers);
	if (ifc_node->tries == 0) { /* new node */
	    ifc_node->tries = -1;
	    ifc_node->u.alias = node;
	} else if (ifc_node != node
		   && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
	    /* must merge two hosts' nodes */
	    Interface  *ifc_i, *next_ifc_i;

	    if (ifc_node->tries == -1) {
		Node *tmp = ifc_node->u.alias;

		ifc_node->u.alias = node;
		ifc_node = tmp;
	    }

	    /* Merge ifc_node (foo_i) into node (foo_n) */

	    if (ifc_node->tries > node->tries)
		node->tries = ifc_node->tries;

	    for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
		Neighbor *nb_i, *next_nb_i, *nb_n;
		Interface *ifc_n = find_interface(ifc_i->addr, node);

		old_neighbors = ifc_n->neighbors;
		for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
		    next_nb_i = nb_i->next;
		    for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
			if (nb_i->addr == nb_n->addr) {
			    if (nb_i->metric != nb_n->metric
				|| nb_i->threshold != nb_n->threshold)
				logit(LOG_WARNING, 0,
				    "inconsistent %s for neighbor %s of %s",
				    "metric/threshold",
				    inet_fmt(nb_i->addr),
				    inet_fmt(node->addr));
			    free(nb_i);
			    break;
			}
		    if (!nb_n) { /* no match for this neighbor yet */
			nb_i->next = ifc_n->neighbors;
			ifc_n->neighbors = nb_i;
		    }
		}

		next_ifc_i = ifc_i->next;
		free(ifc_i);
	    }

	    ifc_node->tries = -1;
	    ifc_node->u.alias = node;
	}
	
	ifc = find_interface(ifc_addr, node);
	old_neighbors = ifc->neighbors;
	
	/* Add the neighbors for this interface */
	while (ncount--) {
	    u_int32_t 	neighbor;
	    Neighbor   *nb;
	    Node       *n_node;

	    if (datalen < 4) {
		logit(LOG_WARNING, 0, "received truncated neighbor list from %s",
		    inet_fmt(src));
		return;
	    }

	    GET_ADDR(neighbor);
	    neighbor = htonl(neighbor);
	    datalen -= 4;

	    for (nb = old_neighbors; nb; nb = nb->next)
		if (nb->addr == neighbor) {
		    if (metric != nb->metric || threshold != nb->threshold)
			logit(LOG_WARNING, 0,
			    "inconsistent %s for neighbor %s of %s",
			    "metric/threshold",
			    inet_fmt(nb->addr), inet_fmt(node->addr));
		    goto next_neighbor;
		}

	    nb = (Neighbor *) malloc(sizeof(Neighbor));
	    nb->next = ifc->neighbors;
	    ifc->neighbors = nb;
	    nb->addr = neighbor;
	    nb->metric = metric;
	    nb->threshold = threshold;
	    nb->flags = 0;

	    n_node = find_node(neighbor, &routers);
	    if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
		ask(neighbor);
		n_node->tries = 1;
	    }

	  next_neighbor: ;
	}
    }
}

void accept_neighbors2(u_int32_t src, u_int32_t dst, u_char *p, int datalen,
		       u_int32_t level)
{
    Node       *node = find_node(src, &routers);
    u_int broken_cisco = ((level & 0xffff) == 0x020a); /* 10.2 */
    /* well, only possibly_broken_cisco, but that's too long to type. */

    if (node->tries == 0)	/* Never heard of 'em; must have hit them at */
	node->tries = 1;	/* least once, though...*/
    else if (node->tries == -1)	/* follow alias link */
	node = node->u.alias;

    while (datalen > 0) {	/* loop through interfaces */
	u_int32_t		ifc_addr;
	u_char		metric, threshold, ncount, flags;
	Node   	       *ifc_node;
	Interface      *ifc;
	Neighbor       *old_neighbors;

	if (datalen < 4 + 4) {
	    logit(LOG_WARNING, 0, "received truncated interface record from %s",
		inet_fmt(src));
	    return;
	}

	ifc_addr = *(u_int32_t*)p;
	p += 4;
	metric = *p++;
	threshold = *p++;
	flags = *p++;
	ncount = *p++;
	datalen -= 4 + 4;

	if (broken_cisco && ncount == 0)	/* dumb Ciscos */
		ncount = 1;
	if (broken_cisco && ncount > 15)	/* dumb Ciscos */
		ncount = ncount & 0xf;

	/* Fix up any alias information */
	ifc_node = find_node(ifc_addr, &routers);
	if (ifc_node->tries == 0) { /* new node */
	    ifc_node->tries = -1;
	    ifc_node->u.alias = node;
	} else if (ifc_node != node
		   && (ifc_node->tries > 0  ||  ifc_node->u.alias != node)) {
	    /* must merge two hosts' nodes */
	    Interface  *ifc_i, *next_ifc_i;

	    if (ifc_node->tries == -1) {
		Node *tmp = ifc_node->u.alias;

		ifc_node->u.alias = node;
		ifc_node = tmp;
	    }

	    /* Merge ifc_node (foo_i) into node (foo_n) */

	    if (ifc_node->tries > node->tries)
		node->tries = ifc_node->tries;

	    for (ifc_i = ifc_node->u.interfaces; ifc_i; ifc_i = next_ifc_i) {
		Neighbor *nb_i, *next_nb_i, *nb_n;
		Interface *ifc_n = find_interface(ifc_i->addr, node);

		old_neighbors = ifc_n->neighbors;
		for (nb_i = ifc_i->neighbors; nb_i; nb_i = next_nb_i) {
		    next_nb_i = nb_i->next;
		    for (nb_n = old_neighbors; nb_n; nb_n = nb_n->next)
			if (nb_i->addr == nb_n->addr) {
			    if (nb_i->metric != nb_n->metric
				|| nb_i->threshold != nb_n->threshold)
				logit(LOG_WARNING, 0,
				    "inconsistent %s for neighbor %s of %s",
				    "metric/threshold",
				    inet_fmt(nb_i->addr),
				    inet_fmt(node->addr));
			    free(nb_i);
			    break;
			}
		    if (!nb_n) { /* no match for this neighbor yet */
			nb_i->next = ifc_n->neighbors;
			ifc_n->neighbors = nb_i;
		    }
		}

		next_ifc_i = ifc_i->next;
		free(ifc_i);
	    }

	    ifc_node->tries = -1;
	    ifc_node->u.alias = node;
	}
	
	ifc = find_interface(ifc_addr, node);
	old_neighbors = ifc->neighbors;
	
	/* Add the neighbors for this interface */
	while (ncount-- && datalen > 0) {
	    u_int32_t 	neighbor;
	    Neighbor   *nb;
	    Node       *n_node;

	    if (datalen < 4) {
		logit(LOG_WARNING, 0, "received truncated neighbor list from %s",
		    inet_fmt(src));
		return;
	    }

	    neighbor = *(u_int32_t*)p;
	    p += 4;
	    datalen -= 4;
	    if (neighbor == 0)
		/* make leaf nets point to themselves */
		neighbor = ifc_addr;

	    for (nb = old_neighbors; nb; nb = nb->next)
		if (nb->addr == neighbor) {
		    if (metric != nb->metric || threshold != nb->threshold)
			logit(LOG_WARNING, 0,
			    "inconsistent %s for neighbor %s of %s",
			    "metric/threshold",
			    inet_fmt(nb->addr), inet_fmt(node->addr));
		    goto next_neighbor;
		}

	    nb = (Neighbor *) malloc(sizeof(Neighbor));
	    nb->next = ifc->neighbors;
	    ifc->neighbors = nb;
	    nb->addr = neighbor;
	    nb->metric = metric;
	    nb->threshold = threshold;
	    nb->flags = flags | NF_PRESENT;

	    n_node = find_node(neighbor, &routers);
	    if (n_node->tries == 0  &&  !target_addr) { /* it's a new router */
		ask(neighbor);
		n_node->tries = 1;
	    }

	  next_neighbor: ;
	}
    }
}


void check_vif_state(void)
{
    logit(LOG_NOTICE, 0, "network marked down...");
}


int retry_requests(Node *node)
{
    int	result;

    if (node) {
	result = retry_requests(node->left);
	if (node->tries > 0  &&  node->tries < retries) {
	    if (node->version)
		ask2(node->addr);
	    else
		ask(node->addr);
	    node->tries++;
	    result = 1;
	}
	return retry_requests(node->right) || result;
    } else
	return 0;
}


char *inet_name(u_int32_t addr)
{
    struct hostent *e;

    e = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);

    return e ? e->h_name : 0;
}


void print_map(Node *node)
{
    if (node) {
	char *name, *addr;
	
	print_map(node->left);

	addr = inet_fmt(node->addr);
	if (!target_addr
	    || (node->tries >= 0 && node->u.interfaces)
	    || (node->tries == -1
		&& node->u.alias->tries >= 0
		&& node->u.alias->u.interfaces)) {
	    if (show_names && (name = inet_name(node->addr)))
		printf("%s (%s):", addr, name);
	    else
		printf("%s:", addr);
	    if (node->tries < 0)
		printf(" alias for %s\n\n", inet_fmt(node->u.alias->addr));
	    else if (!node->u.interfaces)
		printf(" no response to query\n\n");
	    else {
		Interface *ifc;

		if (node->version)
		    printf(" <v%d.%d>", node->version & 0xff,
					(node->version >> 8) & 0xff);
		printf("\n");
		for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
		    Neighbor *nb;
		    char *ifc_name = inet_fmt(ifc->addr);
		    int ifc_len = strlen(ifc_name);
		    int count = 0;

		    printf("    %s:", ifc_name);
		    for (nb = ifc->neighbors; nb; nb = nb->next) {
			if (count > 0)
			    printf("%*s", ifc_len + 5, "");
			printf("  %s", inet_fmt(nb->addr));
			if (show_names  &&  (name = inet_name(nb->addr)))
			    printf(" (%s)", name);
			printf(" [%d/%d", nb->metric, nb->threshold);
			if (nb->flags) {
			    u_short flags = nb->flags;
			    if (flags & DVMRP_NF_TUNNEL)
				    printf("/tunnel");
			    if (flags & DVMRP_NF_SRCRT)
				    printf("/srcrt");
			    if (flags & DVMRP_NF_QUERIER)
				    printf("/querier");
			    if (flags & DVMRP_NF_DISABLED)
				    printf("/disabled");
			    if (flags & DVMRP_NF_DOWN)
				    printf("/down");
			}
                        printf("]\n");
			count++;
		    }
		}
		printf("\n");
	    }
	}
	print_map(node->right);
    }
}


char *graph_name(u_int32_t addr, char *buf, size_t l)
{
    char *name;

    if (show_names && (name = inet_name(addr)))
	strlcpy(buf, name, l);
    else
	inet_fmt(addr);

    return buf;
}


void graph_edges(Node *node)
{
    Interface *ifc;
    Neighbor *nb;
    char name[100];

    if (node) {
	graph_edges(node->left);
	if (node->tries >= 0) {
	    printf("  %d {$ NP %d0 %d0 $} \"%s%s\" \n",
		   (int) node->addr,
		   node->addr & 0xFF, (node->addr >> 8) & 0xFF,
		   graph_name(node->addr, name, sizeof(name)),
		   node->u.interfaces ? "" : "*");
	    for (ifc = node->u.interfaces; ifc; ifc = ifc->next)
		for (nb = ifc->neighbors; nb; nb = nb->next) {
		    Node *nb_node = find_node(nb->addr, &routers);
		    Neighbor *nb2;

		    if (nb_node->tries < 0)
			nb_node = nb_node->u.alias;

		    if (node != nb_node &&
			(!(nb2 = find_neighbor(node->addr, nb_node))
			 || node->addr < nb_node->addr)) {
			printf("    %d \"%d/%d",
			       nb_node->addr, nb->metric, nb->threshold);
			if (nb2 && (nb2->metric != nb->metric
				    || nb2->threshold != nb->threshold))
			    printf(",%d/%d", nb2->metric, nb2->threshold);
			if (nb->flags & NF_PRESENT)
			    printf("%s%s",
				   nb->flags & DVMRP_NF_SRCRT ? "" :
				   nb->flags & DVMRP_NF_TUNNEL ? "E" : "P",
				   nb->flags & DVMRP_NF_DOWN ? "D" : "");
			printf("\"\n");
		    }
		}
	    printf("    ;\n");
	}
	graph_edges(node->right);
    }
}

void elide_aliases(Node *node)
{
    if (node) {
	elide_aliases(node->left);
	if (node->tries >= 0) {
	    Interface *ifc;

	    for (ifc = node->u.interfaces; ifc; ifc = ifc->next) {
		Neighbor *nb;

		for (nb = ifc->neighbors; nb; nb = nb->next) {
		    Node *nb_node = find_node(nb->addr, &routers);

		    if (nb_node->tries < 0)
			nb->addr = nb_node->u.alias->addr;
		}
	    }
	}
	elide_aliases(node->right);
    }
}

void graph_map(void)
{
    time_t now = time(0);
    char *nowstr = ctime(&now);

    nowstr[24] = '\0';		/* Kill the newline at the end */
    elide_aliases(routers);
    printf("GRAPH \"Multicast Router Connectivity: %s\" = UNDIRECTED\n",
	   nowstr);
    graph_edges(routers);
    printf("END\n");
}


int get_number(int *var, int deflt, char ***pargv, int *pargc)
{
    if ((*pargv)[0][2] == '\0') { /* Get the value from the next argument */
	if (*pargc > 1  &&  isdigit((unsigned char)(*pargv)[1][0])) {
	    (*pargv)++, (*pargc)--;
	    *var = atoi((*pargv)[0]);
	    return 1;
	} else if (deflt >= 0) {
	    *var = deflt;
	    return 1;
	} else
	    return 0;
    } else {			/* Get value from the rest of this argument */
	if (isdigit((unsigned char)(*pargv)[0][2])) {
	    *var = atoi((*pargv)[0] + 2);
	    return 1;
	} else {
	    return 0;
	}
    }
}


u_int32_t host_addr(char *name)
{
    struct hostent *e = gethostbyname(name);
    int addr;

    if (e)
	memcpy(&addr, e->h_addr_list[0], e->h_length);
    else {
	addr = inet_addr(name);
	if (addr == -1)
	    addr = 0;
    }

    return addr;
}


int main(int argc, char **argv)
{
    int flood = FALSE, graph = FALSE;
    struct pollfd set[1];
    
    setlinebuf(stderr);

    if (geteuid() != 0) {
	fprintf(stderr, "must be root\n");
	exit(1);
    }

    argv++, argc--;
    while (argc > 0 && argv[0][0] == '-') {
	switch (argv[0][1]) {
	  case 'd':
	    if (!get_number(&debug, DEFAULT_DEBUG, &argv, &argc))
		goto usage;
	    break;
	  case 'f':
	    flood = TRUE;
	    break;
	  case 'g':
	    graph = TRUE;
	    break;
	  case 'n':
	    show_names = FALSE;
	    break;
	  case 'r':
	    if (!get_number(&retries, -1, &argv, &argc))
		goto usage;
	    break;
	  case 't':
	    if (!get_number(&timeout, -1, &argv, &argc))
		goto usage;
	    break;
	  default:
	    goto usage;
	}
	argv++, argc--;
    }

    if (argc > 1) {
      usage:	
	fprintf(stderr,
		"usage: map-mbone [-f] [-g] [-n] [-t timeout] %s\n\n",
		"[-r retries] [-d [debug-level]] [router]");
        fprintf(stderr, "\t-f  Flood the routing graph with queries\n");
        fprintf(stderr, "\t    (True by default unless `router' is given)\n");
        fprintf(stderr, "\t-g  Generate output in GraphEd format\n");
        fprintf(stderr, "\t-n  Don't look up DNS names for routers\n");
	exit(1);
    } else if (argc == 1 && !(target_addr = host_addr(argv[0]))) {
	fprintf(stderr, "Unknown host: %s\n", argv[0]);
	exit(2);
    }

    if (debug)
	fprintf(stderr, "Debug level %u\n", debug);

    init_igmp();

    {				/* Find a good local address for us. */
	int udp;
	struct sockaddr_in addr;
	socklen_t addrlen = sizeof(addr);

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
#if (defined(BSD) && (BSD >= 199103))
	addr.sin_len = sizeof addr;
#endif
	addr.sin_addr.s_addr = dvmrp_group;
	addr.sin_port = htons(2000); /* any port over 1024 will do... */
	if ((udp = socket(AF_INET, SOCK_DGRAM, 0)) < 0
	    || connect(udp, (struct sockaddr *) &addr, sizeof(addr)) < 0
	    || getsockname(udp, (struct sockaddr *) &addr, &addrlen) < 0) {
	    perror("Determining local address");
	    exit(1);
	}
	close(udp);
	our_addr = addr.sin_addr.s_addr;
    }

    /* Send initial seed message to all local routers */
    ask(target_addr ? target_addr : allhosts_group);

    if (target_addr) {
	Node *n = find_node(target_addr, &routers);

	n->tries = 1;

	if (flood)
	    target_addr = 0;
    }

    /* Main receive loop */
    set[0].fd = igmp_socket;
    set[0].events = POLLIN;
    for(;;) {
	int 		count, recvlen;
	socklen_t dummy = 0;

	count = poll(set, 1, timeout * 1000);

	if (count < 0) {
	    if (errno != EINTR)
		perror("poll");
	    continue;
	} else if (count == 0) {
	    logit(LOG_DEBUG, 0, "Timed out receiving neighbor lists");
	    if (retry_requests(routers))
		continue;
	    else
		break;
	}

	recvlen = recvfrom(igmp_socket, recv_buf, RECV_BUF_SIZE,
			   0, NULL, &dummy);
	if (recvlen >= 0)
	    accept_igmp(recvlen);
	else if (errno != EINTR)
	    perror("recvfrom");
    }

    printf("\n");

    if (graph)
	graph_map();
    else {
	if (!target_addr)
	    printf("Multicast Router Connectivity:\n\n");
	print_map(routers);
    }

    exit(0);
}

/* dummies */
void accept_prune(u_int32_t src, u_int32_t dst, char *p, int datalen)
{
}
void accept_graft(u_int32_t src, u_int32_t dst, char *p, int datalen)
{
}
void accept_g_ack(u_int32_t src, u_int32_t dst, char *p, int datalen)
{
}
void add_table_entry(u_int32_t origin, u_int32_t mcastgrp)
{
}
void accept_leave_message(u_int32_t src, u_int32_t dst, u_int32_t group)
{
}
void accept_mtrace(u_int32_t src, u_int32_t dst, u_int32_t group, char *data,
		   u_int no, int datalen)
{
}
void accept_membership_query(u_int32_t src, u_int32_t dst, u_int32_t group,
			     int tmo)
{
}
void accept_info_request(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
{
}
void accept_info_reply(u_int32_t src, u_int32_t dst, u_char *p, int datalen)
{
}
