/*
* Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that: (1) source code distributions
* retain the above copyright notice and this paragraph in its entirety, (2)
* distributions including binary code include the above copyright notice and
* this paragraph in its entirety in the documentation or other materials
* provided with the distribution, and (3) all advertising materials mentioning
* features or use of this software display the following acknowledgement:
* ``This product includes software developed by the University of California,
* Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
* the University nor the names of its contributors may be used to endorse
* or promote products derived from this software without specific prior
* written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Internet, ethernet, port, and protocol string to address
* and address to string conversion routines
*/
#include <sys/cdefs.h>
#ifndef lint
__RCSID("$NetBSD: addrtoname.c,v 1.13 2024/09/02 16:15:29 christos Exp $");
#endif
#ifdef USE_ETHER_NTOHOST
#if defined(NET_ETHERNET_H_DECLARES_ETHER_NTOHOST)
/*
* OK, just include <net/ethernet.h>.
*/
#include <net/ethernet.h>
#elif defined(NETINET_ETHER_H_DECLARES_ETHER_NTOHOST)
/*
* OK, just include <netinet/ether.h>
*/
#include <netinet/ether.h>
#elif defined(SYS_ETHERNET_H_DECLARES_ETHER_NTOHOST)
/*
* OK, just include <sys/ethernet.h>
*/
#include <sys/ethernet.h>
#elif defined(ARPA_INET_H_DECLARES_ETHER_NTOHOST)
/*
* OK, just include <arpa/inet.h>
*/
#include <arpa/inet.h>
#elif defined(NETINET_IF_ETHER_H_DECLARES_ETHER_NTOHOST)
/*
* OK, include <netinet/if_ether.h>, after all the other stuff we
* need to include or define for its benefit.
*/
#define NEED_NETINET_IF_ETHER_H
#else
/*
* We'll have to declare it ourselves.
* If <netinet/if_ether.h> defines struct ether_addr, include
* it. Otherwise, define it ourselves.
*/
#ifdef HAVE_STRUCT_ETHER_ADDR
#define NEED_NETINET_IF_ETHER_H
#else /* HAVE_STRUCT_ETHER_ADDR */
struct ether_addr {
/* Beware FreeBSD calls this "octet". */
unsigned char ether_addr_octet[MAC_ADDR_LEN];
};
#endif /* HAVE_STRUCT_ETHER_ADDR */
#endif /* what declares ether_ntohost() */
#ifdef NEED_NETINET_IF_ETHER_H
/*
* Include diag-control.h before <net/if.h>, which too defines a macro
* named ND_UNREACHABLE.
*/
#include "diag-control.h"
#include <net/if.h> /* Needed on some platforms */
#include <netinet/in.h> /* Needed on some platforms */
#include <netinet/if_ether.h>
#endif /* NEED_NETINET_IF_ETHER_H */
#ifndef HAVE_DECL_ETHER_NTOHOST
/*
* No header declares it, so declare it ourselves.
*/
extern int ether_ntohost(char *, const struct ether_addr *);
#endif /* !defined(HAVE_DECL_ETHER_NTOHOST) */
#endif /* USE_ETHER_NTOHOST */
/*
* Return a name for the IP address pointed to by ap. This address
* is assumed to be in network byte order.
*
* NOTE: ap is *NOT* necessarily part of the packet data, so you
* *CANNOT* use the ND_TCHECK_* or ND_TTEST_* macros on it. Furthermore,
* even in cases where it *is* part of the packet data, the caller
* would still have to check for a null return value, even if it's
* just printing the return value with "%s" - not all versions of
* printf print "(null)" with "%s" and a null pointer, some of them
* don't check for a null pointer and crash in that case.
*
* The callers of this routine should, before handing this routine
* a pointer to packet data, be sure that the data is present in
* the packet buffer. They should probably do those checks anyway,
* as other data at that layer might not be IP addresses, and it
* also needs to check whether they're present in the packet buffer.
*/
const char *
ipaddr_string(netdissect_options *ndo, const u_char *ap)
{
struct hostent *hp;
uint32_t addr;
struct hnamemem *p;
memcpy(&addr, ap, sizeof(addr));
p = &hnametable[addr & (HASHNAMESIZE-1)];
for (; p->nxt; p = p->nxt) {
if (p->addr == addr)
return (p->name);
}
p->addr = addr;
p->nxt = newhnamemem(ndo);
/*
* Print names unless:
* (1) -n was given.
* (2) Address is foreign and -f was given. (If -f was not
* given, f_netmask and f_localnet are 0 and the test
* evaluates to true)
*/
if (!ndo->ndo_nflag &&
(addr & f_netmask) == f_localnet) {
#ifdef HAVE_CASPER
if (capdns != NULL) {
hp = cap_gethostbyaddr(capdns, (char *)&addr, 4,
AF_INET);
} else
#endif
hp = gethostbyaddr((char *)&addr, 4, AF_INET);
if (hp) {
char *dotp;
/*
* Return a name for the IP6 address pointed to by ap. This address
* is assumed to be in network byte order.
*/
const char *
ip6addr_string(netdissect_options *ndo, const u_char *ap)
{
struct hostent *hp;
union {
nd_ipv6 addr;
struct for_hash_addr {
char fill[14];
uint16_t d;
} addra;
} addr;
struct h6namemem *p;
const char *cp;
char ntop_buf[INET6_ADDRSTRLEN];
memcpy(&addr, ap, sizeof(addr));
p = &h6nametable[addr.addra.d & (HASHNAMESIZE-1)];
for (; p->nxt; p = p->nxt) {
if (memcmp(&p->addr, &addr, sizeof(addr)) == 0)
return (p->name);
}
memcpy(p->addr, addr.addr, sizeof(nd_ipv6));
p->nxt = newh6namemem(ndo);
/*
* Do not print names if -n was given.
*/
if (!ndo->ndo_nflag) {
#ifdef HAVE_CASPER
if (capdns != NULL) {
hp = cap_gethostbyaddr(capdns, (char *)&addr,
sizeof(addr), AF_INET6);
} else
#endif
hp = gethostbyaddr((char *)&addr, sizeof(addr),
AF_INET6);
if (hp) {
char *dotp;
/*
* Convert an octet to two hex digits.
*
* Coverity appears either:
*
* not to believe the C standard when it asserts that a uint8_t is
* exactly 8 bits in size;
*
* not to believe that an unsigned type of exactly 8 bits has a value
* in the range of 0 to 255;
*
* not to believe that, for a range of unsigned values, if you shift
* one of those values right by 4 bits, the maximum result value is
* the maximum value shifted right by 4 bits, with no stray 1's shifted
* in;
*
* not to believe that 255 >> 4 is 15;
*
* so it gets upset that we're taking a "tainted" unsigned value, shifting
* it right 4 bits, and using it as an index into a 16-element array.
*
* So we do a stupid pointless masking of the result of the shift with
* 0xf, to hammer the point home to Coverity.
*/
static inline char *
octet_to_hex(char *cp, uint8_t octet)
{
*cp++ = hex[(octet >> 4) & 0xf];
*cp++ = hex[(octet >> 0) & 0xf];
return (cp);
}
/* Find the hash node that corresponds the ether address 'ep' */
const char *
etheraddr_string(netdissect_options *ndo, const uint8_t *ep)
{
int i;
char *cp;
struct enamemem *tp;
int oui;
char buf[BUFSIZE];
tp = lookup_emem(ndo, ep);
if (tp->e_name)
return (tp->e_name);
#ifdef USE_ETHER_NTOHOST
if (!ndo->ndo_nflag) {
char buf2[BUFSIZE];
/*
* This is a non-const copy of ep for ether_ntohost(), which
* has its second argument non-const in OpenBSD. Also saves a
* type cast.
*/
struct ether_addr ea;
while ((sv = getservent()) != NULL) {
int port = ntohs(sv->s_port);
i = port & (HASHNAMESIZE-1);
if (strcmp(sv->s_proto, "tcp") == 0)
table = &tporttable[i];
else if (strcmp(sv->s_proto, "udp") == 0)
table = &uporttable[i];
else
continue;
while (table->name)
table = table->nxt;
if (ndo->ndo_nflag) {
(void)snprintf(buf, sizeof(buf), "%d", port);
table->name = strdup(buf);
} else
table->name = strdup(sv->s_name);
if (table->name == NULL)
(*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
"%s: strdup", __func__);
/*
* Initialize the ethers hash table. We take two different approaches
* depending on whether or not the system provides the ethers name
* service. If it does, we just wire in a few names at startup,
* and etheraddr_string() fills in the table on demand. If it doesn't,
* then we suck in the entire /etc/ethers file at startup. The idea
* is that parsing the local file will be fast, but spinning through
* all the ethers entries via NIS & next_etherent might be very slow.
*
* XXX pcap_next_etherent doesn't belong in the pcap interface, but
* since the pcap module already does name-to-address translation,
* it's already does most of the work for the ethernet address-to-name
* translation, so we just pcap_next_etherent as a convenience.
*/
static void
init_etherarray(netdissect_options *ndo)
{
const struct etherlist *el;
struct enamemem *tp;
#ifdef USE_ETHER_NTOHOST
char name[256];
#else
struct pcap_etherent *ep;
FILE *fp;
/*
* Initialize the address to name translation machinery. We map all
* non-local IP addresses to numeric addresses if ndo->ndo_fflag is true
* (i.e., to prevent blocking on the nameserver). localnet is the IP address
* of the local network. mask is its subnet mask.
*/
void
init_addrtoname(netdissect_options *ndo, uint32_t localnet, uint32_t mask)
{
if (ndo->ndo_fflag) {
f_localnet = localnet;
f_netmask = mask;
}
if (ndo->ndo_nflag)
/*
* Simplest way to suppress names.
*/
return;