/* eXternal RESolver -- Francis.Dupont@inria.fr, September 1997 */

#include <sys/param.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <sys/sysctl.h>
#include <sys/ioctl.h>

#include <net/if.h>
#include <net/if_atm.h>
#include <net/if_dl.h>
#include <net/if_types.h>
#include <net/route.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <netdb.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <strings.h>

int s, seq = 1;
struct {
	struct rt_msghdr m_rtm;
	char m_space[512];
} m_rtmsg0, m_rtmsg;
struct rt_addrinfo info;

struct map {
	struct in_addr ina;
	struct atm_pseudohdr aph;
} map[] = { 
	{ { 0xb1c133c1 }, {{ 0x03, 0, 0, 0x65 }} },
	{ { 0 }, {{ 0x03, 0, 0, 0x65 }} }
};

#define dst		((struct sockaddr_in *)info.rti_info[RTAX_DST])
#define gateway		((struct sockaddr_dl *)info.rti_info[RTAX_GATEWAY])

#define ROUNDUP(a) \
	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n)	(x += ROUNDUP((n)->sa_len))

int
main(argc, argv)
    int argc;
    char *argv[];
{
    register struct rt_msghdr *rtm;
    register char *cp;
    register int i, cc;
    register struct map *match;
    struct sockaddr *sa;
    struct sockaddr_dl *sdl;

    if ((s = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
	perror("socket");
	exit(1);
    }
    for (;;) {
	(void)bzero(&m_rtmsg0, sizeof(m_rtmsg0));

	cc = read(s, (char *)&m_rtmsg0, sizeof(m_rtmsg0));
	if (cc < 0) {
	    perror("read");
	    exit(1);
	}
	if (cc == 0) {
	    fprintf(stderr, "read returns 0?\n");
	    exit(1);
	}
	rtm = &m_rtmsg0.m_rtm;
	cp = m_rtmsg0.m_space;

	if (rtm->rtm_version != RTM_VERSION) {
	    fprintf(stderr, "bad version (%d)\n", rtm->rtm_version);
	    continue;
	}
	if (rtm->rtm_type != RTM_RESOLVE)
	    continue;
	if (rtm->rtm_msglen > cc)
		cc = rtm->rtm_msglen;
	bzero(&info, sizeof(info));
	info.rti_addrs = rtm->rtm_addrs;
	if ((info.rti_addrs & RTA_DST) == 0) {
	    fprintf(stderr, "no destination\n");
	    continue;
	}
	for (i = 0; i < RTAX_MAX; i++) {
	    if ((info.rti_addrs & (1 << i)) == 0)
		continue;
	    info.rti_info[i] = sa = (struct sockaddr *)cp;
	    ADVANCE(cp, sa);
	}
	if (cp > (char *)(&m_rtmsg0 + 1)) {
	    fprintf(stderr, "read overflow\n");
	    continue;
	}
	if (dst->sin_family != AF_INET) {
	    fprintf(stderr, "no IP destination\n");
	    continue;
	}

	printf("addr=%s\n", inet_ntoa(dst->sin_addr));
	for (match = map; match->ina.s_addr != 0; match++)
		if (dst->sin_addr.s_addr == match->ina.s_addr)
			break;

	bzero(&m_rtmsg, sizeof(m_rtmsg));
	rtm = &m_rtmsg.m_rtm;
	cp = m_rtmsg.m_space;

	rtm->rtm_version = RTM_VERSION;
	rtm->rtm_type = RTM_DELETE;
	rtm->rtm_flags = RTF_UP|RTF_HOST;
	rtm->rtm_addrs = RTA_DST;
	rtm->rtm_pid = getpid();
	rtm->rtm_seq = seq++;

	bcopy(dst, cp, dst->sin_len);
	ADVANCE(cp, (struct sockaddr *)dst);

	if (cp > (char *)(&m_rtmsg + 1)) {
	    fprintf(stderr, "write0 overflow\n");
	    continue;
	}
	rtm->rtm_msglen = cc = cp - (char *)rtm;

	if (write(s, (char *)&m_rtmsg, cc) < 0) {
	    perror("write0");
	    exit(1);
	}

	rtm->rtm_type = RTM_ADD;
	rtm->rtm_flags = RTF_UP|RTF_HOST|RTF_STATIC;
	if (match->ina.s_addr == 0)
	    rtm->rtm_flags |= RTF_REJECT;
	rtm->rtm_addrs |= RTA_GATEWAY;
	rtm->rtm_seq = seq++;

	sdl = (struct sockaddr_dl *)cp;
	sdl->sdl_len = sizeof(*sdl);
	sdl->sdl_family = AF_LINK;
	sdl->sdl_nlen = 3;
	bcopy("en0", sdl->sdl_data, 3);
	sdl->sdl_alen = sizeof(match->aph);
	bcopy(&match->aph, LLADDR(sdl), sizeof(match->aph));

	ADVANCE(cp, (struct sockaddr *)sdl);

	if (cp > (char *)(&m_rtmsg + 1)) {
	    fprintf(stderr, "write overflow\n");
	    continue;
	}
	rtm->rtm_msglen = cc = cp - (char *)rtm;

	if (write(s, (char *)&m_rtmsg, cc) < 0) {
	    perror("write");
	    exit(1);
	}
    }
}
