/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) Microsoft Corporation. All rights reserved. // #include #include #include #include #include #include #include "dhcpd.h" /* * DHCP Standard Options. */ #define DHCP_OPTION_PAD 0 #define DHCP_OPTION_SUBNET_MASK 1 #define DHCP_OPTION_TIME_OFFSET 2 #define DHCP_OPTION_ROUTER_ADDRESS 3 #define DHCP_OPTION_TIME_SERVERS 4 #define DHCP_OPTION_IEN116_NAME_SERVERS 5 #define DHCP_OPTION_DOMAIN_NAME_SERVERS 6 #define DHCP_OPTION_LOG_SERVERS 7 #define DHCP_OPTION_COOKIE_SERVERS 8 // overridden w/ Command Line #define DHCP_OPTION_LPR_SERVERS 9 #define DHCP_OPTION_IMPRESS_SERVERS 10 // overridden w/ Boot File #define DHCP_OPTION_RLP_SERVERS 11 #define DHCP_OPTION_HOST_NAME 12 #define DHCP_OPTION_BOOT_FILE_SIZE 13 #define DHCP_OPTION_MERIT_DUMP_FILE 14 #define DHCP_OPTION_DOMAIN_NAME 15 #define DHCP_OPTION_SWAP_SERVER 16 #define DHCP_OPTION_ROOT_DISK 17 #define DHCP_OPTION_EXTENSIONS_PATH 18 /* * IP Layer Parameters - per host */ #define DHCP_OPTION_BE_A_ROUTER 19 #define DHCP_OPTION_NON_LOCAL_SOURCE_ROUTING 20 #define DHCP_OPTION_POLICY_FILTER_FOR_NLSR 21 #define DHCP_OPTION_MAX_REASSEMBLY_SIZE 22 #define DHCP_OPTION_DEFAULT_TTL 23 #define DHCP_OPTION_PMTU_AGING_TIMEOUT 24 #define DHCP_OPTION_PMTU_PLATEAU_TABLE 25 /* * IP Layer Parameters - per interface. */ #define DHCP_OPTION_MTU 26 #define DHCP_OPTION_ALL_SUBNETS_MTU 27 #define DHCP_OPTION_BROADCAST_ADDRESS 28 #define DHCP_OPTION_PERFORM_MASK_DISCOVERY 29 #define DHCP_OPTION_BE_A_MASK_SUPPLIER 30 #define DHCP_OPTION_PERFORM_ROUTER_DISCOVERY 31 #define DHCP_OPTION_ROUTER_SOLICITATION_ADDR 32 #define DHCP_OPTION_STATIC_ROUTES 33 #define DHCP_OPTION_TRAILERS 34 #define DHCP_OPTION_ARP_CACHE_TIMEOUT 35 #define DHCP_OPTION_ETHERNET_ENCAPSULATION 36 /* * TCP Parameters - per host */ #define DHCP_OPTION_TTL 37 #define DHCP_OPTION_KEEP_ALIVE_INTERVAL 38 #define DHCP_OPTION_KEEP_ALIVE_DATA_SIZE 39 /* * Application Layer Parameters */ #define DHCP_OPTION_NETWORK_INFO_SERVICE_DOM 40 #define DHCP_OPTION_NETWORK_INFO_SERVERS 41 #define DHCP_OPTION_NETWORK_TIME_SERVERS 42 /* * Vender Specific Information Option */ #define DHCP_OPTION_VENDOR_SPEC_INFO 43 /* * NetBIOS Over TCP/IP Name Server Options */ #define DHCP_OPTION_NETBIOS_NAME_SERVER 44 #define DHCP_OPTION_NETBIOS_DATAGRAM_SERVER 45 #define DHCP_OPTION_NETBIOS_NODE_TYPE 46 #define DHCP_OPTION_NETBIOS_SCOPE_OPTION 47 /* * X Window System Options */ #define DHCP_OPTION_XWINDOW_FONT_SERVER 48 #define DHCP_OPTION_XWINDOW_DISPLAY_MANAGER 49 /* * DHCP Extensions */ #define DHCP_OPTION_REQUESTED_ADDRESS 50 #define DHCP_OPTION_LEASE_TIME 51 #define DHCP_OPTION_OK_TO_OVERLAY 52 #define DHCP_OPTION_MESSAGE_TYPE 53 #define DHCP_OPTION_SERVER_IDENTIFIER 54 #define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 #define DHCP_OPTION_MESSAGE 56 #define DHCP_OPTION_MESSAGE_LENGTH 57 #define DHCP_OPTION_RENEWAL_TIME 58 /* T1 */ #define DHCP_OPTION_REBIND_TIME 59 /* T2 */ #define DHCP_OPTION_CLIENT_CLASS_INFO 60 #define DHCP_OPTION_CLIENT_ID 61 /* * More Application Layer Parameters */ #define DHCP_OPTION_NIS_PLUS_DOM 64 #define DHCP_OPTION_NIS_PLUS 65 /* * Overlaid Header Field Replacements */ #define DHCP_OPTION_TFTP_SERVER_NAME 66 #define DHCP_OPTION_TFTP_BOOTFILE_NAME 67 /* * Even More Application Layer Parameters */ #define DHCP_OPTION_MOBILE_IP_HOME_AGENTS 68 #define DHCP_OPTION_SMTP_SERVERS 69 #define DHCP_OPTION_POP_SERVERS 70 #define DHCP_OPTION_NNTP_SERVERS 71 #define DHCP_OPTION_WWW_SERVERS 72 #define DHCP_OPTION_FINGER_SERVERS 73 #define DHCP_OPTION_IRC_SERVERS 74 #define DHCP_OPTION_STREETTALK_SERVERS 75 #define DHCP_OPTION_STREETTALK_DIR_SERVERS 76 /* * Option codes from 77 to 127 are reserved through the * Internet Assigned Numbers Authority (iana@isi.edu). */ /* * PXE Parameters */ #define DHCP_OPTION_PXE_CLIENT_ARCH_ID 93 #define DHCP_OPTION_PXE_CLIENT_NIC_ID 94 #define DHCP_OPTION_PXE_CLIENT_ID 97 /* * Option codes from 128 to 254 are for site-specific options. */ #define DHCP_OPTION_END 255 ////////////////////////////////////////////////////////////////////////////// #define DHCP_MAGIC_COOKIE 0x63825363 #pragma pack(push) #pragma pack(1) struct OPTIONS { BYTE Code; BYTE Length; BYTE Data[1]; }; struct DHCP_CMD { BYTE op; BYTE htype; BYTE hlen; BYTE hops; UINT32 xid; UINT16 secs; UINT16 flags; // UNUSED for BOOTP UINT32 ciaddr; UINT32 yiaddr; UINT32 siaddr; UINT32 giaddr; BYTE chaddr[16]; CHAR sname[64]; CHAR file[128]; union { struct { UINT32 cookie; OPTIONS options; } DHCP; struct { OPTIONS options; } BOOTP; }; }; #pragma pack(pop) typedef enum { BOOTREQUEST = 1, BOOTREPLY = 2 } BOOT_MESSAGE_TYPE; typedef enum { DHCPDISCOVER = 1, DHCPOFFER = 2, DHCPREQUEST = 3, DHCPDECLINE = 4, DHCPACK = 5, DHCPNAK = 6, DHCPRELEASE = 7, DHCPINFORM = 8 } DHCP_MESSAGE_TYPE; #define MESSAGE_TYPE_MIN DHCPDISCOVER #define MESSAGE_TYPE_MAX DHCPINFORM #define MESSAGE_TYPE_COUNT (MESSAGE_TYPE_MAX-MESSAGE_TYPE_MIN+1) CHAR *szMessageTypes[MESSAGE_TYPE_COUNT] = {"Discover", "Offer", "Request", "Decline", "Ack", "Nak", "Release", "Inform" }; #define OPERATOR_TYPE_MIN BOOTREQUEST #define OPERATOR_TYPE_MAX BOOTREPLY #define OPERATOR_TYPE_COUNT (OPERATOR_TYPE_MAX-OPERATOR_TYPE_MIN+1) CHAR *szOperatorTypes[OPERATOR_TYPE_COUNT] = {"Request", "Reply"}; #define ARRAYOF(x) (sizeof(x)/sizeof(x[0])) ////////////////////////////////////////////////////////////////////////////// // static void NormalizeMacRep(PSTR str) { do { if (*str == ':') *str = '-'; } while (*++str != '\0'); } ////////////////////////////////////////////////////////////////////////////// // static UINT64 IdFromMac(PBYTE pbMac) { UINT64 idMac = 0; ((PBYTE)&idMac)[0] = pbMac[5]; ((PBYTE)&idMac)[1] = pbMac[4]; ((PBYTE)&idMac)[2] = pbMac[3]; ((PBYTE)&idMac)[3] = pbMac[2]; ((PBYTE)&idMac)[4] = pbMac[1]; ((PBYTE)&idMac)[5] = pbMac[0]; return idMac; } #if 0 static VOID MacFromId(UINT64 idMac, PBYTE pbMac) { pbMac[0] = ((PBYTE)&idMac)[5]; pbMac[1] = ((PBYTE)&idMac)[4]; pbMac[2] = ((PBYTE)&idMac)[3]; pbMac[3] = ((PBYTE)&idMac)[2]; pbMac[4] = ((PBYTE)&idMac)[1]; pbMac[5] = ((PBYTE)&idMac)[0]; } #endif ////////////////////////////////////////////////////////////////////////////// // CDhcpNode::CDhcpNode() { Enabled = FALSE; Name[0] = '\0'; idMac = 0; iaLast = 0; iaAssigned = 0; } ////////////////////////////////////////////////////////////////////////////// // CDhcpState::CDhcpState() { strcpy(m_szBootFile, "pxe.com"); strcpy(m_szCommand, ""); } CDhcpState::~CDhcpState() { } ////////////////////////////////////////////////////////////////////////////// // static UINT32 ReadIpAddr(PCHAR psz) { UINT32 nAddr = 0; UINT32 nDigit = 0; for (; *psz; psz++) { if (*psz >= '0' && *psz <= '9') { nDigit = nDigit * 10 + *psz - '0'; } else if (*psz == '.') { nAddr = (nAddr << 8) | nDigit; nDigit = 0; } } nAddr = (nAddr << 8) | nDigit; return nAddr; } static void ReadMacFlags(CDhcpNode *pNode, PCHAR pszFlags) { while (*pszFlags) { /////////////////////////////////////////////////////////// Find Flag. // while (*pszFlags && isspace(*pszFlags)) { pszFlags++; } if (*pszFlags == '\0' || *pszFlags == ';' || *pszFlags == '#') { break; } char *argn = pszFlags; // Argument name while (*pszFlags && !isspace(*pszFlags) && *pszFlags != ':' && *pszFlags != '=') { pszFlags++; } if (*pszFlags == ':' || *pszFlags == '=') { // Terminate name. *pszFlags++ = '\0'; } char *argp = pszFlags; // Argument parameter while (*pszFlags && !isspace(*pszFlags)) { pszFlags++; } if (*pszFlags) { // Terminate parameter *pszFlags++ = '\0'; } //////////////////////////////////////////////////////// Process Flag. // if (_strnicmp(argn, "enable", 4) == 0) { pNode->Enabled = TRUE; } else if (_strnicmp(argn, "disable", 4) == 0) { pNode->Enabled = FALSE; } else if (_strnicmp(argn, "name", 4) == 0) { strcpy(pNode->Name, argp); } } } void CDhcpState::CheckMacFlags(CDhcpNode *pNode) { if (pNode->Name[0] == '\0') { sprintf(pNode->Name, "IP_%d_%d_%d_%d", ((PBYTE)&pNode->iaAssigned)[3], ((PBYTE)&pNode->iaAssigned)[2], ((PBYTE)&pNode->iaAssigned)[1], ((PBYTE)&pNode->iaAssigned)[0]); } else { PCHAR psz; if ((psz = strchr(pNode->Name, '.')) != NULL) { *psz = '\0'; } } } BOOL CDhcpState::AddMacEntry(PCSTR psz) { UINT IdMac[6] = { 0,0,0,0,0,0}; UINT IpAddr[4] = { 0,0,0,0 }; INT nRead = sscanf(psz, "%02x-%02x-%02x-%02x-%02x-%02x,%d.%d.%d.%d", &IdMac[5], &IdMac[4], &IdMac[3], &IdMac[2], &IdMac[1], &IdMac[0], &IpAddr[3], &IpAddr[2], &IpAddr[1], &IpAddr[0]); if (nRead != 6 && nRead != 10) { return FALSE; } CDhcpNode *pNode = new CDhcpNode; pNode->Enabled = TRUE; pNode->idMac = 0; for (INT n = 0; n < 6; n++) { ((PBYTE)&pNode->idMac)[n] = (BYTE)IdMac[n]; } for (INT n = 0; n < 4; n++) { ((PBYTE)&pNode->iaAssigned)[n] = (BYTE)IpAddr[n]; } CheckMacFlags(pNode); if (m_pSink) { m_pSink->OnDhcpMessage(pNode->iaAssigned, "Added mac %02x-%02x-%02x-%02x-%02x-%02x", ((PBYTE)&pNode->idMac)[5], ((PBYTE)&pNode->idMac)[4], ((PBYTE)&pNode->idMac)[3], ((PBYTE)&pNode->idMac)[2], ((PBYTE)&pNode->idMac)[1], ((PBYTE)&pNode->idMac)[0] ); } m_MacsById.Insert(pNode->idMac, pNode); return TRUE; } BOOL CDhcpState::ReadMacEntries(PCSTR pszFile) { PCHAR psz; CHAR entry[768]; FILE *fMap = fopen(pszFile, "r"); if (fMap != NULL) { while ((psz = fgets(entry, sizeof(entry), fMap)) != NULL) { while (*psz && isspace(*psz)) { psz++; } if (*psz == '\0' || *psz == ';' || *psz == '#') { // Comment continue; } else if (*psz == '*') { // Defaults } else { char flags[768] = ""; UINT IdMac[6] = { 0,0,0,0,0,0}; UINT IpAddr[4] = { 0,0,0,0 }; CDhcpNode *pNode = new CDhcpNode; NormalizeMacRep(psz); pNode->idMac = 0; INT nRead = sscanf(psz, "%02x-%02x-%02x-%02x-%02x-%02x %d.%d.%d.%d %[^\n]s", &IdMac[5], &IdMac[4], &IdMac[3], &IdMac[2], &IdMac[1], &IdMac[0], &IpAddr[3], &IpAddr[2], &IpAddr[1], &IpAddr[0], flags); pNode->idMac = 0; for (INT n = 0; n < 6; n++) { ((PBYTE)&pNode->idMac)[n] = (BYTE)IdMac[n]; } for (INT n = 0; n < 4; n++) { ((PBYTE)&pNode->iaAssigned)[n] = (BYTE)IpAddr[n]; } if (nRead == 6 || nRead >= 10) { ReadMacFlags(pNode, flags); CheckMacFlags(pNode); m_MacsById.Insert(pNode->idMac, pNode); pNode = NULL; } else { delete pNode; pNode = NULL; if (m_pSink) { m_pSink->OnDhcpMessage(0, "INI Error: %s", psz); } } } } fclose(fMap); return TRUE; } return FALSE; } CDhcpNode *CDhcpState::FindMacEntry(UINT64 idMac) { return m_MacsById.Find(idMac); } BOOL CDhcpState::IsPxeDhcpPacket(PBYTE pbPacket, UINT32 cbPacket) { DHCP_CMD *dhcpPacket = (DHCP_CMD *)pbPacket; OPTIONS *pOption = NULL; OPTIONS *pOptionEnd = (OPTIONS *)(pbPacket + cbPacket); if (DHCP_MAGIC_COOKIE == ntohl(dhcpPacket->DHCP.cookie)) { pOption = &dhcpPacket->DHCP.options; } else { pOption = &dhcpPacket->BOOTP.options; } while (pOption < pOptionEnd) { if (pOption->Code == DHCP_OPTION_CLIENT_CLASS_INFO) { if (pOption->Length >= 9 && memcmp(pOption->Data, "PXEClient", 9) == 0) { return TRUE; } } if (pOption->Code == 0) pOption = (OPTIONS *)(((CHAR *)pOption) + 1); else pOption = (OPTIONS *)(((CHAR *)pOption) + pOption->Length + 2); } return FALSE; } static BOOL GetDhcpOptions(DHCP_CMD *dhcpPacket, UINT32 cbPacket, PCHAR pszName, PCHAR pszType, PCHAR pszClas, PUINT16 pusPxeArch, PUINT16 pusPxeVers, GUID * pguidPxeClientId ) { OPTIONS *pOptionEnd = (OPTIONS *)(((CHAR *)dhcpPacket) + cbPacket); // dump all the options OPTIONS *pOption = NULL; if (DHCP_MAGIC_COOKIE == ntohl(dhcpPacket->DHCP.cookie)) { pOption = &dhcpPacket->DHCP.options; } else { pOption = &dhcpPacket->BOOTP.options; } CHAR szTemp[256]; if (pszName == NULL) { pszName = szTemp; } if (pszType == NULL) { pszType = szTemp; } if (pszClas == NULL) { pszClas = szTemp; } pszName[0] = pszType[0] = pszClas[0] = '\0'; *pusPxeArch = 0; *pusPxeVers = 0; memset(pguidPxeClientId, 0, sizeof(*pguidPxeClientId)); while (pOption < pOptionEnd) { switch (pOption->Code) { case DHCP_OPTION_HOST_NAME: // HOST NAME strncpy(pszName, (PCHAR)pOption->Data, pOption->Length); pszName[pOption->Length] = '\0'; break; case DHCP_OPTION_MESSAGE_TYPE: if ((MESSAGE_TYPE_MAX >= pOption->Data[0]) && (MESSAGE_TYPE_MIN <= pOption->Data[0])) { strcpy(pszType, szMessageTypes[pOption->Data[0] - MESSAGE_TYPE_MIN]); } else { sprintf(pszType, "UNKNOWN (0x%02x)", pOption->Data[0]); } break; case DHCP_OPTION_CLIENT_CLASS_INFO: // CLASS IDENTIFIER strncpy(pszClas, (PCHAR)pOption->Data, pOption->Length); pszClas[pOption->Length] = '\0'; break; case DHCP_OPTION_PXE_CLIENT_ARCH_ID: *pusPxeArch = ntohs(*(PUINT16)pOption->Data); break; case DHCP_OPTION_PXE_CLIENT_NIC_ID: if (pOption->Data[0] == 1) { *pusPxeArch = ntohs(*(PUINT16)(pOption->Data + 1)); } break; case DHCP_OPTION_PXE_CLIENT_ID: if (pOption->Data[0] == 0) { memcpy(pguidPxeClientId, pOption->Data + 1, 16); } break; case DHCP_OPTION_END: pOption = pOptionEnd; break; } if (0 == pOption->Code) pOption = (OPTIONS *)(((CHAR *)pOption) + 1); else pOption = (OPTIONS *)(((CHAR *)pOption) + pOption->Length + 2); } return TRUE; } void AddOption(OPTIONS *&pOption, BYTE code, BYTE len, BYTE *data) { pOption->Code = code; pOption->Length = len; if (len > 0) { memcpy(pOption->Data, data, len); } // increment pointer to next option location, include code and len bytes too if (DHCP_OPTION_PAD == pOption->Code || DHCP_OPTION_END == pOption->Code) { pOption = (OPTIONS *)(((CHAR *)pOption) + 1); } else { pOption = (OPTIONS *)(((CHAR *)pOption) + pOption->Length + 2); } } void AddOption(OPTIONS *&pOption, BYTE code, UINT32 iaAddr) { iaAddr = htonl(iaAddr); AddOption(pOption, code, sizeof(iaAddr), (BYTE*)&iaAddr); } VOID CDhcpState::Configure(IDhcpSink *pSink, INT argc, PCHAR *argv) { m_pSink = pSink; m_fQuiet = FALSE; for (INT arg = 0; arg < argc; arg++) { if (argv[arg][0] == '-' || argv[arg][0] == '/') { CHAR args[1024]; strcpy(args, argv[arg]); char *argn = args+1; // Argument name char *argp = argn; // Argument parameter while (*argp && *argp != ':') { argp++; } if (*argp == ':') { *argp++ = '\0'; } switch (argn[0]) { case 'b': // Set boot file. case 'B': strcpy(m_szBootFile, argp); break; case 'c': // Set cookie. case 'C': if (m_szCommand[0]) { strcat(m_szCommand, " "); strcat(m_szCommand, argp); } else { strcpy(m_szCommand, argp); } break; case 'i': // Set .ini file name. case 'I': if (!ReadMacEntries(argp)) { m_pSink->OnDhcpMessage(0, "Error reading `%s'."); } break; case 'm': // Mac address of client case 'M': NormalizeMacRep(argp); AddMacEntry(argp); break; case 'q': case 'Q': m_fQuiet = TRUE; break; case '?': case 'h': case 'H': break; } } else { } } m_pSink->OnDhcpMessage(0, "Boot File: %s", m_szBootFile); m_pSink->OnDhcpMessage(0, "CommandLn: %s", m_szCommand); } ////////////////////////////////////////////////////////////////////////////// // CDhcp::CDhcp(CDhcp *pNext) { m_pNext = pNext; m_pState = NULL; m_pSink = NULL; m_nAddr = 0; m_nPort = 0; } CDhcp::~CDhcp() { if (m_pSink) { m_pSink->OnDhcpMessage(m_nAddr, "Server stopped."); } if (m_pNext) { delete m_pNext; m_pNext = NULL; } } VOID CDhcp::OnNetworkChange(UINT32 nAddr, UINT32 nMask, UINT32 nGate) { if (m_nAddr == nAddr) { m_nMask = nMask; m_nGate = nGate; if (m_pSink) { m_pSink->OnDhcpMessage(nAddr, "Network change %s/%s", SocketToString(m_nMask), SocketToString(m_nGate)); } } if (m_pNext) { m_pNext->OnNetworkChange(nAddr, nMask, nGate); } } VOID CDhcp::OnNetworkDelete(UINT32 nAddr, CDhcp **ppNext) { if (m_nAddr == nAddr) { *ppNext = m_pNext; if (m_pNext) { m_pNext->OnNetworkDelete(nAddr, ppNext); } m_pNext = NULL; delete this; } else { if (m_pNext) { m_pNext->OnNetworkDelete(nAddr, &m_pNext); } } } VOID CDhcp::Configure(UINT32 nAddr, UINT32 nMask, UINT32 nGate, CDhcpState *pState, IDhcpSink *pSink, UINT16 nPort) { m_pState = pState; m_nAddr = nAddr; m_nMask = nMask; m_nGate = nGate; m_nPort = nPort; m_pSink = pSink; if (m_pSink) { m_pSink->OnDhcpMessage(0, "DHCP Server: %s:%d", SocketToString(m_nAddr), m_nPort); } } VOID CDhcp::OnSocketRecv(UINT dwError, UINT32 nAddr, UINT16 nPort, PBYTE pbData, UINT cbData) { #if 0 m_pSink->OnDhcpMessage(nAddr, "=> port %d to %d", nPort, m_nPort); #endif if (dwError) { m_pSink->OnDhcpSocketError(dwError); done: return; } if (nAddr == 0) { nAddr = INADDR_BROADCAST; } CHAR szName[256]; CHAR szType[256]; CHAR szClas[256]; UINT16 usPxeArchId; UINT16 usPxeUndiId; GUID guidPxeClientId; DHCP_CMD *dhcpRequest = (DHCP_CMD *)pbData; UINT64 idMac = IdFromMac(dhcpRequest->chaddr); CDhcpNode *pEntry = m_pState->FindMacEntry(idMac); BOOL fIsPxe = m_pState->IsPxeDhcpPacket(pbData, cbData); if (idMac == 0) { // Ignore requests without the ETHERNET Address. goto done; } GetDhcpOptions(dhcpRequest, cbData, szName, szType, szClas, &usPxeArchId, &usPxeUndiId, &guidPxeClientId); if (BOOTREQUEST != dhcpRequest->op || DHCP_MAGIC_COOKIE != ntohl(dhcpRequest->DHCP.cookie)) { goto done; } if ((DHCP_OPTION_MESSAGE_TYPE != dhcpRequest->DHCP.options.Code) || (DHCPREQUEST != dhcpRequest->DHCP.options.Data[0] && DHCPDISCOVER != dhcpRequest->DHCP.options.Data[0])) { goto done; } if (pEntry == NULL && DHCPDISCOVER != dhcpRequest->DHCP.options.Data[0]) { goto done; } if (pEntry == NULL && m_pState->m_fQuiet) { goto done; } if (m_pSink) { m_pSink->OnDhcpMessage(nAddr, "=> %s from %02x-%02x-%02x-%02x-%02x-%02x port %d", szType, ((PBYTE)&idMac)[5], ((PBYTE)&idMac)[4], ((PBYTE)&idMac)[3], ((PBYTE)&idMac)[2], ((PBYTE)&idMac)[1], ((PBYTE)&idMac)[0], nPort); m_pSink->OnDhcpMessage(nAddr, " %s%s %s", fIsPxe ? "[PXE] " : "", szClas, szName); } if (pEntry == NULL || pEntry->Enabled == FALSE) { goto done; } ///// Send the Reply. BYTE PacketOut[1600]; // fill in packet before sending DHCP_CMD *pdhcpReply = (DHCP_CMD *)PacketOut; memset(pdhcpReply, 0, sizeof(*pdhcpReply)); // broadcast, or use the client specified address for replies if (dhcpRequest->ciaddr) { pEntry->iaLast = ntohl(dhcpRequest->ciaddr); } pdhcpReply->op = BOOTREPLY; // REPLY pdhcpReply->htype = 1; // ETHERNET pdhcpReply->hlen = 6; // 6 BYTE HARDWARE ADDRESS LENGTH (MAC) pdhcpReply->hops = 0; pdhcpReply->xid = dhcpRequest->xid; pdhcpReply->secs = 0; pdhcpReply->flags = dhcpRequest->flags; pdhcpReply->ciaddr = dhcpRequest->ciaddr; if (fIsPxe && dhcpRequest->ciaddr == htonl(pEntry->iaAssigned)) { pdhcpReply->yiaddr = 0; } else { pdhcpReply->yiaddr = htonl(pEntry->iaAssigned); } pdhcpReply->siaddr = htonl(m_nAddr); pdhcpReply->giaddr = 0; memcpy(pdhcpReply->chaddr, dhcpRequest->chaddr, 16); sprintf(pdhcpReply->sname, "%d.%d.%d.%d", ((BYTE *)&m_nAddr)[3], ((BYTE *)&m_nAddr)[2], ((BYTE *)&m_nAddr)[1], ((BYTE *)&m_nAddr)[0]); strcpy(pdhcpReply->file, m_pState->m_szBootFile); pdhcpReply->DHCP.cookie = htonl(DHCP_MAGIC_COOKIE); // start pointing to first location for options OPTIONS *pOptions = &pdhcpReply->DHCP.options; // START OF OPTION ADDITIONS // DHCP MESSAGE TYPE OPTION - MUST BE FIRST OPTION BYTE data[1] = { DHCPACK }; if (dhcpRequest->DHCP.options.Data[0] == DHCPREQUEST) { data[0] = DHCPACK; if (m_pSink) { m_pSink->OnDhcpAck(pEntry->iaAssigned, idMac); } } else if (dhcpRequest->DHCP.options.Data[0] == DHCPDISCOVER) { data[0] = DHCPOFFER; if (m_pSink) { m_pSink->OnDhcpOffer(pEntry->iaAssigned, idMac); } } AddOption(pOptions, DHCP_OPTION_MESSAGE_TYPE, 1, data); if (fIsPxe) { AddOption(pOptions, DHCP_OPTION_SERVER_IDENTIFIER, m_nAddr); if (m_nGate) { AddOption(pOptions, DHCP_OPTION_ROUTER_ADDRESS, m_nGate); } else { // Older PXE implementations (like the Windows Boot Floppy) // won't boot without a router address. AddOption(pOptions, DHCP_OPTION_ROUTER_ADDRESS, m_nAddr); } AddOption(pOptions, DHCP_OPTION_SUBNET_MASK, m_nMask); BYTE ClientId[17]; ClientId[0] = 0x00; memcpy(ClientId + 1, &guidPxeClientId, sizeof(guidPxeClientId)); AddOption(pOptions, DHCP_OPTION_PXE_CLIENT_ID, sizeof(ClientId), ClientId); AddOption(pOptions, DHCP_OPTION_CLIENT_CLASS_INFO, 9, (BYTE*)"PXEClient"); AddOption(pOptions, DHCP_OPTION_HOST_NAME, (BYTE)strlen(pEntry->Name), (BYTE*)pEntry->Name); AddOption(pOptions, DHCP_OPTION_COOKIE_SERVERS, (BYTE)strlen(m_pState->m_szCommand), (BYTE*)m_pState->m_szCommand); AddOption(pOptions, DHCP_OPTION_END, 0, NULL); } else { AddOption(pOptions, DHCP_OPTION_SERVER_IDENTIFIER, m_nAddr); if (m_nGate) { AddOption(pOptions, DHCP_OPTION_ROUTER_ADDRESS, m_nGate); } AddOption(pOptions, DHCP_OPTION_SUBNET_MASK, m_nMask); AddOption(pOptions, DHCP_OPTION_HOST_NAME, (BYTE)strlen(pEntry->Name), (BYTE*)pEntry->Name); // AddOption(pOptions, DHCP_OPTION_END, 0, NULL); } // END OF OPTION ADDITIONS // calc size of added options, minus the single option already included UINT32 dwOptionSize = (UINT32)pOptions - (UINT32)&pdhcpReply->DHCP.options; UINT32 dwPacketOutSize = sizeof(*pdhcpReply) + dwOptionSize - 3; // 3 since first option is already included in structure // always AF_INET if (nAddr == 0) { nAddr = INADDR_BROADCAST; } // broadcast, or use the client specified address for replies if (ntohs(dhcpRequest->flags) & 0x8000) { nAddr = INADDR_BROADCAST; } else { if (dhcpRequest->ciaddr) { nAddr = pEntry->iaLast; } else { nAddr = INADDR_BROADCAST; } } #if 0 nAddr = INADDR_BROADCAST; #endif m_pSink->OnDhcpMessage(nAddr, "<= Reply to %02x-%02x-%02x-%02x-%02x-%02x\n", ((PBYTE)&pEntry->idMac)[5], ((PBYTE)&pEntry->idMac)[4], ((PBYTE)&pEntry->idMac)[3], ((PBYTE)&pEntry->idMac)[2], ((PBYTE)&pEntry->idMac)[1], ((PBYTE)&pEntry->idMac)[0]); SocketSend(nAddr, DHCP_CLIENT_PORT, (PBYTE)PacketOut, dwPacketOutSize); goto done; } // ///////////////////////////////////////////////////////////////// End of File.