зеркало из https://github.com/mozilla/pjs.git
367 строки
9.7 KiB
C++
367 строки
9.7 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "License"); you may not use this file except in
|
|
* compliance with the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS"
|
|
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
|
|
* the License for the specific language governing rights and limitations
|
|
* under the License.
|
|
*
|
|
* The Original Code is Mozilla Communicator client code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape Communications
|
|
* Corporation. Portions created by Netscape are Copyright (C) 1998
|
|
* Netscape Communications Corporation. All Rights Reserved.
|
|
*/
|
|
#ifdef XP_PC
|
|
#include <windows.h>
|
|
#ifdef WIN32_LEAN_AND_MEAN
|
|
#include <winsock2.h>
|
|
#endif
|
|
#include "net.h"
|
|
#include "nsISupports.h"
|
|
#include "merrors.h"
|
|
#include "prnetdb.h"
|
|
#include "plstr.h"
|
|
#include "prmem.h"
|
|
#include "prlog.h"
|
|
#include "nslocks.h"
|
|
|
|
#ifdef DEBUG
|
|
static PRLogModuleInfo* gDNSLogModuleInfo;
|
|
|
|
#define DNS_TRACE_LOOKUPS 0x1
|
|
#define DNS_TRACE_SLOW 0x2 // XXX not yet implemented
|
|
|
|
#define DNS_LOG_TEST(_lm,_bit) (PRIntn((_lm)->level) & (_bit))
|
|
|
|
#define DNS_TRACE(_bit,_args) \
|
|
PR_BEGIN_MACRO \
|
|
if (DNS_LOG_TEST(gDNSLogModuleInfo,_bit)) { \
|
|
PR_LogPrint _args; \
|
|
} \
|
|
PR_END_MACRO
|
|
|
|
#else
|
|
#define DNS_TRACE(_bit,_args)
|
|
#endif
|
|
|
|
struct SocketWaiter {
|
|
void Init(PRFileDesc* fd) {
|
|
mSocket = fd;
|
|
mNext = NULL;
|
|
}
|
|
|
|
PRFileDesc* mSocket;
|
|
SocketWaiter* mNext;
|
|
};
|
|
|
|
struct DNSCacheEntry {
|
|
nsresult Init(void* aContext, const char* aHost, PRFileDesc* fd);
|
|
|
|
void Destroy();
|
|
|
|
static DNSCacheEntry** Lookup(const char* aHost);
|
|
|
|
void* mContext;
|
|
char* mHost;
|
|
SocketWaiter* mSockets;
|
|
PRHostEnt* mHostEnt;
|
|
int mError;
|
|
PRBool mFinished;
|
|
HANDLE mHandle;
|
|
DNSCacheEntry* mNext;
|
|
};
|
|
|
|
extern "C" {
|
|
static UINT gMSGFoundDNS;
|
|
UINT gMSGAsyncSelect;
|
|
HWND gDNSWindow;
|
|
};
|
|
|
|
static DNSCacheEntry* gDNSCache;
|
|
|
|
void
|
|
DNSCacheEntry::Destroy()
|
|
{
|
|
PR_Free(mHost);
|
|
PR_Free(mHostEnt);
|
|
SocketWaiter* sw = mSockets;
|
|
while (NULL != sw) {
|
|
SocketWaiter* next = sw->mNext;
|
|
PR_Free(sw);
|
|
sw = next;
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
DNSCacheEntry::Init(void* aContext, const char* aHost, PRFileDesc* fd)
|
|
{
|
|
mContext = aContext;
|
|
mError = 0;
|
|
mFinished = PR_FALSE;
|
|
mHandle = NULL;
|
|
mNext = NULL;
|
|
|
|
mHost = PL_strdup(aHost);
|
|
mHostEnt = (PRHostEnt*) PR_Malloc(sizeof(char) * PR_NETDB_BUF_SIZE);
|
|
mSockets = (SocketWaiter*) PR_Malloc(sizeof(SocketWaiter));
|
|
mSockets->Init(fd);
|
|
|
|
if ((NULL == mHost) || (NULL == mHostEnt) || (NULL == mSockets)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
mHostEnt->h_name = NULL;
|
|
mHostEnt->h_aliases = NULL;
|
|
mHostEnt->h_addr_list = NULL;
|
|
return NS_OK;
|
|
}
|
|
|
|
DNSCacheEntry**
|
|
DNSCacheEntry::Lookup(const char* aHost)
|
|
{
|
|
DNSCacheEntry** pentry = &gDNSCache;
|
|
DNSCacheEntry* entry = *pentry;
|
|
while (NULL != entry) {
|
|
if (PL_strcasecmp(aHost, entry->mHost) == 0) {
|
|
return pentry;
|
|
}
|
|
pentry = &entry->mNext;
|
|
entry = entry->mNext;
|
|
}
|
|
return pentry;
|
|
}
|
|
|
|
#if defined(NO_NETWORK_POLLING)
|
|
extern "C" PRFileDesc* net_GetFileDescFromOSFD(PRInt32);
|
|
extern "C" void net_AsyncSelect(PRFileDesc* fd, long netMask);
|
|
#endif /* NO_NETWORK_POLLING */
|
|
|
|
static LRESULT CALLBACK
|
|
#if defined(WIN16)
|
|
__loadds
|
|
#endif
|
|
EventProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (gMSGFoundDNS == uMsg) {
|
|
// Get error code now
|
|
int error = WSAGETASYNCERROR(lParam);
|
|
|
|
DNS_TRACE(DNS_TRACE_LOOKUPS,
|
|
("DNS lookup: handle=%p error=%d", (HANDLE)wParam, error));
|
|
|
|
// Search cache for matching handle
|
|
LRESULT rv = 1;
|
|
DNSCacheEntry** pentry = &gDNSCache;
|
|
DNSCacheEntry* entry = gDNSCache;
|
|
while (NULL != entry) {
|
|
if (!entry->mFinished && ((HANDLE)wParam == entry->mHandle)) {
|
|
DNS_TRACE(DNS_TRACE_LOOKUPS,
|
|
("DNS lookup: finished '%s'", entry->mHost));
|
|
|
|
// Found a match
|
|
entry->mFinished = PR_TRUE;
|
|
entry->mError = error;
|
|
if (0 != error) {
|
|
rv = 0;
|
|
}
|
|
SocketWaiter** psw = &entry->mSockets;
|
|
SocketWaiter* sw = entry->mSockets;
|
|
while (NULL != sw) {
|
|
// Poke netlib
|
|
int iWantMore = NET_ProcessNet(sw->mSocket, NET_SOCKET_FD);
|
|
*psw = sw->mNext;
|
|
PR_Free(sw);
|
|
sw = *psw;
|
|
}
|
|
#if XXX_DONT_CACHE_DNS_LOOKUPS
|
|
if (NULL == entry->mSockets) {
|
|
// Now that entry is complete, remove it
|
|
*pentry = entry->mNext;
|
|
entry->Destroy();
|
|
PR_Free(entry);
|
|
}
|
|
#endif
|
|
return rv;
|
|
}
|
|
pentry = &entry->mNext;
|
|
entry = entry->mNext;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
#if defined(NO_NETWORK_POLLING)
|
|
if (gMSGAsyncSelect == uMsg) {
|
|
PRFileDesc* fd;
|
|
|
|
/*
|
|
* Enter the Libnet lock for the call to net_GetFileDescFromOSFD(...)
|
|
*/
|
|
LIBNET_LOCK();
|
|
|
|
fd = net_GetFileDescFromOSFD(wParam);
|
|
if (NULL != fd) {
|
|
/*
|
|
* Queue up another asynchronous notification before calling
|
|
* NET_ProcessNet(...). This way, we do not need to konw if the socket
|
|
* is still active after the call (or if it was closed...). If the
|
|
* connection is closed, the asynchronous request will be canceled...
|
|
*/
|
|
net_AsyncSelect(fd,
|
|
FD_READ | FD_WRITE | FD_ACCEPT | FD_CONNECT | FD_CLOSE | FD_OOB);
|
|
|
|
/* Process the connection which has data... */
|
|
NET_ProcessNet(fd, NET_SOCKET_FD);
|
|
}
|
|
|
|
LIBNET_UNLOCK();
|
|
}
|
|
#endif /* NO_NETWORK_POLLING */
|
|
|
|
return DefWindowProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
void net_InitAsyncDNS()
|
|
{
|
|
static char *windowClass = "NETLIB:DNSWindow";
|
|
|
|
#ifdef DEBUG
|
|
gDNSLogModuleInfo = PR_NewLogModule("netlibdns");
|
|
#endif
|
|
|
|
// Allocate a message id for the event handler
|
|
gMSGFoundDNS = RegisterWindowMessage("Mozilla:DNSMessage");
|
|
gMSGAsyncSelect = RegisterWindowMessage("Mozilla:AsyncSelectMessage");
|
|
|
|
// Register the class for the event receiver window
|
|
WNDCLASS wc;
|
|
wc.style = 0;
|
|
wc.lpfnWndProc = EventProc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = NULL;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = (HBRUSH) NULL;
|
|
wc.lpszMenuName = (LPCSTR) NULL;
|
|
wc.lpszClassName = windowClass;
|
|
RegisterClass(&wc);
|
|
|
|
// Create the event receiver window
|
|
gDNSWindow = CreateWindow(windowClass,
|
|
"Mozilla:DNSEventHandler",
|
|
0, 0, 0, 10, 10,
|
|
NULL, NULL, NULL,
|
|
NULL);
|
|
|
|
DNS_TRACE(DNS_TRACE_LOOKUPS,
|
|
("DNS lookup: hidden window=%p msg=%d",
|
|
gDNSWindow, gMSGFoundDNS));
|
|
}
|
|
|
|
extern "C" int
|
|
NET_AsyncDNSLookup(void* aContext,
|
|
const char* aHostPort,
|
|
PRHostEnt** aHoststructPtrPtr,
|
|
PRFileDesc* aSocket);
|
|
|
|
int
|
|
NET_AsyncDNSLookup(void* aContext,
|
|
const char* aHostPort,
|
|
PRHostEnt** aHoststructPtrPtr,
|
|
PRFileDesc* aSocket)
|
|
{
|
|
/* DNS initialization failed... */
|
|
if (NULL == gDNSWindow) {
|
|
PR_ASSERT(0);
|
|
return -1;
|
|
}
|
|
|
|
*aHoststructPtrPtr = NULL;
|
|
|
|
// Look in cache first
|
|
DNSCacheEntry** pentry = DNSCacheEntry::Lookup(aHostPort);
|
|
DNSCacheEntry* entry = *pentry;
|
|
if (NULL != entry) {
|
|
// If lookup returned an error then indicate that to the caller...
|
|
if ((0 != entry->mError) && entry->mFinished) {
|
|
WSASetLastError(entry->mError);
|
|
if (NULL == entry->mSockets) {
|
|
// XXX can't get here, right?
|
|
// Delete entry when last socket is done with it...
|
|
*pentry = entry->mNext;
|
|
entry->Destroy();
|
|
PR_Free(entry);
|
|
}
|
|
DNS_TRACE(DNS_TRACE_LOOKUPS,
|
|
("DNS lookup: '%s' error=%d\n", entry->mHost, entry->mError));
|
|
return -1;
|
|
}
|
|
|
|
// If lookup finished then return the answer
|
|
if ((NULL != entry->mHostEnt->h_name) && entry->mFinished) {
|
|
DNS_TRACE(DNS_TRACE_LOOKUPS,
|
|
("DNS lookup: '%s' finished\n", entry->mHost));
|
|
*aHoststructPtrPtr = entry->mHostEnt;
|
|
return 0;
|
|
}
|
|
|
|
// See if we are already waiting for the answer
|
|
SocketWaiter* sw = entry->mSockets;
|
|
while (NULL != sw) {
|
|
if (sw->mSocket == aSocket) {
|
|
return MK_WAITING_FOR_LOOKUP;
|
|
}
|
|
sw = sw->mNext;
|
|
}
|
|
|
|
// Add this socket to the wait list
|
|
sw = (SocketWaiter*) PR_Malloc(sizeof(SocketWaiter));
|
|
if (NULL == sw) {
|
|
return -1;
|
|
}
|
|
sw->Init(aSocket);
|
|
sw->mSocket = aSocket;
|
|
sw->mNext = entry->mSockets;
|
|
entry->mSockets = sw;
|
|
DNS_TRACE(DNS_TRACE_LOOKUPS,
|
|
("DNS lookup: still waiting for host='%s'\n", entry->mHost));
|
|
return MK_WAITING_FOR_LOOKUP;
|
|
}
|
|
|
|
// Create a new cache entry for the host name
|
|
entry = (DNSCacheEntry*) PR_Malloc(sizeof(DNSCacheEntry));
|
|
if (NULL == entry) {
|
|
return -1;
|
|
}
|
|
if (NS_OK != entry->Init(aContext, aHostPort, aSocket)) {
|
|
entry->Destroy();
|
|
PR_Free(entry);
|
|
return -1;
|
|
}
|
|
|
|
// Start async lookup on that host name
|
|
HANDLE h = WSAAsyncGetHostByName(gDNSWindow,
|
|
gMSGFoundDNS,
|
|
entry->mHost,
|
|
(char *)entry->mHostEnt,
|
|
PR_NETDB_BUF_SIZE);
|
|
DNS_TRACE(DNS_TRACE_LOOKUPS,
|
|
("DNS lookup: begin waiting for host='%s' handle=%p\n",
|
|
entry->mHost, h));
|
|
if (NULL == h) {
|
|
entry->Destroy();
|
|
PR_Free(entry);
|
|
return -1;
|
|
}
|
|
entry->mHandle = h;
|
|
entry->mNext = *pentry;
|
|
*pentry = entry;
|
|
return MK_WAITING_FOR_LOOKUP;
|
|
}
|
|
|
|
#endif
|