/*   
*     bootp.c
*/
#include "pcdefs.h"
#include "protocol.h"
#include "route.h"
#include "config.h"
#include "bootp.h"
#include "../defs.h"
#include "funcdef.h"

/* bootp routines - These routines are based on the stanford/clarkson
   bootp code. Originally developed at Stanford University.

   Bootp is a UDP based protocol that determines the clients IP address and
   gateway information etc.
*/

static u_long	xid = 0;
static int bootp_send(u_char *, u_int);
struct bpport bpstat;

extern struct icmp icmpout;
extern u_short nnipident;
extern u_char myether[DADDLEN];
extern struct route_st route[NROUTE];
extern int neterrno;
extern int pktclass, slip_mode;

/*
 *	bootp_send
 *   send some data out in a UDP packet
 */
static int
bootp_send(bpp, n)
u_char *bpp;
u_int	n;
{
    struct {
	    DLAYER d;
	    IPLAYER i;
	    UDPLAYER u;
	    struct bootp bootp;
    } outpkt;
    struct pseudotcp otcps;

/*
*  make sure that we have the right dlayer address
*/
    outpkt.d = icmpout.d;
    outpkt.i = icmpout.i;
    outpkt.i.protocol = PROTUDP;
    otcps.z = 0;
    otcps.proto = PROTUDP;
    memcpy(&otcps, &outpkt.i.ipsource, 2 * sizeof(u_long));
    outpkt.u.dest = htons(IPPORT_BOOTPS);
    outpkt.u.source = htons(IPPORT_BOOTPC);
    memcpy(&outpkt.bootp, bpp, n);
    n += sizeof(struct udph);
    otcps.tcplen = outpkt.u.length = htons(n);

/*
*  put in checksum
*/
    outpkt.u.check = 0;
    outpkt.u.check = tcpcheck(&otcps, (struct tcph _far *)&outpkt.u, n);

/*
*   iplayer for send
*/
    n += sizeof(struct iph);
    outpkt.i.tlen = htons(n);
    outpkt.i.ident = htons(nnipident);
    nnipident++;
    outpkt.i.check = 0;
    outpkt.i.check = ipcheck(&outpkt.i, sizeof(struct iph));
    n += sizeof (DLAYER);

    return pkxmit(&outpkt, n);
}

void
parse_bootp(bp)    /* parse an incoming bootp packet */
struct bootp *bp;
{
#ifdef notdef
	int     x,items,len;
	struct machinfo *sp;
	char	message[20], *cp;
	u_char	*c;
#endif
	int     gateway = 0;

	if (bp->bp_op != BOOTREPLY ||
	    memcmp(bp->bp_chaddr, myether, DADDLEN) || bp->bp_xid != xid) {
		return;
	}

	Scon.myip = bp->bp_yiaddr;	/* set my ip address */
#ifdef notyet
	nameserver = bp->bp_siaddr;
#endif

	set_netmask();
#ifdef notdef
	blankip.i.ipdest = Scon.broadcast;
#endif

	if (!memcmp(bp->bp_vend,VM_RFC1048,4)) {
dfputs("bootp: packet contains RFC1048 info\r\n");
#ifdef notdef
		printf("BootP: RFC1048 Style BootP Packet Received\n");
		c = bp->bp_vend + 4;
		while ((*c != 255) && ((c - bp->bp_vend) < 64)) {
			switch (*c) {
			case 0:        /* nop pad */
				c++;
				break;
			case 1:        /* subnet mask */
				len = *(c + 1);
				c += 2;
	/*
				memcpy(Scon.snetmask, c, 4);
	*/
				Scon.snetmask = *(uint32 *)c;
				printf("BootP: Subnet is %d.%d.%d.%d\n",
				   *c, *(c+1), *(c+2), *(c+3));
				c += len;
				break;
			case 2:        /* time offset */
				c += *(c + 1) + 2;
				break;

			case 3:       /* gateways      */
				len = *(c + 1);
				items = len / 4;
				c += 2;
				for (x = 0; x < items; x++) {
					sprintf(message, "%d.%d.%d.%d", *c,
					    *(c+1), *(c+2), *(c+3));
					if (!(sp = addmachinfo(message))) {
						printf("Out of Memory Adding Gateway - addmachinfo()\n");
						return(-1);
					}
					gateway++;
					printf("BootP: Add Gateway #%d IP %s\n",
					   gateway, sp->hname);
					sp->gateway = gateway;
					sp->hostip = *(uint32 *)c;
					sp->mstat = HFILE;
					c += 4;
				}
				break;
			case 4:        /* time servers */
			case 5:        /* IEN=116 name server */
				c += *(c + 1) + 2;
				break;
			case 6:        /* domain name server  */
				len = *(c + 1);
				items = len / 4;
				c += 2;
				for (x = 0; x < items; x++) {
					sprintf(message, "%d.%d.%d.%d", *c,
					    *(c+1), *(c+2), *(c+3));
					if (!(sp = addmachinfo(message))) {
						printf("Out of Memory Adding Nameserver - addmachinfo()\n");
						return(-1);
					}
					nameserver++;
					printf("BootP: Add Nameserver #%d IP %s\n",
					   nameserver, sp->hname);
					sp->nameserv = nameserver;
					sp->hostip = *(uint32 *)c;
					sp->mstat = HFILE;
					if (!Sns)
						Sns = sp;
					c += 4;
				}
				Scon.nstype = 1;
				break;
			case 7:        /* log server */
			case 8:        /* cookie server */
			case 9:        /* lpr server */
			case 10:       /* impress server */
			case 11:       /* rlp server */
				c += *(c + 1) + 2;
				break;
			case 12:       /* client host name    */
				len = *(c + 1);
				strncpy(message, c + 2, len);
				message[len] = 0;
				if (!(sp = addmachinfo(message))) {
					printf("Out of Memory Adding client name - addmachinfo()\n");
					return(-1);
				}
				printf("BootP: This Clients Name is %s\n",
				    sp->hname);
				if (!strlen(Scon.me)) 
					strncpy(Scon.me, sp->hname, 31);
				Scon.me[31] = 0;
				if (cp = strchr(sp->hname, '.')) {
					/* assume fully qualified name if a . is
					 * in hostname */
					if (!Scon.domainpath) {
						/* if no domain set yet */
						message[0] = 0;
						while (cp) {
							strcat(message, ",");
							strcat(message, cp + 1);
							cp = strchr(cp+1, '.');
						}
						Scon.domainpath =
						    malloc(strlen(message) + 1);
						strcpy(Scon.domainpath,message);
						printf("BootP: Setting Domainpath to (%s)\n",Scon.domainpath);
						removejunk(Scon.domainpath);
					}
				}
				c += len + 2;
				break;
			case 255:
				break;
			default:
				c += *(c + 1) + 2;
				break;                        
			}       /* end switch */
		}       /* end while    */
#endif
	}   /* end if comparen */

	if (!route[0].real) {
		if (bp->bp_giaddr)
			setgate(0, bp->bp_giaddr);
		else
			setgate(0, bp->bp_siaddr);
        }
	bpstat.newdata = 1;		/* tell them we got it */
}

/* main processing of bootp lookup request
   calls sendbootp to send a bootp request,
   sets up the udp listen port etc.
   handles retries
*/
int
bootp_process()
{
    int	i;
    struct bootp bootp_data;
    u_long	end_time;

    memset(&bootp_data, 0, sizeof(struct bootp));
    bootp_data.bp_op = BOOTREQUEST;

    bootp_data.bp_htype = (u_char)pktclass;
    if (!slip_mode) {
	bootp_data.bp_hlen = DADDLEN;
	memcpy(bootp_data.bp_chaddr, myether, DADDLEN);
    }

    xid = n_clicks();	/* get a unique transaction ID */
    bootp_data.bp_xid = xid;
    bootp_data.bp_secs = htons(1);
    bpstat.listen = htons(IPPORT_BOOTPC);
    bpstat.newdata = 0;		/* clear any current data */
    for (i = 0; i < BOOTP_RETRIES; i++) {
	if (bootp_send((u_char *)&bootp_data, sizeof(struct bootp)) < 0) {
		neterrno = NETERR_SYSTEM;
		return -1;
	}
	end_time = n_clicks() + 18L;
	while (n_clicks() < end_time) {
		netsleep();
		if (bpstat.newdata)
			return 0;
	}
    }
    neterrno = NETERR_TIMEOUT;
    return -1;
}

void
set_netmask()
{
    if (!Scon.snetmask)
	switch ((u_char)Scon.myip >> 6) {
	    case 0:			/* class A */
	    case 1:
		Scon.snetmask = 0x000000ffL;
		break;
	    case 2:			/* class B */
		Scon.snetmask = 0x0000ffffL;
		break;
	    case 3:			/* class C */
		Scon.snetmask = 0x00ffffffL;
	}
    if (!Scon.broadcast) {
	    Scon.broadcast = Scon.myip | ~Scon.snetmask;
    }
}
