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.
qps-packaging/src/lookup.cpp

285 lines
7.2 KiB

/*
* lookup.cpp
* This file is part of qps -- Qt-based visual process status monitor
*
* Copyright 1997-1999 Mattias Engdegård
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
// This module implements asynchronous address->hostname lookup.
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <signal.h>
#include <netinet/in.h>
#include <netdb.h>
#include "lookup.h"
#include "svec.cpp"
// declarations of static members
char *Lookup::argv0;
int Lookup::maxtitlelen;
UintQueue::UintQueue() { first = last = 0; };
void UintQueue::enqueue(unsigned x)
{
if (last < 0)
{
// make room for inserting more elements
int step = qMax(first + 1, 8);
queue.setSize(first + step + 1);
for (int i = first; i > last; i--)
queue[i + step] = queue[i];
first += step;
last += step;
}
queue.set(last--, x);
}
unsigned UintQueue::dequeue()
{
if (isEmpty())
fatal("UintQueue: queue empty");
return queue[first--];
}
// constructor for node head
Hostnode::Hostnode() : next(this), prev(this) {}
// create a new cache node, initialized with null string
Hostnode::Hostnode(unsigned addr) : ipaddr(addr), next(0), prev(0) {}
// must be called on the head of the list
void Hostnode::moveToFront(Hostnode *node)
{
if (next != node)
{
Hostnode *p = node->prev, *n = node->next;
p->next = n;
n->prev = p;
node->next = next;
node->prev = this;
next->prev = node;
next = node;
}
}
// must be called on the head of the list
void Hostnode::deleteLast()
{
Hostnode *nuke = prev;
prev = nuke->prev;
prev->next = this;
delete nuke;
}
// must be called on the head of the list
void Hostnode::insertFirst(Hostnode *node)
{
node->prev = this;
node->next = next;
next->prev = node;
next = node;
}
Lookup::Lookup() // : hostdict(17)
{
sockfd = -1; // no lookup helper is running
readsn = writesn = 0;
outstanding = 0;
}
// empty destructor, workaround for gcc bug
Lookup::~Lookup() {}
// look up host name (addr is in host byte order)
// a null name means it is been looked up (signal will be sent when done)
QString Lookup::hostname(unsigned addr)
{
// first look in our cache
Hostnode *hn = hostdict.value(addr, NULL);
if (hn)
{
hostlru.moveToFront(hn);
}
else
{
hn = new Hostnode(addr);
if (hostdict.count() >= hostname_cache_size)
{
// remove least recently used item
hostdict.remove(hostlru.last()->ipaddr);
hostlru.deleteLast();
}
hostlru.insertFirst(hn);
hostdict.insert(addr, hn);
// if(hostdict.count() > hostdict.size() * 3)
// hostdict.resize(hostdict.count());
if (addr == 0)
hn->name = "*";
else
request(addr);
}
return hn->name;
}
void Lookup::request(unsigned addr)
{
addrqueue.enqueue(addr);
if (sockfd < 0)
{
int socks[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, socks);
// launch a new helper
signal(SIGCHLD, SIG_IGN); // Linux does automatic child reaping, nice
switch (fork())
{
case -1: // error
return; // don't bother, we'll try again next time
case 0: // child
close(socks[0]);
do_child(socks[1]);
break;
default: // parent
close(socks[1]);
sockfd = socks[0];
readsn = new QSocketNotifier(sockfd, QSocketNotifier::Read, this);
connect(readsn, SIGNAL(activated(int)), SLOT(receive_result(int)));
writesn = new QSocketNotifier(sockfd, QSocketNotifier::Write, this);
connect(writesn, SIGNAL(activated(int)), SLOT(send_request(int)));
break;
}
}
writesn->setEnabled(true);
}
// the child helper process loop
void Lookup::do_child(int fd)
{
setproctitle("qps-dns-helper");
// close unused fds
for (int i = 0; i < fd; i++)
close(i);
for (;;)
{
unsigned addr;
int ret = read(fd, &addr, sizeof(addr));
if (ret <= 0)
{
_exit(0); // connection closed
}
struct hostent *h = gethostbyaddr((char *)&addr, sizeof(addr), AF_INET);
char buf[256];
if (!h)
{
unsigned a = htonl(addr);
sprintf(buf, "%d.%d.%d.%d", (a >> 24) & 0xff, (a >> 16) & 0xff,
(a >> 8) & 0xff, a & 0xff);
}
else
{
strncpy(buf, h->h_name, sizeof(buf));
}
// if parent died, we'll get SIGPIPE here and terminate
// automatically
write(fd, &addr, sizeof(addr));
int len = strlen(buf);
write(fd, &len, sizeof(len));
write(fd, buf, len);
}
}
// slot: receive result from helper
void Lookup::receive_result(int)
{
unsigned addr;
int len;
char buf[256];
if (read(sockfd, &addr, sizeof(addr)) <= 0 ||
read(sockfd, &len, sizeof(len)) <= 0 || read(sockfd, buf, len) <= 0)
{
// helper has died
delete readsn;
delete writesn;
close(sockfd);
sockfd = -1;
return;
}
buf[len] = '\0';
Hostnode *hn = hostdict.value(addr, NULL);
if (!hn)
return; // gone from cache
hn->name = buf;
emit resolved(addr);
outstanding--;
// if there is nothing more in the queue, kill the helper
if (addrqueue.isEmpty() && outstanding == 0)
{
close(sockfd);
sockfd = -1;
delete readsn;
delete writesn;
}
}
// slot: send request to the helper
void Lookup::send_request(int)
{
if (addrqueue.isEmpty())
{
writesn->setEnabled(false);
return;
}
unsigned addr = addrqueue.dequeue();
if (write(sockfd, &addr, sizeof(addr)) < 0)
{
// This shouldn't happen, try to repair it anyway
addrqueue.enqueue(addr);
delete readsn;
delete writesn;
close(sockfd);
sockfd = -1;
return;
}
outstanding++;
}
// register and measure the space for modifying the visible command line
void Lookup::initproctitle(char **argv, char **envp)
{
argv0 = argv[0];
while (*envp)
envp++;
maxtitlelen = envp[-1] + strlen(envp[-1]) - argv0 - 2;
}
// set the process title (idea snarfed from sysvinit (thanks Miquel) and
// refined by peeking into wu-ftpd)
void Lookup::setproctitle(const char *txt)
{
memset(argv0, 0, maxtitlelen);
strncpy(argv0, txt, maxtitlelen - 1);
}