/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netdb.h>
#include <string.h>
#include <signal.h>
#include <setjmp.h>
#include <arpa/inet.h>
#include "snoop.h"
static sigjmp_buf nisjmp;
#define MAXHASH 1024 /* must be a power of 2 */
#define SEPARATORS " \t\n"
struct hostdata {
struct hostdata *h_next;
char *h_hostname;
int h_pktsout;
int h_pktsin;
};
struct hostdata4 {
struct hostdata4 *h4_next;
char *h4_hostname;
int h4_pktsout;
int h4_pktsin;
struct in_addr h4_addr;
};
struct hostdata6 {
struct hostdata6 *h6_next;
char *h6_hostname;
int h6_pktsout;
int h6_pktsin;
struct in6_addr h6_addr;
};
static struct hostdata *addhost(int, const void *, const char *, char **);
static struct hostdata4 *h_table4[MAXHASH];
static struct hostdata6 *h_table6[MAXHASH];
#define iphash(e) ((e) & (MAXHASH-1))
/* ARGSUSED */
static void
wakeup(int n)
{
siglongjmp(nisjmp, 1);
}
extern char *inet_ntoa();
static struct hostdata *
iplookup(struct in_addr ipaddr)
{
register struct hostdata4 *h;
struct hostent *hp = NULL;
struct netent *np;
int error_num;
struct hostdata *retval;
for (h = h_table4[iphash(ipaddr.s_addr)]; h; h = h->h4_next) {
if (h->h4_addr.s_addr == ipaddr.s_addr)
return ((struct hostdata *)h);
}
/* not found. Put it in */
if (ipaddr.s_addr == htonl(INADDR_BROADCAST))
return (addhost(AF_INET, &ipaddr, "BROADCAST", NULL));
if (ipaddr.s_addr == htonl(INADDR_ANY))
return (addhost(AF_INET, &ipaddr, "OLD-BROADCAST", NULL));
/*
* Set an alarm here so we don't get held up by
* an unresponsive name server.
* Give it 3 sec to do its work.
*/
if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
(void) snoop_alarm(3, wakeup);
hp = getipnodebyaddr((char *)&ipaddr, sizeof (int),
AF_INET, &error_num);
if (hp == NULL && inet_lnaof(ipaddr) == 0) {
np = getnetbyaddr(inet_netof(ipaddr), AF_INET);
if (np)
return (addhost(AF_INET, &ipaddr, np->n_name,
np->n_aliases));
}
(void) snoop_alarm(0, wakeup);
}
retval = addhost(AF_INET, &ipaddr,
hp ? hp->h_name : inet_ntoa(ipaddr),
hp ? hp->h_aliases : NULL);
if (hp != NULL)
freehostent(hp);
return (retval);
}
static struct hostdata *
ip6lookup(const struct in6_addr *ip6addr)
{
struct hostdata6 *h;
struct hostent *hp = NULL;
int error_num;
char addrstr[INET6_ADDRSTRLEN];
char *addname;
struct hostdata *retval;
for (h = h_table6[iphash(((uint32_t *)ip6addr)[3])]; h;
h = h->h6_next) {
if (IN6_ARE_ADDR_EQUAL(&h->h6_addr, ip6addr))
return ((struct hostdata *)h);
}
/* not in the hash table, put it in */
if (IN6_IS_ADDR_UNSPECIFIED(ip6addr))
return (addhost(AF_INET6, ip6addr, "UNSPECIFIED", NULL));
/*
* Set an alarm here so we don't get held up by
* an unresponsive name server.
* Give it 3 sec to do its work.
*/
if (! rflg && sigsetjmp(nisjmp, 1) == 0) {
(void) snoop_alarm(3, wakeup);
hp = getipnodebyaddr(ip6addr, sizeof (struct in6_addr),
AF_INET6, &error_num);
(void) snoop_alarm(0, wakeup);
} else {
hp = NULL;
}
if (hp != NULL)
addname = hp->h_name;
else {
(void) inet_ntop(AF_INET6, ip6addr, addrstr, INET6_ADDRSTRLEN);
addname = addrstr;
}
retval = addhost(AF_INET6, ip6addr, addname, hp ? hp->h_aliases : NULL);
if (hp != NULL)
freehostent(hp);
return (retval);
}
static struct hostdata *
addhost(int family, const void *ipaddr, const char *name, char **aliases)
{
struct hostdata **hp, *n = NULL;
extern FILE *namefile;
int hashval;
static char aname[128];
char *np;
static struct hostdata h;
int ind;
switch (family) {
case AF_INET:
n = (struct hostdata *)malloc(sizeof (struct hostdata4));
if (n == NULL)
goto alloc_failed;
memset(n, 0, sizeof (struct hostdata4));
n->h_hostname = strdup(name);
if (n->h_hostname == NULL)
goto alloc_failed;
((struct hostdata4 *)n)->h4_addr =
*(const struct in_addr *)ipaddr;
hashval = ((struct in_addr *)ipaddr)->s_addr;
hp = (struct hostdata **)&h_table4[iphash(hashval)];
break;
case AF_INET6:
n = (struct hostdata *)malloc(sizeof (struct hostdata6));
if (n == NULL)
goto alloc_failed;
memset(n, 0, sizeof (struct hostdata6));
n->h_hostname = strdup(name);
if (n->h_hostname == NULL)
goto alloc_failed;
memcpy(&((struct hostdata6 *)n)->h6_addr, ipaddr,
sizeof (struct in6_addr));
hashval = ((const int *)ipaddr)[3];
hp = (struct hostdata **)&h_table6[iphash(hashval)];
break;
default:
fprintf(stderr, "snoop: ERROR: Unknown address family: %d",
family);
exit(1);
}
n->h_next = *hp;
*hp = n;
if (namefile != NULL) {
if (family == AF_INET) {
np = inet_ntoa(*(const struct in_addr *)ipaddr);
if (np) {
(void) fprintf(namefile, "%s\t%s", np, name);
if (aliases) {
for (ind = 0;
aliases[ind] != NULL;
ind++) {
(void) fprintf(namefile, " %s",
aliases[ind]);
}
}
(void) fprintf(namefile, "\n");
}
} else if (family == AF_INET6) {
np = (char *)inet_ntop(AF_INET6, (void *)ipaddr, aname,
sizeof (aname));
if (np) {
(void) fprintf(namefile, "%s\t%s", np, name);
if (aliases) {
for (ind = 0;
aliases[ind] != NULL;
ind++) {
(void) fprintf(namefile, " %s",
aliases[ind]);
}
}
(void) fprintf(namefile, "\n");
}
} else {
(void) fprintf(stderr, "addhost: unknown family %d\n",
family);
}
}
return (n);
alloc_failed:
if (n)
free(n);
(void) fprintf(stderr, "addhost: no mem\n");
aname[0] = '\0';
memset(&h, 0, sizeof (struct hostdata));
h.h_hostname = aname;
return (&h);
}
char *
addrtoname(int family, const void *ipaddr)
{
switch (family) {
case AF_INET:
return (iplookup(*(const struct in_addr *)ipaddr)->h_hostname);
case AF_INET6:
return (ip6lookup((const struct in6_addr *)ipaddr)->h_hostname);
}
(void) fprintf(stderr, "snoop: ERROR: unknown address family: %d\n",
family);
exit(1);
/* NOTREACHED */
}
void
load_names(fname)
char *fname;
{
char buf[1024];
char *addr, *name, *alias;
FILE *f;
unsigned int addrv4;
struct in6_addr addrv6;
int family;
void *naddr;
(void) fprintf(stderr, "Loading name file %s\n", fname);
f = fopen(fname, "r");
if (f == NULL) {
perror(fname);
return;
}
while (fgets(buf, 1024, f) != NULL) {
addr = strtok(buf, SEPARATORS);
if (addr == NULL || *addr == '#')
continue;
if (inet_pton(AF_INET6, addr, (void *)&addrv6) == 1) {
family = AF_INET6;
naddr = (void *)&addrv6;
} else if ((addrv4 = inet_addr(addr)) != (ulong_t)-1) {
family = AF_INET;
naddr = (void *)&addrv4;
}
name = strtok(NULL, SEPARATORS);
if (name == NULL)
continue;
while ((alias = strtok(NULL, SEPARATORS)) != NULL &&
(*alias != '#')) {
(void) addhost(family, naddr, alias, NULL);
}
(void) addhost(family, naddr, name, NULL);
/* Note: certain addresses such as broadcast are skipped */
}
(void) fclose(f);
}
/*
* lgetipnodebyname: looks up hostname in cached address data. This allows
* filtering on hostnames from the .names file to work properly, and
* avoids name clashes between domains. Note that only the first of the
* ipv4, ipv6, or v4mapped address will be returned, because the
* cache does not contain information on multi-homed hosts.
*/
/*ARGSUSED*/
struct hostent *
lgetipnodebyname(const char *name, int af, int flags, int *error_num)
{
int i;
struct hostdata4 *h;
struct hostdata6 *h6;
static struct hostent he; /* host entry */
static struct in6_addr h46_addr[MAXADDRS]; /* v4mapped address */
static char h_name[MAXHOSTNAMELEN]; /* hostname */
static char *list[MAXADDRS]; /* addr_list array */
struct hostent *hp = &he;
int ind;
(void) memset((char *)hp, 0, sizeof (struct hostent));
hp->h_name = h_name;
h_name[0] = '\0';
strcpy(h_name, name);
hp->h_addrtype = AF_INET6;
hp->h_addr_list = list;
for (i = 0; i < MAXADDRS; i++)
hp->h_addr_list[i] = NULL;
ind = 0;
/* ipv6 lookup */
if (af == AF_INET6) {
hp->h_length = sizeof (struct in6_addr);
for (i = 0; i < MAXHASH; i++) {
for (h6 = h_table6[i]; h6; h6 = h6->h6_next) {
if (strcmp(name, h6->h6_hostname) == 0) {
if (ind >= MAXADDRS - 1) {
/* too many addresses */
return (hp);
}
/* found ipv6 addr */
hp->h_addr_list[ind] =
(char *)&h6->h6_addr;
ind++;
}
}
}
}
/* ipv4 or v4mapped lookup */
if (af == AF_INET || (flags & AI_ALL)) {
for (i = 0; i < MAXHASH; i++) {
for (h = h_table4[i]; h; h = h->h4_next) {
if (strcmp(name, h->h4_hostname) == 0) {
if (ind >= MAXADDRS - 1) {
/* too many addresses */
return (hp);
}
if (af == AF_INET) {
/* found ipv4 addr */
hp->h_addrtype = AF_INET;
hp->h_length =
sizeof (struct in_addr);
hp->h_addr_list[ind] =
(char *)&h->h4_addr;
ind++;
} else {
/* found v4mapped addr */
hp->h_length =
sizeof (struct in6_addr);
hp->h_addr_list[ind] =
(char *)&h46_addr[ind];
IN6_INADDR_TO_V4MAPPED(
&h->h4_addr,
&h46_addr[ind]);
ind++;
}
}
}
}
}
return (ind > 0 ? hp : NULL);
}