winamp/Src/Components/wac_network/wac_network_dns.cpp
2024-09-24 14:54:57 +02:00

278 lines
5.8 KiB
C++

/*
** JNetLib
** Copyright (C) 2000-2007 Nullsoft, Inc.
** Author: Justin Frankel
** File: asyncdns.cpp - JNL portable asynchronous DNS implementation
** License: see jnetlib.h
*/
#include "netinc.h"
#include "util.h"
#include "wac_network_dns.h"
#include <time.h>
#ifdef _WIN32
#include <strsafe.h>
#endif
enum
{
MODE_RESOLVE = 0,
MODE_REVERSE = 1,
};
struct cache_entry
{
time_t last_used; // timestamp.
bool resolved;
int mode; // 1=reverse
unsigned short port;
char hostname[ 256 ];
addrinfo *addr;
int sockettype;
};
wa::Components::WAC_Network_AsyncDNS::WAC_Network_AsyncDNS( int max_cache_entries )
{
m_thread_kill = 1;
m_thread = 0;
m_cache_size = max_cache_entries;
m_cache = (cache_entry *)malloc( sizeof( cache_entry ) * m_cache_size );
if ( m_cache )
memset( m_cache, 0, sizeof( cache_entry ) * m_cache_size );
else
m_cache_size = 0;
}
wa::Components::WAC_Network_AsyncDNS::~WAC_Network_AsyncDNS()
{
m_thread_kill = 1;
#ifdef _WIN32
if ( m_thread )
{
WaitForSingleObject( m_thread, INFINITE );
CloseHandle( m_thread );
}
#else
if ( m_thread )
{
void *p;
pthread_join( m_thread, &p );
}
#endif//!_WIN32
// free all the addrinfo stuff
for ( int x = 0; x < m_cache_size; x++ )
{
if ( m_cache[ x ].addr )
freeaddrinfo( m_cache[ x ].addr );
}
free( m_cache );
}
int wa::Components::WAC_Network_AsyncDNS::resolvenow( const char *hostname, unsigned short port, addrinfo **addr, int sockettype )
{
addrinfo hints;
memset( &hints, 0, sizeof( hints ) );
hints.ai_family = PF_UNSPEC;
if ( hostname )
hints.ai_flags = AI_NUMERICHOST;
else
hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE;
hints.ai_socktype = sockettype;
char portString[ 32 ] = { 0 };
sprintf( portString, "%u", (unsigned int)port );
if ( getaddrinfo( hostname, portString, &hints, addr ) == 0 )
{
return 0;
}
else
{
hints.ai_flags = 0;
if ( getaddrinfo( hostname, portString, &hints, addr ) == 0 )
return 0;
else
return -1;
}
}
#ifdef _WIN32
unsigned long WINAPI wa::Components::WAC_Network_AsyncDNS::_threadfunc( LPVOID _d )
#else
unsigned int WAC_Network_AsyncDNS::_threadfunc( void *_d )
#endif
{
int nowinsock = JNL::open_socketlib();
wa::Components::WAC_Network_AsyncDNS *_this = (WAC_Network_AsyncDNS *)_d;
int x;
for ( x = 0; x < _this->m_cache_size && !_this->m_thread_kill; x++ )
{
if ( _this->m_cache[ x ].last_used && !_this->m_cache[ x ].resolved )
{
if ( !nowinsock )
{
if ( _this->m_cache[ x ].mode == 0 )
{
addrinfo *res = 0;
if ( resolvenow( _this->m_cache[ x ].hostname, _this->m_cache[ x ].port, &res, _this->m_cache[ x ].sockettype ) == 0 )
{
_this->m_cache[ x ].addr = res;
}
else
{
_this->m_cache[ x ].addr = 0; //INADDR_NONE;
}
}
else if ( _this->m_cache[ x ].mode == 1 )
{
/*
hostent *ent;
// TODO: replace with getnameinfo for IPv6
ent=gethostbyaddr((const char *)&_this->m_cache[x].addr,4,AF_INET);
if (ent)
lstrcpyn(_this->m_cache[x].hostname, ent->h_name, 256);
else
_this->m_cache[x].hostname[0]=0;
*/
}
_this->m_cache[ x ].resolved = true;
}
else
{
if ( _this->m_cache[ x ].mode == 0 )
{
_this->m_cache[ x ].addr = 0;//INADDR_NONE;
_this->m_cache[ x ].resolved = true;
}
else if ( _this->m_cache[ x ].mode == 1 )
{
_this->m_cache[ x ].hostname[ 0 ] = 0;
_this->m_cache[ x ].resolved = true;
}
}
}
}
if ( !nowinsock )
JNL::close_socketlib();
_this->m_thread_kill = 1;
return 0;
}
int wa::Components::WAC_Network_AsyncDNS::resolve( const char *hostname, unsigned short port, addrinfo **addr, int sockettype )
{
// return 0 on success, 1 on wait, -1 on unresolvable
int x;
for ( x = 0; x < m_cache_size; x++ )
{
if ( !strcasecmp( m_cache[ x ].hostname, hostname ) && port == m_cache[ x ].port && m_cache[ x ].mode == 0 && m_cache[ x ].sockettype == sockettype )
{
m_cache[ x ].last_used = time( 0 );
if ( m_cache[ x ].resolved )
{
if ( m_cache[ x ].addr == 0 )//INADDR_NONE)
{
return DNS_RESOLVE_UNRESOLVABLE;
}
*addr = m_cache[ x ].addr;
return DNS_RESOLVE_SUCCESS;
}
makesurethreadisrunning();
return DNS_RESOLVE_WAIT;
}
}
// add to resolve list
int oi = -1;
for ( x = 0; x < m_cache_size; x++ )
{
if ( !m_cache[ x ].last_used )
{
oi = x;
break;
}
if ( ( oi == -1 || m_cache[ x ].last_used < m_cache[ oi ].last_used ) && m_cache[ x ].resolved )
{
oi = x;
}
}
if ( oi == -1 )
{
return DNS_RESOLVE_UNRESOLVABLE;
}
#ifdef _WIN32
StringCchCopyA( m_cache[ oi ].hostname, 256, hostname );
#elif defined(__APPLE__)
strlcpy( m_cache[ oi ].hostname, hostname, 255 );
#else
strncpy( m_cache[ oi ].hostname, hostname, 255 );
m_cache[ oi ].hostname[ 255 ] = 0;
#endif
m_cache[ oi ].port = port;
m_cache[ oi ].mode = 0;
m_cache[ oi ].addr = 0;//INADDR_NONE;
m_cache[ oi ].resolved = false;
m_cache[ oi ].last_used = time( 0 );
m_cache[ oi ].sockettype = sockettype;
makesurethreadisrunning();
return DNS_RESOLVE_WAIT;
}
void wa::Components::WAC_Network_AsyncDNS::makesurethreadisrunning( void )
{
if ( m_thread_kill )
{
#ifdef _WIN32
if ( m_thread )
{
WaitForSingleObject( m_thread, INFINITE );
CloseHandle( m_thread );
}
DWORD id;
m_thread_kill = 0;
m_thread = CreateThread( NULL, 0, _threadfunc, (LPVOID)this, 0, &id );
if ( !m_thread )
{
#else
if ( m_thread )
{
void *p;
pthread_join( m_thread, &p );
}
m_thread_kill = 0;
if ( pthread_create( &m_thread, NULL, ( void *( * ) ( void * ) )_threadfunc, (void *)this ) != 0 )
{
#endif
m_thread_kill = 1;
}
}
}
#define CBCLASS wa::Components::WAC_Network_AsyncDNS
START_DISPATCH;
CB( API_DNS_RESOLVE, resolve );
END_DISPATCH;
#undef CBCLASS