/* Code to implement a bootp client.  It discovers the client's own
 * address, the address of the server to boot from, and the name of
 * the file to boot. */

/*
 *------------------------------------------------------------------
 *
 * $Source: /mit/cgw/src/tftp.vax/RCS/bootp.c,v $
 * $Revision: 1.3 $
 * $Date: 88/07/11 19:07:16 $
 * $State: Exp $
 * $Author: jon $
 * $Locker: jon $
 *
 * $Log:	bootp.c,v $
 * Revision 1.3  88/07/11  19:07:16  jon
 * Sommerfeld's changes.
 * 
 * Revision 1.2  88/06/09  12:43:15  wesommer
 * Hacked by jon.
 * 
 * 
 *------------------------------------------------------------------
 */

#ifndef lint
static char *rcsid_ether_c = "$Header: bootp.c,v 1.3 88/07/11 19:07:16 jon Locked $";
#endif	lint

#ifdef ETHER
#include "ether.h"
#else
#include "vii.h"
#endif

#define IP_BRD  0x00000000	/* perhaps 255.255.255.255 at some point? */
/*#define IP_BRD  0xf1004812	/* Crock!!! (18.72.0.241) */
/*#define IP_BRD  0x00004412	/* Crock!!! (18.68.0.0) */
/*#define IP_BRD  0x00000a12	/* Crock!!! (18.10.0.0) */

#define UDPPROT 17

#define BOOTP_SERVER_PORT 0x4300
#define BOOTP_CLIENT_PORT 0x4400

#define CHADDR_LEN 16
#define SNAME_LEN 64
#define FILE_LEN 128
#define VEND_LEN 64

#define BOOTREQUEST 1
#define BOOTREPLY 2

struct bootp_pkt {
    ln_hdr_type ln_hdr;  	/* local net header */
    char ip_vhl;	        /* Internet header length in 32 bit words */
    char ip_tsrv;		/* Type of service */
    short ip_len;		/* Total packet length including header */
    short ip_id;		/* ID for fragmentation */
    short ip_flgs;		/* flags and fragment offset */
    char ip_time;		/* Time to live (secs) */
    char ip_prot;		/* protocol */
    short ip_chksum;		/* Header checksum */
    long ip_src;		/* Source name */
    long  ip_dst;		/* Destination name */
    short ud_srcp;		/* source port */
    short ud_dstp;		/* dest port */
    short ud_len;		/* length of UDP packet */
    short ud_cksum;		/* UDP checksum */
    char bp_op;			/* opcode */
    char bp_htype;		/* hardware type */
    char bp_hlen;		/* hardware address length */
    char bp_hops;		/* hop count for cross-gateway booting */
    long bp_xid;		/* transaction ID */
    short bp_secs;		/* seconds since client started booting */
    short bp_unused;
    long bp_ciaddr;		/* client IP address */
    long bp_yiaddr;		/* 'your' (client) IP address */
    long bp_siaddr;		/* server IP address */
    long bp_giaddr;		/* gateway IP address */
    char bp_chaddr[CHADDR_LEN];	/* client hardware address */
    char bp_sname[SNAME_LEN];	/* server host name */
    char bp_file[FILE_LEN];	/* file name */
    char bp_vend[VEND_LEN];	/* vendor specific */
};

struct bootp_pkt pkt;

do_bootp(gen_file, mladdr, miaddr, siaddr, giaddr, file)
char *gen_file;
unsigned char *mladdr;
long *miaddr, *siaddr, *giaddr;
char **file;
{
    register int i;

    puts("bootp ");
    for (;;) {
	    bzero (&pkt, sizeof(pkt));
	    pkt.ip_vhl = 0x45;
	    pkt.ip_tsrv = 0;
	    pkt.ip_len = swab(sizeof(pkt) - sizeof(ln_hdr_type));
	    pkt.ip_id = 0x42;
	    pkt.ip_flgs = 0;
	    pkt.ip_time = 0x30;
	    pkt.ip_prot = UDPPROT;
	    pkt.ip_src = 0;
	    pkt.ip_dst = IP_BRD;
	    pkt.ip_chksum = 0;
	    pkt.ip_chksum = ~cksum(&pkt.ip_vhl, 10, 0);
	    pkt.ud_srcp = BOOTP_CLIENT_PORT;
	    pkt.ud_dstp = BOOTP_SERVER_PORT;
	    pkt.ud_len = swab(swab(pkt.ip_len) - 20);
	    pkt.ud_cksum = 0;
	    pkt.bp_op = BOOTREQUEST;
#ifdef ETHER
	    pkt.bp_htype = ETHER_HTYPE_BYTE;
	    pkt.bp_hlen = ETHER_HLEN;
#else
	    pkt.bp_htype = VII_HTYPE;
	    pkt.bp_hlen = VII_HLEN;
#endif
	    pkt.bp_hops = 0;
	    pkt.bp_xid = 1243;	/* should be the SID of the processor */
	    pkt.bp_secs = 1;	/* should put some pseudo time here */
	    pkt.bp_ciaddr = 0;
	    pkt.bp_yiaddr = 0;
	    pkt.bp_siaddr = 0;
	    pkt.bp_giaddr = 0;
#ifdef ETHER
	    etadcpy (mladdr, pkt.bp_chaddr);
#else
	    pkt.bp_chaddr[0] = *mladdr;
	    pkt.bp_chaddr[1] = '\0';
#endif
	    pkt.bp_sname[0] = '\0';
	    printf("gen_file = %s\n", gen_file);
	    strcpy(pkt.bp_file, gen_file);
	    pkt.bp_vend[0] = '\0';

	    puts(".");
	    net_write(&pkt, -1, sizeof(pkt) - sizeof(ln_hdr_type));
	
	    for (i = 1000; i > 0; i--) {
		    if (net_read(&pkt, sizeof(pkt)) == 0)
			    continue;
#ifdef DEBUG
		    dump_ether_header(pkt.ln_hdr.et_dst);
#endif
		    if (pkt.ip_vhl != 0x45) continue;
		    if (~cksum(&pkt.ip_vhl, 10, 0) != 0) continue;
		    if (pkt.ip_prot != UDPPROT) continue;
		    if (pkt.ud_dstp != BOOTP_CLIENT_PORT) continue;
		    if (pkt.bp_op != BOOTREPLY) continue;
#ifdef DEBUG
		    puts("\nciaddr: "); phex(pkt.bp_ciaddr);
		    puts("\nyiaddr: "); phex(pkt.bp_yiaddr);
		    puts("\nsiaddr: "); phex(pkt.bp_siaddr);
		    puts("\ngiaddr: "); phex(pkt.bp_giaddr);
		    puts("\nfile: "); puts(pkt.bp_file);
#endif
		    puts("\n");
		    if (*miaddr == 0)
			    *miaddr = pkt.bp_yiaddr;
		    *siaddr = pkt.bp_siaddr;
		    *giaddr = pkt.bp_giaddr;
		    *file = pkt.bp_file;
		    return;
	    }
    }
}
