|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* $/Plasma20/Sources/Plasma/NucleusLib/pnAsyncCoreExe/Private/Win32/pnAceW32Dns.cpp
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
#include "../../Pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Private
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
enum {
|
|
|
|
WM_LOOKUP_EXIT = WM_APP,
|
|
|
|
WM_LOOKUP_FOUND_HOST
|
|
|
|
};
|
|
|
|
|
|
|
|
const unsigned kMaxLookupName = 128;
|
|
|
|
|
|
|
|
struct Lookup {
|
|
|
|
LINK(Lookup) link;
|
|
|
|
AsyncCancelId cancelId;
|
|
|
|
HANDLE cancelHandle;
|
|
|
|
FAsyncLookupProc lookupProc;
|
|
|
|
unsigned port;
|
|
|
|
void * param;
|
|
|
|
wchar_t name[kMaxLookupName];
|
|
|
|
char buffer[MAXGETHOSTSTRUCT];
|
|
|
|
};
|
|
|
|
|
|
|
|
static CCritSect s_critsect;
|
|
|
|
static LISTDECL(Lookup, link) s_lookupList;
|
|
|
|
static HANDLE s_lookupThread;
|
|
|
|
static HWND s_lookupWindow;
|
|
|
|
static unsigned s_nextLookupCancelId = 1;
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Internal functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
static void LookupProcess (Lookup * lookup, unsigned error) {
|
|
|
|
unsigned count = 0;
|
|
|
|
NetAddress * addrs = nil;
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
return;
|
|
|
|
|
|
|
|
const HOSTENT & host = * (HOSTENT *) lookup->buffer;
|
|
|
|
if (host.h_addrtype != AF_INET)
|
|
|
|
return;
|
|
|
|
if (host.h_length != sizeof(in_addr))
|
|
|
|
return;
|
|
|
|
if (!host.h_addr_list)
|
|
|
|
return;
|
|
|
|
|
|
|
|
in_addr const * const * const inAddr = (in_addr **) host.h_addr_list;
|
|
|
|
|
|
|
|
// count the number of addresses
|
|
|
|
while (inAddr[count])
|
|
|
|
++count;
|
|
|
|
|
|
|
|
// allocate a buffer large enough to hold all the addresses
|
|
|
|
addrs = (NetAddress *)malloc(sizeof(*addrs) * count);
|
|
|
|
MemZero(addrs, sizeof(*addrs) * count);
|
|
|
|
|
|
|
|
// fill in address data
|
|
|
|
const uint16_t port = htons((uint16_t) lookup->port);
|
|
|
|
for (unsigned i = 0; i < count; ++i) {
|
|
|
|
sockaddr_in * inetaddr = (sockaddr_in *) &addrs[i];
|
|
|
|
inetaddr->sin_family = AF_INET;
|
|
|
|
inetaddr->sin_addr = *inAddr[i];
|
|
|
|
inetaddr->sin_port = port;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (host.h_name && host.h_name[0])
|
|
|
|
StrToUnicode(lookup->name, host.h_name, arrsize(lookup->name));
|
|
|
|
|
|
|
|
if (lookup->lookupProc)
|
|
|
|
lookup->lookupProc(lookup->param, lookup->name, count, addrs);
|
|
|
|
|
|
|
|
// we can delete the operation outside an IoConn critical
|
|
|
|
// section because it isn't linked into an ioConn opList
|
|
|
|
// and because connection attempts are not waitable
|
|
|
|
ASSERT(!lookup->link.IsLinked());
|
|
|
|
delete lookup;
|
|
|
|
PerfSubCounter(kAsyncPerfNameLookupAttemptsCurr, 1);
|
|
|
|
|
|
|
|
free(addrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
static void LookupFindAndProcess (HANDLE cancelHandle, unsigned error) {
|
|
|
|
// find the operation for this cancel handle
|
|
|
|
Lookup * lookup;
|
|
|
|
s_critsect.Enter();
|
|
|
|
for (lookup = s_lookupList.Head(); lookup; lookup = s_lookupList.Next(lookup)) {
|
|
|
|
if (lookup->cancelHandle == cancelHandle) {
|
|
|
|
lookup->cancelHandle = nil;
|
|
|
|
s_lookupList.Unlink(lookup);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
if (lookup)
|
|
|
|
LookupProcess(lookup, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
static unsigned THREADCALL LookupThreadProc (AsyncThread * thread) {
|
|
|
|
static const char WINDOW_CLASS[] = "AsyncLookupWnd";
|
|
|
|
WNDCLASS wc;
|
|
|
|
memset(&wc, 0, sizeof(wc));
|
|
|
|
wc.lpfnWndProc = DefWindowProc;
|
|
|
|
wc.hInstance = GetModuleHandle(0);
|
|
|
|
wc.lpszClassName = WINDOW_CLASS;
|
|
|
|
RegisterClass(&wc);
|
|
|
|
|
|
|
|
s_lookupWindow = CreateWindow(
|
|
|
|
WINDOW_CLASS,
|
|
|
|
WINDOW_CLASS,
|
|
|
|
WS_OVERLAPPED,
|
|
|
|
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
|
|
|
|
(HWND)0,
|
|
|
|
(HMENU) 0,
|
|
|
|
wc.hInstance,
|
|
|
|
0
|
|
|
|
);
|
|
|
|
if (!s_lookupWindow)
|
|
|
|
ErrorFatal(__LINE__, __FILE__, "CreateWindow %#x", GetLastError());
|
|
|
|
|
|
|
|
HANDLE lookupStartEvent = (HANDLE) thread->argument;
|
|
|
|
SetEvent(lookupStartEvent);
|
|
|
|
|
|
|
|
MSG msg;
|
|
|
|
while (GetMessage(&msg, s_lookupWindow, 0, 0)) {
|
|
|
|
if (msg.message == WM_LOOKUP_FOUND_HOST)
|
|
|
|
LookupFindAndProcess((HANDLE) msg.wParam, HIWORD(msg.lParam));
|
|
|
|
else if (msg.message == WM_LOOKUP_EXIT)
|
|
|
|
break;
|
|
|
|
else {
|
|
|
|
TranslateMessage(&msg);
|
|
|
|
DispatchMessage(&msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// fail all pending name lookups
|
|
|
|
for (;;) {
|
|
|
|
s_critsect.Enter();
|
|
|
|
Lookup * lookup = s_lookupList.Head();
|
|
|
|
if (lookup) {
|
|
|
|
WSACancelAsyncRequest(lookup->cancelHandle);
|
|
|
|
lookup->cancelHandle = nil;
|
|
|
|
s_lookupList.Unlink(lookup);
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
if (!lookup)
|
|
|
|
break;
|
|
|
|
|
|
|
|
LookupProcess(lookup, (unsigned) -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleanup
|
|
|
|
DestroyWindow(s_lookupWindow);
|
|
|
|
s_lookupWindow = nil;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
static void StartLookupThread () {
|
|
|
|
if (s_lookupThread)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// create a shutdown event
|
|
|
|
HANDLE lookupStartEvent = CreateEvent(
|
|
|
|
(LPSECURITY_ATTRIBUTES) 0,
|
|
|
|
true, // manual reset
|
|
|
|
false, // initial state off
|
|
|
|
(LPCTSTR) 0 // name
|
|
|
|
);
|
|
|
|
if (!lookupStartEvent)
|
|
|
|
ErrorFatal(__LINE__, __FILE__, "CreateEvent %#x", GetLastError());
|
|
|
|
|
|
|
|
// create a thread to perform lookups
|
|
|
|
s_lookupThread = (HANDLE) AsyncThreadCreate(
|
|
|
|
LookupThreadProc,
|
|
|
|
lookupStartEvent,
|
|
|
|
L"AsyncLookupThread"
|
|
|
|
);
|
|
|
|
|
|
|
|
WaitForSingleObject(lookupStartEvent, INFINITE);
|
|
|
|
CloseHandle(lookupStartEvent);
|
|
|
|
ASSERT(s_lookupWindow);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Module functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void DnsDestroy (unsigned exitThreadWaitMs) {
|
|
|
|
if (s_lookupThread) {
|
|
|
|
PostMessage(s_lookupWindow, WM_LOOKUP_EXIT, 0, 0);
|
|
|
|
WaitForSingleObject(s_lookupThread, exitThreadWaitMs);
|
|
|
|
CloseHandle(s_lookupThread);
|
|
|
|
s_lookupThread = nil;
|
|
|
|
ASSERT(!s_lookupWindow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
*
|
|
|
|
* Public functions
|
|
|
|
*
|
|
|
|
***/
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void AsyncAddressLookupName (
|
|
|
|
AsyncCancelId * cancelId, // out
|
|
|
|
FAsyncLookupProc lookupProc,
|
|
|
|
const wchar_t name[],
|
|
|
|
unsigned port,
|
|
|
|
void * param
|
|
|
|
) {
|
|
|
|
ASSERT(lookupProc);
|
|
|
|
ASSERT(name);
|
|
|
|
|
|
|
|
PerfAddCounter(kAsyncPerfNameLookupAttemptsCurr, 1);
|
|
|
|
PerfAddCounter(kAsyncPerfNameLookupAttemptsTotal, 1);
|
|
|
|
|
|
|
|
// Get name/port
|
|
|
|
char ansiName[kMaxLookupName];
|
|
|
|
StrToAnsi(ansiName, name, arrsize(ansiName));
|
|
|
|
if (char * portStr = StrChr(ansiName, ':')) {
|
|
|
|
if (unsigned newPort = StrToUnsigned(portStr + 1, nil, 10))
|
|
|
|
port = newPort;
|
|
|
|
*portStr = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize lookup
|
|
|
|
Lookup * lookup = new Lookup;
|
|
|
|
lookup->lookupProc = lookupProc;
|
|
|
|
lookup->port = port;
|
|
|
|
lookup->param = param;
|
|
|
|
StrCopy(lookup->name, name, arrsize(lookup->name));
|
|
|
|
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
// Start the lookup thread if it wasn't started already
|
|
|
|
StartLookupThread();
|
|
|
|
s_lookupList.Link(lookup);
|
|
|
|
|
|
|
|
// get cancel id; we can avoid checking for zero by always using an odd number
|
|
|
|
ASSERT(s_nextLookupCancelId & 1);
|
|
|
|
s_nextLookupCancelId += 2;
|
|
|
|
*cancelId = lookup->cancelId = (AsyncCancelId) s_nextLookupCancelId;
|
|
|
|
|
|
|
|
// Perform async lookup
|
|
|
|
lookup->cancelHandle = WSAAsyncGetHostByName(
|
|
|
|
s_lookupWindow,
|
|
|
|
WM_LOOKUP_FOUND_HOST,
|
|
|
|
ansiName,
|
|
|
|
&lookup->buffer[0],
|
|
|
|
sizeof(lookup->buffer)
|
|
|
|
);
|
|
|
|
if (!lookup->cancelHandle) {
|
|
|
|
PostMessage(s_lookupWindow, WM_LOOKUP_FOUND_HOST, nil, (unsigned) -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void AsyncAddressLookupAddr (
|
|
|
|
AsyncCancelId * cancelId, // out
|
|
|
|
FAsyncLookupProc lookupProc,
|
|
|
|
const NetAddress & address,
|
|
|
|
void * param
|
|
|
|
) {
|
|
|
|
ASSERT(lookupProc);
|
|
|
|
|
|
|
|
PerfAddCounter(kAsyncPerfNameLookupAttemptsCurr, 1);
|
|
|
|
PerfAddCounter(kAsyncPerfNameLookupAttemptsTotal, 1);
|
|
|
|
|
|
|
|
// Initialize lookup
|
|
|
|
Lookup * lookup = new Lookup;
|
|
|
|
lookup->lookupProc = lookupProc;
|
|
|
|
lookup->port = 1;
|
|
|
|
lookup->param = param;
|
|
|
|
NetAddressToString(
|
|
|
|
address,
|
|
|
|
lookup->name,
|
|
|
|
arrsize(lookup->name),
|
|
|
|
kNetAddressFormatNodeNumber
|
|
|
|
);
|
|
|
|
|
|
|
|
s_critsect.Enter();
|
|
|
|
{
|
|
|
|
// Start the lookup thread if it wasn't started already
|
|
|
|
StartLookupThread();
|
|
|
|
s_lookupList.Link(lookup);
|
|
|
|
|
|
|
|
// get cancel id; we can avoid checking for zero by always using an odd number
|
|
|
|
ASSERT(s_nextLookupCancelId & 1);
|
|
|
|
s_nextLookupCancelId += 2;
|
|
|
|
*cancelId = lookup->cancelId = (AsyncCancelId) s_nextLookupCancelId;
|
|
|
|
|
|
|
|
// Perform async lookup
|
|
|
|
u_long addr = ((const sockaddr_in *) &address)->sin_addr.S_un.S_addr;
|
|
|
|
lookup->cancelHandle = WSAAsyncGetHostByAddr(
|
|
|
|
s_lookupWindow,
|
|
|
|
WM_LOOKUP_FOUND_HOST,
|
|
|
|
(const char *) &addr,
|
|
|
|
sizeof(addr),
|
|
|
|
AF_INET,
|
|
|
|
&lookup->buffer[0],
|
|
|
|
sizeof(lookup->buffer)
|
|
|
|
);
|
|
|
|
if (!lookup->cancelHandle) {
|
|
|
|
PostMessage(s_lookupWindow, WM_LOOKUP_FOUND_HOST, nil, (unsigned) -1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
void AsyncAddressLookupCancel (
|
|
|
|
FAsyncLookupProc lookupProc,
|
|
|
|
AsyncCancelId cancelId // nil = cancel all with specified lookupProc
|
|
|
|
) {
|
|
|
|
s_critsect.Enter();
|
|
|
|
for (Lookup * lookup = s_lookupList.Head(); lookup; lookup = s_lookupList.Next(lookup)) {
|
|
|
|
if (lookup->lookupProc && (lookup->lookupProc != lookupProc))
|
|
|
|
continue;
|
|
|
|
if (cancelId && (lookup->cancelId != cancelId))
|
|
|
|
continue;
|
|
|
|
if (!lookup->cancelHandle)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// cancel this request
|
|
|
|
WSACancelAsyncRequest(lookup->cancelHandle);
|
|
|
|
lookup->cancelHandle = nil;
|
|
|
|
|
|
|
|
// initiate user callback
|
|
|
|
PostMessage(s_lookupWindow, WM_LOOKUP_FOUND_HOST, nil, (unsigned) -1);
|
|
|
|
}
|
|
|
|
s_critsect.Leave();
|
|
|
|
}
|