/* To be on the safe side set memory model to huge in the compiler 
   options of Turbo C IDE! (Why?)
*/
/* A simple program that goes on reading character from the keyboard
   (till Ctrl-C is pressed, when it exits) and sends that character
   in the 15th byte of an ethernet packet to a fixed destination
   machine. This program received all packets and prints
   the 15th byte of all the non-broadcast packets as a character

   The ethernet address of the destination machine is 0x0800274080F1
   The ethernet address of this machine where this program runs is 0x08002790E766
*/

/* To test this program one needs 2 copies of this program where in the 2nd copy
   interchange the destination and source address in the send_packet buffer array.
   In that case these 2 pograms will just transfer keypress to the other
   and print the received characters
   */

/* The following header files are included as per the manual entries
   (in Turbo C Reference Guide) for the functions used in this file */
#include <stdio.h>
#include <dos.h>
#include <string.h>
#include <conio.h>


/* Prototype declaration for the wrapper function for "calling" packet driver functions */
/* These functions are defined later  in this file */
static int driver_info (int intno,int handle,int *version, int *class,int *type,int *number,int *basic);
static int access_type (int intno,int if_class,int if_type,int if_number, char *type, unsigned typelen, void interrupt (*receiver) (void) );
static int release_type (int intno,int handle);
static int send_pkt (int intno, char *buffer, unsigned length);

/* prototype declarations for the helper functions defined later */
int isPdInstalled(); /* checks whether a packet driver is installed against some software interrupt */

/* Read about "void interrupt" type from Turbo C Reference Guide */
void interrupt receiver (unsigned bp, unsigned di, unsigned si,
                     unsigned ds, unsigned es, unsigned dx,
                     unsigned cx, unsigned bx, unsigned ax,
                     unsigned ip, unsigned cs, unsigned flags ); /* The receiver function called by the packet driver */
void handle_packet(unsigned char packet[]); /* the recever function calls this for handling the received packet */
int isBroadCastPacket(unsigned char packet[]); /* Checks whether the packet is an ethernet broadcast packet - 
						  destination address 0xffffff */

/* Global data variables */
/* Send packet has 1st 6 bytes to store the destination ethernet address and next 6 bytes to store this machine's 
   ethernet address */
unsigned char send_packet[64] = {0x08,0x00,0x27,0x40, 0x80, 0xF1,     0x08, 0x00, 0x27, 0x90, 0xE7, 0x66}; /* used in main() */
/* The following is for the other machine - destination and source ethernet addresses are interchanged */
/* unsigned char send_packet[64] = {0x08, 0x00, 0x27, 0x90, 0xE7, 0x66,    0x08,0x00,0x27,0x40, 0x80, 0xF1}; */
unsigned char receive_packet[1522]; /* receiver() uses this for storing the received packet */


static int Derr; /* For storing the error code returned by the packet driver functions */


/* definition of wrapper functions for invoking packet driver functions */
static int access_type(int intno, int if_class, int if_type, int if_number, char *type, unsigned typelen, void interrupt (*receiver)()) {
	union REGS regs;
	struct SREGS sregs;

	/* void segread(struct SREGS *segp);
	   Prototype in dos.h
           Remarks segread places the current values of the segment
	   registers into the structure pointed to by segp. */
	segread(&sregs); /* Read Turbo C Reference Guide */
	regs.h.dl = if_number;		/* Number */
	sregs.ds = FP_SEG(type);	/* Segment address of packet-type template! Read Turbo C reference Guide for FP_SEG */
	regs.x.si = FP_OFF(type);	/* Segment address of packet-type template! Read Turbo C reference Guide for FP_SEG */
	regs.x.cx = typelen;		/* Length of type */
	sregs.es = FP_SEG(receiver);	/* Address of receive handler */
	regs.x.di = FP_OFF(receiver);
	regs.x.bx = if_type;		/* Type */
	regs.h.ah = 2; /* pds111.txt says this is for ACCESS_TYPE function  - access_type() */
	regs.h.al = if_class;		/* Class */
	int86x(intno,&regs,&regs,&sregs); /* int86x() for invoking software interrup - read Turbo C Reference Guide */
	if(regs.x.cflag){
		Derr = regs.h.dh;
		return -1;
	} else
		return regs.x.ax;
}

static int release_type(int intno, int handle) {
	union REGS regs;

	regs.x.bx = handle;
	regs.h.ah = 3;  /* pds111.txt says this is for RELEASE_TYPE function -  release_type() */
	int86(intno,&regs,&regs); /* int86() for invoking software interrup - read Turbo C Reference Guide */
	if(regs.x.cflag){
		Derr = regs.h.dh;
		return -1;
	} else
		return 0;
}

static int send_pkt(int intno, char *buffer, unsigned length) {
	union REGS regs;
	struct SREGS sregs;

	segread(&sregs);
	sregs.ds = FP_SEG(buffer);
	sregs.es = FP_SEG(buffer); /* for buggy univation pkt driver - CDY */
	regs.x.si = FP_OFF(buffer);
	regs.x.cx = length;
	regs.h.ah = 4; /* pds111.txt says this is for SEND_PKT function send_pkt() */
	int86x(intno,&regs,&regs,&sregs); /* int86x() for invoking software interrup - read Turbo C Reference Guide */
	if(regs.x.cflag){
		Derr = regs.h.dh;
		return -1;
	} else
		return 0;
}

/* The following function - receiver() - is registered with the packet driver 
   by call to access_type() in main() */
/* Read Turbo C reference gude for "void interrupt type function */
void interrupt receiver (unsigned bp, unsigned di, unsigned si,
                     unsigned ds, unsigned es, unsigned dx,
                     unsigned cx, unsigned bx, unsigned ax,
                     unsigned ip, unsigned cs, unsigned flags )
{
  /* Read pds111.txt to know that packet driver (normally) calls receiver twice for each packet -
     once with ax = 0 and once with ax = 1
  */

  if (ax == 0) /* ax==0 -> Packet driver asks for a buffer */
  {            /* We provide the buffer at ES:DI
                  If the application has no buffer avail, es, di must be set to zero */
    es = FP_SEG(receive_packet); /* FP_SEG is a macro - read Turbo C Guide for details */
    di = FP_OFF(receive_packet); /* FP_OFF is a macro - read Turbo C Guide for details */
    /* putch('0'); */
  }

  if (ax == 1) /* Packet driver has filled our provided buffer */
  {
    /* Call our external c-function packet_receive to process the received data */
    /* putch('1');*/
    handle_packet(receive_packet);
  }
}

void handle_packet(unsigned char packet[]) {
	/* putch('H');*/
	if(!isBroadCastPacket(packet)) {
		/* printf("%x%x%x%x%x%x", packet[0], packet[1], packet[2], packet[3], packet[4], packet[5]); */
		printf("%c", packet[14]); /* print the 15th character in the packet */
	}
}

int isBroadCastPacket(unsigned char packet[]) { /* returns 1 if it's a ethernet broadcast packet, 0 otherwise */
	if(packet[0] == 0xff && packet[1] == 0xff && packet[2] == 0xff && packet[3] == 0xff && packet[4] == 0xff && packet[5] == 0xff) {
		return(1);
	}
	return (0);
}

static int driver_info(int intno, int handle, int *version, int *class, int *type, int *number, int *basic) {
	union REGS regs;

	regs.x.bx = handle;
	regs.h.ah = 1; /* pds111.txt says this is for DRIVER_INFO function driver_info() */
	regs.h.al = 0xff;
	int86(intno,&regs,&regs);
	if(regs.x.cflag){
		Derr = regs.h.dh;
		return -1;
	}
	if(version != NULL)
		*version = regs.x.bx;
	if(class != NULL)
		*class = regs.h.ch;
	if(type != NULL)
		*type = regs.x.dx;
	if(number != NULL)
		*number = regs.h.cl;
	if(basic != NULL)
		*basic = regs.h.al;
	return 0;
}

/* Test for the presence of a packet driver at an interrupt number.
 * Return 0 if no packet driver.
 */
int isPdInstalled() {
        char *t="PKT DRVR"; /* Packet Driver Signature */
	char far *p;
	char sig[9]; /* to store string of length 8 from interrupt vector */
	unsigned char swint; /* software interrupt may vary from 0x60 to 0x80 */

	for(swint = 0x60; swint<=0x80; swint++) {
		p=(char far *)getvect(swint); /* returns the location of the interrupt function */
		movedata(FP_SEG(p), FP_OFF(p)+3, FP_SEG(sig), FP_OFF(sig), 8);
		/* printf("%8s!!%s!!", sig,t);*/
		if ( strncmp(sig, t, 8) == 0) {
			/* printf("1. PKT found at 0x%x! ", swint); */
			break;
		} /* else {
			printf("1. PKT not at 0x%x!", i);
		} */
        }
	return (swint <= 0x80?swint:0);
}

main() {
	int ok;

	/* For storing the interrupt number against which the packet driver is installed */
	int intno;

	/* For storing information provided by driver_info() function */
	int version;
	int class;
	int type;
	int number;
	int basic;

	/* For stroing the handle returned by access_type */
	int handle;

	/* data variables */
	int c;

	ok = isPdInstalled();
        if ( ok != 0) {
		intno = ok;
		printf("Packet found at 0x%x\n", ok);
        } else {
		printf("No Packet Found...\n");
		exit(1);
	}
	/* static int driver_info (int intno,int handle,int *version, int *class,int *type,int *number,int *basic); */
	ok = driver_info(intno, -1, &version, &class, &type, &number, &basic);
	printf("driver_info() returned: version %d, class %d, type %d, number %d, basid %d.\n", version, class, type, number, basic);
	/* static int access_type(int intno, int if_class, int if_type, int if_number, char *type, unsigned typelen, void interrupt (*receiver)()) */
	/* handle = access_type(intno, class, ANYTYPE, 0,iptype,2, Pkvec[if_pk->dev]); */
	handle = access_type(intno, class, type, number, NULL, 0, receiver); /* for any type of packet this receiver will be called */
	printf("access_type() returned.\n");
	do {
		c = getche();
		send_packet[14] = c;
		ok = send_pkt(intno, send_packet,64);
		if (ok == -1) {
			printf("Cannot send packet with %c!\n", c);
		}
	} while (c != 3);
	release_type(intno, handle);
}
