556 lines
13 KiB
C
556 lines
13 KiB
C
#include "su.h"
|
|
#include "pxe_cmn.h"
|
|
#include "pxe_api.h"
|
|
#include "tftp_api.h"
|
|
#include "udp_api.h"
|
|
#include "dhcp.h"
|
|
#include "pxe.h"
|
|
|
|
|
|
|
|
#define htons( a ) ((((a) & 0xFF00) >> 8) |\
|
|
(((a) & 0x00FF) << 8))
|
|
|
|
|
|
//
|
|
// packet - Work buffer used to hold DHCP ACK and BINL REPLY packets.
|
|
// These packets will be read into this buffer using the
|
|
// PXENV API service PXENV_GET_BINL_INFO.
|
|
//
|
|
|
|
BOOTPLAYER packet;
|
|
|
|
//
|
|
// PxenvApiCall() - see su.asm for details
|
|
//
|
|
|
|
extern UINT16
|
|
PxenvApiCall(
|
|
UINT16 service,
|
|
void far *param
|
|
);
|
|
|
|
|
|
//
|
|
// GetPacket()
|
|
//
|
|
// Description:
|
|
// Get cached packet from PXENV API.
|
|
//
|
|
// Passed:
|
|
// packet := Far pointer to packet buffer
|
|
// packet_type := see pxe_api.h for PXENV_PACKET_TYPE_xxx #defines
|
|
//
|
|
// Returns:
|
|
// -1 := Packet could not be transfered to buffer
|
|
// size := Number of bytes transfered into packet buffer
|
|
//
|
|
// Warning:
|
|
// No check is made to see if buffer is actually large enough to
|
|
// hold the entire packet. The buffer should be of type BOOTPLAYER.
|
|
//
|
|
|
|
SHORT
|
|
GetPacket(
|
|
void far *packet,
|
|
UINT16 packet_type
|
|
)
|
|
{
|
|
t_PXENV_GET_BINL_INFO gbi;
|
|
|
|
//
|
|
// Check for invalid parameters
|
|
//
|
|
|
|
if (packet == NULL) {
|
|
BlPrint("\nGetPacket() NULL pointers\n");
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Request size of packet by sending a size of zero.
|
|
//
|
|
|
|
gbi.packet_type = packet_type;
|
|
gbi.buffer_size = 0;
|
|
|
|
if (PxenvApiCall(PXENV_GET_BINL_INFO, &gbi) != PXENV_EXIT_SUCCESS) {
|
|
BlPrint("\nGetPacket() PXENV API FAILURE #1\n");
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Transfer cached packet into buffer.
|
|
//
|
|
|
|
gbi.buffer_offset = FP_OFF(packet);
|
|
gbi.buffer_segment = FP_SEG(packet);
|
|
|
|
if (PxenvApiCall(PXENV_GET_BINL_INFO, &gbi) != PXENV_EXIT_SUCCESS) {
|
|
BlPrint("\nGetPacket() PXENV API FAILURE #2\n");
|
|
return -1;
|
|
}
|
|
|
|
return (SHORT)gbi.buffer_size;
|
|
}
|
|
|
|
|
|
//
|
|
// FindOption()
|
|
//
|
|
// Description:
|
|
// Find the subnet mask option in a DHCP packet.
|
|
//
|
|
// Passed:
|
|
// Packet := IN Pointer to DHCP packet.
|
|
// PacketLength := IN Length of DHCP packet.
|
|
// pLength := OUT Length of option.
|
|
//
|
|
// Returns:
|
|
// pointer to option data; NULL if not present
|
|
//
|
|
|
|
UINT32 *
|
|
FindOption(
|
|
UINT8 Option,
|
|
BOOTPLAYER *Packet,
|
|
UINT16 PacketLength,
|
|
UINT8 * pLength
|
|
)
|
|
{
|
|
UINT8 *options;
|
|
UINT8 *end;
|
|
|
|
//
|
|
// Verify parameters
|
|
//
|
|
|
|
if (pLength != NULL) {
|
|
*pLength = 0;
|
|
}
|
|
|
|
if ( *((ULONG *)Packet->vendor.v.magic) != VM_RFC1048 ) {
|
|
return NULL;
|
|
}
|
|
|
|
end = (UINT8 *)Packet + PacketLength;
|
|
|
|
for ( options = (UINT8 *)&Packet->vendor.v.flags;
|
|
options < end; ) {
|
|
|
|
if ( *options == Option ) {
|
|
if (pLength != NULL) {
|
|
*pLength = *(UINT8*)(options + 1);
|
|
}
|
|
return (UINT32 *)(options + 2);
|
|
}
|
|
|
|
if ( *options == 0xFF ) {
|
|
return NULL;
|
|
}
|
|
|
|
if ( *options == DHCP_PAD ) {
|
|
options++;
|
|
} else {
|
|
options += 2 + options[1];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
UCHAR *
|
|
FindVendorOption(
|
|
UINT8 Option,
|
|
UINT8 VendorOption,
|
|
BOOTPLAYER *Packet,
|
|
UINT16 PacketLength,
|
|
UINT8 * pLength
|
|
)
|
|
{
|
|
UINT8 *start;
|
|
UINT8 *options;
|
|
UINT8 *end;
|
|
UCHAR cb;
|
|
|
|
if (pLength != NULL) {
|
|
*pLength = 0;
|
|
}
|
|
|
|
start = (UINT8*)FindOption( Option, Packet, PacketLength, &cb );
|
|
if (start == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
end = (UINT8 *)start + cb;
|
|
|
|
for ( options = start;
|
|
options < end; ) {
|
|
|
|
if ( *options == VendorOption ) {
|
|
if (pLength != NULL) {
|
|
*pLength = *(UINT8*)(options + 1);
|
|
}
|
|
return (UCHAR *)(options + 2);
|
|
}
|
|
|
|
if ( *options == 0xFF ) {
|
|
return NULL;
|
|
}
|
|
|
|
if ( *options == DHCP_PAD ) {
|
|
options++;
|
|
} else {
|
|
options += 2 + options[1];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// strlen()
|
|
//
|
|
// Description:
|
|
// Works like std C.
|
|
//
|
|
|
|
int
|
|
strlen(UCHAR *s1)
|
|
{
|
|
int n = 0;
|
|
|
|
if (s1 != NULL)
|
|
while (*s1++)
|
|
++n;
|
|
|
|
return n;
|
|
}
|
|
|
|
|
|
//
|
|
// strcpy()
|
|
//
|
|
// Description:
|
|
// Works like std C.
|
|
//
|
|
|
|
UCHAR *
|
|
strcpy(UCHAR *s1, UCHAR *s2)
|
|
{
|
|
UCHAR *s = s1;
|
|
|
|
if (s1 != NULL && s2 != NULL)
|
|
while ((*s1++ = *s2++) != 0)
|
|
;
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
//
|
|
// strncpy()
|
|
//
|
|
// Description:
|
|
// Works like std C.
|
|
//
|
|
|
|
UCHAR *
|
|
strncpy(UCHAR *s1, UCHAR *s2, int n)
|
|
{
|
|
UCHAR *s = s1;
|
|
|
|
if (s1 != NULL && s2 != NULL && n > 0)
|
|
while (n--)
|
|
if ((*s1++ = *s2++) == 0)
|
|
break;
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
//
|
|
// memset()
|
|
//
|
|
// Description:
|
|
// Works like std C.
|
|
//
|
|
|
|
PUCHAR
|
|
memset(
|
|
PUCHAR Destination,
|
|
UCHAR Value,
|
|
int Length
|
|
)
|
|
{
|
|
while (Length--) {
|
|
*Destination++ = Value;
|
|
}
|
|
|
|
return Destination;
|
|
}
|
|
|
|
|
|
//
|
|
// PxenvTftp()
|
|
//
|
|
// Description:
|
|
// Try to transfer the protect-mode loader using information from
|
|
// DHCP ACK and BINL REPLY packets.
|
|
//
|
|
// Passed:
|
|
// DownloadAddr := Physical address, in client machine, to transfer to.
|
|
// FileName := File name sent down in BINL REPLY packet.
|
|
//
|
|
|
|
BOOLEAN
|
|
PxenvTftp(
|
|
)
|
|
{
|
|
UINT16 status;
|
|
UINT16 packetLength;
|
|
t_PXENV_TFTP_READ_FILE tftp;
|
|
int pathLength;
|
|
UINT32 clientIp;
|
|
UINT32 serverIp;
|
|
UINT32 gatewayIp;
|
|
UINT32 *optionPtr;
|
|
UINT32 subnetMask;
|
|
UCHAR *FileName;
|
|
UCHAR cb;
|
|
UCHAR *optionVendor;
|
|
|
|
|
|
//
|
|
// Get the DHCP ACK packet.
|
|
//
|
|
|
|
if ((packetLength = GetPacket(&packet, PXENV_PACKET_TYPE_DHCP_ACK)) == -1) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Get client IP address, server IP address, default gateway IP address,
|
|
// and subnet mask from the DHCP ACK packet.
|
|
//
|
|
|
|
clientIp = *(UINT32 *)packet.yip;
|
|
serverIp = *(UINT32 *)packet.sip;
|
|
//BlPrint("PxenvTftp: DHCP ACK yip = %lx, sip = %lx\n", *(UINT32 *)packet.yip, *(UINT32 *)packet.sip);
|
|
|
|
optionPtr = FindOption( DHCP_ROUTER, &packet, packetLength, NULL );
|
|
if ( optionPtr != NULL ) {
|
|
//BlPrint("PxenvTftp: DHCP ACK router = %lx\n", *optionPtr);
|
|
gatewayIp = *optionPtr;
|
|
} else {
|
|
//BlPrint("PxenvTftp: DHCP ACK gip = %lx\n", *(UINT32 *)packet.gip);
|
|
gatewayIp = *(UINT32 *)packet.gip;
|
|
}
|
|
|
|
optionPtr = FindOption( DHCP_SUBNET, &packet, packetLength, NULL );
|
|
if ( optionPtr != NULL ) {
|
|
//BlPrint("PxenvTftp: DHCP ACK subnet = %lx\n", *optionPtr);
|
|
subnetMask = *optionPtr;
|
|
} else {
|
|
//BlPrint("PxenvTftp: DHCP ACK subnet not specified\n");
|
|
subnetMask = 0;
|
|
}
|
|
|
|
//
|
|
// Get the BINL REPLY packet.
|
|
//
|
|
|
|
if ((packetLength = GetPacket(&packet, PXENV_PACKET_TYPE_BINL_REPLY)) == -1) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Values for client IP address, server IP address, default gateway IP address,
|
|
// and subnet mask that are present in the BINL REPLY packet override those
|
|
// in the DHCP ACK packet.
|
|
//
|
|
|
|
if ( *(UINT32 *)packet.yip != 0 ) {
|
|
clientIp = *(UINT32 *)packet.yip;
|
|
}
|
|
if ( *(UINT32 *)packet.sip != 0 ) {
|
|
serverIp = *(UINT32 *)packet.sip;
|
|
}
|
|
//BlPrint("PxenvTftp: BINL REPLY yip = %lx, sip = %lx\n", *(UINT32 *)packet.yip, *(UINT32 *)packet.sip);
|
|
|
|
optionPtr = FindOption( DHCP_ROUTER, &packet, packetLength, NULL );
|
|
if ( optionPtr != NULL ) {
|
|
//BlPrint("PxenvTftp: BINL REPLY router = %lx\n", *optionPtr);
|
|
gatewayIp = *optionPtr;
|
|
} else if ( *(UINT32 *)packet.gip != 0 ) {
|
|
//BlPrint("PxenvTftp: BINL REPLY router = %lx\n", *(UINT32 *)packet.gip);
|
|
gatewayIp = *(UINT32 *)packet.gip;
|
|
}
|
|
|
|
optionPtr = FindOption( DHCP_SUBNET, &packet, packetLength, NULL );
|
|
if ( optionPtr != NULL ) {
|
|
//BlPrint("PxenvTftp: BINL REPLY subnet = %lx\n", *optionPtr);
|
|
subnetMask = *optionPtr;
|
|
}
|
|
|
|
//
|
|
// Determine whether we need to send packets via the gateway.
|
|
//
|
|
|
|
//BlPrint("PxenvTftp: clientIp = %lx, serverIp = %lx, subnet = %lx\n", clientIp, serverIp, subnetMask);
|
|
//BlPrint(" router = %lx\n", gatewayIp);
|
|
if ( (clientIp & subnetMask) == (serverIp & subnetMask) ) {
|
|
//BlPrint("PxenvTftp: subnets match. clearing router address\n");
|
|
gatewayIp = 0;
|
|
}
|
|
//PxenvApiCall(-1, NULL);
|
|
|
|
|
|
//
|
|
// Now fill in the TFTP TRANSFER parameter structure
|
|
//
|
|
|
|
memset( (PUCHAR)&tftp, 0, sizeof( tftp ) );
|
|
|
|
//
|
|
// Find the name and path of the NTLDR that we are going to download.
|
|
// This is specified by a DHCP Vendor option tag. If this tag
|
|
// is missing we will default to NTLDR and the same path as
|
|
// startrom.com.
|
|
//
|
|
FileName = (UCHAR*)FindOption( DHCP_LOADER_PATH, &packet, packetLength, &cb );
|
|
if ( FileName == NULL || cb > 128 ) {
|
|
//
|
|
// We could not find the DHCP_LOADER_PATH or it was longer
|
|
// than we expected. We will use the default name of
|
|
// <path>\NTLDR where the <path> is the same as that used
|
|
// to download startrom.com
|
|
//
|
|
|
|
pathLength = strlen(packet.bootfile);
|
|
while (pathLength > 0) {
|
|
--pathLength;
|
|
if (packet.bootfile[pathLength] == '\\') {
|
|
++pathLength; // advance it past the '\'
|
|
break;
|
|
}
|
|
}
|
|
|
|
strncpy(tftp.FileName, packet.bootfile, pathLength);
|
|
strcpy(tftp.FileName + pathLength, "NTLDR");
|
|
|
|
} else {
|
|
|
|
// We found the DHCP_LOADER_PATH option. We will use that
|
|
// as is to download the loader
|
|
strncpy(tftp.FileName, FileName, cb);
|
|
|
|
}
|
|
|
|
//
|
|
// Loader will be transfered to 1MB region and must not be more
|
|
// than to 2MB in length.
|
|
//
|
|
|
|
tftp.BufferSize = 0x200000L;
|
|
tftp.BufferOffset = 0x100000L;
|
|
|
|
//
|
|
// Set the Server and gateway address
|
|
//
|
|
*((UINT32 *)tftp.ServerIPAddress) = serverIp;
|
|
*((UINT32 *)tftp.GatewayIPAddress) = gatewayIp;
|
|
|
|
//
|
|
// Check whether we are going to use multicast download or not. The
|
|
// multicast options are set in a DHCP option tag (DHCP_LOADER_MCAST_OPTIONS).
|
|
// These are encapsulated options and work the same way as Vendor options.
|
|
// If these are missing then unicast transfer will be used.
|
|
//
|
|
optionVendor = FindVendorOption( DHCP_LOADER_MCAST_OPTIONS, PXE_MTFTP_IP, &packet, packetLength, &cb );
|
|
if ( optionVendor != NULL && cb == 4 ) {
|
|
|
|
*(UINT32*)tftp.McastIPAddress = *(UINT32*)optionVendor;
|
|
|
|
optionVendor = FindVendorOption( DHCP_LOADER_MCAST_OPTIONS, PXE_MTFTP_CPORT, &packet, packetLength, &cb );
|
|
if (optionVendor == NULL || cb != 2) {
|
|
return TRUE;
|
|
}
|
|
|
|
tftp.TFTPClntPort = htons( *(UINT16*)optionVendor );
|
|
|
|
optionVendor = FindVendorOption( DHCP_LOADER_MCAST_OPTIONS, PXE_MTFTP_SPORT, &packet, packetLength, &cb );
|
|
if (optionVendor == NULL || cb != 2) {
|
|
return TRUE;
|
|
}
|
|
|
|
tftp.TFTPSrvPort = htons( *(UINT16*)optionVendor );
|
|
|
|
optionVendor = FindVendorOption( DHCP_LOADER_MCAST_OPTIONS, PXE_MTFTP_TMOUT, &packet, packetLength, &cb );
|
|
if (optionVendor == NULL || cb != 1) {
|
|
return TRUE;
|
|
}
|
|
|
|
tftp.TFTPOpenTimeOut = *(UINT8*)optionVendor;
|
|
|
|
optionVendor = FindVendorOption( DHCP_LOADER_MCAST_OPTIONS, PXE_MTFTP_DELAY, &packet, packetLength, &cb );
|
|
if (optionVendor == NULL || cb != 1) {
|
|
return TRUE;
|
|
}
|
|
|
|
tftp.TFTPReopenDelay = *(UINT8*)optionVendor;
|
|
}
|
|
|
|
|
|
#if DBG
|
|
|
|
BlPrint("Downloading Loader:\n");
|
|
BlPrint("FileName = %s\n", tftp.FileName );
|
|
BlPrint("BufferSize = %lx\n", tftp.BufferSize );
|
|
BlPrint("BufferOffset = %lx\n", tftp.BufferOffset );
|
|
BlPrint("ServerIPAddress = %d.%d.%d.%d\n",
|
|
tftp.ServerIPAddress[0],
|
|
tftp.ServerIPAddress[1],
|
|
tftp.ServerIPAddress[2],
|
|
tftp.ServerIPAddress[3] );
|
|
BlPrint("GatewayIPAddress = %d.%d.%d.%d\n",
|
|
tftp.GatewayIPAddress[0],
|
|
tftp.GatewayIPAddress[1],
|
|
tftp.GatewayIPAddress[2],
|
|
tftp.GatewayIPAddress[3] );
|
|
BlPrint("McastIPAddress = %d.%d.%d.%d\n",
|
|
tftp.McastIPAddress[0],
|
|
tftp.McastIPAddress[1],
|
|
tftp.McastIPAddress[2],
|
|
tftp.McastIPAddress[3] );
|
|
BlPrint("TFTPClntPort = %d\n", htons( tftp.TFTPClntPort ) );
|
|
BlPrint("TFTPSrvPort = %d\n", htons( tftp.TFTPSrvPort ) );
|
|
BlPrint("TFTPOpenTimeOut = %d\n", tftp.TFTPOpenTimeOut );
|
|
BlPrint("TFTPReopenDelay = %d\n", tftp.TFTPReopenDelay );
|
|
|
|
BlPrint("\n\nPress any key to start download...\n" );
|
|
|
|
_asm {
|
|
push ax
|
|
mov ax, 0
|
|
int 16h
|
|
pop ax
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Transfer image from TFTP server
|
|
//
|
|
status = PxenvApiCall(PXENV_TFTP_READ_FILE, &tftp);
|
|
if (status != PXENV_EXIT_SUCCESS) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/* EOF - mtftp.c */
|