?? tftpd.c
字號:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Module Name:
tftpd.c
Abstract:
This contains the TFTP server (daemon) routines for the
bootloader. For the most part, these operate on top of the udp.c
module. There are some global data structures that are shared with
the tftp.c module.
Functions:
Notes:
--*/
#include <windows.h>
#include <udp.h>
#include <tftp.h>
TFtpdServerRegistryEntry TFtpdServerRegistry[MAX_TFTP_SERVER_PROCS];
BOOL bDoLaunch = FALSE;
// Holds address of last TFTP download peer
EDBG_ADDR TftpPeerAddr = {0};
extern BYTE TFTPFrameBuffer[];
void EbootInitTFtpd( void ) {
int i;
for( i = 0; i < MAX_TFTP_SERVER_PROCS; i++ )
TFtpdServerRegistry[i].fInUse = 0;
}
int __cdecl tolower(int c) {
return ((c>='A') && (c<='Z')) ? (c-'A'+'a') : c;
}
// This function is called to associate a callback function with a given TFTP file name.
// When a packet arrives that specifies the given file name, the callback will be called.
// The requested operation is given, either TFTPD_OPEN, TFTPD_READ, TFTPD_WRITE, or TFTPD_DESTROY
// and a pointer to a data buffer is given along with it. The amount of valid data in the buffer is stored in
// cwLength. The amount of data cannot exceed 512 bytes, and in fact, must always be 512 bytes unless this is
// the last packet that will be transferred over the link. If the number of data bytes is < 512 the link
// is closing.
// The file name used for the associated link is also passed, allowing a single routine to act as a server for
// multiple files. The callback should not return until either all the data has been written to or taken from
// the buffer because the buffer will later be destroyed. It should then return 0 to indicate that there are no
// errors and the link may continue. If an error exists, the callback should return non-zero and the link will
// be destroyed.
// When calling the TFtpServerRegister() routine, it will return 0 if the callback was successfully registered,
// or non-zero if there are no more registration slots available.
UINT16 EbootTFtpdServerRegister( char *pszFileName, TFtpdCallBackFunc pfCallBack ) {
int i;
if ((strlen(pszFileName) + 1) > MAX_TFTP_FILENAME) return 1;
for( i = 0; i < MAX_TFTP_SERVER_PROCS; i++ )
if (TFtpdServerRegistry[i].fInUse == 0)
break;
if (i == MAX_TFTP_SERVER_PROCS)
return 1;
TFtpdServerRegistry[i].fInUse = 1;
strcpy( TFtpdServerRegistry[i].szFileName, pszFileName );
TFtpdServerRegistry[i].pfCallBack = pfCallBack;
return 0;
}
// A server process can call this routine if it wants to remove itself as a server for the given file name
void EbootTFtpdServerUnRegister( char *pszFileName ) {
int i;
for( i = 0; i < MAX_TFTP_SERVER_PROCS; i++ )
if (!strcmp( TFtpdServerRegistry[i].szFileName, pszFileName ))
break;
if (i != MAX_TFTP_SERVER_PROCS) {
TFtpdServerRegistry[i].fInUse = 0;
// Make sure to kill all links associated with this server
for( i = 0; i < MAX_TFTP_LINKS; i++ )
if (TFtpLinks[i].State != TFTP_STATE_IDLE && TFtpLinks[i].pfCallBack == TFtpdServerRegistry[i].pfCallBack)
TFtpKillLink( (UINT16)i, TFTP_ERROR_FILENOTFOUND, "Server Process Has UnRegistered Itself" );
}
}
// This routine generates a pseudo random sequence using a 16-bit LFSR. These are used as source ports
// for the TFTP protocol. Note that because XOR was used, 0 is a dead value and will never be
// returned. The value of all 1's can be made the dead value if XNOR is used.
UINT16 GenerateSrcPort( void ) {
static UINT16 wLastValue = 1;
UINT16 wTempValue;
int i;
// Loop here until we get a value that isn't in use
do {
// For a 16-bit LFSR shifting to the left, I'll take bits 15,14,12,3
wTempValue = 0xD008 & wLastValue;
// Now XOR those bits together to get the bit shifted in on the right
wTempValue ^= (wTempValue >> 12);
wTempValue ^= (wTempValue >> 2);
wTempValue ^= (wTempValue >> 1);
// Shift it and OR in the new bit
wLastValue = (wLastValue << 1) | (wTempValue & 0x0001);
// Now compare the new value in wLastValue against all the source ports that are in use
// Also keep us out of the normal well known port range
for( i = 0; i < MAX_TFTP_LINKS; i++ )
if (wLastValue < htons(1024) || (TFtpLinks[i].State != TFTP_STATE_IDLE && TFtpLinks[i].SrcAddr.wPort == wLastValue))
break;
}
while(i != MAX_TFTP_LINKS);
return wLastValue;
}
// This routine is called when a packet arrives to form a new server link. It will then attempt
// to determine if there is a callback procedure associated with that file name, and
// if so, it will then call that process with the TFTPD_OPEN command. If all goes well, the
// routine returns 0. If there is an error, it will return non-zero. The slot number that
// is to be used for this link in the TFtpLinks[] table is passed in so that the callback
// function pointer can be initialized for the link. This is done to avoid the overhead of
// looking up the callback address for every succeeding packet.
UINT16 TFtpdFormNewLink( EDBG_ADDR *pMyAddr, BYTE *pFrameBuffer, UINT16 iLinkSlot, UINT16 *pwMsg ) {
char *pszParse;
int i;
// Convert both the strings to lower case so that the comparisons here will be case insensitive
pszParse = (char *)(pwMsg + 1);
for( i = 0; i < 2; pszParse++ ) {
if (*pszParse == '\0')
i++;
*pszParse = tolower(*pszParse);
}
// Make sure we have a server process registered for this file name
for( i = 0; i < MAX_TFTP_SERVER_PROCS; i++ )
if (!strcmp( TFtpdServerRegistry[i].szFileName, (char *)(pwMsg + 1) ))
break;
if (i == MAX_TFTP_SERVER_PROCS) {
EdbgOutputDebugString( "No Server Process Associated With File Name %s\r\n", (char *)(pwMsg + 1) );
return 1;
}
else {
// Go ahead and fill out the link record so that I can call TFtpKillLink() if I have too.
nNumTFtpLinksInUse++;
TFtpLinks[iLinkSlot].pfCallBack = TFtpdServerRegistry[i].pfCallBack;
strcpy( TFtpLinks[iLinkSlot].szFileName, (char *)(pwMsg + 1) );
TFtpLinks[iLinkSlot].wBlockNumber = 0;
TFtpLinks[iLinkSlot].SrcAddr = *pMyAddr;
TFtpLinks[iLinkSlot].SrcAddr.wPort = GenerateSrcPort();
SrcAddrFromFrame( &(TFtpLinks[iLinkSlot].DestAddr), pFrameBuffer );
// gmd - store address in global so we can filter out EDBG commands from
// other hosts.
memcpy(&TftpPeerAddr, &(TFtpLinks[iLinkSlot].DestAddr), sizeof(EDBG_ADDR));
EdbgOutputDebugString( "Locked Down Link %d\r\n", iLinkSlot );
EdbgOutputDebugString( "Src IP %s Port %H ",
inet_ntoa( TFtpLinks[iLinkSlot].SrcAddr.dwIP ), ntohs(TFtpLinks[iLinkSlot].SrcAddr.wPort));
EdbgOutputDebugString( "Dest IP %s Port %H\r\n",
inet_ntoa( TFtpLinks[iLinkSlot].DestAddr.dwIP ), ntohs(TFtpLinks[iLinkSlot].DestAddr.wPort) );
// See if this is a read request
if (ntohs(*((UINT16 *)pwMsg)) == 1) {
TFtpLinks[iLinkSlot].DataDir = O2HLink;
TFtpLinks[iLinkSlot].State = TFTP_STATE_XFER_BUSY;
}
// Otherwise it should be a write request
else if (ntohs(*((UINT16 *)pwMsg)) == 2) {
TFtpLinks[iLinkSlot].DataDir = H2OLink;
TFtpLinks[iLinkSlot].State = TFTP_STATE_XFER_WAIT;
}
else {
TFtpLinks[iLinkSlot].State = TFTP_STATE_XFER_WAIT;
TFtpKillLink( iLinkSlot, TFTP_ERROR_ILLEGALOPERATION, "Illegal TFTP Operation - Read/Write Req Expected" );
return 2;
}
// Make sure they are using binary transfer mode
if (strcmp( "octet", (char *)(pwMsg + 1) + strlen((char *)(pwMsg + 1)) + 1 )) {
TFtpKillLink( iLinkSlot, TFTP_ERROR_ACCESSVIOLATION, "You must access files in binary mode." );
return 3;
}
// Now we will inform the server process of the new link and send any appropriate data/acknowledge back.
// Also, errors can be generated down there that could terminate the link.
TFtpdCallBack( iLinkSlot, TFTPD_OPEN );
}
return 0;
}
void TFtpdCallBack( UINT16 iLinkSlot, TFtpdCallBackOps Operation ) {
char *pszErrorMsg;
switch(Operation) {
case TFTPD_OPEN:
// Call open to give the callback a chance to initialize
if (TFtpLinks[iLinkSlot].pfCallBack( TFtpLinks[iLinkSlot].szFileName, TFTPD_OPEN, NULL, NULL, &pszErrorMsg )) {
TFtpKillLink( iLinkSlot, TFTP_ERROR_FILENOTFOUND, pszErrorMsg );
return;
}
// If it's for a Read request we need to respond with data, recursively call ourselves
if (TFtpLinks[iLinkSlot].DataDir == O2HLink) {
TFtpdCallBack( iLinkSlot, TFTPD_READ );
return;
}
// If it's for a Write request, we need to respond with an acknowledge
else {
*((UINT16 *)TFtpLinks[iLinkSlot].Buffer) = htons(4);
// This block number should be 0
*((UINT16 *)TFtpLinks[iLinkSlot].Buffer + 1) = htons(TFtpLinks[iLinkSlot].wBlockNumber);
TFtpLinks[iLinkSlot].cwMsgLen = 4;
}
break;
case TFTPD_READ:
// We need to send a data packet back
*((UINT16 *)TFtpLinks[iLinkSlot].Buffer) = htons(3);
TFtpLinks[iLinkSlot].wBlockNumber++;
*((UINT16 *)TFtpLinks[iLinkSlot].Buffer + 1) = htons(TFtpLinks[iLinkSlot].wBlockNumber);
if (TFtpLinks[iLinkSlot].pfCallBack( TFtpLinks[iLinkSlot].szFileName, TFTPD_READ,
TFtpLinks[iLinkSlot].Buffer + 4, &(TFtpLinks[iLinkSlot].cwMsgLen), &pszErrorMsg ) ) {
TFtpKillLink( iLinkSlot, TFTP_ERROR_ACCESSVIOLATION, pszErrorMsg );
return;
}
// Add in length for the control words
TFtpLinks[iLinkSlot].cwMsgLen += 4;
TFtpLinks[iLinkSlot].State = TFTP_STATE_XFER_WAIT;
break;
case TFTPD_WRITE:
// We need to process the data and send an acknowledge back
*((UINT16 *)TFtpLinks[iLinkSlot].Buffer) = htons(4);
*((UINT16 *)TFtpLinks[iLinkSlot].Buffer + 1) = htons(TFtpLinks[iLinkSlot].wBlockNumber);
TFtpLinks[iLinkSlot].cwMsgLen -= 4;
if (TFtpLinks[iLinkSlot].pfCallBack( TFtpLinks[iLinkSlot].szFileName, TFTPD_WRITE,
TFtpLinks[iLinkSlot].Buffer + 4, &(TFtpLinks[iLinkSlot].cwMsgLen), &pszErrorMsg ) ) {
TFtpKillLink( iLinkSlot, TFTP_ERROR_ACCESSVIOLATION, pszErrorMsg );
return;
}
// The acknowledge length is just the control words
TFtpLinks[iLinkSlot].cwMsgLen = 4;
TFtpLinks[iLinkSlot].State = TFTP_STATE_XFER_WAIT;
break;
case TFTPD_DESTROY:
// Just tell them that it's dead and return
TFtpLinks[iLinkSlot].pfCallBack( TFtpLinks[iLinkSlot].szFileName, TFTPD_DESTROY, NULL, NULL, &pszErrorMsg );
return;
} // switch(Operation)
// Send the packet back to the host
EbootSendUDP(TFTPFrameBuffer, &(TFtpLinks[iLinkSlot].DestAddr), &(TFtpLinks[iLinkSlot].SrcAddr),
TFtpLinks[iLinkSlot].Buffer, TFtpLinks[iLinkSlot].cwMsgLen );
TFtpLinks[iLinkSlot].cwRetries = 0;
TFtpLinks[iLinkSlot].tLastTransmit = OEMEthGetSecs();
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -