/*
 * Public Release 3
 *
 * $Id: standalone.c,v 1.3.2.2 1998/10/20 19:32:01 swright Exp $
 */

/*
 * ------------------------------------------------------------------------
 * 
 * GateD, Release 3.5.5 
 * 
 * Copyright (c) 1996, 1997, 1998 The Regents of the University of Michigan
 * All Rights Reserved
 * 
 * License to use, copy, modify, and distribute this software and its
 * documentation can be obtained from Merit at the University of Michigan.
 * 
 * Merit GateDaemon Project
 * 4251 Plymouth Road, Suite C
 * Ann Arbor, MI 48105
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE
 * UNIVERSITY OF MICHIGAN AND MERIT DO NOT WARRANT THAT THE
 * FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR
 * THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. The Regents of the
 * University of Michigan and Merit shall not be liable for
 * any special, indirect, incidental or consequential damages with respect
 * to any claim by Licensee or any third party arising from use of the
 * software. GateDaemon was originated and developed through release 3.0
 * by Cornell University and its collaborators.
 * 
 * Please forward bug fixes, enhancements and questions to the
 * gated mailing list: gated-people@gated.merit.edu.
 * 
 * ---------------------
 * 
 * Copyright (c) 1990,1991,1992,1993,1994,1995 by Cornell University.
 * All rights reserved.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE.
 * 
 * Royalty-free licenses to redistribute GateD Release
 * 3 in whole or in part may be obtained by writing to:
 * 
 * GateDaemon Project
 * Information Technologies/Network Resources
 * 200 CCC
 * Cornell University
 * Ithaca, NY  14853-2601  USA
 * 
 * GateD is based on Kirton's EGP and UC Berkeley's routing
 * daemon	 (routed).
 * Development of GateD has been supported in part by the
 * National Science Foundation.
 * 
 * Please forward bug fixes, enhancements and questions to the
 * gated mailing list: gated-people@gated.cornell.edu.
 * 
 * ------------------------------------------------------------------------
 * 
 * Portions of this software may fall under the following
 * copyrights:
 * 
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms are
 * permitted provided that the above copyright notice and
 * this paragraph are duplicated in all such forms and that
 * any documentation, advertising materials, and other
 * materials related to such distribution and use
 * acknowledge that the software was developed by the
 * University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote
 * products derived from this software without specific
 * prior written permission.  THIS SOFTWARE IS PROVIDED
 * ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * This copyright has ben automaticly added by the util/addcopyright.pl program.
 * __END_OF_COPYRIGHT__
 */


#define	MALLOC_OK
#include "include.h"
#if defined(PROTO_INET)
#include "inet.h"
#endif /* PROTO_INET */
#if defined(PROTO_INET6)
#include "inet6.h"
#endif /* PROTO_INET6 */

/* Support for stand-alone programs (ripquery, gdc) */
#if defined(PROTO_INET)
sockaddr_un *inet_masks[34] = { 0 };
byte inet_mask_list[SOCKADDR_IN_LEN * (sizeof (struct in_addr) * NBBY + 1)];
struct sock_info sock_info[256] = { { 0 } };
#endif /* PROTO_INET */

#if defined(PROTO_INET6)
sockaddr_un *inet6_masks[130] = { 0 };
byte inet6_mask_list[SOCKADDR_IN6_LEN * (sizeof (struct in6_addr) * NBBY + 1)];
#endif /* PROTO_INET6 */

struct gtime task_time;

const bits ll_type_bits[] = {
    { LL_OTHER,		"Unknown" },
    { LL_8022,		"802.2" },
    { LL_X25,		"X.25" },
    { LL_PRONET,	"ProNET" },
    { LL_HYPER,		"HyperChannel" }
};


void
task_assert __PF3(file, const char *,
		  line, const int,
		  test, const char *)
{
    fprintf(stderr,
	    "Assertion failed: file \"%s\", line %d: %s",
	    file,
	    line,
	    test);

    /* Exit with a core dump */
    abort();
}


void_t
task_mem_malloc __PF2(tp, task *,
		      size, size_t)
{
    void_t p;

    p = (void_t) malloc(size);
    if (!p) {
	(void) fprintf(stderr,
		       "malloc: Can not malloc(%d)",
		       size);
	abort();
    }

    return p;
}


void_t
task_mem_calloc __PF3(tp, task *,
		      number, u_int,
		      size, size_t)
{
    void_t p;

    p = (void_t) calloc(number, size);
    if (!p) {
	(void) fprintf(stderr,
		       "calloc: Can not calloc(%d, %d)",
		       number,
		       size);
	abort();
    }

    return p;
}


/*ARGSUSED*/
void
task_mem_free __PF2(tp, task *,
		    p, void_t)
{
    if (p) {
	free((caddr_t) p);
    }
}


/**/

u_short
task_get_port __PF4(tf, trace *,
		    name, const char *,
		    proto, const char *,
		    default_port, u_short)
{
    struct servent *se = getservbyname((char *)name, proto);
    u_short port;

    if (se) {
	port = se->s_port;
    } else {
	port = default_port;
	(void) fprintf(stderr,
		       "task_get_port: getservbyname(\"%s\", \"%s\") failed, using port %d\n",
		       name,
		       proto,
		       htons(port));
    }

    return port;
}

int
task_get_proto __PF3(tf, trace *,
		     name, const char *,
		     default_proto, int)
{
    struct protoent *pe = getprotobyname(name);
    int proto;

    if (pe) {
	proto = pe->p_proto;
    } else {
	proto = default_proto;
	(void) fprintf(stderr,
		       "task_get_proto: getprotobyname(\"%s\") failed, using proto %d\n",
		       name,
		       proto);
    }

    return proto;
}


/*
 *	Init all kinds of inet structures
 */
#if defined(PROTO_INET)
void
inet_family_init __PF0(void)
{
    u_int32 mask = 0;
    sockaddr_un **mp = inet_masks;
    sockaddr_un *mpp = (sockaddr_un *) ((void_t) inet_mask_list);

    /* Build all possible contiguous masks */
    while (TRUE) {
	sockcopy(sockbuild_in(0, htonl(mask)), mpp);
	mask_insert(*mp++ = mpp);

	if (mask == INADDR_HOSTMASK) {
	    break;
	}
	mask = (mask >> 1) | 0x80000000l;
	mpp = (sockaddr_un *) ((void_t) ((byte *) mpp + SOCKADDR_IN_LEN));
    }
    
    sock_init_family(AF_INET,
		     (SOCKADDR_IN_LEN - sizeof sock2in(inet_addr_default)),
		     SOCKADDR_IN_LEN,
		     inet_mask_list,
		     sizeof inet_mask_list,
		     "sockaddr_un.in");

    /* Build useful addresses */
}

static char *sock_buf;
static void_t sock_bufp;
static int sock_buf_size;

#define	BUF_ALLOC(ap, type, len) do { \
    int XXlen = ROUNDUP((len), sizeof (u_long)); \
    if ((caddr_t) sock_bufp + XXlen > sock_buf + sock_buf_size) \
	sock_bufp = (void_t) sock_buf; \
    ap = (type *) sock_bufp; \
    sock_bufp = (caddr_t) sock_bufp + XXlen; \
} while (0)

#define	SOCKBUILD(ap, type, len, af) do { \
    int Xlen = (len); \
    BUF_ALLOC(ap, type, Xlen); \
    socksize(ap) = Xlen; \
    socktype(ap) = (af); \
} while (0)

/*
 *	Build an inet address
 */
sockaddr_un *
sockbuild_in __PF2(port, u_short,
		   addr, u_int32)
{
    register sockaddr_un *sock;
    
    SOCKBUILD(sock, sockaddr_un, SOCKADDR_IN_LEN, AF_INET);
    sock2port(sock) = port;
    sock2ip(sock) = addr;

    return sock;
}

/* XXX - masks should be stored in a radix tree... */
struct mask_entry {
    struct mask_entry *rtm_forw;
    struct mask_entry *rtm_back;
    sockaddr_un *rtm_mask;
} ;

static int mask_dup = TRUE;
static struct mask_entry mask_list = { &mask_list, &mask_list} ;

#define	MASK_LIST(rtm)	for (rtm = mask_list.rtm_forw; rtm != &mask_list; rtm = rtm->rtm_forw)
#define	MASK_LIST_END(rtm)

sockaddr_un *
mask_locate __PF1(mask, register sockaddr_un *)
{
    register u_int len;
    register struct mask_entry *rtm;
    register byte *cp = (byte *) mask + socksize(mask);
    static block_t mask_block_index;

#ifdef PROTO_INET6		/* HITACHI_INET6 */
	if (socktype(mask) == AF_INET6) {
		mask->in6.gin6_flowinfo = 0;
		mask->in6.gin6_port = 0;
	}
#endif

    if (cp[-1]) {
	len = socksize(mask);
    } else {
	/* Trim the mask */
	while (cp-- > (byte *) mask && !*cp) ;
	len = cp - (byte *) mask + 1;

	assert(socksize(mask) >= 2);
    }
    
    MASK_LIST(rtm) {
	if (socksize(rtm->rtm_mask) > len) {
	    /* Not in list */
	    goto New;
	}
	if (socksize(rtm->rtm_mask) == len) {
	    register byte *cp1 = (byte *) rtm->rtm_mask;
	    register byte *cp2 = (byte *) mask;
	    byte *lp = cp1 + len;

	    while (++cp2, ++cp1 < lp) {
		if (*cp1 > *cp2) {
		    /* Not in list */
		    goto New;
		}
		if (*cp1 < *cp2) {
		    /* This is not the one */
		    goto Continue;
		}
	    }

	    /* Found it */
	    goto Return;
	}

    Continue:
	;
    } MASK_LIST_END(rtm) ;

 New:
    /* Insert at the end of the list */
    rtm = rtm->rtm_back;

    INSQUE(task_mem_calloc(0, 1, sizeof(struct mask_entry)), rtm);
    rtm = rtm->rtm_forw;
    rtm->rtm_mask = mask_dup ? sockdup(mask) : mask;
    socksize(rtm->rtm_mask) = len;

 Return:
    return rtm->rtm_mask;
}

void
mask_insert __PF1(mask, register sockaddr_un *)
{
    sockaddr_un *new_mask;
    
    mask_dup = FALSE;
    new_mask = mask_locate(mask);
    mask_dup = TRUE;
    assert(new_mask == mask);
}

sockaddr_un *
sockdup __PF1(src, sockaddr_un *)
{
    register size_t len = socksize(src);
    register byte *sp = (byte *) src;
    sockaddr_un *dst;
    register byte *dp;
    block_t  block_index = SI_INDEX(socktype(src));

    dst = (sockaddr_un *) task_mem_malloc((task *) 0, len);

    dp = (byte *) dst;
    while (len--) {
	*dp++ = *sp++;
    }

    return dst;
}

void
sock_init_family __PF6(family, u_int,
		       offset, u_int,
		       size, size_t,
		       mask_list, byte *,
		       mask_size, size_t,
		       name, const char *)
{
    struct sock_info *sip;
    
    assert(family < 256);

    sip = &sock_info[family];
    sip->si_family = family;
    sip->si_offset = offset;
    sip->si_size = size;
    sip->si_mask_count = mask_size / size;
    sip->si_mask_min = (sockaddr_un *) ((void_t) mask_list);
    sip->si_mask_max = (sockaddr_un *) ((void_t) (mask_list + mask_size));
/*    sip->si_index = task_block_init(size, name); */
}

#endif	/* PROTO_INET */

#if defined(PROTO_INET6)
#define	INET6_IFPS_ALLROUTERS	IFPS_KEEP1	/* We joined the all-routers group on this interface */
sockaddr_un *inet6_addr_allnodes;	/* All nodes multicast address */
sockaddr_un *inet6_addr_allrouters;	/* All routers multicast address */

static const bits inet6_if_bits[] = {
    { INET6_IFPS_ALLROUTERS, "AllRouters" },
    { 0 }
};

/*
 *	Init all kinds of IPv6 structures
 */
void
inet6_family_init __PF0(void)
{
    int plen = 0;
    struct in6_addr ia;
    sockaddr_un *addr;
    sockaddr_un **mp = inet6_masks;
    sockaddr_un *mpp = (sockaddr_un *) ((void_t) inet6_mask_list);
    byte *cp, *lp;

    /* Get an address to work with */
    bzero((void_t) &ia, sizeof(ia));
    addr = sockbuild_in6(0, (byte *) &ia);

    /* Build all possible contiguous masks */
    /* Add null mask */
    sockcopy(addr, mpp);
    mask_insert(*mp++ = mpp);
    mpp = (sockaddr_un *) ((void_t) ((byte *) mpp + SOCKADDR_IN6_LEN));

    for (cp = (byte *) &addr->in6.gin6_addr, lp = cp + sizeof(ia);
	 cp < lp; cp++) {
	int bit = NBBY;

	*cp = (byte) 0;

	while (bit--) {
	    *cp |= 1 << bit;
	    sockcopy(addr, mpp);
	    mask_insert(*mp++ = mpp);
	    mpp = (sockaddr_un *) ((void_t) ((byte *) mpp + SOCKADDR_IN6_LEN));
	}
    }
    
    sock_init_family(AF_INET6,
		     (SOCKADDR_IN6_LEN - sizeof sock2in6(inet6_addr_default)),
		     SOCKADDR_IN6_LEN,
		     inet6_mask_list,
		     sizeof inet6_mask_list,
		     "sockaddr_un.in6");

    bzero((void_t) &ia, sizeof(ia));
    ia.s6_addr[0] = 0xff;
    ia.s6_addr[1] = 2;
    ia.s6_addr[15] = 1;
    inet6_addr_allnodes = sockdup(sockbuild_in6(0, (byte *) &ia));
    ia.s6_addr[15] = 2;
    inet6_addr_allrouters = sockdup(sockbuild_in6(0, (byte *) &ia));

}

sockaddr_un *
sockbuild_in6 __PF2(port, u_short,
                   addr, byte *)
{
    register sockaddr_un *sock;
     
    SOCKBUILD(sock, sockaddr_un, SOCKADDR_IN6_LEN, AF_INET6);
    sock2port6(sock) = port;
    bcopy(addr, &sock2in6(sock), sizeof(struct in6_addr));

    return sock;
}

#endif /* PROTO_INET6 */

