/*
* Copyright (C) 1998-2007 Luca Deri <deri@ntop.org>
*
* http://www.ntop.org/
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "ntop.h"
/* Global */
static char hex[] = "0123456789ABCDEF";
/* Forward */
static u_int _ns_get16(const u_char *src);
static int _ns_name_ntop(const u_char *src,
char *dst, size_t dstsiz);
static int _dn_skipname(const u_char *ptr, const u_char *eom); /* forward */
static int _ns_name_uncompress(const u_char *msg,
const u_char *eom, const u_char *src,
char *dst, size_t dstsiz);
static int _ns_name_unpack(const u_char *msg,
const u_char *eom, const u_char *src,
u_char *dst, size_t dstsiz);
static void updateDeviceHostNameInfo(HostAddr addr, char* symbolic, int actualDeviceId, int type);
static void updateHostNameInfo(HostAddr addr, char* symbolic, int type);
/* #define DNS_DEBUG */
/* #define MDNS_DEBUG */
/* **************************************** */
static void updateDeviceHostNameInfo(HostAddr addr, char* symbolic, int actualDeviceId, int type) {
HostTraffic *el;
if(myGlobals.ntopRunState > FLAG_NTOPSTATE_RUN) return;
/* Search the instance and update its name */
if(myGlobals.device[actualDeviceId].virtualDevice) return;
for(el=getFirstHost(actualDeviceId); el != NULL; el = getNextHost(actualDeviceId, el)) {
if((el->hostNumIpAddress != NULL) && (addrcmp(&el->hostIpAddress, &addr) == 0)) {
accessAddrResMutex("updateHostNameInfo");
if(el != NULL) {
unsigned short i;
if(strlen(symbolic) >= (MAX_LEN_SYM_HOST_NAME-1))
symbolic[MAX_LEN_SYM_HOST_NAME-2] = '\0';
for(i=0; i<strlen(symbolic); i++)
if(isupper(symbolic[i])) symbolic[i] = tolower(symbolic[i]);
setResolvedName(el, symbolic, type);
}
releaseAddrResMutex();
}
}
}
/* **************************************** */
static void updateHostNameInfo(HostAddr addr, char* symbolic, int type) {
int i;
for(i=0; i<myGlobals.numDevices; i++)
updateDeviceHostNameInfo(addr, symbolic, i, type);
}
/* ************************************ */
static int validDNSChar(char c) {
if((c == '-') || (c == '_') || (c == '.')) return(1);
if((c >= '0') && (c <= '9')) return(1);
if((c >= 'a') && (c <= 'z')) return(1);
if((c >= 'A') && (c <= 'Z')) return(1);
return(0);
}
/* ************************************ */
static int validDNSName(char *name) {
int i, len;
if(name == NULL)
return(0);
else
len = strlen(name);
for(i=0; i<len; i++)
if(!validDNSChar(name[i]))
return(0);
return(1);
}
/* ************************************ */
static void resolveAddress(HostAddr *hostAddr, short keepAddressNumeric) {
char symAddr[MAX_LEN_SYM_HOST_NAME];
short symAddrType=FLAG_HOST_SYM_ADDR_TYPE_NONE;
StoredAddress storedAddress;
int i, addToCacheFlag=0, updateRecord=0;
struct hostent *hp = NULL;
char* resolvedAddress;
char keyBuf[LEN_ADDRESS_BUFFER];
char dataBuf[sizeof(StoredAddress)+4];
datum key_data;
datum data_data;
static int reportedFreaky = FALSE;
if(myGlobals.ntopRunState > FLAG_NTOPSTATE_RUN) return;
myGlobals.numResolveAddressCalls++;
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Entering resolveAddress()");
#endif
memset(&keyBuf, 0, sizeof(keyBuf));
memset(&dataBuf, 0, sizeof(dataBuf));
memset(&storedAddress, 0, sizeof(storedAddress));
key_data.dptr = _addrtonum(hostAddr, keyBuf, sizeof(keyBuf));
key_data.dsize = strlen(keyBuf)+1;
if(myGlobals.dnsCacheFile == NULL) {
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Leaving resolveAddress(), dnsCacheFile NULL");
#endif
myGlobals.numResolveNoCacheDB++;
return; /* ntop is quitting... */
}
data_data = gdbm_fetch(myGlobals.dnsCacheFile, key_data);
myGlobals.numResolveCacheDBLookups++;
/* First check whether the address we search for is cached... */
if((data_data.dptr != NULL) &&
(data_data.dsize == (sizeof(StoredAddress))) &&
(myGlobals.actTime - ((StoredAddress*)data_data.dptr)->recordCreationTime < CONST_DNSCACHE_LIFETIME)) {
StoredAddress *retrievedAddress;
retrievedAddress = (StoredAddress*)data_data.dptr;
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Fetched '%s' from cache for [%s]",
retrievedAddress->symAddress, keyBuf);
#endif
/* Sanity check */
if(strlen(retrievedAddress->symAddress) > MAX_LEN_SYM_HOST_NAME) {
strncpy(symAddr, retrievedAddress->symAddress, MAX_LEN_SYM_HOST_NAME-4);
symAddr[MAX_LEN_SYM_HOST_NAME-1] = '\0';
symAddr[MAX_LEN_SYM_HOST_NAME-2] = '.';
symAddr[MAX_LEN_SYM_HOST_NAME-3] = '.';
symAddr[MAX_LEN_SYM_HOST_NAME-4] = '.';
} else
strncpy(symAddr, retrievedAddress->symAddress, MAX_LEN_SYM_HOST_NAME-1);
symAddrType = retrievedAddress->symAddressType;
myGlobals.numResolvedFromCache++;
updateHostNameInfo(*hostAddr, retrievedAddress->symAddress, retrievedAddress->symAddressType);
free(data_data.dptr);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Leaving resolveAddress() - resolved from cache");
#endif
return;
} else {
if(data_data.dptr != NULL) {
#ifdef GDBM_DEBUG
if (data_data.dsize == (sizeof(StoredAddress)))
traceEvent(CONST_TRACE_INFO, "GDBM_DEBUG: Dropped data for %s [wrong data size]", keyBuf);
else
traceEvent(CONST_TRACE_INFO, "GDBM_DEBUG: Ignored old record for %s", keyBuf);
#endif
free(data_data.dptr);
#ifdef GDBM_DEBUG
} else {
traceEvent(CONST_TRACE_INFO, "GDBM_DEBUG: Unable to retrieve %s", keyBuf);
#endif
}
}
if((!keepAddressNumeric) && (myGlobals.ntopRunState <= FLAG_NTOPSTATE_RUN)) {
char theAddr[17];
int family, size;
#ifdef HAVE_GETIPNODEBYADDR
int error_num;
#endif
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Resolving %s...", addrtostr(hostAddr));
#endif
addrget(hostAddr,theAddr,&family,&size);
#ifdef HAVE_NETDB_H
h_errno = NETDB_SUCCESS;
#endif
#ifdef PARM_USE_HOST
{
FILE* fd;
char buffer[64];
int i, len;
if (hostAddr->hostFamily == AF_INET)
cwd = "/usr/bin/host";
else (hostAddr->hostFamily == AF_INET6)
cwd = "/usr/bin/host -t aaaa";
safe_snprintf(__FILE__, __LINE__, buffer, sizeof(buffer),
"%s %s",
cwd,theAddr);
fd = popen(buffer, "r");
if(fd == NULL) {
dataBuf[0] = '\0';
} else {
char *rspStr;
memset(dataBuf, 0, sizeof(dataBuf));
rspStr = fgets(dataBuf, sizeof(dataBuf), fd);
pclose(fd);
if(rspStr == NULL)
dataBuf[0] = '\0';
}
/*
# host 131.114.21.9
9.21.114.131.IN-ADDR.ARPA domain name pointer faeta.unipi.it
#
*/
len = strlen(dataBuf);
if(len > 0) {
dataBuf[--len] = '\0';
for(i=len; i>0; i--)
if(dataBuf[i] == ' ')
break;
}
if((len > 0) && (i > 0) && (dataBuf[i] == ' ')) {
res = &dataBuf[i+1];
myGlobals.numResolvedFromHostAddresses++;
symAddrType=FLAG_HOST_SYM_ADDR_TYPE_NAME;
} else {
res = _addrtostr(hostAddr, dataBuf, sizeof(dataBuf));
myGlobals.numKeptNumericAddresses++;
symAddrType=FLAG_HOST_SYM_ADDR_TYPE_IP;
}
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Resolved to %s from hosts file.", res);
#endif
}
#else /* PARM_USE_HOST */
myGlobals.numAttemptingResolutionWithDNS++;
addToCacheFlag = 0 /* Assume we'll get something, so we do want to add it later on */;
#ifdef HAVE_GETIPNODEBYADDR
hp = getipnodebyaddr((const void*)theAddr,
size,family,
&error_num);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Called getipnodebyaddr(): RC=%d 0x%X [%s]", error_num,
hp, hp != NULL ? (char*)hp->h_name : "");
#endif
#else /* default */
#if defined(HAVE_GETHOSTBYADDR_R) && !defined(LINUX)
/* Linux seems to ha some problems with gethostbyaddr_r */
#ifdef SOLARIS
{
struct hostent _hp, *__hp;
char buffer[512];
hp = gethostbyaddr_r((const char*)theAddr, size,
family,
&_hp,
buffer, sizeof(buffer),
&h_errnop);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Called gethostbyaddr_r(): RC=%d 0x%X [%s]",
h_errnop,
hp, hp != NULL ? (char*)hp->h_name : "");
#endif
#else /* SOLARIS */
hp = gethostbyaddr_r((const char*)theAddr, size,
family,
&_hp,
buffer, sizeof(buffer),
&__hp,
&h_errnop);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Called gethostbyaddr_r(): RC=%d 0x%X [%s]",
h_errnop,
hp, hp != NULL ? (char*)hp->h_name : "");
#endif /* DNS_DEBUG */
#endif /* SOLARIS */
}
#else
hp = (struct hostent*)gethostbyaddr((char*)theAddr,size,family);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Called gethostbyaddr(): RC=%d 0x%X [%s]",
h_errno,
hp, hp != NULL ? (char*)hp->h_name : "not meaningful, hp is null");
#endif
//
#endif
#endif
if (
#ifdef HAVE_NETDB_H
(h_errno == NETDB_SUCCESS) &&
#endif
#ifdef WIN32
(WSAGetLastError() == 0 /* OK */) &&
#endif
(hp != NULL) &&
(hp->h_name != NULL) &&
validDNSName(hp->h_name) &&
(strcmp(hp->h_name, addrtostr(hostAddr)) != 0)) {
char *dotp = (char*)hp->h_name;
updateRecord = 1;
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Resolved to %s.", dotp);
#endif
strncpy(dataBuf, dotp, sizeof(dataBuf));
if(myGlobals.runningPref.domainName[0] != '\0') {
int dataLen = strlen(dataBuf)-strlen(myGlobals.runningPref.domainName);
if((dataLen > 0) && (!strcmp(&dataBuf[dataLen], myGlobals.runningPref.domainName))) {
int foundDot=0;
for(i=0; i<dataLen-1; i++)
if(dataBuf[i] == '.') {
foundDot = 1;
break;
}
if(!foundDot)
dataBuf[dataLen-1] = '\0';
}
}
resolvedAddress = dataBuf;
myGlobals.numResolvedWithDNSAddresses++;
symAddrType=FLAG_HOST_SYM_ADDR_TYPE_NAME;
} else {
myGlobals.numKeptNumericAddresses++;
symAddrType=FLAG_HOST_SYM_ADDR_TYPE_IP;
/* Failed, but why? */
switch (
#ifdef HAVE_NETDB_H
h_errno
#elif HAVE_GETIPNODEBYADDR
error_num
#else
h_errno
#endif
) {
case NETDB_SUCCESS:
/* This is the freaky one - it returned NULL (nothing) which tells you to look
* at the error code, but the error code says SUCCESS.
*
* Treat as NOT FOUND, but use reportedFreaky to put out one message per run.
* Known to happen under Linux, other OSes uncertain...
*/
if((reportedFreaky == FALSE) && (hp != NULL)) {
reportedFreaky = TRUE;
traceEvent(CONST_TRACE_NOISY, "gethost... call returned NULL/NETDB_SUCCESS - "
"this is odd, but apparently normal");
}
myGlobals.numDNSErrorHostNotFound++;
break;
case HOST_NOT_FOUND:
myGlobals.numDNSErrorHostNotFound++;
break;
case NO_DATA:
myGlobals.numDNSErrorNoData++;
break;
case NO_RECOVERY:
myGlobals.numDNSErrorNoRecovery++;
break;
case TRY_AGAIN:
myGlobals.numDNSErrorTryAgain++;
addToCacheFlag = 1 /* Don't add this */;
break;
default:
myGlobals.numDNSErrorOther++;
addToCacheFlag = 1 /* Don't add this */;
traceEvent(CONST_TRACE_ERROR, "gethost... call, returned unknown error code %d",
#ifdef HAVE_NETDB_H
h_errno
#elif HAVE_GETIPNODEBYADDR
error_num
#else
h_errno
#endif
);
}
resolvedAddress = _addrtostr(hostAddr, dataBuf , sizeof(dataBuf));
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Unable to resolve %s", resolvedAddress);
#endif
}
#endif /* PARM_USE_HOST */
} else {
myGlobals.numKeptNumericAddresses++;
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Unable to resolve %s", resolvedAddress);
#endif
resolvedAddress = _addrtostr(hostAddr, dataBuf, sizeof(dataBuf));
}
#ifdef HAVE_GETIPNODEBYADDR
if(hp != NULL)
freehostent(hp);
#endif
if (addToCacheFlag == 0) {
int len;
if(strlen(resolvedAddress) > MAX_LEN_SYM_HOST_NAME) {
strncpy(symAddr, resolvedAddress, MAX_LEN_SYM_HOST_NAME-4);
symAddr[MAX_LEN_SYM_HOST_NAME-1] = '\0';
symAddr[MAX_LEN_SYM_HOST_NAME-2] = '.';
symAddr[MAX_LEN_SYM_HOST_NAME-3] = '.';
symAddr[MAX_LEN_SYM_HOST_NAME-4] = '.';
} else
strncpy(symAddr, resolvedAddress, MAX_LEN_SYM_HOST_NAME-1);
for(i=0; symAddr[i] != '\0'; i++)
symAddr[i] = (char)tolower(symAddr[i]);
memset(storedAddress.symAddress, 0, sizeof(storedAddress.symAddress));
len = min(sizeof(storedAddress.symAddress)-1, strlen(symAddr));
memcpy(storedAddress.symAddress, symAddr, len);
storedAddress.symAddress[len] = '\0';
storedAddress.recordCreationTime = myGlobals.actTime;
storedAddress.symAddressType = symAddrType;
/* key_data has been set already */
data_data.dptr = (void*)&storedAddress;
data_data.dsize = sizeof(storedAddress) /* Remember, StoredAddress has 1 byte padding so don't add more here */;
if(updateRecord) {
updateHostNameInfo(*hostAddr, symAddr, symAddrType);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Updating %s", symAddr);
#endif
} else {
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: NOT updating %s", symAddr);
#endif
}
if(myGlobals.dnsCacheFile == NULL) {
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Leaving resolveAddress()");
#endif
return; /* ntop is quitting... */
}
if(gdbm_store(myGlobals.dnsCacheFile, key_data, data_data, GDBM_REPLACE) != 0)
traceEvent(CONST_TRACE_ERROR, "dnsCache error adding '%s', %s", symAddr,
#if defined(WIN32) && defined(__GNUC__)
"no additional information available"
#else
gdbm_strerror(gdbm_errno)
#endif
);
else {
myGlobals.dnsCacheStoredLookup++;
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Added data: '%s'='%s'(%d)",
key_data.dptr,
((StoredAddress*)data_data.dptr)->symAddress,
((StoredAddress*)data_data.dptr)->symAddressType);
#endif
}
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Leaving Resolveaddress()");
#endif
}
}
/* *************************** */
static void queueAddress(HostAddr elem, int forceResolution) {
datum key_data, data_data;
char dataBuf[sizeof(StoredAddress)+4];
int rc;
if((!forceResolution)
&& myGlobals.runningPref.trackOnlyLocalHosts
&& (!_pseudoLocalAddress(&elem, NULL, NULL)))
return;
/*
The address queue is far too long. This is usefult for
avoiding problems due to DOS applications
*/
if(myGlobals.addressQueuedCurrent > (MAX_NUM_QUEUED_ADDRESSES*myGlobals.numDevices)) {
static char shownMsg = 0;
if(!shownMsg) {
shownMsg = 1;
traceEvent(CONST_TRACE_WARNING, "Address resolution queue is full [%u slots]",
MAX_NUM_QUEUED_ADDRESSES);
traceEvent(CONST_TRACE_INFO, "Addresses in excess won't be resolved - ntop continues");
}
return;
}
/* Fix - Burton Strauss (BStrauss@acm.org) 2002-04-04
Make sure dataBuf has a value and
Prevent increment of queue length on failure (i.e. add of existing value)
Incidentally, speed this up by eliminating the fetch/store sequence in favor of
a single store.
*/
if (elem.hostFamily == AF_INET) {
key_data.dptr = (void*)&elem.Ip4Address.s_addr;
key_data.dsize = 4;
}
#ifdef INET6
else if (elem.hostFamily == AF_INET6) {
key_data.dptr = (void*)&elem.Ip6Address.s6_addr;
key_data.dsize = 16;
}
#endif
else {
traceEvent(CONST_TRACE_WARNING, "Invalid address family (%d) found!",
elem.hostFamily);
return;
}
safe_snprintf(__FILE__, __LINE__, dataBuf, sizeof(dataBuf), "%s", addrtostr(&elem));
data_data.dptr = dataBuf;
data_data.dsize = strlen(dataBuf)+1;
rc = gdbm_store(myGlobals.addressQueueFile, key_data, data_data, GDBM_INSERT);
if (rc == 0) {
accessMutex(&myGlobals.queueAddressMutex, "dequeueAddress");
myGlobals.addressQueuedCurrent++, myGlobals.addressQueuedCount++;
if (myGlobals.addressQueuedCurrent > myGlobals.addressQueuedMax)
myGlobals.addressQueuedMax = myGlobals.addressQueuedCurrent;
releaseMutex(&myGlobals.queueAddressMutex);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Queued address '%s' [addr queue=%d/max=%d]",
dataBuf, myGlobals.addressQueuedCurrent, myGlobals.addressQueuedMax);
#endif
} else {
/* rc = 1 is duplicate key, which is fine. Other codes are problems... */
if (rc != 1) {
traceEvent(CONST_TRACE_ERROR, "Queue of address '%s' failed (%s) [addr queue=%d/max=%d]",
dataBuf,
#if defined(WIN32) && defined(__GNUC__)
"no additional information available",
#else
gdbm_strerror(gdbm_errno),
#endif
myGlobals.addressQueuedCurrent,
myGlobals.addressQueuedMax);
traceEvent(CONST_TRACE_INFO, "ntop processing continues, address will not be resolved");
} else {
accessMutex(&myGlobals.queueAddressMutex, "dequeueAddress");
myGlobals.addressQueuedDup++;
releaseMutex(&myGlobals.queueAddressMutex);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Duplicate queue of address '%s' ignored",
dataBuf);
#endif
}
}
signalCondvar(&myGlobals.queueAddressCondvar);
}
/* ************************************ */
void cleanupAddressQueue(void) {
/* Nothing to do */
}
/* ************************************ */
/*
Remove from queue an address that was waiting to be
resolved
*/
void purgeQueuedV4HostAddress(u_int32_t addr) {
datum key_data;
struct in_addr addrv4;
addrv4.s_addr = addr;
/*
traceEvent(CONST_TRACE_INFO, "purgeQueuedV4HostAddress(%s)",
_intoa(addrv4, buf, sizeof(buf)));
*/
key_data.dptr = (void*)&addr;
key_data.dsize = 4;
if(gdbm_delete(myGlobals.addressQueueFile, key_data)) {
accessMutex(&myGlobals.queueAddressMutex, "purgeQueuedV4HostAddress");
if(myGlobals.addressQueuedCurrent > 0) myGlobals.addressQueuedCurrent--;
releaseMutex(&myGlobals.queueAddressMutex);
}
}
/* ************************************ */
void* dequeueAddress(void *_i) {
int dqaIndex = (int)((long)_i);
HostAddr addr;
datum key_data, data_data;
traceEvent(CONST_TRACE_INFO,
"THREADMGMT[t%lu]: DNSAR(%d): Address resolution thread running",
pthread_self(), dqaIndex+1);
while(myGlobals.ntopRunState <= FLAG_NTOPSTATE_RUN) {
#ifdef DEBUG
traceEvent(CONST_TRACE_INFO, "DEBUG: Waiting for address to resolve...");
#endif
waitCondvar(&myGlobals.queueAddressCondvar);
if(myGlobals.ntopRunState > FLAG_NTOPSTATE_RUN) break;
#ifdef DEBUG
traceEvent(CONST_TRACE_INFO, "DEBUG: Address resolution started...");
#endif
data_data = gdbm_firstkey(myGlobals.addressQueueFile);
while(data_data.dptr != NULL) {
int size = data_data.dsize;
if(myGlobals.ntopRunState > FLAG_NTOPSTATE_RUN)
break;
if (size == sizeof(struct in_addr)) {
/*addrput(AF_INET, &addr, data_data.dptr);*/
addr.hostFamily = AF_INET;
memcpy(&addr.Ip4Address.s_addr, data_data.dptr, size);
}
#ifdef INET6
else if (size == sizeof(struct in6_addr)) {
addr.hostFamily = AF_INET6;
memcpy(&addr.Ip6Address.s6_addr, data_data.dptr, size);
}
#endif
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Dequeued address... [%s][key=%s] (#addr=%d)",
addrtostr(&addr), key_data.dptr == NULL ? "<>" : key_data.dptr,
myGlobals.addressQueuedCurrent);
#endif
resolveAddress(&addr, 0);
#ifdef DNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: Resolved address %s", addrtostr(&addr));
#endif
accessMutex(&myGlobals.queueAddressMutex, "dequeueAddress");
if(myGlobals.addressQueuedCurrent > 0) myGlobals.addressQueuedCurrent--;
releaseMutex(&myGlobals.queueAddressMutex);
gdbm_delete(myGlobals.addressQueueFile, data_data);
key_data = data_data;
data_data = gdbm_nextkey(myGlobals.addressQueueFile, key_data);
free(key_data.dptr); /* Free the 'formed' data_data */
}
} /* endless loop */
myGlobals.dequeueAddressThreadId[dqaIndex] = 0;
traceEvent(CONST_TRACE_INFO, "THREADMGMT[t%lu]: DNSAR(%d): Address resolution thread terminated [p%d]",
pthread_self(), dqaIndex+1, getpid());
return(NULL);
}
#ifdef INET6
/* ************************************ */
char* _intop(struct in6_addr *addr, char *buf, u_short buflen) {
return (char *)inet_ntop(AF_INET6, addr, buf, buflen);
}
/* ************************************ */
char* intop(struct in6_addr *addr) {
static char ntop_buf[INET6_ADDRSTRLEN+1];
memset(ntop_buf, 0, INET6_ADDRSTRLEN);
return (char *)_intop(addr, ntop_buf,sizeof(ntop_buf));
}
#endif /*INET6 */
/* ************************************ */
/*
* A faster replacement for inet_ntoa().
*/
char* _intoa(struct in_addr addr, char* buf, u_short bufLen) {
char *cp, *retStr;
u_int byte;
int n;
cp = &buf[bufLen];
*--cp = '\0';
n = 4;
do {
byte = addr.s_addr & 0xff;
*--cp = byte % 10 + '0';
byte /= 10;
if (byte > 0) {
*--cp = byte % 10 + '0';
byte /= 10;
if (byte > 0)
*--cp = byte + '0';
}
*--cp = '.';
addr.s_addr >>= 8;
} while (--n > 0);
/* Convert the string to lowercase */
retStr = (char*)(cp+1);
return(retStr);
}
/* ************************************ */
char* intoa(struct in_addr addr) {
static char buf[sizeof "ff:ff:ff:ff:ff:ff:255.255.255.255"];
return(_intoa(addr, buf, sizeof(buf)));
}
/* ************************************ */
char* addrtostr(HostAddr *addr) {
if (addr == NULL)
return NULL;
switch(addr->hostFamily) {
case AF_INET:
return(char *)(intoa(addr->Ip4Address));
#ifdef INET6
case AF_INET6:
return(char *)(intop(&addr->Ip6Address));
#endif
default: return("???");
}
}
/* ************************************ */
char * _addrtostr(HostAddr *addr, char* buf, u_short bufLen) {
if (addr == NULL)
return NULL;
switch(addr->hostFamily) {
case AF_INET:
return (_intoa(addr->Ip4Address,buf,bufLen));
#ifdef INET6
case AF_INET6:
return (_intop(&addr->Ip6Address,buf,bufLen));
#endif
default: return("???");
}
}
/* ************************************ */
char * _addrtonum(HostAddr *addr, char* buf, u_short bufLen) {
if((addr == NULL) || (buf == NULL))
return NULL;
switch(addr->hostFamily) {
case AF_INET:
safe_snprintf(__FILE__, __LINE__, buf, bufLen, "%u", addr->Ip4Address.s_addr);
break;
#ifdef INET6
case AF_INET6:
if(_intop(&addr->Ip6Address, buf, bufLen) == NULL)
BufferTooSmall(buf, bufLen);
break;
#endif
default:
return("???");
}
return(buf);
}
/* ******************************* */
int fetchAddressFromCache(HostAddr hostIpAddress, char *buffer, int *type) {
char keyBuf[LEN_ADDRESS_BUFFER];
datum key_data;
datum data_data;
if(buffer == NULL) return(0);
memset(&keyBuf, 0, sizeof(keyBuf));
myGlobals.numFetchAddressFromCacheCalls++;
if(addrfull(&hostIpAddress) || addrnull(&hostIpAddress)) {
strcpy(buffer, "0.0.0.0");
*type = FLAG_HOST_SYM_ADDR_TYPE_IP;
return(0);
}
key_data.dptr = _addrtonum(&hostIpAddress,keyBuf, sizeof(keyBuf));
key_data.dsize = strlen(key_data.dptr)+1;
if(myGlobals.dnsCacheFile == NULL) return(0); /* ntop is quitting... */
data_data = gdbm_fetch(myGlobals.dnsCacheFile, key_data);
if((data_data.dptr != NULL) && (data_data.dsize == (sizeof(StoredAddress))) ) {
StoredAddress *retrievedAddress;
retrievedAddress = (StoredAddress*)data_data.dptr;
*type = retrievedAddress->symAddressType;
#ifdef GDBM_DEBUG
traceEvent(CONST_TRACE_INFO, "GDBM_DEBUG: gdbm_fetch(..., {%s, %d}) = %s, age %d",
key_data.dptr, key_data.dsize,
retrievedAddress->symAddress,
myGlobals.actTime - retrievedAddress->recordCreationTime);
#endif
if (myGlobals.actTime - retrievedAddress->recordCreationTime < CONST_DNSCACHE_LIFETIME) {
myGlobals.numFetchAddressFromCacheCallsOK++;
safe_snprintf(__FILE__, __LINE__, buffer, MAX_LEN_SYM_HOST_NAME, "%s", retrievedAddress->symAddress);
} else {
myGlobals.numFetchAddressFromCacheCallsSTALE++;
buffer[0] = '\0';
}
free(data_data.dptr);
} else {
myGlobals.numFetchAddressFromCacheCallsFAIL++;
#ifdef GDBM_DEBUG
if(data_data.dptr != NULL)
traceEvent(CONST_TRACE_WARNING, "GDBM_DEBUG: Dropped data for %s [wrong data size]", keyBuf);
else
traceEvent(CONST_TRACE_WARNING, "GDBM_DEBUG: Unable to retrieve %s", keyBuf);
#endif
buffer[0] = '\0';
*type = FLAG_HOST_SYM_ADDR_TYPE_IP;
/* It might be that the size of the retrieved data is wrong */
if(data_data.dptr != NULL) free(data_data.dptr);
}
#ifdef DEBUG
{
char buf[LEN_ADDRESS_BUFFER];
traceEvent(CONST_TRACE_INFO, "fetchAddressFromCache(%s) returned '%s'",
_addrtostr(&hostIpAddress, buf, sizeof(buf)), buffer);
}
#endif
return(1);
}
/* ******************************* */
/* This function automatically updates the instance name */
void ipaddr2str(HostAddr hostIpAddress, int updateHost) {
char buf[MAX_LEN_SYM_HOST_NAME+1];
int type;
myGlobals.numipaddr2strCalls++;
if(fetchAddressFromCache(hostIpAddress, buf, &type) && (buf[0] != '\0')) {
if(updateHost) updateHostNameInfo(hostIpAddress, buf, type);
} else {
queueAddress(hostIpAddress, !updateHost);
}
}
/* ************************************ */
char* etheraddr_string(const u_char *ep, char *buf) {
u_int i, j;
char *cp;
cp = buf;
if ((j = *ep >> 4) != 0)
*cp++ = hex[j];
else
*cp++ = '0';
*cp++ = hex[*ep++ & 0xf];
for(i = 5; (int)--i >= 0;) {
*cp++ = ':';
if ((j = *ep >> 4) != 0)
*cp++ = hex[j];
else
*cp++ = '0';
*cp++ = hex[*ep++ & 0xf];
}
*cp = '\0';
return (buf);
}
/* ************************************ */
char* llcsap_string(u_char sap) {
char *cp;
static char buf[sizeof("sap 00")];
cp = buf;
strncpy(cp, "sap ", sizeof(buf));
cp += strlen(cp);
*cp++ = hex[sap >> 4 & 0xf];
*cp++ = hex[sap & 0xf];
*cp++ = '\0';
/* traceEvent(CONST_TRACE_INFO, "%s", buf); */
return(buf);
}
/* ************************************ */
/*
The FDDI code below has been grabbed from
tcpdump
*/
static u_char fddi_bit_swap[] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
};
void extract_fddi_addrs(struct fddi_header *fddip, char *fsrc, char *fdst)
{
int i;
for (i = 0; i < 6; ++i)
fdst[i] = fddi_bit_swap[fddip->dhost[i]];
for (i = 0; i < 6; ++i)
fsrc[i] = fddi_bit_swap[fddip->shost[i]];
}
/* ************************************ */
static u_int _ns_get16(const u_char *src) {
u_int dst;
NS_GET16(dst, src);
return (dst);
}
/* ************************************ */
int printable(int ch) {
return (ch > 0x20 && ch < 0x7f);
}
/* ************************************ */
static int special(int ch) {
switch (ch) {
case 0x22: /* '"' */
case 0x2E: /* '.' */
case 0x3B: /* ';' */
case 0x5C: /* '\\' */
/* Special modifiers in zone files. */
case 0x40: /* '@' */
case 0x24: /* '$' */
return (1);
default:
return (0);
}
}
/* ************************************ */
static int _ns_name_ntop(const u_char *src,
char *dst, size_t dstsiz) {
const u_char *cp;
char *dn, *eom;
u_char c;
u_int n;
static char digits[] = "0123456789";
cp = src;
dn = dst;
eom = dst + dstsiz;
while ((n = *cp++) != 0) {
if ((n & NS_CMPRSFLGS) != 0) {
/* Some kind of compression pointer. */
errno = EMSGSIZE;
return (-1);
}
if (dn != dst) {
if (dn >= eom) {
errno = EMSGSIZE;
return (-1);
}
*dn++ = '.';
}
if (dn + n >= eom) {
errno = EMSGSIZE;
return (-1);
}
for (; n > 0; n--) {
c = *cp++;
if (special(c)) {
if (dn + 1 >= eom) {
errno = EMSGSIZE;
return (-1);
}
*dn++ = '\\';
*dn++ = (char)c;
} else if (!printable(c)) {
if (dn + 3 >= eom) {
errno = EMSGSIZE;
return (-1);
}
*dn++ = '\\';
*dn++ = digits[c / 100];
*dn++ = digits[(c % 100) / 10];
*dn++ = digits[c % 10];
} else {
if (dn >= eom) {
errno = EMSGSIZE;
return (-1);
}
*dn++ = (char)c;
}
}
}
if (dn == dst) {
if (dn >= eom) {
errno = EMSGSIZE;
return (-1);
}
*dn++ = '.';
}
if (dn >= eom) {
errno = EMSGSIZE;
return (-1);
}
*dn++ = '\0';
return (dn - dst);
}
/* ************************************ */
static char* _res_skip_rr(char *cp, char *eom) {
int tmp;
int dlen;
if ((tmp = _dn_skipname((u_char *)cp, (u_char *)eom)) == -1)
return (NULL); /* compression error */
cp += tmp;
if ((cp + RRFIXEDSZ) > eom)
return (NULL);
cp += INT16SZ; /* type */
cp += INT16SZ; /* class */
cp += INT32SZ; /* ttl */
dlen = _ns_get16((u_char*)cp);
cp += INT16SZ; /* dlen */
cp += dlen;
if (cp > eom)
return (NULL);
return (cp);
}
/* ************************************ */
static int dn_expand_(const u_char *msg, const u_char *eom, const u_char *src,
char *dst, int dstsiz) {
int n = _ns_name_uncompress(msg, eom, src, dst, (size_t)dstsiz);
if (n > 0 && dst[0] == '.')
dst[0] = '\0';
return (n);
}
/* ************************************ */
static int _ns_name_unpack(const u_char *msg,
const u_char *eom, const u_char *src,
u_char *dst, size_t dstsiz) {
const u_char *srcp, *dstlim;
u_char *dstp;
int n, len, checked;
len = -1;
checked = 0;
dstp = dst;
srcp = src;
dstlim = dst + dstsiz;
if (srcp < msg || srcp >= eom) {
errno = EMSGSIZE;
return (-1);
}
/* Fetch next label in domain name. */
while ((n = *srcp++) != 0) {
/* Check for indirection. */
switch (n & NS_CMPRSFLGS) {
case 0:
/* Limit checks. */
if (dstp + n + 1 >= dstlim || srcp + n >= eom) {
errno = EMSGSIZE;
return (-1);
}
checked += n + 1;
*dstp++ = n;
memcpy(dstp, srcp, n);
dstp += n;
srcp += n;
break;
case NS_CMPRSFLGS:
if (srcp >= eom) {
errno = EMSGSIZE;
return (-1);
}
if (len < 0)
len = srcp - src + 1;
srcp = msg + (((n & 0x3f) << 8) | (*srcp & 0xff));
if (srcp < msg || srcp >= eom) { /* Out of range. */
errno = EMSGSIZE;
return (-1);
}
checked += 2;
/*
* Check for loops in the compressed name;
* if we've looked at the whole message,
* there must be a loop.
*/
if (checked >= eom - msg) {
errno = EMSGSIZE;
return (-1);
}
break;
default:
errno = EMSGSIZE;
return (-1); /* flag error */
}
}
*dstp = '\0';
if (len < 0)
len = srcp - src;
return (len);
}
/* ************************************ */
static int _ns_name_uncompress(const u_char *msg,
const u_char *eom, const u_char *src,
char *dst, size_t dstsiz) {
u_char tmp[NS_MAXCDNAME];
int n;
if ((n = _ns_name_unpack(msg, eom, src, tmp, sizeof tmp)) == -1)
return (-1);
if (_ns_name_ntop(tmp, dst, dstsiz) == -1)
return (-1);
return (n);
}
/* ************************************ */
static int _ns_name_skip(const u_char **ptrptr, const u_char *eom) {
const u_char *cp;
u_int n;
cp = *ptrptr;
while (cp < eom && (n = *cp++) != 0) {
/* Check for indirection. */
switch (n & NS_CMPRSFLGS) {
case 0: /* normal case, n == len */
cp += n;
continue;
case NS_CMPRSFLGS: /* indirection */
cp++;
break;
default: /* illegal type */
errno = EMSGSIZE;
return (-1);
}
break;
}
if (cp > eom) {
errno = EMSGSIZE;
return (-1);
}
*ptrptr = cp;
return (0);
}
/* ************************************ */
static int _dn_skipname(const u_char *ptr, const u_char *eom) {
const u_char *saveptr = ptr;
if (_ns_name_skip(&ptr, eom) == -1)
return (-1);
return (ptr - saveptr);
}
/* ************************************ */
static void msdns_filter_name(char *msg) {
int i, j, max = strlen(msg);
for(i=0, j=0; i<max; i++) {
#ifdef MDNS_DEBUG
if(0) traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: [i=%d][%c][%d]", i, msg[i], msg[i]);
#endif
if(msg[i] != '\\') {
if(msg[i] > 0) /* FIX: can we do better? */
msg[j++] = msg[i];
} else {
char tmpStr[8], tmpStr2[8];
int id;
if((msg[i+1] >= '0') && (msg[i+1] <= '9')) {
#ifdef MDNS_DEBUG
if(0){
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: [i=%d][%c][%d]", i+1, msg[i+1], msg[i+1]);
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: [i=%d][%c][%d]", i+2, msg[i+2], msg[i+2]);
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: [i=%d][%c][%d]", i+3, msg[i+3], msg[i+3]);
}
#endif
tmpStr[0] = msg[i+1];
tmpStr[1] = msg[i+2];
tmpStr[2] = msg[i+3];
tmpStr[3] = '\0';
id = atoi(tmpStr);
if(id == 128)
msg[j++] = '\'';
else if(id < 128) {
safe_snprintf(__FILE__, __LINE__, tmpStr2, sizeof(tmpStr2), "%c", id);
msg[j++] = tmpStr2[0];
}
i += 3;
} else {
i++;
msg[j++] = msg[i];
}
}
}
msg[j] = '\0';
}
/* ************************************ */
static char* _res_skip(char *msg,
int numFieldsToSkip,
char *eom) {
char *cp;
HEADER *hp;
int tmp;
int n;
/*
* Skip the header fields.
*/
hp = (HEADER *)msg;
cp = msg + HFIXEDSZ;
/*
* skip question records.
*/
n = (int)ntohs((unsigned short int)hp->qdcount);
if (n > 0) {
while (--n >= 0 && cp < eom) {
tmp = _dn_skipname((u_char *)cp, (u_char *)eom);
if (tmp == -1) return(NULL);
cp += tmp;
cp += INT16SZ; /* type */
cp += INT16SZ; /* class */
}
}
if (--numFieldsToSkip <= 0) return(cp);
/*
* skip myGlobals.authoritative answer records
*/
n = (int)ntohs((unsigned short int)hp->ancount);
if (n > 0) {
while (--n >= 0 && cp < eom) {
cp = _res_skip_rr(cp, eom);
if (cp == NULL) return(NULL);
}
}
if (--numFieldsToSkip == 0) return(cp);
/*
* skip name server records
*/
n = (int)ntohs((unsigned short int)hp->nscount);
if (n > 0) {
while (--n >= 0 && cp < eom) {
cp = _res_skip_rr(cp, eom);
if (cp == NULL) return(NULL);
}
}
if (--numFieldsToSkip == 0) return(cp);
/*
* skip additional records
*/
n = (int)ntohs((unsigned short int)hp->arcount);
if (n > 0) {
while (--n >= 0 && cp < eom) {
cp = _res_skip_rr(cp, eom);
if (cp == NULL) return(NULL);
}
}
return(cp);
}
/* ************************************ */
static void setHostName(HostTraffic *srcHost, char *name) {
u_short tmpStrLen = min(strlen(name), MAX_LEN_SYM_HOST_NAME);
strncpy(srcHost->hostResolvedName, name, tmpStrLen);
srcHost->hostResolvedName[tmpStrLen] = '\0';
}
/* ************************************ */
//#define MDNS_DEBUG
static void handleMdnsName(HostTraffic *srcHost, u_short sport, u_char *mdns_name) {
char *mdnsStrtokState, *name = NULL, *appl = NULL, *proto = NULL, *domain = NULL;
char *tmpStr = strdup((char*)mdns_name);
if(tmpStr != NULL) {
msdns_filter_name(tmpStr);
/* S's Music._daap._tcp.localcal */
#ifdef MDNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: (1) [%s]", tmpStr);
#endif
name = strtok_r(tmpStr, "._", &mdnsStrtokState);
if(name) {
appl = strtok_r(NULL, "._", &mdnsStrtokState);
if(appl) {
proto = strtok_r(NULL, "._", &mdnsStrtokState);
if(proto) {
domain = strtok_r(NULL, "._", &mdnsStrtokState);
}
}
}
if((domain != NULL)
&& ((!strcmp(domain, "local"))
|| (!strcmp(domain, "localafpovertcp"))
)) {
#ifdef MDNS_DEBUG
traceEvent(CONST_TRACE_INFO, "DNS_DEBUG: (2) [%s] [%s][%s][%s][%s]",
srcHost->hostNumIpAddress,
name, appl, proto, domain);
#endif
if((!strcmp(appl, "ipp")) || (!strcmp(appl, "printer"))) {
/* Printer */
FD_SET(FLAG_HOST_TYPE_PRINTER, &srcHost->flags);
setHostName(srcHost, name);
} else if(!strcmp(appl, "afpovertcp")) {
/* Sharing name under MacOS */
setHostName(srcHost, name);
} else if(!strcmp(appl, "workstation")) {
/* Host name under MacOS */
setHostName(srcHost, strtok(name, "["));
} else if(!strcmp(appl, "http")) {
/* HTTP server */
FD_SET(FLAG_HOST_TYPE_SVC_HTTP, &srcHost->flags);
} else if(!strcmp(appl, "daap")) {
/* Digital Audio Access Protocol (daap.sourceforge.net) */
updateHostUsers(name, BITFLAG_DAAP_USER, srcHost);
}
} else if(name && appl && (!strcmp(appl, "local"))) {
setHostName(srcHost, name);
}
free(tmpStr);
}
}
/* ************************************ */
/*
This function needs to be rewritten from scratch
as it does not check boundaries (see ** below)
*/
u_int16_t handleDNSpacket(HostTraffic *srcHost, u_short sport,
const u_char *ipPtr,
DNSHostInfo *hostPtr,
short length,
short *isRequest,
short *positiveReply) {
querybuf answer;
u_char *cp = NULL;
char **aliasPtr;
u_char *eom = NULL, *bp;
char **addrPtr;
int type=0, class, queryType = T_A;
int qdcount, ancount, arcount, nscount=0, buflen;
int origClass=0;
int numAliases = 0;
int numAddresses = 0;
int i;
int len;
int dlen;
char haveAnswer;
short addr_list_idx=0;
char printedAnswers = FALSE;
char *host_aliases[MAX_ALIASES];
int host_aliases_len[MAX_ALIASES], n;
u_char hostbuf[4096];
char *addr_list[MAX_ADDRESSES + 1];
u_int16_t transactionId, flags;
/* Never forget to copy the buffer !!!!! */
cp = (u_char*)(ipPtr);
memcpy(&transactionId, cp, 2); transactionId = ntohs(transactionId);
memcpy(&flags, &cp[2], 2); flags = ntohs(flags);
/* reset variables */
memset(host_aliases, 0, sizeof(host_aliases));
memset(host_aliases_len, 0, sizeof(host_aliases_len));
memset(hostbuf, 0, sizeof(hostbuf));
memset(addr_list, 0, sizeof(addr_list));
#ifdef DEBUG
traceEvent(CONST_TRACE_INFO, "id=0x%X - flags=0x%X", transactionId, flags);
#endif
if(length > sizeof(answer))
length = sizeof(answer);
memset(&answer, 0, sizeof(answer));
memcpy(&answer, ipPtr, length);
*isRequest = (short)!(flags & 0x8000);
*positiveReply = (short)!(flags & 0x0002);
if(answer.qb1.rcode != 0 /* NOERROR */) {
return(transactionId);
}
/*
Don't change it to eom = (u_char *)(&answer+length);
unless you want to core dump !
*/
#if 0
eom = (u_char *)(ipPtr+length);
#else
eom = (u_char *) &answer + length;
#endif
qdcount = (int)ntohs((unsigned short int)answer.qb1.qdcount);
ancount = (int)ntohs((unsigned short int)answer.qb1.ancount);
arcount = (int)ntohs((unsigned short int)answer.qb1.arcount);
nscount = (int)ntohs((unsigned short int)answer.qb1.nscount);
/*
* If there are no answer, n.s. or additional records
* then return with an error.
*/
if (ancount == 0 && nscount == 0 && arcount == 0) {
return(transactionId);
}
bp = hostbuf;
buflen = sizeof(hostbuf);
cp = (u_char *) &answer+HFIXEDSZ;
/* Process first question section. */
if (qdcount-- > 0) {
n = (short)dn_expand_(answer.qb2, eom, cp, hostPtr->queryName, MAXDNAME);
if (n<0)
return(transactionId);
cp += n;
if (cp + INT16SZ >eom)
return(transactionId);
hostPtr->queryType = GetShort(cp);
cp += INT16SZ;
if (cp > eom)
return(transactionId);
}
/* Skip over rest of question section. */
while (qdcount-- > 0) {
n = (short)_dn_skipname(cp, eom);
if (n < 0)
return(transactionId);
cp += n + QFIXEDSZ;
if (cp > eom)
return(transactionId);
}
aliasPtr = host_aliases;
addrPtr = addr_list;
haveAnswer = FALSE;
while (--ancount >= 0 && cp < eom) {
n = (short)dn_expand_(answer.qb2, eom, cp, (char *)bp, buflen);
if (n < 0)
return(transactionId);
cp += n;
if (cp + 3 * INT16SZ + INT32SZ > eom)
return(transactionId);
type = GetShort(cp);
class = GetShort(cp);
cp += INT32SZ; /* skip TTL */
dlen = GetShort(cp);
if (cp + dlen > eom)
return(transactionId);
if (type == T_CNAME) {
/*
* Found an alias.
*/
cp += dlen;
if (aliasPtr >= &host_aliases[MAX_ALIASES-1]) {
continue;
}
*aliasPtr++ = (char *)bp;
n = (short)strlen((char *)bp) + 1;
host_aliases_len[numAliases] = n;
numAliases++;
bp += n;
buflen -= n;
continue;
} else if (type == T_PTR) {
/*
* Found a "pointer" to the real name.
*
* E.g. : 89.10.67.213.in-addr.arpa
*
*/
char *a, *b, *c, *d, dnsBuf[128], *strtokState;
unsigned long theDNSaddr;
len = strlen((char*)bp);
if(bp[0] == '_') {
/* Multicast DNS */
n = (short)dn_expand_(answer.qb2, eom, cp, (char *)bp, buflen);
if (n < 0) {
cp += n;
continue;
}
cp += n;
handleMdnsName(srcHost, sport, bp);
haveAnswer = TRUE;
continue;
} else {
if(len >= (sizeof(dnsBuf)-1)) len = sizeof(dnsBuf)-2;
xstrncpy(dnsBuf, (char*)bp, len);
d = strtok_r(dnsBuf, ".", &strtokState);
c = strtok_r(NULL, ".", &strtokState);
b = strtok_r(NULL, ".", &strtokState);
a = strtok_r(NULL, ".", &strtokState);
if(a && b && c && d) {
theDNSaddr = htonl(atoi(a)*(256*256*256)+atoi(b)*(256*256)+atoi(c)*256+atoi(d));
if(addr_list_idx >= MAX_ADDRESSES) break; /* Further check */
memcpy(&addr_list[addr_list_idx++], (char*)&theDNSaddr, sizeof(char*));
hostPtr->addrLen = INADDRSZ;
hostPtr->addrList[0] = theDNSaddr;
n = (short)dn_expand_(answer.qb2, eom, cp, (char *)bp, buflen);
if (n < 0) {
cp += n;
continue;
}
cp += n;
len = strlen((char *)bp) + 1;
memcpy(hostPtr->name, bp, len);
haveAnswer = TRUE;
}
}
break;
} else if (type != T_A) {
cp += dlen;
continue;
}
if (dlen != INADDRSZ)
return(transactionId);
if (haveAnswer) {
/*
* If we've already got 1 address, we aren't interested
* in addresses with a different length or class.
*/
if (dlen != hostPtr->addrLen) {
cp += dlen;
continue;
}
if (class != origClass) {
cp += dlen;
continue;
}
} else {
/*
* First address: record its length and class so we
* only save additonal ones with the same attributes.
*/
hostPtr->addrLen = dlen;
origClass = class;
hostPtr->addrType = (class == C_IN) ? AF_INET : AF_UNSPEC;
len = strlen((char *)bp) + 1;
memcpy(hostPtr->name, bp, len);
}
/* Align bp on u_int32_t boundary */
#if 0
bp += (((u_int32_t)bp) % sizeof(u_int32_t));
#else
{
u_int32_t padding;
padding=((u_int32_t)((long)bp)) % sizeof(u_int32_t);
bp += padding;
buflen -= padding;
}
#endif
if (bp + dlen >= &hostbuf[sizeof(hostbuf)]) {
break;
}
if (numAddresses >= MAX_ADDRESSES) {
cp += dlen;
continue;
}
memcpy(*addrPtr++ = (char *)bp, cp, dlen);
bp += dlen;
cp += dlen;
buflen -= dlen;
numAddresses++;
haveAnswer = TRUE;
}
if ((queryType == T_A || queryType == T_PTR) && haveAnswer) {
if(sport == 53 /* DNS */) {
/*
* Go through the alias and address lists and return them
* in the hostPtr variable.
*/
if (numAliases > 0) {
for (i = 0; i < numAliases; i++) {
if(host_aliases[i] != NULL)
memcpy(hostPtr->aliases[i], host_aliases[i], host_aliases_len[i]);
else break;
}
hostPtr->aliases[i][0] = '\0';
}
if (numAddresses > 0) {
for (i = 0; i < numAddresses; i++) {
if(addr_list[i] != NULL)
memcpy(&hostPtr->addrList[i], addr_list[i], hostPtr->addrLen);
else break;
}
hostPtr->addrList[i] = 0;
}
}
return(transactionId);
}
/*
* At this point, for the T_A query type, only empty answers remain.
* For other query types, additional information might be found
* in the additional resource records part.
*/
if (!answer.qb1.aa && (queryType != T_A) && (nscount > 0 || arcount > 0)) {
if (printedAnswers) {
putchar('\n');
}
}
cp = (u_char *)_res_skip((char *)&answer, 2, (char *)eom);
while ((--nscount >= 0) && (cp < eom)) {
/*
* Go through the NS records and retrieve the names of hosts
* that serve the requested domain.
*/
n = (short)dn_expand_(answer.qb2, eom, cp, (char *)bp, buflen);
if (n < 0) {
return(transactionId);
}
if(sport == 5353 /* mDNS */)
handleMdnsName(srcHost, sport, bp);
cp += n;
len = strlen((char *)bp) + 1;
if (cp + 3 * INT16SZ + INT32SZ > eom)
return(transactionId);
type = GetShort(cp);
class = GetShort(cp);
cp += INT32SZ; /* skip TTL */
dlen = GetShort(cp);
if (cp + dlen > eom)
return(transactionId);
if (type != T_NS) {
cp += dlen;
}
}
return(transactionId);
}
/* **************************************** */
void checkSpoofing(HostTraffic *hostToCheck, int actualDeviceId) {
HostTraffic *el;
for(el=getFirstHost(actualDeviceId);
el != NULL; el = getNextHost(actualDeviceId, el)) {
if((!addrnull(&el->hostIpAddress))
&& (addrcmp(&el->hostIpAddress,&hostToCheck->hostIpAddress) == 0)) {
/* Spoofing detected */
if((!hasDuplicatedMac(el))
&& (!hasDuplicatedMac(hostToCheck))) {
FD_SET(FLAG_HOST_DUPLICATED_MAC, &hostToCheck->flags);
FD_SET(FLAG_HOST_DUPLICATED_MAC, &el->flags);
if(myGlobals.runningPref.enableSuspiciousPacketDump) {
traceEvent(CONST_TRACE_WARNING,
"Two MAC addresses found for the same IP address %s: [%s/%s] (spoofing detected?)",
el->hostNumIpAddress, hostToCheck->ethAddressString, el->ethAddressString);
dumpSuspiciousPacket(actualDeviceId);
}
}
}
}
}
/* **************************************** */
char* host2networkName(HostTraffic *el, char *buf, u_short buf_len) {
struct in_addr addr = el->hostIpAddress.Ip4Address;
char buf1[64];
addr.s_addr = addr.s_addr & (0xFFFFFFFF << (32-el->network_mask));
safe_snprintf(__FILE__, __LINE__, buf, buf_len, "%s/%d",
_intoa(addr, buf1, sizeof(buf1)),
el->network_mask);
return(buf);
}
syntax highlighted by Code2HTML, v. 0.9.1