/* * Process BOOTREPLY packet. */ static void handle_reply(void) { struct bootp *bp = (struct bootp *) pktbuf; struct ifreq *ifr; struct sockaddr_in *sip; u_char canon_haddr[MAXHADDRLEN]; unsigned char *ha; int len; if (debug) { report(LOG_INFO, " reply for %s", inet_ntoa(bp->bp_yiaddr)); } /* Make sure client is directly accessible. */ ifr = getif(s, &(bp->bp_yiaddr)); if (!ifr) { report(LOG_NOTICE, "no interface for reply to %s", inet_ntoa(bp->bp_yiaddr)); return; } #if 1 /* Experimental (see BUG above) */ /* #ifdef CATER_TO_OLD_CLIENTS ? */ /* * The giaddr field has been set to our "default" IP address * which might not be on the same interface as the client. * In case the client looks at giaddr, (which it should not) * giaddr is now set to the address of the correct interface. */ sip = (struct sockaddr_in *) &(ifr->ifr_addr); bp->bp_giaddr = sip->sin_addr; #endif /* Set up socket address for send to client. */ clnt_addr.sin_family = AF_INET; clnt_addr.sin_addr = bp->bp_yiaddr; clnt_addr.sin_port = htons(bootpc_port); /* Create an ARP cache entry for the client. */ ha = bp->bp_chaddr; len = bp->bp_hlen; if (len > MAXHADDRLEN) len = MAXHADDRLEN; if (bp->bp_htype == HTYPE_IEEE802) { haddr_conv802(ha, canon_haddr, len); ha = canon_haddr; } if (debug > 1) report(LOG_INFO, "setarp %s - %s", inet_ntoa(bp->bp_yiaddr), haddrtoa(ha, len)); setarp(s, &bp->bp_yiaddr, ha, len); /* Send reply with same size packet as request used. */ if (sendto(s, pktbuf, pktlen, 0, (struct sockaddr *) &clnt_addr, sizeof(clnt_addr)) < 0) { report(LOG_ERR, "sendto: %s", get_network_errmsg()); } }
//Completely renumbers and rewrites the lease list from current membory. void ReorderLeases() { int i; AsyncSaveKey (TFTPD32_DHCP_KEY, KEY_LEASE_NUMLEASES, & nAllocatedIP, sizeof(nAllocatedIP), REG_DWORD, szTftpd32IniFile); for(i = 0; i < nAllocatedIP; ++i) { char key [_MAX_PATH]; char* macaddr = haddrtoa(tFirstIP[i]->sMacAddr, 6, ':'); char* addr = inet_ntoa(tFirstIP[i]->dwIP); char* alloc = timetoa(tFirstIP[i]->tAllocated); char* renew = timetoa(tFirstIP[i]->tRenewed); tFirstIP[i]->dwAllocNum = i; if (sSettings.bPersLeases) { sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, i, KEY_LEASE_MAC); SaveKey(TFTPD32_DHCP_KEY, key, macaddr, strlen(macaddr) + 1, REG_SZ, szTftpd32IniFile); sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, i, KEY_LEASE_IP); SaveKey(TFTPD32_DHCP_KEY, key, addr, strlen(addr) + 1, REG_SZ, szTftpd32IniFile); sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, i, KEY_LEASE_ALLOC); SaveKey(TFTPD32_DHCP_KEY, key, alloc, strlen(alloc) + 1, REG_SZ, szTftpd32IniFile); sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, i, KEY_LEASE_RENEW); AsyncSaveKey(TFTPD32_DHCP_KEY, key, renew, strlen(renew) + 1, REG_SZ, szTftpd32IniFile); } } }
//Set the MAC address to all zeros void ZeroMacAddr(struct LL_IP* pCur) { char key [_MAX_PATH]; char* macaddr; sprintf(key, "%s%d%s", KEY_LEASE_PREFIX, pCur->dwAllocNum, KEY_LEASE_MAC); memset(pCur->sMacAddr, 0, sizeof pCur->sMacAddr); macaddr = haddrtoa(pCur->sMacAddr, 6, ':'); if (sSettings.bPersLeases) AsyncSaveKey(TFTPD32_DHCP_KEY, key, macaddr, strlen(macaddr), REG_SZ, szTftpd32IniFile); }
// DHCP Scan // for debugging puropses only void DHCPScan (void) { int Ark; time_t tNow; time (&tNow); for (Ark = 0 ; Ark<nAllocatedIP ; Ark++ ) LOG (15, "Item %d: IP %s, Mac %s, Age %d sec, %s", Ark, inet_ntoa (tFirstIP[Ark]->dwIP), haddrtoa(tFirstIP[Ark]->sMacAddr, 6,':'), tNow - tFirstIP[Ark]->tAllocated, tFirstIP[Ark]->tRenewed ? "Ack" : "Nak" ); } // DHCPScan
// Assignation by MAC Address DWORD DHCP_StaticAssignation (struct dhcp_packet *pPkt) { char szIP[20]; return ( pPkt->htype==HTYPE_ETHER || pPkt->htype==HTYPE_IEEE802 ) && pPkt->hlen==6 && ReadKey ( TFTPD32_DHCP_KEY, haddrtoa(pPkt->chaddr, pPkt->hlen,':'), szIP, sizeof szIP, REG_SZ, szTftpd32IniFile ) ? inet_addr (szIP) : INADDR_NONE; } // DHCP_StaticAssignation
/////////////////////////////////////////// // translation /////////////////////////////////////////// // translate $IP$ and $MAC$ keywords char *TranslateExp (const char *exp, char *to, struct in_addr ip, const char *tMac) { char *q; int Ark; char sz [256]; // somewhat larger that DHCP_FILE_LEN (128 bytes) // truncate input Ark = strnlen ( to, DHCP_FILE_LEN -1 ); to [Ark] = 0; // LOG (1, "bootp file fmt is <%s>\n", exp); // LOG (1, "rqst file is <%s>\n", to); // LOG (1, "IP <%s>\n", inet_ntoa (ip)); // LOG (1, "MAC is <%s>\n", haddrtoa (tMac, 6, '.')); if ( (q=strstr (exp, "$IP$")) != NULL ) { lstrcpyn (sz, exp, 1 + q - exp); lstrcat (sz, inet_ntoa (ip) ); lstrcat (sz, q + sizeof "$IP$" - 1); lstrcpyn (to, sz, DHCP_FILE_LEN - 1); } else if ( (q=strstr (exp, "$MAC$")) != NULL ) { lstrcpyn (sz, exp, 1 + q - exp); lstrcat (sz, haddrtoa (tMac, 6, '.') ); lstrcat (sz, q + sizeof "$MAC$" - 1); lstrcpyn (to, sz, DHCP_FILE_LEN - 1); } else if ( (q=strstr (exp, "$BootFileName$")) != NULL ) { lstrcpyn (sz, exp, 1 + q - exp); lstrcat (sz, to); lstrcat (sz, q + sizeof "$BootFileName$" - 1); // replace to now lstrcpyn (to, sz, DHCP_FILE_LEN - 1); } else lstrcpyn (to, exp, DHCP_FILE_LEN - 1); // truncate to [DHCP_FILE_LEN-1]=0; return to; } // TranslateExp
// frees an item (may crash if allocation in progess) void DHCPDestroyItem (struct LL_IP *pCur) { if (pCur!=NULL) { LOG (5, "Freeing item %s %s", inet_ntoa (pCur->dwIP), haddrtoa (pCur->sMacAddr, 6,':') ) ; // put item at the end of the list and resort array SetIP(pCur, INADDR_NONE); // will be the last item memset(pCur->sMacAddr, 0, 6); qsort (tFirstIP, nAllocatedIP, sizeof *tFirstIP, QsortCompare); qsort (tMAC, nAllocatedIP, sizeof *tMAC, MACCompare); DecNumAllocated(); ReorderLeases(); free ( tFirstIP[nAllocatedIP] ); // free the last item } // wake up DHCP thread --> actualizes GUI with SendLeases WakeUpThread (TH_DHCP); } // DHCPDestroyItem
// Search in configuration file/registry by Mac Address struct LL_IP *DHCPSearchByRegistry (const unsigned char *pMac, int nMacLen) { int Rc; HKEY hKey; char szIP[20]; DWORD dwSize; if (nMacLen!=6) return NULL; // work only for Ethernet and Token Ring szIP[0] = 0; Rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE, // Key handle at root level. TFTPD32_DHCP_KEY, // Path name of child key. 0, // Reserved. KEY_READ, // Requesting read access. & hKey) == ERROR_SUCCESS; // Address of key to be returned. if (Rc) READKEY (haddrtoa(pMac, nMacLen), szIP); CloseHandle (hKey); if (isdigit (szIP[0])) // entry has been found return DHCPReallocItem (NULL, inet_addr (szIP), pMac, nMacLen); return NULL; } // DHCPSearchByRegistry
/* * Setup the arp cache so that IP address 'ia' will be temporarily * bound to hardware address 'ha' of length 'len'. * * s socket fd * ia protocol address * hafamily HW address family * halen HW address data */ void setarp(int s, struct in_addr *ia, int hafamily, u_char *haddr, int halen) { #ifdef SIOCSARP #ifdef WIN_TCP /* This is an SVR4 with different networking code from * Wollongong WIN-TCP. Not quite like the Lachman code. * Code from: [email protected] (Andrew B. Sudell) */ #undef SIOCSARP #define SIOCSARP ARP_ADD struct arptab arpreq; /* Arp table entry */ bzero((caddr_t) &arpreq, sizeof(arpreq)); arpreq.at_flags = ATF_COM; /* Set up IP address */ arpreq.at_in = ia->s_addr; /* Set up Hardware Address */ bcopy(haddr, arpreq.at_enaddr, halen); /* Set the Date Link type. */ /* XXX - Translate (hafamily) to dltype somehow? */ arpreq.at_dltype = DL_ETHER; #else /* WIN_TCP */ /* Good old Berkeley way. */ struct arpreq arpreq; /* Arp request ioctl block */ struct sockaddr_in *si; char *p; bzero((caddr_t) &arpreq, sizeof(arpreq)); arpreq.arp_flags = ATF_INUSE | ATF_COM; /* Set up the protocol address. */ arpreq.arp_pa.sa_family = AF_INET; si = (struct sockaddr_in *) &arpreq.arp_pa; si->sin_addr = *ia; /* Set up the hardware address. */ #ifdef __linux__ /* XXX - Do others need this? -gwr */ /* * Linux requires the sa_family field set. * [email protected] (Al Longyear) */ arpreq.arp_ha.sa_family = hafamily; #endif /* linux */ /* This variable is just to help catch type mismatches. */ p = arpreq.arp_ha.sa_data; bcopy(haddr, p, halen); #endif /* WIN_TCP */ #ifdef SVR4 /* * And now the stuff for System V Rel 4.x which does not * appear to allow SIOCxxx ioctls on a socket descriptor. * Thanks to several people: (all sent the same fix) * Barney Wolff <*****@*****.**>, * [email protected] (Bj|rn Sj|holm), * Michael Kuschke <*****@*****.**>, */ { int fd; struct strioctl iocb; if ((fd=open("/dev/arp", O_RDWR)) < 0) { report(LOG_ERR, "open /dev/arp: %s\n", get_errmsg()); } iocb.ic_cmd = SIOCSARP; iocb.ic_timout = 0; iocb.ic_dp = (char *)&arpreq; iocb.ic_len = sizeof(arpreq); if (ioctl(fd, I_STR, (caddr_t)&iocb) < 0) { report(LOG_ERR, "ioctl I_STR: %s\n", get_errmsg()); } close (fd); } #else /* SVR4 */ /* * On SunOS, the ioctl sometimes returns ENXIO, and it * appears to happen when the ARP cache entry you tried * to add is already in the cache. (Sigh...) * XXX - Should this error simply be ignored? -gwr */ if (ioctl(s, SIOCSARP, (caddr_t) &arpreq) < 0) { report(LOG_ERR, "ioctl SIOCSARP: %s", get_errmsg()); } #endif /* SVR4 */ #else /* SIOCSARP */ #if defined(BSD) && (BSD >= 199306) bsd_arp_set(ia, haddr, halen); #else /* * Oh well, SIOCSARP is not defined. Just run arp(8). * Need to delete partial entry first on some systems. * XXX - Gag! */ int status; char buf[256]; char *a; extern char *inet_ntoa(); a = inet_ntoa(*ia); snprintf(buf, sizeof(buf), "arp -d %s; arp -s %s %s temp", a, a, haddrtoa(haddr, halen)); if (debug > 2) report(LOG_INFO, "%s", buf); status = system(buf); if (status) report(LOG_ERR, "arp failed, exit code=0x%x", status); return; #endif /* ! 4.4 BSD */ #endif /* SIOCSARP */ }
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