You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/lanbrowsing/lisa/netmanager.cpp

1059 lines
32 KiB

/* 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("/.trinity/share/config/")+tmpBase;
if (fileReadable(tmpFilename))
return tmpFilename;
tmpFilename=getenv("TDEDIR");
tmpFilename+=MyString("/share/config/")+tmpBase;
if (fileReadable(tmpFilename))
return tmpFilename;
return "";
}
else if (m_configStyle==KDE2CONFIGSTYLE)
{
FILE *kdeConfig=popen("tde-config --path config","r");
if (kdeConfig==0)
{
std::cout<<"could not execute tde-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);
};*/