/*   
*     bootp.c
*/
#include "pcdefs.h"
#include "mbuf.h"
#include "protocol.h"
#include "route.h"
#include "data.h"
#include "config.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.
*/
#include "bootp.h"
static u_long	xid = 0;
static int _near bootp_send(u_char _far *, u_int);

/*
 *	bootp_send
 *   send some data out in a UDP packet
 */
static int _near
bootp_send(bpp, n)
u_char _far *bpp;
u_int	n;
{

/*
*  make sure that we have the right dlayer address
*/
	udpout.i.protocol = PROTUDP;
	otcps.proto = PROTUDP;
	(void) memcpy((u_char _near *)&otcps,
	    (u_char _near *)&udpout.i.ipsource, 2 * sizeof(u_long));
	udpout.u.dest = htons(IPPORT_BOOTPS);
	udpout.u.source = htons(IPPORT_BOOTPC);
	(void) memcpy((u_char _far *)udpout.data, (u_char _far *)bpp, n);
	n += sizeof(struct udph);
	otcps.tcplen = udpout.u.length = htons(n);

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

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

	return(pkxmit((u_char _far *)&udpout, n + sizeof(struct ether)));
}

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

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

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

	/* Gross hack follows.... */
	switch ((u_char)Scon.myip >> 6) {
	    case 0:			/* class A */
	    case 1:
		nmask = 0x000000ffL;
		break;
	    case 2:			/* class B */
		nmask = 0x0000ffffL;
		break;
	    case 3:			/* class C */
		nmask = 0x00ffffffL;
	}
	if (Scon.snetmask)
		ipall = Scon.myip | ~Scon.snetmask;
	else
		ipall = Scon.myip | ~nmask;
	blankip.i.ipdest = ipall;

	if (!memcmp(bp->bp_vend,VM_RFC1048,4)) {
dfputs("we're in trouble\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)
			route[0].real = bp->bp_giaddr;
		else
			route[0].real = 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 _near
bootp()
{
    int	i;
    struct bootp bootp;
    u_long	end_time;

    (void) memset((u_char _far *)&bootp, 0, sizeof(struct bootp));
    bootp.bp_op = BOOTREQUEST;
    /* hardware type 1 is ethernet. This should be made more robust. */
    bootp.bp_htype = 1;
    bootp.bp_hlen = DADDLEN;
    (void) memcpy((u_char _far *)bootp.bp_chaddr, (u_char _far *)myether,
	DADDLEN);
    xid = n_clicks();	/* get a unique transaction ID */
    bootp.bp_xid = xid;
    bootp.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 _far *)&bootp, sizeof(struct bootp)) < 0)
		return -1;
	end_time = n_clicks() + 18L;
	while (n_clicks() < end_time) {
		netsleep();
		if (bpstat.newdata)
			return 0;
	}
    }
    return -1;
}
