/* $Header: netconfig.c,v 1.2 87/11/03 13:58:10 mar Exp $
 *
 * Network Configuration Program
 *
 * Uses NIP to pick an internet address, gateways, etc, then sets them.
 *
 * by Mark Rosenstein, July 1987
 */

#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/file.h>
#include <sys/socket.h>
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <sys/ioctl.h>
#include <net/nit.h>
#include <net/nip.h>
#include <netdb.h>
#include <arpa/nameser.h>
#include <resolv.h>
#include "netconfig.h"


int	debug = 1;
extern char *getenv();


main(argc, argv, envp)
int	argc;
char	**argv;
char	**envp;
{
    struct nipinfo *nip, *fil;
    struct sockaddr_nit snit;
    struct sockaddr_in sin, *sinp;
    int	s, sockin, count;
    char	*ifc;
    struct nipinfo *parsefile();
    u_char	*pa, *physaddr();
    struct ifreq ifr;
    struct rtentry route;
    char	name[64], *str;
    struct hostent *he;
    u_long	addr;
    int	test = 0;

    if (argc < 2) {
	fprintf(stderr, "usage: %s netname [-t] [debug level]\n", argv[0]);
	exit(1);
    }
    if ((argc > 2) && !strcmp(argv[2], "-t")) {
	test++;
	debug = 1;
	if (argc == 4) {
	    argv[2] = argv[3];
	    argc--;
	}
    }
    if (argc > 2)
      debug = atoi(argv[2]);

    ifc = argv[1];

    if ((pa = physaddr(ifc)) == 0) {
	fprintf(stderr, "%s: Interface %s not found\n", argv[0], ifc);
	exit(1);
    } else if (debug) {
	printf("%s physical address ", ifc);
	ether_print(pa);
    }

    if (!test) {
	/* bring up interface */
	if ((sockin = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
	    perror("socket");
	    exit(1);
	}
	strncpy(ifr.ifr_name, ifc, sizeof(ifr.ifr_name));
	if (ioctl(sockin, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
	    perror("ioctl (SIOCGIFFLAGS)");
	    exit(1);
	}
	ifr.ifr_flags |= IFF_UP | IFF_NOTRAILERS;
	ifr.ifr_flags &= ~(IFF_NOARP);
	if (ioctl(sockin, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
	    perror("ioctl (SIOCSIFFLAGS)");
	    exit(1);
	}
	sin.sin_family = AF_INET; 
	sin.sin_addr.s_addr = 0L;
	ifr.ifr_addr = *(struct sockaddr *)&sin;
	if (ioctl(sockin, SIOCSIFADDR, (caddr_t)&ifr) < 0)
	  perror("ioctl (SIOCSIFADDR)");
    }

    /* open and configure socket */
    if ((s = socket(AF_NIT, SOCK_RAW, 0)) < 0) {
	perror("socket");
	exit(1);
    }
    snit.snit_family = AF_NIT;
    strncpy(snit.snit_ifname, ifc, NITIFSIZ);
    if (bind(s, &snit, sizeof(snit)) < 0) {
	perror("bind");
	exit(1);
    }

    /* use NIP to get what info you can */
    if ((nip = getnip(s, pa)) == NULL && debug) {
	fprintf(stderr, "%s: can't get NIP info\n", argv[0]);
	nip = (struct nipinfo *) malloc(sizeof(struct nipinfo));
	bzero(nip, sizeof(struct nipinfo));
    } else {
	if (debug) {
	    printf("got %s NIP response:\n",
		   (nip->ni_flags & NI_RECOMMEND)?"authoratative":"a");
	    printf("Net Address\t%s\n", inet_ntoa(nip->ni_netaddr));
	    printf("Mask\t\t%s\n", inet_ntoa(nip->ni_netmask));
	    printf("Broadcast\t%s\n", inet_ntoa(nip->ni_broadcast));
	    printf("Lowest\t\t%s\n", inet_ntoa(nip->ni_lowest));
	    printf("Highest\t\t%s\n", inet_ntoa(nip->ni_highest));
	    printf("Recommend\t%s\n", inet_ntoa(nip->ni_recommend));
	    printf("Gateway\t\t%s\n", inet_ntoa(nip->ni_gateway));
	}
    }

    if (nip->ni_flags & NI_RECOMMEND)
      if (verify(s, nip, pa) == OK) 
	goto gotit;

    /* handle address set in rc.conf */
    if ((str = getenv("ADDR")) && (~nip->ni_flags & NI_RECOMMEND)) {
	nip->ni_recommend = inet_addr(str);
	if (nip->ni_recommend != 0)
	  nip->ni_flags |= NI_RECOMMEND;
    }

    /* look for our own config file */
    if ((fil = parsefile(CONFIGFILE)) != NULL) {
#define compress(NI_FLAG, ni_field) if (fil->ni_flags & NI_FLAG && ~nip->ni_flags & NI_FLAG) { nip->ni_field = fil->ni_field; nip->ni_flags |= NI_FLAG; }
	compress(NI_NETADDR, ni_netaddr);
	compress(NI_NETMASK, ni_netmask);
	compress(NI_BROADCAST, ni_broadcast);
	compress(NI_LOWEST, ni_lowest);
	compress(NI_HIGHEST, ni_highest);
	compress(NI_RECOMMEND, ni_recommend);
	compress(NI_GATEWAY, ni_gateway);
#undef compress
    }

    if ((nip->ni_flags & NI_RECOMMEND) && (verify(s, nip, pa) == OK))
      goto gotit;

    if (debug)
      printf("no info, trying hash function\n");

 lostit:
    for (count = 0; count < nip->ni_highest - nip->ni_lowest; count++) {
	nip->ni_recommend = hash(pa, nip, count);
	if (verify(s, nip, pa) == OK)
	  goto gotit;
    }

    fprintf(stderr, "Unable to find an address to use\n");
    exit(2);

 gotit:
    if (debug) {
	printf("Chose parameters:\n");
	printf("\tAddress   %s\n", inet_ntoa(nip->ni_recommend));
	printf("\tMask      %s\n", inet_ntoa(nip->ni_netmask));
	printf("\tBroadcast %s\n", inet_ntoa(nip->ni_broadcast));
	printf("\tGateway   %s\n", inet_ntoa(nip->ni_gateway));
    }

    if (!test) {
	sin.sin_family = AF_INET; 
	sin.sin_addr.s_addr = nip->ni_netmask;
	ifr.ifr_addr = *(struct sockaddr *)&sin;
	if (ioctl(sockin, SIOCSIFNETMASK, (caddr_t)&ifr) < 0)
	  perror("ioctl (SIOCSIFNETMASK)");
	sin.sin_addr.s_addr = nip->ni_recommend;
	ifr.ifr_addr = *(struct sockaddr *)&sin;
	if (ioctl(sockin, SIOCSIFADDR, (caddr_t)&ifr) < 0)
	  perror("ioctl (SIOCSIFADDR)");
	sin.sin_addr.s_addr = nip->ni_broadcast;
	ifr.ifr_addr = *(struct sockaddr *)&sin;
	if (ioctl(sockin, SIOCSIFBRDADDR, (caddr_t)&ifr) < 0)
	  perror("ioctl (SIOCSIFBRDADDR)");
    }
    fprintf(stderr, "Interface %s on with address %s, mask %s, broadcast %s\n",
	   ifc, inet_ntoa(nip->ni_recommend), inet_ntoa(nip->ni_netmask),
	   inet_ntoa(nip->ni_broadcast));

    if (verify(s, nip, pa) == FAIL)
      goto lostit;

    if (!test) {
	route.rt_dst.sa_family = AF_INET;
	bzero(route.rt_dst.sa_data, sizeof(route.rt_dst.sa_data));
	route.rt_gateway.sa_family = AF_INET;
	sinp = (struct sockaddr_in *) &route.rt_gateway;
	sinp->sin_family = AF_INET;
	sinp->sin_addr.s_addr = nip->ni_gateway;
	route.rt_flags = RTF_GATEWAY | RTF_UP;
	if (ioctl(sockin, SIOCADDRT, &route) < 0)
	  perror("ioctl (SIOCADDRT)");
	else
	  fprintf(stderr, "Default gateway set to %s.\n", inet_ntoa(nip->ni_gateway));
    }

    res_init();
    _res.options = RES_INIT | RES_RECURSE | RES_DEFNAMES;
    _res.nscount = 1;
    _res.nsaddr.sin_family = AF_INET;
    _res.nsaddr.sin_addr.s_addr = inet_addr(NAMESERVER_ADDR);

    name[0] = 0;
    addr = nip->ni_recommend;
    he = gethostbyaddr(&addr, sizeof(addr), AF_INET);
    if (he == 0) {

	if ((str = getenv("HOST")) && !strcmp(str, "NOHOST")) {
	    strcpy(name, str);
	    goto newname;
	}

	if (debug)
	  printf("deriving name...\n");
	addr = nip->ni_gateway;
	he = gethostbyaddr(&addr, sizeof(addr), AF_INET);
	if (he != 0) {
	    char tmp[8];

	    if (debug)
	      printf("gateway name %s\n", he->h_name);
	    strcpy(name, he->h_name);
	    str = index(name, '.');
	    if (str)
	      *str = 0;
	    if (!strcmp(&name[strlen(name) - 2], "GW"))
	      name[strlen(name) - 2] = 0;
	    if (name[strlen(name) - 1] != '-')
	      strcat(name, "-");
	    sprintf(tmp, "%d", nip->ni_recommend & ~nip->ni_netmask);
	    strcat(name, tmp);
	    /* Now register hostname */
newname:
	    fprintf(stderr, "Can\'t register new hostnames yet...\n");
	} else {
	    if (debug)
	      printf("Hostname not found\n");
	}
    } else {
	strcpy(name, he->h_name);
	str = index(name, '.');
	if (str)
	  *str = 0;
    }
    fprintf(stderr, "Hostname: %s\n", name);
    if (!test)
      sethostname(name, strlen(name));

    writefile(CONFIGFILE, nip);
    enablenip(s, nip);
    printf("%s\t%s\n", inet_ntoa(nip->ni_recommend), name);
}


hash(pa, ni, x)
u_char	*pa;
struct nipinfo *ni;
int	x;
{
    u_long tmp;

    tmp = pa[3] + x * pa[4] + pa[5];
    if (pa[4] == 0)
      tmp += x;
    tmp %= ni->ni_highest - ni->ni_lowest;
    return(ni->ni_lowest + tmp);
}
