singrdk/base/Windows/bootd/bootd.cpp

751 lines
22 KiB
C++

////////////////////////////////////////////////////////////////////////////
//
// Boot Service
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
#include <winlean.h>
#include <winsock.h>
#include <stdio.h>
#include <assert.h>
#include <iphlpapi.h>
#include "socksr.h"
#include "hashtab.h"
#include "dhcpd.h"
#include "tftpd.h"
#define ARRAYOF(x) (sizeof(x)/sizeof(x[0]))
#define ASSERT(a) assert(a)
#define WM_SOCKET_EVENT (WM_USER+101)
void SocketErrorBox(DWORD dwWSALastError, PCSTR pszFile, INT nLine)
{
CHAR *szError = NULL;
switch (dwWSALastError) {
case WSAEACCES:
szError = "WSAEACCES (%d) Permission denied.";
break;
case WSAEADDRINUSE:
szError = "WSAEADDRINUSE (%d) Address already in use.";
break;
case WSAEADDRNOTAVAIL:
szError = "WSAEADDRNOTAVAIL (%d) Cannot assign requested address.";
break;
case WSAEAFNOSUPPORT:
szError = "WSAEAFNOSUPPORT (%d) Address family not supported by protocol family.";
break;
case WSAEALREADY:
szError = "WSAEALREADY (%d) Operation already in progress.";
break;
case WSAECONNABORTED:
szError = "WSAECONNABORTED (%d) Software caused connection abort.";
break;
case WSAECONNREFUSED:
szError = "WSAECONNREFUSED (%d) Connection refused.";
break;
case WSAECONNRESET:
szError = "WSAECONNRESET (%d) Connection reset by peer.";
break;
case WSAEDESTADDRREQ:
szError = "WSAEDESTADDRREQ (%d) Destination address required.";
break;
case WSAEFAULT:
szError = "WSAEFAULT (%d) Bad address.";
break;
case WSAEHOSTDOWN:
szError = "WSAEHOSTDOWN (%d) Host is down.";
break;
case WSAEHOSTUNREACH:
szError = "WSAEHOSTUNREACH (%d) No route to host.";
break;
case WSAEINPROGRESS:
szError = "WSAEINPROGRESS (%d) Operation now in progress.";
break;
case WSAEINTR:
szError = "WSAEINTR (%d) Interrupted function call.";
break;
case WSAEINVAL:
szError = "WSAEINVAL (%d) Invalid argument.";
break;
case WSAEISCONN:
szError = "WSAEISCONN (%d) Socket is already connected.";
break;
case WSAEMFILE:
szError = "WSAEMFILE (%d) Too many open files.";
break;
case WSAEMSGSIZE:
szError = "WSAEMSGSIZE (%d) Message too long.";
break;
case WSAENETDOWN:
szError = "WSAENETDOWN (%d) Network is down.";
break;
case WSAENETRESET:
szError = "WSAENETRESET (%d) Network dropped connection on reset.";
break;
case WSAENETUNREACH:
szError = "WSAENETUNREACH (%d) Network is unreachable.";
break;
case WSAENOBUFS:
szError = "WSAENOBUFS (%d) No buffer space available.";
break;
case WSAENOPROTOOPT:
szError = "WSAENOPROTOOPT (%d) Bad protocol option.";
break;
case WSAENOTCONN:
szError = "WSAENOTCONN (%d) Socket is not connected.";
break;
case WSAENOTSOCK:
szError = "WSAENOTSOCK (%d) Socket operation on nonsocket.";
break;
case WSAEOPNOTSUPP:
szError = "WSAEOPNOTSUPP (%d) Operation not supported.";
break;
case WSAEPFNOSUPPORT:
szError = "WSAEPFNOSUPPORT (%d) Protocol family not supported.";
break;
case WSAEPROCLIM:
szError = "WSAEPROCLIM (%d) Too many processes.";
break;
case WSAEPROTONOSUPPORT:
szError = "WSAEPROTONOSUPPORT (%d) Protocol not supported.";
break;
case WSAEPROTOTYPE:
szError = "WSAEPROTOTYPE (%d) Protocol wrong type for socket.";
break;
case WSAESHUTDOWN:
szError = "WSAESHUTDOWN (%d) Cannot send after socket shutdown.";
break;
case WSAESOCKTNOSUPPORT:
szError = "WSAESOCKTNOSUPPORT (%d) Socket type not supported.";
break;
case WSAETIMEDOUT:
szError = "WSAETIMEDOUT (%d) Connection timed out.";
break;
case WSATYPE_NOT_FOUND:
szError = "WSATYPE_NOT_FOUND (%d) Class type not found.";
break;
case WSAEWOULDBLOCK:
szError = "WSAEWOULDBLOCK (%d) Resource temporarily unavailable.";
break;
case WSAHOST_NOT_FOUND:
szError = "WSAHOST_NOT_FOUND (%d) Host not found.";
break;
case WSANOTINITIALISED:
szError = "WSANOTINITIALISED (%d) Successful WSAStartup not yet performed.";
break;
case WSANO_DATA:
szError = "WSANO_DATA (%d) Valid name, no data record of requested type.";
break;
case WSANO_RECOVERY:
szError = "WSANO_RECOVERY (%d) This is a nonrecoverable error.";
break;
case WSASYSCALLFAILURE:
szError = "WSASYSCALLFAILURE (%d) System call failure.";
break;
case WSASYSNOTREADY:
szError = "WSASYSNOTREADY (%d) Network subsystem is unavailable.";
break;
case WSATRY_AGAIN:
szError = "WSATRY_AGAIN (%d) Nonauthoritative host not found.";
break;
case WSAVERNOTSUPPORTED:
szError = "WSAVERNOTSUPPORTED (%d) Winsock.";
break;
case WSAEDISCON:
szError = "WSAEDISCON (%d) Graceful shutdown in progress.\nReturned by WS";
break;
default:
szError = "UNKNOWN WSA Socket ERROR! (%d)";
break;
}
printf("%s, Line %d.\n", pszFile, nLine);
printf(szError, dwWSALastError);
printf("\n");
}
//////////////////////////////////////////////////////////////////////////////
//
static inline INT Max(INT a, INT b)
{
return a >= b ? a : b;
}
static inline INT Min(INT a, INT b)
{
return a <= b ? a : b;
}
CHAR * StringDuplicate(PCSTR psz)
{
int n = strlen(psz);
CHAR *pszNew = new CHAR [n + 1];
if (pszNew)
{
memcpy(pszNew, psz, n + 1);
}
assert(pszNew);
return pszNew;
}
UINT32 GetIpAddr(PCSTR pszAddr)
{
if (pszAddr[0] >= '0' && pszAddr[0] <= '9')
{
return ntohl(inet_addr(pszAddr));
}
else
{
HOSTENT *pHost = gethostbyname(pszAddr);
if (pHost)
{
return *(UINT32*)pHost->h_addr_list[0];
}
}
return 0;
}
PCSTR FileTimeToString(UINT64 ftTime)
{
static CHAR rszTimes[16][48];
static int nTimes = 0;
static BOOL bGotTzi = FALSE;
static DWORD dwTzi = TIME_ZONE_ID_UNKNOWN;
static TIME_ZONE_INFORMATION tzi;
if (!bGotTzi)
{
dwTzi = GetTimeZoneInformation(&tzi);
if (dwTzi == TIME_ZONE_ID_UNKNOWN)
{
ZeroMemory(&tzi, sizeof(tzi));
}
bGotTzi = TRUE;
}
SYSTEMTIME stUtc;
SYSTEMTIME stLocal;
CHAR *pszTime = rszTimes[nTimes];
nTimes = (nTimes + 1) % ARRAYOF(rszTimes);
if (!FileTimeToSystemTime((LPFILETIME)&ftTime, &stUtc))
{
strcpy(pszTime, "00:00:00");
return pszTime;
}
else if (!SystemTimeToTzSpecificLocalTime(&tzi, &stUtc, &stLocal))
{
CopyMemory(&stLocal, &stUtc, sizeof(stLocal));
}
sprintf(pszTime, "%2d:%02d:%02d",
// stLocal.wYear,
// stLocal.wMonth,
// stLocal.wDay,
stLocal.wHour,
stLocal.wMinute,
stLocal.wSecond
// stLocal.wMilliseconds / 10
);
return pszTime;
}
VOID OnNodeMessage(UINT32 nAddr, PCSTR pszProt, PCSTR pszMessage, va_list args)
{
UINT64 ft;
GetSystemTimeAsFileTime((LPFILETIME)&ft);
CHAR szBuffer[2048];
PCHAR pszBuffer = szBuffer;
pszBuffer += sprintf(pszBuffer, "%s: ", pszProt);
pszBuffer += vsprintf(pszBuffer, pszMessage, args);
if (nAddr == 0) {
printf("%-8.8s %15.15s %s\n", FileTimeToString(ft), "", szBuffer);
}
else {
printf("%-8.8s %15.15s %s\n", FileTimeToString(ft), SocketToString(nAddr), szBuffer);
}
}
VOID OnNodeMessagef(UINT32 nAddr, PCSTR pszProt, PCSTR pszMessage, ...)
{
va_list args;
va_start(args, pszMessage);
OnNodeMessage(nAddr, pszProt, pszMessage, args);
va_end(args);
}
/////////////////////////////////////////////////////////////////// IDhcpSink.
//
class CDhcpSink : public IDhcpSink
{
public:
VOID OnDhcpMessage(UINT32 nAddr, PCSTR pszMessage, ...)
{
va_list args;
va_start(args, pszMessage);
OnNodeMessage(nAddr, "DHCP", pszMessage, args);
va_end(args);
}
VOID OnDhcpSocketError(INT nErr)
{
OnNodeMessagef(0, "DHCP", "Socket Error: %d", nErr);
}
VOID OnDhcpOffer(UINT32 nAddr, UINT64 idMac)
{
OnNodeMessagef(nAddr, "DHCP", "Offer %s to %02x-%02x-%02x-%02x-%02x-%02x",
SocketToString(nAddr),
((PBYTE)&idMac)[5],
((PBYTE)&idMac)[4],
((PBYTE)&idMac)[3],
((PBYTE)&idMac)[2],
((PBYTE)&idMac)[1],
((PBYTE)&idMac)[0]);
}
VOID OnDhcpAck(UINT32 nAddr, UINT64 idMac)
{
OnNodeMessagef(nAddr, "DHCP", "Assigned %s to %02x-%02x-%02x-%02x-%02x-%02x",
SocketToString(nAddr),
((PBYTE)&idMac)[5],
((PBYTE)&idMac)[4],
((PBYTE)&idMac)[3],
((PBYTE)&idMac)[2],
((PBYTE)&idMac)[1],
((PBYTE)&idMac)[0]);
}
};
class CPxepSink : public IDhcpSink
{
public:
VOID OnDhcpMessage(UINT32 nAddr, PCSTR pszMessage, ...)
{
va_list args;
va_start(args, pszMessage);
OnNodeMessage(nAddr, "PXE ", pszMessage, args);
va_end(args);
}
VOID OnDhcpSocketError(INT nErr)
{
OnNodeMessagef(0, "PXE ", "Socket Error: %d", nErr);
}
VOID OnDhcpOffer(UINT32 nAddr, UINT64 idMac)
{
OnNodeMessagef(nAddr, "PXE ", "Offer boot to %02x-%02x-%02x-%02x-%02x-%02x",
((PBYTE)&idMac)[5],
((PBYTE)&idMac)[4],
((PBYTE)&idMac)[3],
((PBYTE)&idMac)[2],
((PBYTE)&idMac)[1],
((PBYTE)&idMac)[0]);
}
VOID OnDhcpAck(UINT32 nAddr, UINT64 idMac)
{
OnNodeMessagef(nAddr, "PXE ", "Assigned boot to %02x-%02x-%02x-%02x-%02x-%02x",
((PBYTE)&idMac)[5],
((PBYTE)&idMac)[4],
((PBYTE)&idMac)[3],
((PBYTE)&idMac)[2],
((PBYTE)&idMac)[1],
((PBYTE)&idMac)[0]);
}
};
/////////////////////////////////////////////////////////////////// ITftpSink.
//
class CTftpSink : public ITftpSink
{
VOID OnTftpMessage(UINT32 nAddr, PCSTR pszMessage, ...)
{
va_list args;
va_start(args, pszMessage);
OnNodeMessage(nAddr, "TFTP", pszMessage, args);
va_end(args);
}
VOID OnTftpSocketError(INT nErr)
{
OnNodeMessagef(0, "TFTP", "Socket Error: %d", nErr);
}
VOID OnTftpAccessBegin(UINT32 nAddr, UINT16 nPort, BOOL fWrite, PCSTR pszFile)
{
(void)nPort;
if (fWrite) {
OnNodeMessagef(nAddr, "TFTP", "Put %s", pszFile);
}
else {
OnNodeMessagef(nAddr, "TFTP", "Get %s", pszFile);
}
}
VOID OnTftpAccessEnd(UINT32 nAddr, UINT16 nPort, BOOL bSuccess)
{
(void)nPort;
if (bSuccess)
{
OnNodeMessagef(nAddr, "TFTP", "Operation finished.");
}
else
{
OnNodeMessagef(nAddr, "TFTP", "Operation terminated.");
}
}
};
VOID Usage(VOID)
{
printf("Usage:\n"
" bootd.exe [options]\n"
"Options:\n"
" /? or /h - Print this help text.\n"
" /dhcp ... - Set DHCP Daemon Options.\n"
" /b:bootimg - Default boot image.\n"
" /c:command - Set command line in PXE packet.\n"
" /i:inifile - Path name of dhcpd.ini\n"
" /m:xx-xx-xx-xx-xx-xx - PXE boot MAC.\n"
" /m:xx-xx-xx-xx-xx-xx,i.i.i.i - DCHP & PXE boot MAC.\n"
" /tftp ... - Set TFTP Daemon Options\n"
" /e - Exit on first attempt to read an invalid file.\n"
" /r - Disable rename of *.x86 files to pxe.com.N.\n"
" /v - Verbose output.\n"
" /w:directory - Allow writes to the specified directory.\n"
" {directories} - Directories to serve read only.\n"
);
}
//////////////////////////////////////////////////////////////////////////////
//
static HWND s_hWndMain;
BOOL TimerSet(ITimerSink *pTimerSink, UINT nMilliseconds)
{
if (!::SetTimer(s_hWndMain, (UINT_PTR)pTimerSink, nMilliseconds, NULL)) {
return FALSE;
}
return TRUE;
}
BOOL TimerClr(ITimerSink *pTimerSink)
{
return ::KillTimer(s_hWndMain, (UINT_PTR)pTimerSink);
}
int main(int argc, char **argv)
{
INT nResult = 0;
CDhcpSink dhcpSink;
CPxepSink pxepSink;
CTftpSink tftpSink;
CDhcpState *m_pDhcpState = NULL;
CDhcp * m_pDhcpRaw = NULL;
CDhcp * m_pDhcpPxe = NULL;
CTftp * m_pTftp = NULL;
PCHAR rpszAppsArgs[128];
PCHAR rpszDhcpArgs[128];
PCHAR rpszTftpArgs[128];
INT nAppsArgs = 0;
INT nDhcpArgs = 0;
INT nTftpArgs = 0;
// Initialize Winsock, AFTER the window is created above.
WSADATA WSAData;
int iRes = WSAStartup(MAKEWORD(1,1), &WSAData);
if (0 != iRes) {
printf("WSAStartup failed.\n");
return 1;
}
enum {
APPS = 0,
DHCP = 1,
TFTP = 2,
} whose = APPS;
if (argc == 1) {
Usage();
return 0;
}
for (int arg = 1; arg < argc; arg++) {
if (_stricmp(argv[arg], "/apps") == 0 || _stricmp(argv[arg], "-apps") == 0)
{
whose = APPS;
}
else if (_stricmp(argv[arg], "/dhcp") == 0 || _stricmp(argv[arg], "-dhcp") == 0)
{
whose = DHCP;
}
else if (_stricmp(argv[arg], "/tftp") == 0 || _stricmp(argv[arg], "-tftp") == 0)
{
whose = TFTP;
}
else
{
switch (whose)
{
case APPS:
rpszAppsArgs[nAppsArgs++] = argv[arg];
break;
case DHCP:
rpszDhcpArgs[nDhcpArgs++] = argv[arg];
break;
case TFTP:
rpszTftpArgs[nTftpArgs++] = argv[arg];
break;
}
}
}
rpszAppsArgs[nAppsArgs] = NULL;
rpszDhcpArgs[nDhcpArgs] = NULL;
rpszTftpArgs[nTftpArgs] = NULL;
BOOL fNeedHelp = FALSE;
for (INT arg = 0; arg < nAppsArgs; arg++)
{
if (rpszAppsArgs[arg][0] == '-' || rpszAppsArgs[arg][0] == '/')
{
char *argn = rpszAppsArgs[arg]+1; // Argument name
char *argp = argn; // Argument parameter
while (*argp && *argp != ':')
{
argp++;
}
if (*argp == ':')
{
*argp++ = '\0';
}
switch (argn[0])
{
case 'h': // Help
case 'H':
case '?':
fNeedHelp = TRUE;
break;
default:
printf("Unknown argument: %s\n", rpszAppsArgs[arg]);
fNeedHelp = TRUE;
break;
}
}
else
{
printf("Unknown argument: %s\n", rpszAppsArgs[arg]);
fNeedHelp = TRUE;
}
}
if (fNeedHelp) {
Usage();
return 1;
}
// Create a message window.
//
WNDCLASS wc;
ZeroMemory(&wc, sizeof(wc));
wc.lpfnWndProc = DefWindowProc; /* Function to retrieve messages for */
wc.lpszClassName = "bootd"; /* Name used in call to CreateWindow. */
ATOM atomClass = RegisterClass(&wc);
assert(atomClass);
s_hWndMain = CreateWindow("bootd", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
assert(s_hWndMain);
SocketInit(s_hWndMain, WM_SOCKET_EVENT);
UINT nError;
CTftp::ConfigureFiles(&tftpSink, nTftpArgs, rpszTftpArgs);
m_pDhcpState = new CDhcpState();
assert(m_pDhcpState);
m_pDhcpState->Configure(&dhcpSink, nDhcpArgs, rpszDhcpArgs);
printf("\n");
TimerSet(NULL, 10000);
////////////////////////////////////////////////////////// Process Events.
//
MSG msg;
BOOL fScanNetwork = TRUE;
for (;;) {
if (fScanNetwork) {
fScanNetwork = FALSE;
CNetwork *pNetworks = NULL;
SocketLoadNetworks(&pNetworks);
for (; pNetworks != NULL; pNetworks = pNetworks->pNext) {
if (pNetworks->fChanged) {
OnNodeMessagef(pNetworks->nAddr, "::::", "Changed");
if (m_pDhcpRaw) {
m_pDhcpRaw->OnNetworkChange(pNetworks->nAddr,
pNetworks->nMask,
pNetworks->nGate);
}
if (m_pTftp) {
m_pTftp->OnNetworkChange(pNetworks->nAddr,
pNetworks->nMask,
pNetworks->nGate);
}
if (m_pDhcpPxe) {
m_pDhcpPxe->OnNetworkChange(pNetworks->nAddr,
pNetworks->nMask,
pNetworks->nGate);
}
printf("\n");
continue;
}
else if (pNetworks->fDeleted) {
OnNodeMessagef(pNetworks->nAddr, "::::", "Deleted");
if (m_pDhcpRaw) {
m_pDhcpRaw->OnNetworkDelete(pNetworks->nAddr, &m_pDhcpRaw);
}
if (m_pTftp) {
m_pTftp->OnNetworkDelete(pNetworks->nAddr, &m_pTftp);
}
if (m_pDhcpPxe) {
m_pDhcpPxe->OnNetworkDelete(pNetworks->nAddr, &m_pDhcpPxe);
}
printf("\n");
continue;
}
else if (!pNetworks->fCreated) {
continue;
}
OnNodeMessagef(pNetworks->nAddr, "::::", "Mask: %s, Gate: %s",
SocketToString(pNetworks->nMask),
SocketToString(pNetworks->nGate));
m_pDhcpRaw = new CDhcp(m_pDhcpRaw);
assert(m_pDhcpRaw);
m_pDhcpRaw->Configure(pNetworks->nAddr,
pNetworks->nMask,
pNetworks->nGate,
m_pDhcpState, &dhcpSink, DHCP_SERVER_PORT);
nError = SocketCreate(pNetworks->nAddr, DHCP_SERVER_PORT, m_pDhcpRaw);
if (nError != 0)
{
SocketErrorBox(nError, __FILE__, __LINE__);
nResult = 1;
goto abort;
}
m_pTftp = new CTftp(m_pTftp);
assert(m_pTftp);
m_pTftp->Configure(pNetworks->nAddr, TFTP_PORT,
&tftpSink, nTftpArgs, rpszTftpArgs);
nError = SessionFactoryCreate(pNetworks->nAddr, TFTP_PORT, m_pTftp);
if (nError != 0)
{
SocketErrorBox(nError, __FILE__, __LINE__);
nResult = 3;
goto abort;
}
m_pDhcpPxe = new CDhcp(m_pDhcpPxe);
assert(m_pDhcpPxe);
m_pDhcpPxe->Configure(pNetworks->nAddr,
pNetworks->nMask,
pNetworks->nGate,
m_pDhcpState, &pxepSink, PXE_SERVER_PORT);
nError = SocketCreate(pNetworks->nAddr, PXE_SERVER_PORT, m_pDhcpPxe);
if (nError != 0)
{
SocketErrorBox(nError, __FILE__, __LINE__);
nResult = 4;
goto abort;
}
printf("\n");
}
}
DWORD what = MsgWaitForMultipleObjectsEx(0, NULL, INFINITE, QS_ALLEVENTS,
MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
if (what == WAIT_OBJECT_0) {
if (!GetMessage(&msg, NULL, 0, 0)) {
break;
}
if (msg.hwnd == s_hWndMain) {
switch (msg.message) {
case WM_SOCKET_EVENT:
SocketEvent(msg.wParam, msg.lParam);
break;
case WM_TIMER:
if (msg.wParam == 0) {
fScanNetwork = TRUE;
}
else {
ITimerSink *pTimerSink = (ITimerSink *)msg.wParam;
pTimerSink->OnTimerFired();
}
break;
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
abort:
if (m_pDhcpPxe)
{
delete m_pDhcpPxe;
m_pDhcpPxe = NULL;
}
if (m_pTftp)
{
delete m_pTftp;
m_pTftp = NULL;
}
if (m_pDhcpRaw)
{
delete m_pDhcpRaw;
m_pDhcpRaw = NULL;
}
if (m_pDhcpState)
{
delete m_pDhcpState;
m_pDhcpState = NULL;
}
// close down winsock
WSACleanup();
return 0;
}
//
///////////////////////////////////////////////////////////////// End of File.