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
////////////////////////////////////////////////////////////////////////////////////////////// // AddOption Translation // Translates the option values into a value // Use the SnmpCmd syntax ("a ip @" "s string", "i integer", "x string", "u unsigned") ////////////////////////////////////////////////////////////////////////////////////////////// int TranslateParam2Value (char *buffer, int len, const char *opt_val, struct in_addr ip, const char *tMac) { char sz[256]; const char *p, *q; int nIPAddr, nLen, c; // the number of bytes used by n hexa digit 0xABCDE which has 5 digits will form a 4-byte number) static const char cvt[] = { 0, 1, 1, 2, 2, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8 }; if (opt_val[1] == ' ') { p = opt_val+2; // p points on data switch (opt_val [0]) { case 'a' : // list of IP address for (nIPAddr=0 ; *p!=0 && nIPAddr*4<len-4; nIPAddr++ ) { * ((unsigned long *) buffer + nIPAddr) = inet_addr (p); while ( *p!=0 && (isdigit(*p) || *p=='.') ) p++; // go to end of address while ( *p!=0 && *p==' ' ) p++; // skip spaces } return nIPAddr * sizeof (unsigned long); case 's' : // string lstrcpyn (sz, p, len); TranslateExp (sz, buffer, ip, tMac); buffer [len-1] = 0; return lstrlen (buffer); case 'I' : // integer * (unsigned short *) buffer = atoi (p); return sizeof (unsigned short); case 'i' : // integer * (unsigned long *) buffer = atoi (p); return sizeof (unsigned long); case 'N' : // integer network order * (unsigned short *) buffer = htons (atoi (p)); return sizeof (unsigned short); case 'n' : // integer long network order * (unsigned long *) buffer = htonl (atoi (p)); return sizeof (unsigned long); case 'b' : // list of 1 (b)yte digits for (nLen=0 ; *p!=0 && nLen<len-1; nLen++ ) { sscanf(p, "%u", & c); buffer [nLen] = (char) c; while ( *p!=0 && isdigit(*p) ) p++; // to next digit while ( *p!=0 && (*p==' ' || *p=='.' || *p==':') ) p++; // skip spaces } return nLen; case 'x' : // list of he(x) digits for (nLen=0 ; *p!=0 && nLen<len-8; p = q ) { for ( q = p; isxdigit(*q) ; q++ ); // search next field switch ( cvt [ min (q-p, 8) ] ) { case 1 : sscanf(p, "%x", & c); buffer [nLen] = (char) c; break; case 2 : sscanf(p, "%hx", buffer+nLen); break; case 4 : sscanf(p, "%lx", buffer+nLen); break; case 8 : sscanf(p, "%llx", buffer+nLen); break; } nLen += cvt [min (q-p, 8)] ; while ( *q!=0 && (*q==' ' || *q=='.' || *q==':') ) q++; // skip spaces } return nLen; } // switch opt_val[0] } // non trouve --> conserver la chaine lstrcpyn (sz, opt_val, len); TranslateExp (sz, buffer, ip, tMac); ( (char *) buffer) [len-1] = 0; return lstrlen (buffer); } // TranslateParam2Value
////////////////////////////////////////////////////////////////////////////////////////////// // fill DHCP fields ////////////////////////////////////////////////////////////////////////////////////////////// int DHCPOptionsReply (struct dhcp_packet *pDhcpPkt, int nDhcpType) { unsigned char *pOpt = (unsigned char *) (pDhcpPkt->options + (sizeof DHCP_OPTIONS_COOKIE - 1)); HANDLE hFile; struct in_addr *pNearest; int Ark, Evan; char sz[256]; static struct S_DhcpOptions sDhcpOpt [] = // 0 for unspecified { DHO_DHCP_MESSAGE_TYPE, 1, ~TFTPD32_NONE, DHO_DHCP_SERVER_IDENTIFIER, 4, ~TFTPD32_NONE, DHO_SUBNET_MASK, 4, ~TFTPD32_NONE, DHO_ROUTERS, 4, ~TFTPD32_NONE, DHO_DOMAIN_NAME_SERVERS, 4, ~TFTPD32_NONE, DHO_LOG_SERVERS, 4, TFTPD32_SYSLOG_SERVER, DHO_NETBIOS_NAME_SERVERS, 4, ~TFTPD32_NONE, DHO_DHCP_LEASE_TIME, 4, ~TFTPD32_NONE, DHO_DHCP_RENEWAL_TIME, 4, ~TFTPD32_NONE, DHO_DHCP_REBINDING_TIME, 4, ~TFTPD32_NONE, DHO_BOOT_SIZE, 0, TFTPD32_TFTP_SERVER, DHO_DOMAIN_NAME, 0, ~TFTPD32_NONE, DHO_CUSTOM, 0, ~TFTPD32_NONE, DHO_END, 0, ~TFTPD32_NONE, }; //Always pack the magic cookie again, just in case it was corrupted *(DWORD*)(pDhcpPkt->options) = * (DWORD*) DHCP_OPTIONS_COOKIE; // pNearest points on the "good" LAN interface pNearest = FindNearestServerAddress (&pDhcpPkt->yiaddr, & sParamDHCP.dwMask, FALSE); //HACK -- If we are the bootp server, we are also the tftpserver if (sSettings.uServices & TFTPD32_TFTP_SERVER) pDhcpPkt->siaddr = *pNearest; // Next server (TFTP server is enabled) for (Ark=0 ; Ark<SizeOfTab(sDhcpOpt) ; Ark++) { // skip if linked to a service which is not started (change suggested by Colin) if (! (sSettings.uServices & sDhcpOpt[Ark].nServices)) continue; if (sDhcpOpt[Ark].nLen!=0) { *pOpt++ = (unsigned char) sDhcpOpt[Ark].nDHCPOpt ; *pOpt++ = (unsigned char) sDhcpOpt[Ark].nLen; } switch (sDhcpOpt[Ark].nDHCPOpt) { case DHO_DHCP_MESSAGE_TYPE : * pOpt = (unsigned char) nDhcpType ; break ; case DHO_LOG_SERVERS : // fallthrough case DHO_DHCP_SERVER_IDENTIFIER : * (DWORD *) pOpt = pNearest->s_addr; break ; case DHO_SUBNET_MASK : * (DWORD *) pOpt = sParamDHCP.dwMask.s_addr; break ; case DHO_ROUTERS : * (DWORD *) pOpt = (sParamDHCP.dwGateway.s_addr == 0xffffffff ? pDhcpPkt->yiaddr.s_addr : sParamDHCP.dwGateway.s_addr); break ; case DHO_NETBIOS_NAME_SERVERS : case DHO_DOMAIN_NAME_SERVERS : * (DWORD *) pOpt = sParamDHCP.dwDns.s_addr; break; case DHO_DHCP_LEASE_TIME : * (DWORD *) pOpt = htonl (sParamDHCP.nLease * 60); break ; case DHO_DHCP_RENEWAL_TIME : case DHO_DHCP_REBINDING_TIME : * (DWORD *) pOpt = htonl (sParamDHCP.nLease/2 * 60); break ; case DHO_BOOT_SIZE : // translate $IP$ and $MAC$ from boot file name TranslateExp (sParamDHCP.szBootFile, sz, pDhcpPkt->yiaddr, pDhcpPkt->chaddr); hFile = CreateFile(sz, // open the file GENERIC_READ, // open for reading FILE_SHARE_READ, // share for reading NULL, // no security OPEN_EXISTING, // existing file only FILE_ATTRIBUTE_NORMAL, // normal file NULL); // no attr. template if (hFile != INVALID_HANDLE_VALUE) { *pOpt++ = DHO_BOOT_SIZE ; *pOpt++ = sizeof (unsigned short); * (unsigned short *) pOpt = htons ( (unsigned short) (1+GetFileSize (hFile, NULL) / 512) ) ; pOpt += sizeof (unsigned short); CloseHandle( hFile ) ; // close the file } break; case DHO_DOMAIN_NAME : if (sParamDHCP.szDomainName[0]!=0) { *pOpt++ = DHO_DOMAIN_NAME; *pOpt = lstrlen (sParamDHCP.szDomainName); memcpy (pOpt+1, sParamDHCP.szDomainName, *pOpt); pOpt += 1+*pOpt; } break; case DHO_CUSTOM : // Manage custom options for (Evan=0 ; Evan < SizeOfTab (sParamDHCP.t) ; Evan++) if (sParamDHCP.t[Evan].nAddOption != 0) { *pOpt++ = (unsigned char) sParamDHCP.t[Evan].nAddOption; *pOpt = TranslateParam2Value (pOpt+1, 64, sParamDHCP.t[Evan].szAddOption, pDhcpPkt->yiaddr, pDhcpPkt->chaddr); pOpt += 1+*pOpt; } break; case DHO_END : *pOpt++ = DHO_END; *pOpt++ = DHO_PAD; *pOpt++ = DHO_PAD; break; } // switch option pOpt += sDhcpOpt[Ark].nLen ; // points on next field } // for all option return (int) (pOpt - (unsigned char*) pDhcpPkt); } // DHCPOptionsReply