/* netscanner.cpp * * Copyright (c) 2000, Alexander Neundorf, * neundorf@kde.org * * You may distribute under the terms of the GNU General Public * License as specified in the COPYING file. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include "config.h" #include "netscanner.h" #include "ipaddress.h" #include "lisadefines.h" #include <iostream> #ifdef LISA_DEBUG #undef LISA_DEBUG #endif #define LISA_DEBUG 0 #ifdef dcerr #undef dcerr #endif #ifdef mdcerr #undef mdcerr #endif #define dcerr if (LISA_DEBUG==1) std::cerr<<"NetScanner::" #define mdcerr if (LISA_DEBUG==1) std::cerr<<procId<<" NetScanner::" #include <stdio.h> #include <sys/socket.h> #include <sys/types.h> #ifdef __osf__ #undef BYTE_ORDER #define _OSF_SOURCE #undef _MACHINE_ENDIAN_H_ #undef __STDC__ #define __STDC__ 0 #include <netinet/ip.h> #undef __STDC__ #define __STDC__ 1 #endif #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/ip_icmp.h> #ifndef INADDR_NONE #define INADDR_NONE -1 #endif struct ICMPEchoRequest { unsigned char type; unsigned char code; unsigned short int checkSum; unsigned short id; unsigned short seqNumber; }; unsigned short in_cksum(unsigned short *addr, int len) { int nleft = len; int sum(0); unsigned short *w = addr; unsigned short answer = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), we add * sequential 16 bit words to it, and at the end, fold back all the * carry bits from the top 16 bits into the lower 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* 4mop up an odd byte, if necessary */ if (nleft == 1) { *(unsigned char *)(&answer) = *(unsigned char *)w ; sum += answer; } /* 4add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return(answer); } NetScanner::NetScanner(int& rawSocketFD, int strictMode) :procId("") ,m_firstWait(5) ,m_secondWait(15) ,m_strictMode(strictMode) ,m_rawSocketFD(rawSocketFD) ,validator() ,ipRangeStr(";") ,m_maxPings(256) ,m_deliverUnnamedHosts(0) ,m_useNmblookup(0) ,hostList(0) ,tmpIPRange("") {} NetScanner::~NetScanner() { // std::cerr<<"----------- Netscanner dtor "<<std::endl; delete hostList; ::close(m_rawSocketFD); } void addMissingSemicolon(MyString& text) { if (text.isEmpty()) return; if (text[text.length()-1]!=';') text+=';'; } void NetScanner::configure(Config& config) { //ranges are not allowed in strict mode if (!m_strictMode) { ipRangeStr=stripWhiteSpace(config.getEntry("PingAddresses","")); addMissingSemicolon(ipRangeStr); } MyString pingNames=stripWhiteSpace(config.getEntry("PingNames","")); addMissingSemicolon(pingNames); MyString nextName; int semicolonPos=pingNames.find(';'); int hostsAdded(0); while (semicolonPos!=-1) { nextName=pingNames.left(semicolonPos); mdcerr<<"configure(): looking up -"<<nextName<<"-"<<std::endl; //now the name lookup in_addr server_addr; hostent *hp=gethostbyname(nextName.data()); if (hp!=0) { if ((m_strictMode) && (hostsAdded>=STRICTMODEMAXHOSTS)) break; memcpy(&server_addr, hp->h_addr, sizeof(server_addr)); char *ip=inet_ntoa(server_addr); mdcerr<<"configure(): looking up "<<nextName<<" gives -"<<ip<<"-"<<std::endl; ipRangeStr=ipRangeStr+ip+';'; hostsAdded++; } pingNames=pingNames.mid(semicolonPos+1); semicolonPos=pingNames.find(';'); } if ((!ipRangeStr.isEmpty()) && (ipRangeStr[0]==';')) ipRangeStr=ipRangeStr.mid(1); m_deliverUnnamedHosts=config.getEntry("DeliverUnnamedHosts",0); m_useNmblookup=config.getEntry("SearchUsingNmblookup",0); m_maxPings=config.getEntry("MaxPingsAtOnce",256); m_firstWait=config.getEntry("FirstWait",5); m_secondWait=config.getEntry("SecondWait",15); if (m_firstWait<1) m_firstWait=1; if (m_maxPings<8) m_maxPings=8; if (m_maxPings>1024) m_maxPings=1024; //on some systems (Solaris ?) select() doesn't work correctly // if the microseconds are more than 1.000.000 if (m_firstWait>99) m_firstWait=99; if (m_secondWait>99) m_secondWait=99; mdcerr<<"configure(): "<<ipRangeStr<<std::endl; } struct in_addr NetScanner::getIPfromArray(unsigned int index) { //mdcerr<<std::endl<<"*** start ***"<<std::endl; unsigned int tmpIndex(0),indexLeft(index); resetIPRange(); MyString tmp(getNextIPRange()); // mdcerr<<"NetScanner::getIPFromArray: -"<<tmp<<"-"<<std::endl; while (!tmp.isEmpty()) { if (tmp.contains('/')) { //mdcerr<<"net/mask combination detected"<<std::endl; MyString netStr(tmp.left(tmp.find("/"))); MyString maskStr(tmp.mid(tmp.find("/")+1)); unsigned int mask(IPAddress(maskStr).asInt()); unsigned int net(IPAddress(netStr).asInt()&mask); if ((~mask)<indexLeft) { indexLeft=indexLeft-(~mask+1); tmpIndex+=(~mask)+1; //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl; } else { net+=indexLeft; return IPAddress(net).asStruct(); //return string2Struct(ipInt2String(net)); } } else if (tmp.contains('-')==1) { //mdcerr<<"single range detected"<<std::endl; MyString fromIPStr(tmp.left(tmp.find("-"))); MyString toIPStr(tmp.mid(tmp.find("-")+1)); //mdcerr<<"fromIPStr: "<<fromIPStr<<std::endl; //mdcerr<<"toIPStr: "<<toIPStr<<std::endl; unsigned int fromIP(IPAddress(fromIPStr).asInt()); unsigned int toIP(IPAddress(toIPStr).asInt()); //unsigned int fromIP(ipString2Int(fromIPStr)), toIP(ipString2Int(toIPStr)); //index hinter diesem bereich if ((fromIP+indexLeft)>toIP) { tmpIndex+=1+toIP-fromIP; indexLeft=indexLeft-(1+toIP-fromIP); //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl; } //index in diesem bereich else { fromIP+=indexLeft; return IPAddress(fromIP).asStruct(); //return string2Struct(ipInt2String(fromIP)); } } else if (tmp.contains('-')==4) { //mdcerr<<"multiple range detected"<<std::endl; int cp(tmp.find('-')); int from1(atoi(tmp.left(cp).data())); tmp=tmp.mid(cp+1); cp=tmp.find('.'); int to1(atoi(tmp.left(cp).data())); tmp=tmp.mid(cp+1); cp=tmp.find('-'); int from2(atoi(tmp.left(cp).data())); tmp=tmp.mid(cp+1); cp=tmp.find('.'); int to2(atoi(tmp.left(cp).data())); tmp=tmp.mid(cp+1); cp=tmp.find('-'); int from3(atoi(tmp.left(cp).data())); tmp=tmp.mid(cp+1); cp=tmp.find('.'); int to3(atoi(tmp.left(cp).data())); tmp=tmp.mid(cp+1); cp=tmp.find('-'); int from4(atoi(tmp.left(cp).data())); tmp=tmp.mid(cp+1); int to4(atoi(tmp.data())); unsigned int count((1+to4-from4)*(1+to3-from3)*(1+to2-from2)*(1+to1-from1)); if (count<indexLeft) { tmpIndex+=count; indexLeft-=count; //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl; } else { for (int b1=from1; b1<=to1; b1++) for (int b2=from2; b2<=to2; b2++) for (int b3=from3; b3<=to3; b3++) for (int b4=from4; b4<=to4; b4++) { if (tmpIndex==index) { return IPAddress(b1,b2,b3,b4).asStruct(); }; tmpIndex++; indexLeft--; //mdcerr<<"i: "<<tmpIndex<<" left:"<<indexLeft<<std::endl; } } } //single IP address else if (tmp.contains('.')==3) { //mdcerr<<"single IP address detected"<<std::endl; //if (tmpIndex==index) return string2Struct(tmp); if (tmpIndex==index) return IPAddress(tmp).asStruct(); else { tmpIndex++; indexLeft--; //mdcerr<<"i: "<<tmpIndex<<" left: "<<indexLeft<<std::endl; } } //mdcerr<<"nextIPRange: *"<<tmp<<"*"<<std::endl; tmp=getNextIPRange(); } return IPAddress("0.0.0.0").asStruct(); } void NetScanner::resetIPRange() { tmpIPRange=ipRangeStr; } MyString NetScanner::getNextIPRange() { if (tmpIPRange.contains(';')<1) return ""; int cp(tmpIPRange.find(';')); MyString tmp(tmpIPRange.left(cp)); tmpIPRange=tmpIPRange.mid(cp+1); return tmp; } char* NetScanner::ip2Name(struct in_addr ip) { struct hostent *hostname=0; // Set the hostname of node if ( ( hostname = gethostbyaddr( (char *) &ip.s_addr, 4, AF_INET ) ) == 0 ) { mdcerr << "ip2Name gethostbyname* error" << std::endl; return inet_ntoa( ip ); } mdcerr<<"ip2name -"<<hostname->h_name<<std::endl; return hostname->h_name; } void NetScanner::nmblookupScan(std::list<Node>* newList) { mdcerr<<"nmblookupScan()"<<std::endl; //newList->clear(); FILE * nmblookupFile=popen("nmblookup \"*\"","r"); //no success if (nmblookupFile==0) { mdcerr<<"nmblookupScan(): could not start nmblookup"<<std::endl; return; }; MyString dummy(""); char *receiveBuffer=0; int bufferSize(0); int nmblookupFd=fileno(nmblookupFile); struct timeval tv; fd_set fds; int done(0); int timeOuts(0); char *tmpBuf=new char[16*1024]; do { FD_ZERO(&fds); FD_SET(nmblookupFd,&fds); tv.tv_sec=10; tv.tv_usec=0; int result=select(nmblookupFd+1,&fds,0,0,&tv); //error if (result<0) done=1; //timeout else if (result==0) { timeOuts++; //3 time outs make 30 seconds, this should be *much* more than enough if (timeOuts>=3) done=1; } else if (result>0) { //read stuff int bytesRead=::read(nmblookupFd,tmpBuf,16*1024); //pipe closed if (bytesRead==0) done=1; else { char *newBuf=new char[bufferSize+bytesRead]; if (receiveBuffer!=0) { memcpy(newBuf,receiveBuffer,bufferSize); delete [] receiveBuffer; } memcpy(newBuf+bufferSize,tmpBuf,bytesRead); receiveBuffer=newBuf; bufferSize+=bytesRead; } } } while (!done); // Warning: The return value of pclose may be incorrect due to the // SIGCHLD handler that is installed. Ignore it! pclose(nmblookupFile); delete [] tmpBuf; //we received nothing if (receiveBuffer==0) return; //check for a terrminating '\0' if (receiveBuffer[bufferSize-1]=='\0') receiveBuffer[bufferSize-1]='\0'; //std::cerr<<receiveBuffer<<std::endl; tmpBuf=receiveBuffer; int bytesLeft=bufferSize; int bufferState=0; while (bytesLeft>0) { //mdcerr<<"bytesLeft: "<<bytesLeft<<" received: "<<bufferSize<<std::endl; //since we added a terminating \0 we can be sure here char *endOfLine=(char*)memchr(tmpBuf,'\n',bytesLeft); //point to the last character if (endOfLine==0) endOfLine=receiveBuffer+bufferSize-1; //0-terminate the string *endOfLine='\0'; //now tmpBuf to endOfLine is a 0-terminated string int strLength=strlen(tmpBuf); //hmm, if this happens, there must be something really wrong if (strLength>1000) break; bytesLeft=bytesLeft-strLength-1; if (bufferState==0) { if (isdigit(tmpBuf[0])) bufferState=1; } //yes, no else ! if (bufferState==1) { char tmpIP[1024]; //std::cerr<<"tmpBuf: -"<<tmpBuf<<"-"<<std::endl; if (sscanf(tmpBuf,"%s *<00>\n",tmpIP) == 1) { mdcerr<<"nmblookupScan: tmpIP: -"<<tmpIP<<"-"<<std::endl; struct sockaddr_in addr; if ((addr.sin_addr.s_addr = inet_addr(tmpIP)) != INADDR_NONE) if (!hostAlreadyInList(addr.sin_addr.s_addr,newList)) { if (validator.isValid(addr.sin_addr.s_addr)) { mdcerr<<"nmblookupScan: adding "<<inet_ntoa(addr.sin_addr)<<std::endl; newList->push_back(Node(dummy,addr.sin_addr.s_addr)); } } } } tmpBuf=endOfLine+1; }; mdcerr<<"nmblookupScan: finished"<<std::endl; delete [] receiveBuffer; } void NetScanner::pingScan(std::list<Node>* newList) //the ping-version { mdcerr<<"pingScan()"<<std::endl; //newList->clear(); MyString dummy(""); mdcerr<<"pingScan: m_maxPings: "<<m_maxPings<<std::endl; pid_t pid=getpid(); ICMPEchoRequest echo; echo.type=ICMP_ECHO; echo.code=0; echo.id=pid; echo.seqNumber=0; echo.checkSum=0; echo.checkSum=in_cksum((unsigned short *)&echo,8); char receiveBuf[16*1024]; //before we start first read everything what might be in the receive buffer //of the raw socket timeval tv1; //wait a moment for answers tv1.tv_sec = 0; tv1.tv_usec = 0; fd_set clearSet; FD_ZERO(&clearSet); FD_SET(m_rawSocketFD,&clearSet); while(select(m_rawSocketFD,&clearSet,0,0,&tv1)>0) { ::recvfrom(m_rawSocketFD,(char*)&receiveBuf,16*1024,0,0,0); tv1.tv_sec = 0; tv1.tv_usec = 0; FD_ZERO(&clearSet); FD_SET(m_rawSocketFD,&clearSet); } //now the buffer should be empty //wait a moment for answers tv1.tv_sec = 0; tv1.tv_usec = m_firstWait*10*1000;//0.5 sec int loopCount(2); if (m_secondWait<0) loopCount=1; for (int repeatOnce=0; repeatOnce<loopCount; repeatOnce++) { mdcerr<<"******************** starting loop *****************"<<std::endl; unsigned int current(0); int finished(0); while (!finished) { for (int con=0; con<m_maxPings; con++) { struct in_addr tmpIP; do { tmpIP=getIPfromArray(current); current++; // mdcerr<<"pingScan(): trying "<<inet_ntoa(tmpIP)<<std::endl; if (hostAlreadyInList(tmpIP.s_addr,newList)) mdcerr<<"already in list :-)"<<std::endl; if (!validator.isValid(tmpIP.s_addr)) mdcerr<<"pingScan(): invalid IP :-("<<std::endl; //ping only hosts which are allowed to receive the results //and which are not in the list } while ( (tmpIP.s_addr!=0) && ((!validator.isValid(tmpIP.s_addr)) || (hostAlreadyInList(tmpIP.s_addr,newList)))); finished=(tmpIP.s_addr==0); if (!finished) { //send the icmp echo request struct sockaddr_in toAddr; toAddr.sin_family = AF_INET; toAddr.sin_addr = tmpIP; toAddr.sin_port = 0; (void)sendto(m_rawSocketFD,(char*)&echo,sizeof(echo),0,(sockaddr*)&toAddr,sizeof(toAddr)); //int sb=sendto(sockFD,(char*)&echo,sizeof(echo),0,(sockaddr*)&toAddr,sizeof(toAddr)); // mdcerr<<"pingScan: pinging "<<inet_ntoa(toAddr.sin_addr)<<std::endl; } else break; } select(0,0,0,0,&tv1); //now read the answers, hopefully struct sockaddr_in fromAddr; socklen_t length(sizeof(fromAddr)); int received(0); fd_set sockFDset; FD_ZERO(&sockFDset); FD_SET(m_rawSocketFD,&sockFDset); tv1.tv_sec=0; tv1.tv_usec=0; while(select(m_rawSocketFD+1,&sockFDset,0,0,&tv1)>0) { received=recvfrom(m_rawSocketFD, (char*)&receiveBuf, 16*1024, 0, (sockaddr*)&fromAddr, &length); if (received!=-1) { // mdcerr<<"pingScan: received from "<<inet_ntoa(fromAddr.sin_addr)<<" "<<received<<" b, "; struct ip *ipFrame=(ip*)&receiveBuf; int icmpOffset=(ipFrame->ip_hl)*4; icmp *recIcmpFrame=(icmp*)(receiveBuf+icmpOffset); int iType=recIcmpFrame->icmp_type; //if its a ICMP echo reply if ((iType==ICMP_ECHOREPLY) //to an echo request we sent && (recIcmpFrame->icmp_id==pid) //and the host is not yet in the list && (!hostAlreadyInList(fromAddr.sin_addr.s_addr,newList))) { //this is an answer to our request :-) // mdcerr<<"pingScan: adding "<<inet_ntoa(fromAddr.sin_addr)<<std::endl; newList->push_back(Node(dummy,fromAddr.sin_addr.s_addr)); } } tv1.tv_sec=0; tv1.tv_usec=0; FD_ZERO(&sockFDset); FD_SET(m_rawSocketFD,&sockFDset); //FD_SET(sockFD,&sockFDset); } } tv1.tv_sec = 0; tv1.tv_usec = m_secondWait*10*1000;//0.5 sec } mdcerr<<"pingScan() ends"<<std::endl; } void NetScanner::doScan() { mdcerr<<"doScan"<<std::endl; //child std::list<Node>* tmpPingList=new std::list<Node>; mdcerr<<"doScan: created list"<<std::endl; if (m_useNmblookup) nmblookupScan(tmpPingList); pingScan(tmpPingList); // get the names from cache or lookup completeNames(tmpPingList); mdcerr<<"doScan: completed names"<<std::endl; if (m_deliverUnnamedHosts==0) removeUnnamedHosts(tmpPingList); mdcerr<<"doScan: added hosts"<<std::endl; delete hostList; hostList=tmpPingList; } int NetScanner::hostAlreadyInList(int ip, std::list<Node>* nodes) { for (std::list<Node>::iterator node = nodes->begin(); node != nodes->end(); node++) { if (node->ip==ip) return 1; } return 0; } void NetScanner::removeUnnamedHosts(std::list<Node>* nodes) { nodes->remove_if(is_unnamed_node()); } void NetScanner::completeNames(std::list<Node>* nodes) { struct sockaddr_in tmpAddr; //for every host for (std::list<Node>::iterator node = nodes->begin(); node != nodes->end(); node++) { tmpAddr.sin_addr.s_addr=node->ip; mdcerr<<"completeNames: looking up "<<inet_ntoa(tmpAddr.sin_addr)<<std::endl; int done(0); //first look wether we have the name already if (hostList!=0) for (std::list<Node>::iterator oldNode=hostList->begin(); oldNode!=hostList->end(); oldNode++) { if (node->ip==oldNode->ip) { mdcerr<<"completeNames: cached: "<<oldNode->name<<" :-)"<<std::endl; node->name=oldNode->name; done=1; break; } } //otherwise do a name lookup if (!done) { //IPAddress tmpAddress(node->ip); //mdcerr<<"NetScanner::completeNames: doing actual lookup"<<std::endl; node->name=ip2Name(tmpAddr.sin_addr); mdcerr<<"completeNames: resolved: "<<node->name<<std::endl; } } }