int ProcessDHCPMessage (struct dhcp_packet *pDhcpPkt, int *pSize) { unsigned char *p=NULL; struct LL_IP *pCurIP=NULL, *pProposedIP=NULL; // Thanks Sam Leitch ! int Ark, nDhcpType = 0; struct in_addr sRequestedAddr; DWORD sStaticIP; if (IsDHCP (*pDhcpPkt)) { // search DHCP message type p = DHCPSearchOptionsField (pDhcpPkt->options, DHO_DHCP_MESSAGE_TYPE, NULL); if (p!=NULL) nDhcpType = *p; } if (pDhcpPkt->yiaddr.s_addr!=INADDR_ANY && pDhcpPkt->yiaddr.s_addr!=INADDR_NONE ) return FALSE ; // address already assigned // the tab has one undef raw for (Ark=0 ; Ark<SizeOfTab(tDHCPType)-1 && nDhcpType!=tDHCPType[Ark].nType ; Ark++) ; LOG (5, "Rcvd %s Msg for IP %s, Mac %s", tDHCPType[Ark].sType, inet_ntoa (pDhcpPkt->ciaddr), haddrtoa(pDhcpPkt->chaddr, pDhcpPkt->hlen,':')); // if (sParamDHCP.nPoolSize==0) return FALSE; // no allocation pool --> listen only switch (nDhcpType) { case 0 : // BootP if(sParamDHCP.nIgnoreBootp) { LOG (5, "Ignoring Bootp request"); break; } case DHCPDISCOVER : sStaticIP = DHCP_StaticAssignation (pDhcpPkt); if (sStaticIP != INADDR_NONE) { LOG (0, "%s: statically assigned to address %s", haddrtoa(pDhcpPkt->chaddr, pDhcpPkt->hlen,':'), inet_ntoa (* (struct in_addr *) & sStaticIP) ); pDhcpPkt->yiaddr.s_addr = sStaticIP; } else { p = DHCPSearchOptionsField (pDhcpPkt->options, DHO_DHCP_REQUESTED_ADDRESS, NULL); if (p!=NULL) { pDhcpPkt->ciaddr = * (struct in_addr *) p; LOG (5, "Client requested address %s", inet_ntoa (pDhcpPkt->ciaddr)); } pProposedIP = DHCP_IPAllocate (nDhcpType, & pDhcpPkt->ciaddr, pDhcpPkt->chaddr, pDhcpPkt->hlen); if (pProposedIP == NULL) { LOG (1, "no more address or address previously allocated by another server"); return FALSE; } pDhcpPkt->yiaddr.s_addr = pProposedIP->dwIP.s_addr; LOG (2, "%s: proposed address %s", IsDHCP(*pDhcpPkt) ? "DHCP" : "BOOTP", inet_ntoa (pProposedIP->dwIP) ); //If this is a bootp, there is no other response from the client. //Since we don't want leases expiring (or being mistaken for unAcked DHCP offers), //set renewed to a distant time if(nDhcpType == 0 && sStaticIP == INADDR_NONE) // patched by Rolf Offermanns ForceRenewTime(pProposedIP, 0x66666666); // fixed by Sam Leitch } // dynamically assigned address // populate the packet to be returned pDhcpPkt->op = BOOTREPLY; // translate $IP$ and $MAC$ from boot file name TranslateExp (sParamDHCP.szBootFile, pDhcpPkt->file, pDhcpPkt->yiaddr, pDhcpPkt->chaddr); *pSize = DHCPOptionsReply (pDhcpPkt, DHCPOFFER); break ; //NJW Changed how requests are handled to mimic linux -- requests are responded to even if we didn't originally allocate, but only if the requested address is in our pool range case DHCPREQUEST : {BOOL bSERVER = FALSE; // TRUE if Tftpd32 has assigned this address // Static Allocation ? // search field REQUEST ADDR in options sStaticIP = DHCP_StaticAssignation (pDhcpPkt); if (sStaticIP != INADDR_NONE) { // populate the packet to be returned pDhcpPkt->op = BOOTREPLY; pDhcpPkt->yiaddr.s_addr = sStaticIP; // translate $IP$ and $MAC$ from boot file name TranslateExp (sParamDHCP.szBootFile, pDhcpPkt->file, pDhcpPkt->yiaddr, pDhcpPkt->chaddr); *pSize = DHCPOptionsReply (pDhcpPkt, DHCPACK); break; } // has tftpd32 dinamically assigned this address pCurIP = DHCPSearchByMacAddress (pDhcpPkt->chaddr, pDhcpPkt->hlen); if (pCurIP==NULL) return FALSE; // not attributed by Tftpd32 --> do not answer // search field REQUEST ADDR in options // if specified should fit database p = DHCPSearchOptionsField (pDhcpPkt->options, DHO_DHCP_REQUESTED_ADDRESS, NULL); if (p!=NULL) { pDhcpPkt->ciaddr = * (struct in_addr *) p; } if(AddrFitsPool(&pDhcpPkt->ciaddr)) { //Look up the address, if it's not found, or the owner is this macaddr, //or the lease was expired, allow the serving. BOOL wasexpired = FALSE; pProposedIP = DHCPSearchByIP(&pDhcpPkt->ciaddr, &wasexpired); bSERVER = !pProposedIP || wasexpired || (0 == memcmp(pProposedIP->sMacAddr, pDhcpPkt->chaddr, 6)); } if (bSERVER) { pProposedIP = DHCP_IPAllocate (nDhcpType, & pDhcpPkt->ciaddr, pDhcpPkt->chaddr, pDhcpPkt->hlen); if (pProposedIP == NULL) { LOG (1, "no more addresses or address previously allocated by another server"); return FALSE; } if (pProposedIP->tAllocated==0) SetAllocTime(pProposedIP); SetRenewTime(pProposedIP); LOG (5, "Previously allocated address %s acked", inet_ntoa (pProposedIP->dwIP)); // populate the packet to be returned pDhcpPkt->op = BOOTREPLY; pDhcpPkt->yiaddr.s_addr = pProposedIP->dwIP.s_addr; TranslateExp (sParamDHCP.szBootFile, pDhcpPkt->file, pDhcpPkt->yiaddr, pDhcpPkt->chaddr); *pSize = DHCPOptionsReply (pDhcpPkt, DHCPACK); } else { LOG (5, "Client requested address %s which was not allocated by tftpd32 and is either outside our pool or is used by someone else", inet_ntoa (pDhcpPkt->ciaddr) ); return FALSE ; // do not answer } } // Block for bSERVER declaration break; case DHCPDECLINE : // search current item and its precedent pCurIP = DHCPSearchByMacAddress (pDhcpPkt->chaddr, pDhcpPkt->hlen); if (pCurIP!=NULL) { p = DHCPSearchOptionsField (pDhcpPkt->options, DHO_DHCP_REQUESTED_ADDRESS, NULL); if (p!=NULL) { sRequestedAddr.s_addr = * (DWORD *) p; if ( pCurIP->dwIP.s_addr==sRequestedAddr.s_addr) { DHCPDestroyItem (pCurIP); LOG (5, "item destroyed"); } } } //The decline is sent when an address is already in use. Do an ARP and //add a lease for the in-use address { ULONG mac[2]; ULONG maclen = 6; // search field REQUEST ADDR in options // if specified should fit database p = DHCPSearchOptionsField (pDhcpPkt->options, DHO_DHCP_REQUESTED_ADDRESS, NULL); if (p!=NULL) { pDhcpPkt->ciaddr = * (struct in_addr *) p; } if(NO_ERROR == SendARP(pDhcpPkt->ciaddr.s_addr, 0, mac, &maclen)) { pProposedIP = DHCP_IPAllocate (nDhcpType, & pDhcpPkt->ciaddr, (unsigned char*)mac, maclen); if (pProposedIP) { if (pProposedIP->tAllocated==0) SetAllocTime(pProposedIP); ForceRenewTime(pProposedIP, 0x66666666); //Give a bootp lease, since the device may not do dhcp LOG (5, "Added lease for existing address %s", inet_ntoa (pProposedIP->dwIP)); } } } break; case DHCPRELEASE : // do not destroy the item but mark it free pCurIP = DHCPSearchByMacAddress (pDhcpPkt->chaddr, pDhcpPkt->hlen); if (pCurIP!=NULL) // then mac address found in table { ZeroAllocTime(pCurIP); ZeroRenewTime(pCurIP); LOG (5, "item %s released", haddrtoa(pDhcpPkt->chaddr, pDhcpPkt->hlen,':') ); } break; } // switch type DHCPScan(); // answer only to BootP, Request or discover msg return (nDhcpType==0 || nDhcpType==DHCPDISCOVER || nDhcpType==DHCPREQUEST); } // ProcessDHCPMessage
//Read in and initialize the leases void LoadLeases(void) { //We need to make sure the leases we load actually fit in the address pool, so we'll be //tracking the index to the lease file and the index to the allocated list int leaseindex, allocindex; // From Nick : I realized that there was a race condition in that code, // particularly with the reading and saving of KEY_LEASE_NUMLEASES // I’ve added a function, which LoadLeases calls immediately on entry: WaitForMsgQueueToFinish (LL_ID_SETTINGS); nAllocatedIP = 0; ReadKey(TFTPD32_DHCP_KEY, KEY_LEASE_NUMLEASES, &nAllocatedIP, sizeof(nAllocatedIP), REG_DWORD, szTftpd32IniFile); if (nAllocatedIP > sParamDHCP.nPoolSize) { SVC_WARNING ("The pool size is too small for the number of leases, ignoring extra leases"); nAllocatedIP = sParamDHCP.nPoolSize; } allocindex = 0; for(leaseindex = 0; leaseindex < nAllocatedIP; ++leaseindex) { char key [_MAX_PATH]; char tmpval [_MAX_PATH]; tFirstIP[allocindex] = malloc (sizeof(struct LL_IP)); memset(tFirstIP[allocindex], 0, sizeof(struct LL_IP)); tFirstIP[allocindex]->dwAllocNum = leaseindex; sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, leaseindex, KEY_LEASE_MAC); if(ReadKey(TFTPD32_DHCP_KEY, key, tmpval, _MAX_PATH, REG_SZ, szTftpd32IniFile)) atohaddr(tmpval, tFirstIP[allocindex]->sMacAddr, 6); sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, leaseindex, KEY_LEASE_IP); if(ReadKey(TFTPD32_DHCP_KEY, key, tmpval, _MAX_PATH, REG_SZ, szTftpd32IniFile)) tFirstIP[allocindex]->dwIP.s_addr = inet_addr(tmpval); sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, leaseindex, KEY_LEASE_ALLOC); if(ReadKey(TFTPD32_DHCP_KEY, key, tmpval, _MAX_PATH, REG_SZ, szTftpd32IniFile)) tFirstIP[allocindex]->tAllocated = atotime(tmpval); sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, leaseindex, KEY_LEASE_RENEW); if(ReadKey(TFTPD32_DHCP_KEY, key, tmpval, _MAX_PATH, REG_SZ, szTftpd32IniFile)) tFirstIP[allocindex]->tRenewed = atotime(tmpval); // fix errors in date conversion (registry modified at hand) if (tFirstIP[allocindex]->tAllocated == -1) tFirstIP[allocindex]->tAllocated = 0; if (tFirstIP[allocindex]->tRenewed == -1) tFirstIP[allocindex]->tRenewed = 0; //If the address doesn't fit in the pool, don't add it after all //Since we are assuming the leases were written in order, do a quick check for dups //and invalid macaddrs if((!AddrFitsPool(&tFirstIP[allocindex]->dwIP)) || (IsMacEmpty(tFirstIP[allocindex])) || ((allocindex > 0) && (tFirstIP[allocindex]->dwIP.s_addr == tFirstIP[allocindex - 1]->dwIP.s_addr))) { free(tFirstIP[allocindex]); tFirstIP[allocindex] = NULL; } else { tMAC[allocindex] = tFirstIP[allocindex]; //Copy to cross index ++allocindex; //Move on to the next one } } if(allocindex != nAllocatedIP) SetNumAllocated(allocindex); // ensure that data base is sorted (especially if we've dropped some leases in the load) qsort (tMAC, nAllocatedIP, sizeof *tMAC, MACCompare); qsort (tFirstIP, nAllocatedIP, sizeof *tFirstIP, QsortCompare); ReorderLeases(); } // LoadLeases
struct LL_IP *DHCP_IPAllocate2(struct in_addr *pPreviousAddr, const unsigned char *pMac, int nMacLen) { time_t tNow; int Ark; struct LL_IP *pCurIP /*, *pOldestIP*/; #define TWO_MINUTES 120 //If true, the client has requested an IP address that we should try to honor int useprev = (pPreviousAddr->s_addr != INADDR_ANY) && (AddrFitsPool(pPreviousAddr)); // search for the previously allocated mac address if (nMacLen>=6) // Ethernet Mac address { // search if mac the mac address is already known pCurIP = DHCPSearchByMacAddress (pMac, nMacLen); if(pCurIP) { //We found the previous MAC record. If the address requested is invalid, use //the address in the record. If it is valid, erase the record, since we should //give the requested address if(!useprev || (pPreviousAddr->s_addr == pCurIP->dwIP.s_addr)) { LOG (12, "Reply with previously allocated : %s", inet_ntoa (pCurIP->dwIP)); SetAllocTime(pCurIP); return pCurIP; } else //Remove the old address, and continue with the allocation logic { //Destroying the item takes care of the macaddr table as well. DHCPDestroyItem (pCurIP); pCurIP = NULL; } } // mac address found } // mac address not valid // search if requested address can be granted if(useprev) { // is the address already allocated but not renewed (or expired) BOOL wasexpired=FALSE; pCurIP = DHCPSearchByIP (pPreviousAddr, &wasexpired); if (pCurIP!=NULL) { //Only allocate if it's to the same address, or the lease expired, //otherwise reset for the real allocation if(wasexpired || (0 == memcmp(pCurIP->sMacAddr, pMac, nMacLen))) { LOG (5, "Request for %s granted", inet_ntoa (pCurIP->dwIP)); pCurIP = DHCPReallocItem (pCurIP, pPreviousAddr->s_addr, pMac, nMacLen); return pCurIP ; } else pCurIP = NULL; } else //Address not allocated before, just grant it { pCurIP = DHCPReallocItem (NULL, pPreviousAddr->s_addr, pMac, nMacLen); LOG (12, "Reply with requested address : %s", inet_ntoa (pCurIP->dwIP)); return pCurIP; } // } // Requested address asked // A new IP address should be allocated : // First check if the pool is large enough in order to allocate a new address if (sParamDHCP.nPoolSize>0 && nAllocatedIP < sParamDHCP.nPoolSize) { // search for an "hole" in the struct or take last elem + 1 // if an item was allocated and the first item is the first in pool //Don't allocate ip addresses ending in 0 or 255 if (nAllocatedIP>0 && tFirstIP[0]->dwIP.s_addr == sParamDHCP.dwAddr.s_addr) { for ( Ark=1 ; Ark<nAllocatedIP && ntohl (tFirstIP[Ark]->dwIP.s_addr) == AddrInc(tFirstIP[Ark-1]->dwIP); Ark ++ ); pCurIP = DHCPReallocItem (NULL, htonl (AddrInc(tFirstIP[Ark-1]->dwIP)), pMac, nMacLen); } else pCurIP = DHCPReallocItem (NULL, sParamDHCP.dwAddr.s_addr, pMac, nMacLen); // New address : ntohl (tFirstIP[Ark]->dwIP.s_addr) + 1 // it is OK if Ark has reach nAllocatedIP LOG (12, "Reply with new : %s", inet_ntoa (pCurIP->dwIP)); return pCurIP; } // new allocation // no free address, have to reuse an "old" one // try addresses which have not been acknowledged (tAllocated+2 minutes), // or that have expired // time (&tNow); for (Ark=0 ; Ark<nAllocatedIP; Ark++) { pCurIP = tFirstIP[Ark]; if ( pCurIP->tAllocated+TWO_MINUTES < tNow && ((pCurIP->tRenewed==0) || (tNow > pCurIP->tRenewed + (sParamDHCP.nLease * 60)))) { pCurIP = DHCPReallocItem (pCurIP, pCurIP->dwIP.s_addr, pMac, nMacLen); LOG (12, "Reply with reuse : %s", inet_ntoa (pCurIP->dwIP)); return pCurIP; } } // reuse an unacknowledged address /* Since we are replacing holes, unacked, and expired addresses, all addresses are currently used // search for the oldest one (use tAllocated and tRenewed) for (Ark=0, pOldestIP=NULL ; Ark<nAllocatedIP; Ark++) { pCurIP = tFirstIP[Ark]; if ( pCurIP->tRenewed!=0 && pCurIP->tRenewed < (unsigned) (pOldestIP==NULL ? 0xFFFFFFFF : pOldestIP->tRenewed ) && PingApi (&pCurIP->dwIP, DHCP_PINGTIMEOUT, NULL)==PINGAPI_TIMEOUT && PingApi (&pCurIP->dwIP, DHCP_PINGTIMEOUT, NULL)==PINGAPI_TIMEOUT && PingApi (&pCurIP->dwIP, DHCP_PINGTIMEOUT, NULL)==PINGAPI_TIMEOUT ) pOldestIP=pCurIP; } // search for oldest item in pOldestIP if (pOldestIP != NULL) { pCurIP = DHCPReallocItem (pOldestIP, pOldestIP->dwIP.s_addr, pMac, nMacLen); LOG (12, "Reply with reuse : %s", inet_ntoa (pCurIP->dwIP)); return pCurIP; // can be NULL } */ return NULL; } // DHCP_IPAllocate2