/*
mtr -- a network diagnostic tool
Copyright (C) 1997,1998 Matt Kimball
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.
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.
*/
/*
Non-blocking DNS portion --
Copyright (C) 1998 by Simon Kirby <sim@neato.org>
Released under GPL, as above.
*/
#include "config.h"
#ifdef HAVE_ERROR_H
#include <error.h>
#else
#include "portability/error.h"
#endif
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include "mtr.h"
#include "dns.h"
#include "net.h"
#include "utils.h"
#include "packet/sockaddr.h"
struct dns_results {
ip_t ip;
char *name;
struct dns_results *next;
};
static struct dns_results *results;
char *strlongip(
sa_family_t family,
ip_t * ip)
{
#ifdef ENABLE_IPV6
static char addrstr[INET6_ADDRSTRLEN];
return (char *) inet_ntop(family, ip, addrstr, sizeof addrstr);
#else
return inet_ntoa(*ip);
#endif
}
#ifdef ENABLE_IPV6
#define UNUSED_IF_NO_IPV6 /* empty */
#else
#define UNUSED_IF_NO_IPV6 ATTRIBUTE_UNUSED
#endif
static int todns[2], fromdns[2];
static FILE *fromdnsfp;
static int longipstr(
char *s,
ip_t * dst,
int family UNUSED_IF_NO_IPV6)
{
#ifdef ENABLE_IPV6
return inet_pton(family, s, dst);
#else
return inet_aton(s, dst);
#endif
}
static struct dns_results *findip(
struct mtr_ctl *ctl,
ip_t * ip)
{
struct dns_results *t;
for (t = results; t; t = t->next) {
if (addrcmp(ip, &t->ip, ctl->af) == 0)
return t;
}
return NULL;
}
static void set_sockaddr_ip(
sa_family_t family,
struct sockaddr_storage *sa,
ip_t * ip)
{
memset(sa, 0, sizeof(struct sockaddr_storage));
sa->ss_family = family;
memcpy(sockaddr_addr_offset(sa), ip, sockaddr_addr_size(sa));
}
void dns_open(
void)
{
int pid;
if (pipe(todns) < 0) {
error(EXIT_FAILURE, errno, "can't make a pipe for DNS process");
}
if (pipe(fromdns) < 0) {
error(EXIT_FAILURE, errno, "can't make a pipe for DNS process");
}
fflush(stdout);
pid = fork();
if (pid < 0) {
error(EXIT_FAILURE, errno, "can't fork for DNS process");
}
if (pid == 0) {
char buf[2048];
int i;
FILE *infp;
/* Automatically reap children. */
if (signal(SIGCHLD, SIG_IGN) == SIG_ERR) {
error(EXIT_FAILURE, errno, "signal");
}
/* Close all unnecessary FDs.
for debugging and error reporting, keep std-in/out/err. */
for (i = 3; i < fromdns[1]; i++) {
if (i == todns[0])
continue;
if (i == fromdns[1])
continue;
close(i);
}
infp = fdopen(todns[0], "r");
while (fgets(buf, sizeof(buf), infp)) {
ip_t host;
struct sockaddr_storage sa;
socklen_t salen;
char hostname[NI_MAXHOST];
char result[INET6_ADDRSTRLEN + NI_MAXHOST + 2];
if (!fork()) {
int rv;
buf[strlen(buf) - 1] = 0; /* chomp newline. */
sa_family_t family = (buf[0] == '4') ? AF_INET : AF_INET6;
longipstr(buf +1, &host, family);
set_sockaddr_ip(family, &sa, &host);
salen = (family == AF_INET) ? sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6);
rv = getnameinfo((struct sockaddr *) &sa, salen,
hostname, sizeof(hostname), NULL, 0, 0);
if (rv == 0) {
snprintf(result, sizeof(result),
"%s %s\n", strlongip(family, &host), hostname);
rv = write(fromdns[1], result, strlen(result));
if (rv < 0)
error(0, errno, "write DNS lookup result");
}
exit(EXIT_SUCCESS);
}
}
exit(EXIT_SUCCESS);
} else {
int flags;
/* the parent. */
close(todns[0]); /* close the pipe ends we don't need. */
close(fromdns[1]);
fromdnsfp = fdopen(fromdns[0], "r");
flags = fcntl(fromdns[0], F_GETFL, 0);
flags |= O_NONBLOCK;
fcntl(fromdns[0], F_SETFL, flags);
}
}
int dns_waitfd(
void)
{
return fromdns[0];
}
void dns_ack(
struct mtr_ctl *ctl)
{
char buf[2048], host[NI_MAXHOST], name[NI_MAXHOST];
ip_t hostip;
struct dns_results *r;
while (fgets(buf, sizeof(buf), fromdnsfp)) {
sscanf(buf, "%s %s", host, name);
longipstr(host, &hostip, ctl->af);
r = findip(ctl, &hostip);
if (r)
r->name = xstrdup(name);
else
error(0, 0, "dns_ack: Couldn't find host %s", host);
}
}
#ifdef ENABLE_IPV6
int dns_waitfd6(
void)
{
return -1;
}
void dns_ack6(
void)
{
return;
}
#endif
char *dns_lookup2(
struct mtr_ctl *ctl,
ip_t * ip)
{
struct dns_results *r;
char buf[INET6_ADDRSTRLEN + 2]; // af_byte + addr + null
int rv;
r = findip(ctl, ip);
if (r) {
/* we've got a result. */
if (r->name)
return r->name;
} else {
r = xmalloc(sizeof(struct dns_results));
memcpy(&r->ip, ip, sizeof(r->ip));
r->name = NULL;
r->next = results;
results = r;
char ip4or6 = (ctl->af == AF_INET) ? '4' : '6';
snprintf(buf, sizeof(buf), "%c%s\n", ip4or6, strlongip(ctl->af, ip));
rv = write(todns[1], buf, strlen(buf));
if (rv < 0)
error(0, errno, "couldn't write to resolver process");
}
return NULL;
}
char *dns_lookup(
struct mtr_ctl *ctl,
ip_t * ip)
{
char *t;
if (!ctl->dns || !ctl->use_dns)
return NULL;
t = dns_lookup2(ctl, ip);
return t ? t : strlongip(ctl->af, ip);
}
/* XXX check if necessary/exported. */
/* Resolve an IP address to a hostname. */
struct hostent *addr2host(
const char *addr,
int family)
{
int len = 0;
switch (family) {
case AF_INET:
len = sizeof(struct in_addr);
break;
#ifdef ENABLE_IPV6
case AF_INET6:
len = sizeof(struct in6_addr);
break;
#endif
}
return gethostbyaddr(addr, len, family);
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>