/* File MSNBTP.C
 * Bootp requestor
 *
 * Copyright (C) 1991, University of Waterloo.
 *	Copyright (C) 1982, 1995, Trustees of Columbia University in the 
 *	City of New York.  The MS-DOS Kermit software may not be, in whole 
 *	or in part, licensed or sold for profit as a software product itself,
 *	nor may it be included in or distributed with commercial products
 *	or otherwise distributed by commercial concerns to their clients 
 *	or customers without written permission of the Office of Kermit 
 *	Development and Distribution, Columbia University.  This copyright 
 *	notice must not be removed, altered, or obscured.
 *
 * Original version created by Erick Engelke of the University of
 *  Waterloo, Waterloo, Ontario, Canada.
 * Adapted and modified for MS-DOS Kermit by Joe R. Doupnik, 
 *  Utah State University, jrd@cc.usu.edu, jrd@usu.Bitnet.
 *
 * Last edit
 * 12 Jan 1995 v3.14
 *
 *   BOOTP - Boot Protocol, RFCs 951, 1048, and 1395
 *
 */

#include "msntcp.h"
#include "msnlib.h"

/*
 * structure for send and receives
 */
typedef struct bootp {
	byte	 bp_op;		/* packet op code / message type. */
	byte	 bp_htype;	/* hardware address type, 1 = 10 mb ethernet */
	byte	 bp_hlen;	/* hardware address len, eg '6' for 10mb eth */
	byte	 bp_hops;	/* client sets to zero, optionally used by
				   gateways in cross-gateway booting. */
	longword bp_xid;	/* transaction ID, a random number */
	word	 bp_secs;	/* filled in by client, seconds elapsed since
				   client started trying to boot. */
	word	 bp_spare;
	longword bp_ciaddr;	/* client IP address filled in by client */
				/*  if known*/
	longword bp_yiaddr;	/* 'your' (client) IP address
				   filled by server if client doesn't know */
	longword bp_siaddr;	/* server IP address returned in bootreply */
	longword bp_giaddr;	/* gateway IP address,
				   used in optional cross-gateway booting. */
	byte	 bp_chaddr[16];	/* client hardware address, filled by client */
	byte	 bp_sname[64];	/* optional server host name, null terminated*/

	byte	 bp_file[128];	/* boot file name, null terminated string
				   'generic' name or null in bootrequest,
				   fully qualified directory-path
				   name in bootreply. */
	byte	 bp_vend[64];	/* optional vendor-specific area */
};

/* bootp.bp_op */
#define BOOTREQUEST 	1
#define BOOTREPLY	2

/* UDP port numbers, server and client */
#define	IPPORT_BOOTPS	67
#define	IPPORT_BOOTPC	68


/******** the following is stolen from NCSA which came from CUTCP *********/
/* I have not implemented these, but someone may wish to in the future so */
/* I kept them around.                                                    */
/**************************************************************************/

/*
 * "vendor" data permitted for Stanford boot clients.
 */
struct vend {
	byte 	 v_magic[4];	/* magic number */
	longword v_flags;	/* flags/opcodes, etc. */
	byte 	 v_unused[56];	/* currently unused */
};

#define	VM_STANFORD	"STAN"		/* v_magic for Stanford */
#define VM_RFC1048      "\x63\x82\x53\x63" /* magic cookie for BOOTP */
					/* high byte listed first */
/* v_flags values */
#define	VF_PCBOOT	1	/* an IBMPC or Mac wants environment info */
#define	VF_HELP		2		/* help me, I'm not registered */
#define TAG_BOOTFILE_SIZE       13     /* tag used by vend fields rfc 1048 */

/* global variables */
longword bootphost = 0xffffffffL;	/* broadcast IP */
word bootptimeout = 30;			/* seconds timeout */
extern word arp_hardware, MAC_len;	/* media details from msnpdi.asm */
extern byte kdomain[];			/* our domain */
extern byte kbtpserver[];		/* IP of responding Bootp server */
extern	eth_address eth_addr;		/* six byte array */

/*
 * do_bootp - Checks global variables bootptimeout, bootphost
 *             if no host specified, the broadcast address
 *             returns 0 on success and sets ip address
 */

int 
do_bootp(void)
{
	udp_Socket bsock;
	longword sendtimeout, bootptmo;
	word magictimeout, len;
	struct bootp sendbootp;				/* outgoing data */
	struct bootp bootp;				/* incoming data */
	struct bootp register * sbp, * rbp;
	longword xid;
	byte *p, *q;
	extern byte * hostname;

	/* If we are running SLIP or ODI's SLIP_PPP which do not use
	MAC level addresses use zeros for arp_hardware and MAC_len,
	non-standard but perhaps useful to some cisco terminal servers */

	sbp = &sendbootp;			/* registered addresses */
	rbp = &bootp;				/* incoming response */
	memset((byte *)sbp, 0, sizeof(struct bootp));
	memset((byte *)rbp, 0, sizeof(struct bootp));

	outs("\r\n Requesting a Bootp server ");
	bcopy(&eth_addr[2], &xid, sizeof(xid)); /* unique value from board */
	my_ip_addr = 0;		/* init our IP address to unknown */

	if (udp_open(&bsock, IPPORT_BOOTPC, bootphost, IPPORT_BOOTPS) == 0)
		{
        	outs("\n\r Unable to find a bootp server");
        	return (-1);
		}

	bootptmo = set_timeout(bootptimeout);
	magictimeout = (word)((xid & 7) + 7); 	/* between 7 and 14 seconds */

	sbp->bp_op = BOOTREQUEST;
	sbp->bp_htype = (byte)(arp_hardware & 0xff);
	    	/* Copy into position the Magic Number used by Bootp */
	bcopy(VM_RFC1048, sbp->bp_vend, 4);		/* high byte first */

	sbp->bp_hlen = (byte) MAC_len;		/* length of MAC address */
	sbp->bp_xid = xid;
	sbp->bp_secs = htons(1);
	bcopy(eth_addr, sbp->bp_chaddr, MAC_len);
	kbtpserver[0] = 0;

    while (1 == 1) {
	sock_write(&bsock, (byte *)sbp, sizeof(struct bootp));
        sbp->bp_secs += magictimeout;			/* for next time */
        sendtimeout = set_timeout(magictimeout += (xid > 5) & 7);
	outs(".");				/* progress tics */
        while (chk_timeout(sendtimeout) != TIMED_OUT)
		{
		if ((chk_timeout(bootptmo) == TIMED_OUT) ||
			(chkcon() != 0))		/* ^C sensor */
	    		{
			outs(" Timed out, sorry");
			sock_close(&bsock);
			while (chkcon()) ;		/* soak up ^C's */
			return (-1);			/* fail */
			}

    		if (tcp_tick(&bsock) == 0) /* major network error if UDP fails */
			{
			outs(" Network troubles, quitting");
			sock_close(&bsock);
			return (-1);			/* fail */
			}

		if (sock_dataready(&bsock) == 0)
	    		continue;			/* no data */

 		               /* got a response, lets consider it */
		if ((sizeof(struct bootp) > sock_fastread(&bsock, (byte *)rbp,
			sizeof(struct bootp))) || (rbp->bp_xid != sbp->bp_xid))
			{
			bcopy((byte *)rbp, 0, sizeof(struct bootp)); /*clear*/
                	continue;    /* too small for bootp pkt, not for us */
			}

		if (*(long *)rbp->bp_vend != *(long *)sbp->bp_vend)
				continue;  /* magic cookies do not match */

		my_ip_addr = ntohl(rbp->bp_yiaddr);
		    		/* RFC1048 compliant BOOTP vendor field */
		p = &rbp->bp_vend[4]; /* Point just after vendor field */
		q = p + 60;	/* q = end of all possible vendor data */

		while (*p != 255 && (q - p) > 0)
			switch(*p)
			  {
                          case 0: /* Nop Pad character */
                                 p++;
                                 break;
                          case 1: /* Subnet Mask */
				 sin_mask = ntohl(*(longword *)(&p[2]));
				 p += *(p+1) + 2;
				 break;
			  case 3: /* gateways */
				for (len = 0; len < *(p+1); len += 4)
				  arp_add_gateway(NULL,
				     ntohl(*(longword*)(&p[2 + len])));
				p += *(p+1) + 2;
                                break;
			  case 6: /* Domain Name Servers (BIND) */
				for (len = 0; len < *(p+1); len += 4)
				    add_server(&last_nameserver,
					MAX_NAMESERVERS, def_nameservers,
				     ntohl(*(longword*)(&p[2 + len])));
				p += *(p+1) + 2;
				break;
#ifndef KERMIT
			  case 8: /* cookie server */
				for (len = 0; len < *(p+1); len += 4)
					add_server(&last_cookie, MAX_COOKIES,
					cookie,ntohl(*(longword*)(&p[2+len])));
				p += *(p+1) + 2;
				break;
#endif /* KERMIT */
			  case 12: /* our hostname, hopefully complete */
				bcopyff(p+2, hostname, (int)(p[1] & 0xff));
				hostname[(int)(p[1] & 0xff)] = '\0';
				p += *(p+1) + 2;
				break;
			  case 15: /* RFC-1395, Domain Name tag */
			  	bcopyff(p+2, kdomain, (int)(p[1] & 0xff));
				kdomain[(int)(p[1] & 0xff)] = '\0';
				p += *(p+1) + 2;
				break;
                          case 255:
				break;
			  case 2: /* Time offset */
			  case 4: /* time servers */
                          case 5: /* IEN=116 name server */
			  case 7: /* log server */
                          case 9: /* lpr server */
                          case 10: /* impress server */
                          case 11: /* rlp server */
                          default:
			  	p += *(p+1) + 2;
				break;
                          } 		/* end of switch */
		ntoa(kbtpserver, ntohl(rbp->bp_siaddr));
					/* server IP to dotted decimal */
		sock_close(&bsock);
    		return (my_ip_addr != 0? 0: -1); /* -1 for fail, 0 for succ */
	        }			/* while (chk_timeout...*/
    	      }				/* while (1 == 1)	*/
}
