diff options
Diffstat (limited to 'lanbrowsing/lisa/netmanager.cpp')
-rw-r--r-- | lanbrowsing/lisa/netmanager.cpp | 1058 |
1 files changed, 1058 insertions, 0 deletions
diff --git a/lanbrowsing/lisa/netmanager.cpp b/lanbrowsing/lisa/netmanager.cpp new file mode 100644 index 00000000..e652ce97 --- /dev/null +++ b/lanbrowsing/lisa/netmanager.cpp @@ -0,0 +1,1058 @@ + /* netmanager.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 "netmanager.h" +#include "lisadefines.h" + +#include <iostream> +#include <unistd.h> + +#include <sys/un.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <strings.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <pwd.h> + +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + + +#ifdef LISA_DEBUG +#undef LISA_DEBUG +#endif + +#ifdef dcerr +#undef dcerr +#endif + +#ifdef mdcerr +#undef mdcerr +#endif + +#define LISA_DEBUG 0 +#define dcerr if (LISA_DEBUG==1) std::cerr<<"NetManager " +#define mdcerr if (LISA_DEBUG==1) std::cerr<<procId<<" NetManager " + +static void sig_child_handler(int) +{ + int saved_errno = errno; + + while (waitpid(-1, NULL, WNOHANG) > 0) + ; // repeat + + errno = saved_errno; +} + +NetManager::NetManager(int& rawSocketFD, int portToUse, MyString configFile, int configStyle, int strictMode) +:NetScanner(rawSocketFD,strictMode) +//,validator() +,m_listenFD(-1) +,m_bcFD(-1) +,m_basePort(portToUse) +,m_pipeFD(-1) +,m_receiveBuffer(0) +,m_receivedBytes(0) +,m_childPid(0) +,m_lastUpdate(0) + +,m_isInformed(0) +,m_isBeingScanned(0) +,m_firstRun(1) +,m_serverServer(0) +,m_servedThisPeriod(0) + +,m_serveCount(0) +,m_refreshTime(60) +,m_initialRefreshTime(60) +,m_increasedRefreshTime(0) +,m_broadcastAddress(0) +,m_extraConfigFile(configFile) +,m_configStyle(configStyle) +,m_usedConfigFileName("") +{ + mdcerr<<"NetManager::NetManager"<<std::endl; + m_startedAt=time(0); + + struct sigaction act; + act.sa_handler=sig_child_handler; + sigemptyset(&(act.sa_mask)); + sigaddset(&(act.sa_mask), SIGCHLD); + // Make sure we don't block this signal. gdb tends to do that :-( + sigprocmask(SIG_UNBLOCK, &(act.sa_mask), 0); + + act.sa_flags = SA_NOCLDSTOP; +#ifdef SA_RESTART + act.sa_flags |= SA_RESTART; +#endif + + sigaction( SIGCHLD, &act, NULL ); +} + +NetManager::~NetManager() +{ + mdcerr<<"NetManager destructor ..."<<std::endl; + if (m_receiveBuffer!=0) delete [] m_receiveBuffer; + ::close(m_listenFD); + ::close(m_bcFD); +} + +void NetManager::readConfig() +{ + m_usedConfigFileName=getConfigFileName(); + if (m_usedConfigFileName.isEmpty()) + { + std::cout<<"configfile not found"<<std::endl; + std::cout<<"use the command line option --help and \ntake a look at the README for more information"<<std::endl; + exit(1); + } + + Config config(m_usedConfigFileName); + NetManager::configure(config); + NetScanner::configure(config); + validator.configure(config); + //after reading the new configuration we should really update + m_lastUpdate=0; +} + +void NetManager::configure(Config& config) +{ + m_refreshTime=config.getEntry("UpdatePeriod",300); + MyString tmp=stripWhiteSpace(config.getEntry("BroadcastNetwork","0.0.0.0/255.255.255.255;")); + tmp=tmp+";"; + mdcerr<<"NetManager::readConfig: "<<tmp<<std::endl; + MyString netAddressStr=tmp.left(tmp.find('/')); + tmp=tmp.mid(tmp.find('/')+1); + tmp=tmp.left(tmp.find(';')); + mdcerr<<"NetManager::readConfig: broadcastNet "<<netAddressStr<<" with mask "<<tmp<<std::endl; + int netMask=inet_addr(tmp.c_str()); + int netAddress=inet_addr(netAddressStr.c_str()); + m_broadcastAddress= netAddress | (~netMask); + mdcerr<<"NetManager::readConfig: net "<<std::ios::hex<<netAddress<<" with mask "<<netMask<<" gives "<<m_broadcastAddress<<std::endl; + + //maybe this way we can avoid that all servers on the net send + //their requests synchronously, since now the refreshtime isn't + //always the eact value of m_refreshTime, but differs always slightly + if ((m_refreshTime%SELECT_TIMEOUT)==0) m_refreshTime+=2; + //some limits from half a minute to half an hour + if (m_refreshTime<30) m_refreshTime=30; + if (m_refreshTime>1800) m_refreshTime=1800; + m_initialRefreshTime=m_refreshTime; +} + +int NetManager::prepare() +{ + mdcerr<<"NetManager::prepare"<<std::endl; + + ::close(m_listenFD); + ::close(m_bcFD); + int result(0); + if (m_strictMode) + { + m_listenFD=::socket(AF_LOCAL, SOCK_STREAM, 0); + //m_listenFD=::socket(AF_LOCAL, SOCK_STREAM, IPPROTO_TCP); + MyString socketName("/tmp/resLisa-"); + struct passwd *user = getpwuid( getuid() ); + if ( user ) + socketName+=user->pw_name; + else + //should never happen + socketName+="???"; + ::unlink(socketName.data()); + sockaddr_un serverAddr; + if (socketName.length() >= sizeof(serverAddr.sun_path)) + { + std::cout<<"NetManager::prepare: your user name \""<<user->pw_name<<"\" is too long, exiting."<<std::endl; + return 0; + } + memset((void*)&serverAddr, 0, sizeof(serverAddr)); + serverAddr.sun_family=AF_LOCAL; + strncpy(serverAddr.sun_path,socketName.data(),sizeof(serverAddr.sun_path)); + result=::bind(m_listenFD,(sockaddr*) &serverAddr,sizeof(serverAddr)); + if (result!=0) + { + std::cout<<"NetManager::prepare: bind (UNIX socket) failed, errno: "<<errno<<std::endl; + return 0; + } + } + else + { + //create a listening port and listen + m_listenFD = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (m_listenFD==-1) + { + std::cout<<"NetManager::prepare: socket(TCP) failed, errno: "<<errno<<std::endl; + return 0; + } + + sockaddr_in serverAddress; +// bzero((char*)&serverAddress, sizeof(serverAddress)); + memset((void*)&serverAddress, 0, sizeof(serverAddress)); + serverAddress.sin_family = AF_INET; + serverAddress.sin_addr.s_addr = htonl(INADDR_ANY); + serverAddress.sin_port = htons(m_basePort); + + int on(1); + result=setsockopt(m_listenFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + if (result!=0) + { + std::cout<<"NetManager::prepare: setsockopt(SO_REUSEADDR) failed"<<std::endl; + return 0; + } + result=::bind(m_listenFD, (struct sockaddr *) &serverAddress, sizeof(serverAddress)); + if (result!=0) + { + std::cout<<"NetManager::prepare: bind (TCP) failed, errno: "<<errno<<std::endl; + return 0; + } + } + result=::listen(m_listenFD, 32); + if (result!=0) + { + std::cout<<"NetManager::prepare: listen failed"<<std::endl; + return 0; + } + mdcerr<<"NetManager::prepare: listening on port "<<m_basePort<<"..."<<std::endl; + + return 1; +} + +void NetManager::generateFDset(fd_set *tmpFDs) +{ + mdcerr<<"NetManager::generateFDset"<<std::endl; + + FD_ZERO(tmpFDs); + FD_SET(m_listenFD,tmpFDs); + mdcerr<<"NetManager::generateFDset: adding listen FD="<<m_listenFD<<std::endl; +// for (Client* tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next()) + for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++) + if (tmpClient->fd()!=-1) + { + mdcerr<<"NetManager::generateFDset: adding client FD="<<tmpClient->fd()<<std::endl; + FD_SET(tmpClient->fd(),tmpFDs); + } + + if (m_pipeFD!=-1) + { + mdcerr<<"NetManager::generateFDset: adding pipeFD="<<m_pipeFD<<std::endl; + FD_SET(m_pipeFD,tmpFDs); + } + + if ((m_bcFD!=-1) && (!m_strictMode)) + { + mdcerr<<"NetManager::generateFDset: adding m_bcFD="<<m_bcFD<<std::endl; + FD_SET(m_bcFD,tmpFDs); + } +} + +int NetManager::waitForSomethingToHappen(fd_set *tmpFDs) +{ + mdcerr<<"NetManager::waitForSomethingToHappen for 10 seconds"<<std::endl; + if (m_firstRun) + { + tv.tv_sec = 1; + m_firstRun=0; + } + else + tv.tv_sec = SELECT_TIMEOUT; + + tv.tv_usec = 0; + //generateFDset(tmpFDs); + int result=select(getMaxFD(),tmpFDs,0,0,&tv); + if (result>0) return 1; + else return 0; +} + +int NetManager::getMaxFD() +{ + int maxFD(m_listenFD); +// for (Client* tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next()) + for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++) + if (tmpClient->fd()>maxFD) + maxFD=tmpClient->fd(); + + if (m_pipeFD>maxFD) maxFD=m_pipeFD; + if (m_bcFD>maxFD) maxFD=m_bcFD; + + mdcerr<<"NetManager::getMaxFD()="<<maxFD+1<<std::endl; + return maxFD+1; +} + +int fileReadable(const MyString& filename) +{ + FILE *file=::fopen(filename.data(), "r"); + if (file==0) + return 0; + fclose(file); + return 1; +} + +MyString NetManager::getConfigFileName() +{ + MyString tmpBase(CONFIGFILEBASENAME); + + if (m_strictMode) + tmpBase=STRICTCONFIGFILEBASENAME; + + if (!m_extraConfigFile.isEmpty()) + m_configStyle=EXTRACONFIGSTYLE; + + MyString tmpFilename; + if (m_configStyle==EXTRACONFIGSTYLE) + { + tmpFilename=m_extraConfigFile; + if (fileReadable(tmpFilename)) + return tmpFilename; + return ""; + } + else if (m_configStyle==UNIXCONFIGSTYLE) + { + tmpFilename=getenv("HOME"); + tmpFilename+=MyString("/.")+tmpBase; + if (fileReadable(tmpFilename)) + return tmpFilename; + tmpFilename="/etc/"; + tmpFilename+=tmpBase; + if (fileReadable(tmpFilename)) + return tmpFilename; + return ""; + } +/* else if (m_configStyle==KDE1CONFIGSTYLE) + { + tmpFilename=getenv("HOME"); + tmpFilename+=MyString("/.kde/share/config/")+tmpBase; + if (fileReadable(tmpFilename)) + return tmpFilename; + tmpFilename=getenv("KDEDIR"); + tmpFilename+=MyString("/share/config/")+tmpBase; + if (fileReadable(tmpFilename)) + return tmpFilename; + return ""; + } + else if (m_configStyle==KDE2CONFIGSTYLE) + { + FILE *kdeConfig=popen("kde-config --path config","r"); + if (kdeConfig==0) + { + std::cout<<"could not execute kde-config, check your KDE 2 installation\n"<<std::endl; + return ""; + }; + //this should be large enough + char buf[4*1024]; + memset(buf,0,4*1024); + fgets(buf,4*1024-1,kdeConfig); + // Warning: The return value of plcose may be incorrect due to the + // SIGCHLD handler that is installed. Ignore it! + pclose(kdeConfig); + int length=strlen(buf); + if (buf[length-1]=='\n') buf[length-1]='\0'; + MyString kdeDirs(buf); + while (kdeDirs.contains(':')) + { + MyString nextDir=kdeDirs.left(kdeDirs.find(':')); + kdeDirs=kdeDirs.mid(kdeDirs.find(':')+1); + nextDir=nextDir+tmpBase; + mdcerr<<"trying to open "<<nextDir<<std::endl; + if (fileReadable(nextDir)) + return nextDir; + }; + kdeDirs=kdeDirs+tmpBase; + mdcerr<<"trying to open "<<kdeDirs<<std::endl; + if (fileReadable(kdeDirs)) + return kdeDirs; + kdeDirs="/etc/"; + kdeDirs=kdeDirs+tmpBase; + if (fileReadable(kdeDirs)) + return kdeDirs; + return ""; + }*/ + return ""; +} + +int NetManager::run() +{ + int continueMainLoop(1); + //not forever + while(continueMainLoop) + { + mdcerr<<"\nNetManager::run: next loop: "<<clients.size()<<" clients"<<std::endl; + fd_set tmpFDs; + generateFDset(&tmpFDs); + + int result=waitForSomethingToHappen(&tmpFDs); + mdcerr<<"NetManager::run: something happened..."<<std::endl; + //timeout + if (result==0) + { + mdcerr<<"NetManager::run: serverServer=="<<m_serverServer<<std::endl; + mdcerr<<"NetManager::run: scanning after timeout"<<std::endl; + scan(); + } + else + { + //a new connection ? + if (FD_ISSET(m_listenFD,&tmpFDs)) + { + mdcerr<<"NetManager::run: on m_listenFD"<<std::endl; + struct sockaddr_in clientAddress; + socklen_t clilen(sizeof(clientAddress)); +// bzero((char*)&clientAddress, clilen); + memset((void*)&clientAddress,0,sizeof(clientAddress)); + int connectionFD=::accept(m_listenFD,(struct sockaddr *) &clientAddress, &clilen); + if ((validator.isValid(clientAddress.sin_addr.s_addr)) || (m_strictMode)) + { + mdcerr<<"NetManager::run: adding client"<<std::endl; + addClient(connectionFD); + m_servedThisPeriod=1; + m_refreshTime=m_initialRefreshTime; + m_increasedRefreshTime=0; + } + else + { + mdcerr<<"NetManager::run: kicking client"<<std::endl; + ::close(connectionFD); + } + } + checkClientsAndPipes(&tmpFDs); + } + serveAndClean(); + } + return 1; +} + +void NetManager::addClient(int socketFD) +{ + mdcerr<<"NetManager::addClient on FD="<<socketFD<<std::endl; + if (socketFD==-1) return; + Client c(this,socketFD,0); + clients.push_back(c); +} + +void NetManager::checkClientsAndPipes(fd_set *tmpFDs) +{ + mdcerr<<"NetManager::checkClientsAndPipes()"<<std::endl; + //actually the clients should not send anything +// for (Client *tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next()) + for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++) + { + mdcerr<<"NetManager::checkClientsAndPipes: checking client"<<std::endl; + if (FD_ISSET(tmpClient->fd(),tmpFDs)) + { + mdcerr<<"NetManager::checkClientsAndPipes: client sent something"<<std::endl; + tmpClient->read(); + } + } + + //now check wether we received a broadcast + //m_bcFD should always be -1 in strictMode + if ((m_bcFD!=-1) && (!m_strictMode)) + { + mdcerr<<"NetManager::checkClientsAndPipe: checking bcFD"<<std::endl; + //yes ! + if (FD_ISSET(m_bcFD,tmpFDs)) + answerBroadcast(); + } + + //read the stuff from the forked pipe + if (m_pipeFD!=-1) + { + mdcerr<<"NetManager::checkClientsAndPipe: checking pipe"<<std::endl; + if (FD_ISSET(m_pipeFD,tmpFDs)) + { + mdcerr<<"NetManager::checkClientsAndPipes: pipe sent something"<<std::endl; + int result=readDataFromFD(m_pipeFD); + if (result!=1) + { + ::close(m_pipeFD); + m_pipeFD=-1; + mdcerr<<"NetManager::checkClientsAndPipes: everything read from pipe from proc "<<m_childPid<<std::endl; + processScanResults(); + } + } + } +} + +void NetManager::answerBroadcast() +{ + //actually we should never get here in strictMode + if (m_strictMode) return; + + //this one is called only in checkClientsAndPipes() + //if we are sure that we received something on m_bcFD + //so we don't need to check here again + + mdcerr<<"NetManager::answerBroadcast: received BC"<<std::endl; + struct sockaddr_in sAddr; + socklen_t length(sizeof(sockaddr_in)); + char buf[1024]; + int result=recvfrom(m_bcFD, (char*)buf, 1024, 0, (sockaddr*)&sAddr,&length); + mdcerr<<"NetManager::answerBroadcast: received successfully"<<std::endl; + //did recvfrom() succeed ? + //our frame is exactly MYFRAMELENGTH bytes big, if the received one has a different size, + //it must be something different + if (result!=MYFRAMELENGTH) return; + //if it has the correct size, it also must have the correct identifier + MyFrameType *frame=(MyFrameType*)(void*)buf; + if ((ntohl(frame->id)!=MY_ID) || + ((ntohl(frame->unused1)==getpid()) && (ntohl(frame->unused2)==m_startedAt))) + { + mdcerr<<"NetManager::answerBroadcast: must be the same machine"<<std::endl; + return; + } + + //mdcerr<<"received "<<ntohl(buf[0])<<" from "<<inet_ntoa(sAddr.sin_addr)<<hex<<" "; + //mdcerr<<sAddr.sin_addr.s_addr<<" //"<<ntohl(sAddr.sin_addr.s_addr)<<dec<<std::endl; + //will we answer this request ? + if (!validator.isValid(sAddr.sin_addr.s_addr)) + { + mdcerr<<"NetManager::answerBroadcast: invalid sender"<<std::endl; + return; + } + + //create the answering socket + + int answerFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (answerFD==-1) + { + mdcerr<<"NetManager::answerBroadcast: could not create answering socket"<<std::endl; + return; + } + + sAddr.sin_family=AF_INET; + sAddr.sin_port=htons(m_basePort+1); + MyFrameType answerFrame; + answerFrame.id=htonl(MY_ID); + answerFrame.unused1=0; + answerFrame.unused2=0; + //don't care about the result + mdcerr<<"NetManager::answerBroadcast: sending answer"<<std::endl; + + result=::sendto(answerFD,(char*)&answerFrame,sizeof(answerFrame),0,(sockaddr*)&sAddr,length); + mdcerr<<"sent "<<result<<" byte using sendto"<<std::endl; + ::close(answerFD); + //sent answer +} + +void NetManager::serveAndClean() +{ + //try to serve the request +// for (Client *tmpClient=clients.first(); tmpClient!=0; tmpClient=clients.next()) + for (std::list<Client>::iterator tmpClient=clients.begin(); tmpClient != clients.end(); tmpClient++) + { + mdcerr<<"NetManager::serveAndClean: trying to get info"<<std::endl; + tmpClient->tryToGetInfo(); + } + + //try to delete served the clients + /** PM: substituted STL code + for (Client* tmpClient=clients.first();tmpClient!=0; tmpClient=clients.next()) + { + //if we served the client or if he's already half a minute + //connected remove it + //this way we get rid of clients if something went wrong and + //maybe it's even a security point, I don't know + if ((tmpClient->done()) || (tmpClient->age()>30)) + { + mdcerr<<"NetManager::serveAndClean: removing Client"<<std::endl; + clients.remove(tmpClient); + tmpClient=clients.first(); + } + }*/ + clients.remove_if(client_is_done()); +} + +void NetManager::scan() +{ + mdcerr<<"NetManager::scan()"<<std::endl; + if (isBeingScanned()) return; + + time_t currentTime=time(0); + mdcerr<<"currentTime: "<<currentTime<<" lastUpdate: "<<m_lastUpdate<<std::endl; + if ((currentTime-m_lastUpdate)<m_refreshTime) return; + mdcerr<<"NetManager::scan: scanning..."<<std::endl; + + m_isBeingScanned=1; + int fileDescr[2]; + ::pipe(fileDescr); + mdcerr<<"NetScanner::scan: file-descr[0]: "<<fileDescr[0]<<std::endl; + mdcerr<<"NetScanner::scan: file-descr[1]: "<<fileDescr[1]<<std::endl; + int pid=fork(); + if (pid==-1) + { + mdcerr<<"NetScanner::scan: error occurred"<<std::endl; + return; + } + else if (pid!=0) + { + //parent + ::close(fileDescr[1]); + m_pipeFD=fileDescr[0]; + m_childPid=pid; + return; + } + //child + procId="** child ** "; + mdcerr<<" NetManager::scan: a child was born"<<std::endl; + if (m_strictMode) + { + mdcerr<<" NetManager::scan: scanning myself in strict mode, becoming serverServer"<<std::endl; + doScan(); + //in the child we don't have to call setServerServer() + //since this opens the listening socket, what has to be done + //in the parent process + m_serverServer=1; + } + else + { + int serverAddress=findServerServer(); + if (serverAddress==0) + { + mdcerr<<" NetManager::scan: scanning myself, becoming serverServer"<<std::endl; + doScan(); + //in the child we don't have to call setServerServer() + //since this opens the listening socket, what has to be done + //in the parent process + m_serverServer=1; + } + else + { + mdcerr<<" NetManager::scan: getting list from serverServer"<<std::endl; + getListFromServerServer(serverAddress); + m_serverServer=0; + } + } + mdcerr<<" NetScanner::scan: sending information to parent process"<<std::endl; + writeDataToFD(fileDescr[1],m_serverServer); + mdcerr<<" NetScanner::scan: closed FD: "<<::close(fileDescr[1])<<std::endl; + mdcerr<<" NetScanner::scan: exiting now"<<std::endl; + ::exit(0); +} + +int NetManager::writeDataToFD(int fd, int serverServer) +{ + mdcerr<<" NetManager::writeDataToFD fd="<<fd<<std::endl; + m_serveCount++; + char buffer[1024]; + + int length; +// for (Node* tmpNode=hostList->first(); tmpNode!=0; tmpNode=hostList->next()) + for (std::list<Node>::iterator tmpNode=hostList->begin(); tmpNode!=hostList->end(); tmpNode++) + { + sprintf(buffer,"%u %s\n",tmpNode->ip,tmpNode->name.left(1000).c_str()); + length=strlen(buffer)+1; + const char *currentBuf=buffer; + //make sure that everything is written + while (length>0) + { + int result=::write(fd,currentBuf,length); + mdcerr<<"NetManager::writeDataToFD: wrote "<<result<<" bytes"<<std::endl; + if (result==-1) + { + perror("hmmpf: "); + return 0; + } + length-=result; + currentBuf+=result; + } + } + //and a last line + sprintf(buffer,"%d succeeded\n",serverServer); + length=strlen(buffer)+1; + const char *currentBuf=buffer; + //make sure that everything is written + while (length>0) + { + int result=::write(fd,currentBuf,length); + if (result==-1) return 0; + length-=result; + currentBuf+=result; + } + return 1; +} + +int NetManager::readDataFromFD(int fd) +{ + mdcerr<<"NetManager::readDataFromFD"<<std::endl; + char tmpBuf[64*1024]; + int result=::read(fd,tmpBuf,64*1024); + mdcerr<<"NetManager::readDataFromFD: read "<<result<<" bytes"<<std::endl; + if (result==-1) return -1; + if (result==0) + { + mdcerr<<"NetManager::readDataFromFD: FD was closed"<<std::endl; + return 0; + } + char *newBuf=new char[m_receivedBytes+result]; + if (m_receiveBuffer!=0) memcpy(newBuf,m_receiveBuffer,m_receivedBytes); + memcpy(newBuf+m_receivedBytes,tmpBuf,result); + m_receivedBytes+=result; + if (m_receiveBuffer!=0) delete [] m_receiveBuffer; + m_receiveBuffer=newBuf; + // too much data - abort at 2MB to avoid memory exhaustion + if (m_receivedBytes>2*1024*1024) + return 0; + + return 1; +} + +int NetManager::processScanResults() +{ + mdcerr<<"NetManager::processScanResults"<<std::endl; + if (m_receiveBuffer==0) return 0; + std::list<Node> *newNodes=new std::list<Node>; + + char *tmpBuf=m_receiveBuffer; + int bytesLeft=m_receivedBytes; + mdcerr<<"m_receivedBytes: "<<m_receivedBytes<<" bytesLeft: "<<bytesLeft<<std::endl; + //this should be large enough for a name + //and the stuff which is inserted into the buffer + //comes only from ourselves ... or attackers :-( + char tmpName[1024*4]; + while (bytesLeft>0) + { + int tmpIP=2; // well, some impossible IP address, 0 and 1 are already used for the last line of output + tmpName[0]='\0'; + if ((memchr(tmpBuf,0,bytesLeft)==0) || (memchr(tmpBuf,int('\n'),bytesLeft)==0)) + { + delete newNodes; + hostList->clear(); + + m_lastUpdate=time(0); + delete [] m_receiveBuffer; + m_receiveBuffer=0; + m_receivedBytes=0; + m_isInformed=1; + m_isBeingScanned=0; + return 0; + } + //mdcerr<<"NetManager::processScanResults: processing -"<<tmpBuf; + //since we check for 0 and \n with memchr() we can be sure + //at this point that tmpBuf is correctly terminated + int length=strlen(tmpBuf)+1; + if (length<(4*1024)) + sscanf(tmpBuf,"%u %s\n",&tmpIP,tmpName); + + bytesLeft-=length; + tmpBuf+=length; + mdcerr<<"length: "<<length<<" bytesLeft: "<<bytesLeft<<std::endl; + if ((bytesLeft==0) && ((tmpIP==0) ||(tmpIP==1)) && (strstr(tmpName,"succeeded")!=0)) + { + mdcerr<<"NetManager::processScanResults: succeeded :-)"<<std::endl; + delete hostList; + hostList=newNodes; + + m_lastUpdate=time(0); + delete [] m_receiveBuffer; + m_receiveBuffer=0; + m_receivedBytes=0; + m_isInformed=1; + m_isBeingScanned=0; + adjustRefreshTime(tmpIP); + enableServerServer(tmpIP); + //m_serverServer=tmpIP; + + return 1; + } + else if (tmpIP!=2) + { + //mdcerr<<"NetManager::processScanResults: adding host: "<<tmpName<<" with ip: "<<tmpIP<<std::endl; + newNodes->push_back(Node(tmpName,tmpIP)); + } + } + //something failed :-( + delete newNodes; + hostList->clear(); + + m_lastUpdate=time(0); + delete [] m_receiveBuffer; + m_receiveBuffer=0; + m_receivedBytes=0; + m_isInformed=1; + m_isBeingScanned=0; + + mdcerr<<"NetScanner::processScanResult: finished"<<std::endl; + return 0; +} + +void NetManager::adjustRefreshTime(int serverServer) +{ + //we are becoming server server + if (((m_serverServer==0) && (serverServer)) || (m_servedThisPeriod)) + { + m_increasedRefreshTime=0; + m_refreshTime=m_initialRefreshTime; + } + //nobody accessed the server since the last update + //so increase the refresh time + //this should happen more seldom to the serverServer + //than to others + else + { + //up to the 16 times refresh time + if (m_increasedRefreshTime<4) + { + m_increasedRefreshTime++; + m_refreshTime*=2; + }; + }; + m_servedThisPeriod=0; + +} + +void NetManager::enableServerServer(int on) +{ + mdcerr<<"NetManager::enableServerServer: "<<on<<std::endl; + //in strictMode we don't listen to broadcasts from the network + if (m_strictMode) return; + + m_serverServer=on; + if (on) + { + //nothing has to be done + if (m_bcFD!=-1) return; + // otherwise create the socket which will listen for broadcasts + sockaddr_in my_addr; + my_addr.sin_family=AF_INET; + my_addr.sin_port=htons(m_basePort); + my_addr.sin_addr.s_addr=0; + + m_bcFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (m_bcFD==-1) + { + mdcerr<<"NetManager::enableServerServer: socket() failed"<<std::endl; + m_serverServer=0; + return; + }; + int on(1); + int result=setsockopt(m_bcFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + if (result!=0) + { + mdcerr<<"NetManager::enableServerServer: setsockopt(SO_REUSEADDR) failed"<<std::endl; + m_serverServer=0; + ::close(m_bcFD); + m_bcFD=-1; + return; + }; + result=::bind(m_bcFD, (struct sockaddr *) &my_addr, sizeof(my_addr)); + if (result!=0) + { + mdcerr<<"NetManager::enableServerServer: bind (UDP) failed"<<std::endl; + m_serverServer=0; + ::close(m_bcFD); + m_bcFD=-1; + return; + }; + } + else + { + ::close(m_bcFD); + m_bcFD=-1; + }; +} + +int NetManager::findServerServer() +{ + mdcerr<<" NetManager::findServerServer"<<std::endl; + //actually this should never be called in strictMode + if (m_strictMode) return 0; + if (!validator.isValid(m_broadcastAddress)) + { + mdcerr<<" NetManager::findServerServer: invalid broadcastAddress"<<std::endl; + return 0; + }; + //create a socket for sending the broadcast + //we don't have to set SO_REUSEADDR, since we don't call bind() + //to this socket + int requestFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (requestFD==-1) + { + mdcerr<<" NetManager::findServerServer: could not create request socket"<<std::endl; + return 0; + }; + + int on(1); + //this is actually the only socket which will send broacasts + int result=setsockopt(requestFD, SOL_SOCKET, SO_BROADCAST,(char*)&on, sizeof(on)); + if (result!=0) + { + mdcerr<<"setsockopt(SO_BROADCAST) failed"<<std::endl; + ::close(requestFD); + return 0; + }; + + //create a socket for receiving the answers + int answerFD=::socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (answerFD==-1) + { + mdcerr<<" NetManager::findServerServer"<<std::endl; + ::close(requestFD); + return 0; + }; + + //since this socket will be bound, we have to set SO_REUSEADDR + result=setsockopt(answerFD, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on)); + if (result!=0) + { + mdcerr<<"setsockopt(SO_REUSEADDR) failed"<<std::endl; + ::close(answerFD); + ::close(requestFD); + return 0; + }; + + sockaddr_in my_addr; + my_addr.sin_family=AF_INET; + my_addr.sin_port=htons(m_basePort+1); + my_addr.sin_addr.s_addr=0; + //this one has to be bound + result=::bind(answerFD, (struct sockaddr *) &my_addr, sizeof(my_addr)); + if (result!=0) + { + mdcerr<<"bind (UDP) failed"<<std::endl; + ::close(answerFD); + ::close(requestFD); + return 0; + }; + + //now send the broadcast + struct sockaddr_in sAddr; + sAddr.sin_addr.s_addr=m_broadcastAddress; + sAddr.sin_family=AF_INET; + sAddr.sin_port=htons(m_basePort); + socklen_t length(sizeof(sockaddr_in)); + mdcerr<<" NetManager::findServerServer: broadcasting to: " + <<std::ios::hex<<m_broadcastAddress<<std::ios::dec<<std::endl; + + MyFrameType requestFrame; + requestFrame.id=htonl(MY_ID); + requestFrame.unused1=htonl(getppid()); + requestFrame.unused2=htonl(m_startedAt); + + result=::sendto(requestFD,(char*)&requestFrame,sizeof(requestFrame),0,(sockaddr*)&sAddr,length); + ::close(requestFD); + if (result!=MYFRAMELENGTH) + { + mdcerr<<" NetManager::findServerServer: sent wrong number of bytes: "<<result<<std::endl; + ::close(answerFD); + return 0; + }; + //wait for an answer + struct timeval tv; + tv.tv_sec=0; + tv.tv_usec=1000*250; //0.1 sec + fd_set fdSet; + FD_ZERO(&fdSet); + FD_SET(answerFD,&fdSet); + result=select(answerFD+1,&fdSet,0,0,&tv); + if (result<0) + { + mdcerr<<" NetManager::findServerServer: select() failed: "<<result<<std::endl; + mdcerr<<" NetManager::findServerServer: answerFD="<<answerFD<<std::endl; + perror("select:"); + ::close(answerFD); + return 0; + } + if (result==0) + { + mdcerr<<" NetManager::findServerServer: nobody answered "<<std::endl; + ::close(answerFD); + return 0; + } + mdcerr<<"received offer "<<std::endl; + struct sockaddr_in addr; + length=sizeof(sockaddr_in); + char buf[1024]; + result=recvfrom(answerFD, (char*)buf, 1024, 0, (sockaddr*) &addr,&length); + if (result!=MYFRAMELENGTH) + { + mdcerr<<" NetManager::findServerServer: wrong number of bytes: "<<result<<std::endl; + ::close(answerFD); + return 0; + }; + MyFrameType *frame=(MyFrameType*)(void*)buf; + //wrong identifier ? + if (ntohl(frame->id)!=MY_ID) + { + mdcerr<<" NetManager::findServerServer: wrong id"<<std::endl; + ::close(answerFD); + return 0; + }; + + mdcerr<<"received from "<<inet_ntoa(addr.sin_addr)<<std::endl; + + ::close(answerFD); + //return the ip of the server server in network byte order + return addr.sin_addr.s_addr; +} + +void NetManager::getListFromServerServer( int address) +{ + mdcerr<<"NetManager::getListFromServerServer"<<std::endl; + //actually we should never get here in strictMode + if (m_strictMode) return; + //open a tcp socket to the serverserver + int serverServerFD=::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (serverServerFD==-1) + return; + sockaddr_in addr; + //we get the address already in network byte order + addr.sin_addr.s_addr=address; + addr.sin_family=AF_INET; + addr.sin_port=htons(m_basePort); + int result=::connect(serverServerFD,(sockaddr*)&addr,sizeof(addr)); + if (result!=0) + { + ::close(serverServerFD); + return; + }; + do + { + result=readDataFromFD(serverServerFD); + } while (result==1); + ::close(serverServerFD); + processScanResults(); + mdcerr<<"NetManager::getListFromServerServer succeeded"<<std::endl; +} + +void NetManager::printState() +{ + std::cerr<<"LAN Information Server Lisa "MYVERSION"\nAlexander Neundorf <neundorf@kde.org>\n"; + std::cerr<<"Reading options from config file: "<<m_usedConfigFileName<<std::endl; + std::cerr<<"StrictMode: "<<m_strictMode<<std::endl; + std::cerr<<"ServerServer: "<<m_serverServer<<std::endl; + std::cerr<<"UseNmblookup: "<<m_useNmblookup<<std::endl; + std::cerr<<"Pinging: "<<ipRangeStr<<std::endl; + std::cerr<<"Allowed hosts: "<<validator.validAddresses()<<std::endl; + std::cerr<<"Broadcasting to: "<<std::ios::hex<<ntohl(m_broadcastAddress)<<std::ios::dec<<std::endl; + std::cerr<<"Initial update period: "<<m_initialRefreshTime<<" seconds"<<std::endl; + std::cerr<<"Current update period: "<<m_refreshTime<<" seconds"<<std::endl; + std::cerr<<"Last update: "<<time(0)-m_lastUpdate<<" seconds over"<<std::endl; + std::cerr<<"Waiting "<<m_firstWait<<" 1/100th seconds for echo answers on the first try"<<std::endl; + std::cerr<<"Waiting "<<m_secondWait<<" 1/100th seconds for echo answers on the second try"<<std::endl; + std::cerr<<"Sending "<<m_maxPings<<" echo requests at once"<<std::endl; + std::cerr<<"Publishing unnamed hosts: "<<m_deliverUnnamedHosts<<std::endl; + std::cerr<<"Already served "<<m_serveCount<<" times"<<std::endl; +} + +//this one is not used at the moment +/*int NetManager::uptime() +{ + return (time(0)-m_startedAt); +};*/ |