802 lines
22 KiB
C
802 lines
22 KiB
C
|
/*==========================================================================
|
||
|
*
|
||
|
* Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved.
|
||
|
*
|
||
|
* File: dphelp.c
|
||
|
* Content: allows the dplay winsock sp's to all share a single
|
||
|
* server socket
|
||
|
* History:
|
||
|
* Date By Reason
|
||
|
* ==== == ======
|
||
|
* 18-jul-96 andyco initial implementation
|
||
|
* 25-jul-96 andyco ddhelp now watches dplay procs so it can remove
|
||
|
* them from our list when they go away
|
||
|
* 3-sep-96 andyco don't get stale ip's - pick up a default ip whenever
|
||
|
* we add a servernode. bug 3716.
|
||
|
* 2-oct-96 andyco propagated from \orange\ddhelp.2 to \mustard\ddhelp
|
||
|
* 3-oct-96 andyco made the winmain crit section "cs" a global so we can take
|
||
|
* it in dphelps receive thread before forwarding requests
|
||
|
* 21-jan-97 kipo use LoadLibrary on "wsock32.dll" instead of statically
|
||
|
* linking to it so DDHELP will still run even when Winsock
|
||
|
* is not around. This lets DDRAW and DSOUND work. Fixes
|
||
|
* bug #68596.
|
||
|
* 15-feb-97 andyco moved from ddhelp to the project formerly known as
|
||
|
* ddhelp (playhelp? dplayhlp? dplay.exe? dphost?) Allowed
|
||
|
* one process to host mulitple sessions
|
||
|
* 29-jan-98 sohailm added support for stream enum sessions
|
||
|
*
|
||
|
***************************************************************************/
|
||
|
/*============================================================================
|
||
|
*
|
||
|
* Why this file exists :
|
||
|
*
|
||
|
* when you want to find a dplay game, you send a message to a well
|
||
|
* known port (an enumrequest).
|
||
|
*
|
||
|
* if a game is being hosted on that system, it will listen on that
|
||
|
* port, and respond to the message.
|
||
|
*
|
||
|
* BUT, only one process can listen on a given socket.
|
||
|
*
|
||
|
* So, we let ddhelp.exe listen on that socket, and forward enumrequests
|
||
|
* to all games registered as being hosted on this system.
|
||
|
*
|
||
|
* see also : \%MANROOT%\dplay\wsock\dpsp.h
|
||
|
*
|
||
|
*****************************************************************************/
|
||
|
|
||
|
// todo - should we return error codes on AddServer xproc to our caller?
|
||
|
|
||
|
#include "dphelp.h"
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DPHELP"
|
||
|
|
||
|
/*
|
||
|
* GLOBALS
|
||
|
*/
|
||
|
const IN6_ADDR in6addr_multicast = IN6ADDR_MULTICAST_INIT;
|
||
|
SOCKET gsDatagramListener = INVALID_SOCKET; // we listen for datagrams on this socket
|
||
|
SOCKET gsForwardSocket = INVALID_SOCKET;
|
||
|
SOCKET gsStreamListener; // we listen for tcp connections on this socket
|
||
|
LPSPNODE gNodeList;
|
||
|
BOOL gbInit;
|
||
|
HANDLE ghDatagramReceiveThread,ghStreamReceiveThread;
|
||
|
BOOL gbReceiveShutdown; // receive thread will exit when TRUE
|
||
|
|
||
|
// pointers to Winsock routines returned from GetProcAddress
|
||
|
cb_accept g_accept;
|
||
|
cb_bind g_bind;
|
||
|
cb_closesocket g_closesocket;
|
||
|
cb_gethostbyname g_gethostbyname;
|
||
|
cb_gethostname g_gethostname;
|
||
|
cb_getpeername g_getpeername;
|
||
|
cb_getsockname g_getsockname;
|
||
|
cb_recvfrom g_recvfrom;
|
||
|
cb_recv g_recv;
|
||
|
cb_select g_select;
|
||
|
cb_send g_send;
|
||
|
cb_sendto g_sendto;
|
||
|
cb_setsockopt g_setsockopt;
|
||
|
cb_shutdown g_shutdown;
|
||
|
cb_socket g_socket;
|
||
|
cb_WSAFDIsSet g_WSAFDIsSet;
|
||
|
cb_WSAGetLastError g_WSAGetLastError;
|
||
|
cb_WSAStartup g_WSAStartup;
|
||
|
cb_listen g_listen;
|
||
|
cb_htons g_htons;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DebugPrintAddr"
|
||
|
|
||
|
// helper function called from DEBUGPRINTADDR macro
|
||
|
void DebugPrintAddr(UINT nLevel,LPSTR pStr,SOCKADDR * psockaddr)
|
||
|
{
|
||
|
char buff[INET6_ADDRSTRLEN];
|
||
|
int ret;
|
||
|
LPSOCKADDR_IN6 pin6 = (LPSOCKADDR_IN6)psockaddr;
|
||
|
ULONG ulLength = INET6_ADDRSTRLEN;
|
||
|
|
||
|
ret = WSAAddressToString(psockaddr, sizeof(SOCKADDR_IN6), NULL,
|
||
|
buff, &ulLength);
|
||
|
|
||
|
if (!ret)
|
||
|
DPF(nLevel,"%s af = AF_INET6 : address = %s\n",pStr,buff);
|
||
|
|
||
|
} // DebugPrintAddr
|
||
|
|
||
|
#undef DPF_MODNAME
|
||
|
#define DPF_MODNAME "DebugPrintSocket"
|
||
|
|
||
|
void DebugPrintSocket(UINT level,LPSTR pStr,SOCKET * pSock)
|
||
|
{
|
||
|
SOCKADDR_IN6 sockaddr;
|
||
|
int addrlen=sizeof(sockaddr);
|
||
|
|
||
|
g_getsockname(*pSock,(LPSOCKADDR)&sockaddr,&addrlen);
|
||
|
DEBUGPRINTADDR(level,pStr,&sockaddr);
|
||
|
|
||
|
}
|
||
|
|
||
|
#endif // debug
|
||
|
|
||
|
// this is called every time we add a new server node to our list...
|
||
|
HRESULT GetDefaultHostAddr(SOCKADDR_IN6 * psockaddr)
|
||
|
{
|
||
|
|
||
|
// a-josbor: we used to get the first interface and use that, but WebTV taught
|
||
|
// us that that can be dangerous. So we just use the loopback address.
|
||
|
// It's guaranteed to be there. Or so they say...
|
||
|
|
||
|
ZeroMemory(psockaddr, sizeof(SOCKADDR_IN6));
|
||
|
psockaddr->sin6_family = AF_INET6;
|
||
|
psockaddr->sin6_addr = in6addr_loopback;
|
||
|
|
||
|
return DP_OK;
|
||
|
|
||
|
} // GetDefaultHostAddr
|
||
|
|
||
|
// the functions DPlayHelp_xxx are called from dphelp.c
|
||
|
|
||
|
//
|
||
|
// add a new node to our list of servers which want to have enum
|
||
|
// requests forwarded to them...
|
||
|
HRESULT DPlayHelp_AddServer(LPDPHELPDATA phd)
|
||
|
{
|
||
|
LPSPNODE pNode;
|
||
|
BOOL bFoundIt=FALSE;
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (!gbInit)
|
||
|
{
|
||
|
hr = DPlayHelp_Init();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("dphelp : could not init wsock ! not adding server");
|
||
|
return (hr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// see if we're already watching this process
|
||
|
// if we are, we won't start a watcher thread (below)
|
||
|
pNode = gNodeList;
|
||
|
|
||
|
// search the list
|
||
|
while (pNode && !bFoundIt)
|
||
|
{
|
||
|
if (pNode->pid == phd->pid) bFoundIt = TRUE;
|
||
|
pNode = pNode->pNextNode;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// now, build a new server node
|
||
|
pNode = MemAlloc(sizeof(SPNODE));
|
||
|
if (!pNode)
|
||
|
{
|
||
|
DPF_ERR("could not add new server node OUT OF MEMORY");
|
||
|
return (DPERR_OUTOFMEMORY);
|
||
|
}
|
||
|
|
||
|
pNode->pid = phd->pid;
|
||
|
// build the sockaddr
|
||
|
// dwReserved1 of the phd is the port that the server is listening on
|
||
|
pNode->sockaddr.sin6_family = AF_INET6;
|
||
|
|
||
|
// find the default ip to use w/ this host
|
||
|
hr = GetDefaultHostAddr(&(pNode->sockaddr));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
DPF_ERR("could not get host IP address");
|
||
|
MemFree(pNode);
|
||
|
return (DPERR_UNAVAILABLE);
|
||
|
}
|
||
|
|
||
|
pNode->sockaddr.sin6_port = phd->port;
|
||
|
|
||
|
DPF(5,"dphelp :: adding new server node : pid = %d, port = %d\n",phd->pid,g_htons(phd->port));
|
||
|
|
||
|
// link our new node onto the beginning of the list
|
||
|
pNode->pNextNode = gNodeList;
|
||
|
gNodeList = pNode;
|
||
|
|
||
|
// see if we need to start our watcher thread
|
||
|
if (!bFoundIt)
|
||
|
{
|
||
|
//
|
||
|
// set up a thread to keep on eye on this process.
|
||
|
// we'll let the thread notify us when the process goes away
|
||
|
WatchNewPid(phd);
|
||
|
}
|
||
|
|
||
|
return (DP_OK);
|
||
|
|
||
|
} // DPlayHelp_AddServer
|
||
|
|
||
|
//
|
||
|
// delete the server node from proc pid from our list
|
||
|
// called by "ThreadProc" from DPHELP.c when the process that
|
||
|
// goes away, or from the client side when a session goes away.
|
||
|
//
|
||
|
// if bFreeAll is TRUE, we delete all server nodes for process
|
||
|
// phd->pid. otherwise, we just delete the first server node whose
|
||
|
// port matches phd->port
|
||
|
//
|
||
|
BOOL FAR PASCAL DPlayHelp_DeleteServer(LPDPHELPDATA phd,BOOL bFreeAll)
|
||
|
{
|
||
|
BOOL bFoundIt = FALSE;
|
||
|
LPSPNODE pNode,pNodePrev,pNodeNext;
|
||
|
|
||
|
pNode = gNodeList;
|
||
|
pNodePrev = NULL;
|
||
|
pNodeNext = NULL;
|
||
|
|
||
|
// search the whole list
|
||
|
while (pNode && !bFoundIt)
|
||
|
{
|
||
|
// if we have the right pid, and it's either FreeAll or the right port - cruise it!
|
||
|
if ((pNode->pid == phd->pid) && (bFreeAll || (pNode->sockaddr.sin6_port == phd->port)) )
|
||
|
{
|
||
|
// remove it from the list
|
||
|
if (pNodePrev) pNodePrev->pNextNode = pNode->pNextNode;
|
||
|
else gNodeList = pNode->pNextNode;
|
||
|
|
||
|
if (bFreeAll)
|
||
|
{
|
||
|
// pick up the next one b4 we free pNode
|
||
|
pNodeNext = pNode->pNextNode;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// mark us as done
|
||
|
bFoundIt = TRUE;
|
||
|
pNodeNext = NULL;
|
||
|
}
|
||
|
|
||
|
DPF(5,"dphelp :: deleting server node : pid = %d\n",pNode->pid);
|
||
|
// free up the node
|
||
|
MemFree(pNode);
|
||
|
|
||
|
pNode = pNodeNext;
|
||
|
// pNodePrev doesn't change here...
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// just get the next one
|
||
|
pNodePrev = pNode;
|
||
|
pNode = pNode->pNextNode;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
return FALSE;
|
||
|
|
||
|
} // DPlayHelp_DeleteServer
|
||
|
|
||
|
//
|
||
|
// poke an ip addr into a message blob
|
||
|
// code stolen from \orange\dplay\wsock\winsock.c
|
||
|
void IP6_SetAddr(LPVOID pmsg,SOCKADDR_IN6 * paddrSrc)
|
||
|
{
|
||
|
LPSOCKADDR_IN6 paddrDest; // tempo variable, makes casting less ugly
|
||
|
LPMESSAGEHEADER phead;
|
||
|
|
||
|
phead = (LPMESSAGEHEADER)pmsg;
|
||
|
|
||
|
paddrDest = (SOCKADDR_IN6 *)&(phead->sockaddr);
|
||
|
// poke the new ip addr into the message header
|
||
|
paddrDest->sin6_addr = paddrSrc->sin6_addr;
|
||
|
|
||
|
return;
|
||
|
|
||
|
} // IP6_SetAddr
|
||
|
|
||
|
//
|
||
|
// we get a message. presumably its an enumrequest. forward it to all registered clients.
|
||
|
// we "home" the message (store the received ip addr w/ it) here, 'cause otherwise the clients
|
||
|
// would all think it came from us. we change the token to srvr_token so the clients know it
|
||
|
// came from us (so they don't home it again)
|
||
|
void HandleIncomingMessage(LPBYTE pBuffer,DWORD dwBufferSize,SOCKADDR_IN6 * psockaddr)
|
||
|
{
|
||
|
LPSPNODE pNode = gNodeList;
|
||
|
UINT addrlen = sizeof(SOCKADDR_IN6);
|
||
|
UINT err;
|
||
|
|
||
|
ASSERT(VALID_SP_MESSAGE(pBuffer));
|
||
|
|
||
|
// reset the old token
|
||
|
*( (DWORD *)pBuffer) &= ~TOKEN_MASK;
|
||
|
// set the new token
|
||
|
*( (DWORD *)pBuffer) |= HELPER_TOKEN;
|
||
|
|
||
|
// home it
|
||
|
IP6_SetAddr((LPVOID)pBuffer,psockaddr);
|
||
|
|
||
|
// now, forward the message to all registered servers
|
||
|
while (pNode)
|
||
|
{
|
||
|
DEBUGPRINTADDR(7,"dplay helper :: forwarding enum request to",(SOCKADDR *)&(pNode->sockaddr));
|
||
|
// send out the enum message
|
||
|
err = g_sendto(gsForwardSocket,pBuffer,dwBufferSize,0,(LPSOCKADDR)&(pNode->sockaddr),
|
||
|
addrlen);
|
||
|
if (SOCKET_ERROR == err)
|
||
|
{
|
||
|
err = g_WSAGetLastError();
|
||
|
DPF(0,"dphelp : send failed err = %d\n",err);
|
||
|
}
|
||
|
|
||
|
pNode = pNode->pNextNode;
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
|
||
|
} // HandleIncomingMessage
|
||
|
|
||
|
#if 1
|
||
|
|
||
|
void JoinEnumGroups(SOCKET s)
|
||
|
{
|
||
|
SOCKET_ADDRESS_LIST *pList;
|
||
|
int i;
|
||
|
LPSOCKADDR_IN6 paddr;
|
||
|
HRESULT hr;
|
||
|
|
||
|
//
|
||
|
// join link-local multicast group for enumeration on every link
|
||
|
//
|
||
|
|
||
|
// do a passive getaddrinfo
|
||
|
pList = GetHostAddr();
|
||
|
if (pList)
|
||
|
{
|
||
|
// for each linklocal address
|
||
|
for (i=0; i<pList->iAddressCount; i++)
|
||
|
{
|
||
|
paddr = (LPSOCKADDR_IN6)pList->Address[i].lpSockaddr;
|
||
|
|
||
|
// skip if not linklocal
|
||
|
if (!IN6_IS_ADDR_LINKLOCAL(&paddr->sin6_addr))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// join the multicast group on that ifindex
|
||
|
if (SOCKET_ERROR == JoinEnumGroup(s, paddr->sin6_scope_id))
|
||
|
{
|
||
|
DPF(0,"join enum group failed - err = %d\n",WSAGetLastError());
|
||
|
closesocket(s);
|
||
|
}
|
||
|
}
|
||
|
FreeHostAddr(pList);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// BUF_SIZE is our initial guess at a receive buffer size
|
||
|
// if we get an enum request bigger than this, we'll realloc our
|
||
|
// buffer, and receive successfully if they send again
|
||
|
// (the only way this could happen is if they have password > ~ 1000
|
||
|
// bytes).
|
||
|
#define BUF_SIZE 1024
|
||
|
|
||
|
//
|
||
|
// listen on our socket for enum requests
|
||
|
DWORD WINAPI ListenThreadProc(LPVOID pvUnused)
|
||
|
{
|
||
|
UINT err;
|
||
|
LPBYTE pBuffer=NULL;
|
||
|
SOCKADDR_IN6 sockaddr; // the from address
|
||
|
INT addrlen=sizeof(sockaddr);
|
||
|
DWORD dwBufSize = BUF_SIZE;
|
||
|
|
||
|
DPF(2,"dphelp :: starting udp listen thread ");
|
||
|
|
||
|
pBuffer = MemAlloc(BUF_SIZE);
|
||
|
if (!pBuffer)
|
||
|
{
|
||
|
DPF_ERR("could not alloc dgram receive buffer");
|
||
|
ExitThread(0);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
JoinEnumGroups(gsDatagramListener);
|
||
|
|
||
|
while (1)
|
||
|
{
|
||
|
err = g_recvfrom(gsDatagramListener,pBuffer,dwBufSize,0,(LPSOCKADDR)&sockaddr,&addrlen);
|
||
|
if (SOCKET_ERROR == err)
|
||
|
{
|
||
|
err = g_WSAGetLastError();
|
||
|
if (WSAEMSGSIZE == err)
|
||
|
{
|
||
|
LPBYTE pNewBuffer;
|
||
|
|
||
|
// buffer too small!
|
||
|
dwBufSize *= 2;
|
||
|
|
||
|
DPF(9,"\n udp recv thread - resizing buffer newsize = %d\n",dwBufSize);
|
||
|
pNewBuffer = MemReAlloc(pBuffer,dwBufSize);
|
||
|
if (!pNewBuffer)
|
||
|
{
|
||
|
DPF_ERR("could not realloc dgram receive buffer");
|
||
|
goto ERROR_EXIT;
|
||
|
}
|
||
|
pBuffer = pNewBuffer;
|
||
|
// we can't do anything with this message, since it was truncated...
|
||
|
} // WSAEMSGSIZE
|
||
|
else
|
||
|
{
|
||
|
#ifdef DEBUG
|
||
|
if (WSAEINTR != err)
|
||
|
{
|
||
|
// WSAEINTR is what winsock uses to break a blocking socket out of
|
||
|
// its wait. it means someone killed this socket.
|
||
|
// if it's not that, then it's a real error.
|
||
|
DPF(0,"\n udp recv error - err = %d socket = %d",err,(DWORD)gsDatagramListener);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DPF(9,"\n udp recv error - err = %d socket = %d",err,(DWORD)gsDatagramListener);
|
||
|
}
|
||
|
#endif // DEBUG
|
||
|
|
||
|
// we bail on errors other than WSAEMSGSIZE
|
||
|
goto ERROR_EXIT;
|
||
|
}
|
||
|
} // SOCKET_ERROR
|
||
|
else if ((err >= sizeof(DWORD)) && VALID_SP_MESSAGE(pBuffer))
|
||
|
{
|
||
|
// now, if we succeeded, err is the # of bytes read
|
||
|
DEBUGPRINTADDR(9,"dplay helper :: received enum request from ",(SOCKADDR *)&sockaddr);
|
||
|
// take the dplay lock so no one messes w/ our list of registered serves while we're
|
||
|
// trying to send to them...
|
||
|
ENTER_DPLAYSVR();
|
||
|
|
||
|
HandleIncomingMessage(pBuffer,err,(SOCKADDR_IN6 *)&sockaddr);
|
||
|
|
||
|
// give up the lock
|
||
|
LEAVE_DPLAYSVR();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ASSERT(FALSE);
|
||
|
// ?
|
||
|
}
|
||
|
} // 1
|
||
|
|
||
|
ERROR_EXIT:
|
||
|
DPF(2,"UDP Listen thread exiting");
|
||
|
if (pBuffer) MemFree(pBuffer);
|
||
|
// all done
|
||
|
ExitThread(0);
|
||
|
return 0;
|
||
|
|
||
|
} // UDPListenThreadProc
|
||
|
|
||
|
// startup winsock and find the default ip addr for this machine
|
||
|
HRESULT StartupIP()
|
||
|
{
|
||
|
UINT err;
|
||
|
WSADATA wsaData;
|
||
|
HINSTANCE hWinsock;
|
||
|
|
||
|
// load winsock library
|
||
|
hWinsock = LoadLibrary("wsock32.dll");
|
||
|
if (!hWinsock)
|
||
|
{
|
||
|
DPF(0,"Could not load wsock32.dll\n");
|
||
|
goto LOADLIBRARYFAILED;
|
||
|
}
|
||
|
|
||
|
// get pointers to the entry points we need
|
||
|
|
||
|
g_accept = (cb_accept) GetProcAddress(hWinsock, "accept");
|
||
|
if (!g_accept)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_bind = (cb_bind) GetProcAddress(hWinsock, "bind");
|
||
|
if (!g_bind)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_closesocket = (cb_closesocket) GetProcAddress(hWinsock, "closesocket");
|
||
|
if (!g_closesocket)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_gethostbyname = (cb_gethostbyname) GetProcAddress(hWinsock, "gethostbyname");
|
||
|
if (!g_gethostbyname)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_gethostname = (cb_gethostname) GetProcAddress(hWinsock, "gethostname");
|
||
|
if (!g_gethostname)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_getpeername = (cb_getpeername) GetProcAddress(hWinsock, "getpeername");
|
||
|
if (!g_getpeername)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_getsockname = (cb_getsockname) GetProcAddress(hWinsock, "getsockname");
|
||
|
if (!g_getsockname)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_htons = (cb_htons) GetProcAddress(hWinsock, "htons");
|
||
|
if (!g_htons)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_listen = (cb_listen) GetProcAddress(hWinsock, "listen");
|
||
|
if (!g_listen)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_recv = (cb_recv) GetProcAddress(hWinsock, "recv");
|
||
|
if (!g_recv)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_recvfrom = (cb_recvfrom) GetProcAddress(hWinsock, "recvfrom");
|
||
|
if (!g_recvfrom)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_select = (cb_select) GetProcAddress(hWinsock, "select");
|
||
|
if (!g_select)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_send = (cb_send) GetProcAddress(hWinsock, "send");
|
||
|
if (!g_send)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_sendto = (cb_sendto) GetProcAddress(hWinsock, "sendto");
|
||
|
if (!g_sendto)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_setsockopt = (cb_setsockopt) GetProcAddress(hWinsock, "setsockopt");
|
||
|
if (!g_setsockopt)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_shutdown = (cb_shutdown) GetProcAddress(hWinsock, "shutdown");
|
||
|
if (!g_shutdown)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_socket = (cb_socket) GetProcAddress(hWinsock, "socket");
|
||
|
if (!g_socket)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_WSAFDIsSet = (cb_WSAFDIsSet) GetProcAddress(hWinsock, "__WSAFDIsSet");
|
||
|
if (!g_WSAFDIsSet)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_WSAGetLastError = (cb_WSAGetLastError) GetProcAddress(hWinsock, "WSAGetLastError");
|
||
|
if (!g_WSAGetLastError)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
g_WSAStartup = (cb_WSAStartup) GetProcAddress(hWinsock, "WSAStartup");
|
||
|
if (!g_WSAStartup)
|
||
|
goto GETPROCADDRESSFAILED;
|
||
|
|
||
|
// start up sockets, asking for version 1.1
|
||
|
err = g_WSAStartup(MAKEWORD(1,1), &wsaData);
|
||
|
if (err)
|
||
|
{
|
||
|
DPF(0,"dphelp :: could not start winsock err = %d\n",err);
|
||
|
goto WSASTARTUPFAILED;
|
||
|
}
|
||
|
DPF(3,"dphelp :: started up winsock succesfully");
|
||
|
|
||
|
return DP_OK;
|
||
|
|
||
|
GETPROCADDRESSFAILED:
|
||
|
DPF(0,"Could not find required Winsock entry point");
|
||
|
WSASTARTUPFAILED:
|
||
|
FreeLibrary(hWinsock);
|
||
|
LOADLIBRARYFAILED:
|
||
|
return DPERR_UNAVAILABLE;
|
||
|
} // StartupIP
|
||
|
|
||
|
// helper function to create the socket we listen on
|
||
|
HRESULT GetSocket(SOCKET * psock,DWORD type,PORT port,BOOL bBroadcast,BOOL bListen)
|
||
|
{
|
||
|
SOCKADDR_IN6 sockaddr;
|
||
|
UINT err;
|
||
|
SOCKET sNew;
|
||
|
|
||
|
sNew = g_socket( AF_INET6, type, 0);
|
||
|
if (INVALID_SOCKET == sNew)
|
||
|
{
|
||
|
goto ERROR_EXIT;
|
||
|
}
|
||
|
|
||
|
// set up the sockaddr to bind to
|
||
|
ZeroMemory(&sockaddr, sizeof(sockaddr));
|
||
|
sockaddr.sin6_family = PF_INET6;
|
||
|
sockaddr.sin6_port = port;
|
||
|
|
||
|
// do the bind
|
||
|
if( SOCKET_ERROR == g_bind( sNew, (LPSOCKADDR)&sockaddr, sizeof(sockaddr) ) )
|
||
|
{
|
||
|
goto ERROR_EXIT;
|
||
|
}
|
||
|
|
||
|
if (bListen)
|
||
|
{
|
||
|
LINGER Linger;
|
||
|
|
||
|
// set up socket w/ max listening connections
|
||
|
err = g_listen(sNew,LISTEN_BACKLOG);
|
||
|
if (SOCKET_ERROR == err)
|
||
|
{
|
||
|
err = g_WSAGetLastError();
|
||
|
DPF(0,"init listen socket / listen error - err = %d\n",err);
|
||
|
goto ERROR_EXIT;
|
||
|
}
|
||
|
|
||
|
// set for hard disconnect
|
||
|
Linger.l_onoff=1;
|
||
|
Linger.l_linger=0;
|
||
|
|
||
|
if( SOCKET_ERROR == g_setsockopt( sNew,SOL_SOCKET,SO_LINGER,
|
||
|
(char FAR *)&Linger,sizeof(Linger) ) )
|
||
|
{
|
||
|
err = g_WSAGetLastError();
|
||
|
DPF(0,"Failed to set linger option on the socket = %d\n",err);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// success!
|
||
|
*psock = sNew;
|
||
|
return DP_OK;
|
||
|
|
||
|
ERROR_EXIT:
|
||
|
// clean up and bail
|
||
|
err = g_WSAGetLastError();
|
||
|
DPF(0,"dphelp - could not get helper socket :: err = %d\n",err);
|
||
|
if (INVALID_SOCKET != sNew)
|
||
|
{
|
||
|
g_closesocket(sNew);
|
||
|
}
|
||
|
return E_FAIL;
|
||
|
|
||
|
} // GetSocket
|
||
|
|
||
|
void CloseSocket(SOCKET * psSocket)
|
||
|
{
|
||
|
UINT err;
|
||
|
|
||
|
if (INVALID_SOCKET != *psSocket)
|
||
|
{
|
||
|
if (SOCKET_ERROR == g_closesocket(*psSocket))
|
||
|
{
|
||
|
err = g_WSAGetLastError();
|
||
|
DPF(1,"dphelp : killsocket - socket close err = %d\n",err);
|
||
|
}
|
||
|
|
||
|
*psSocket = INVALID_SOCKET;
|
||
|
}
|
||
|
|
||
|
return ;
|
||
|
|
||
|
} // CloseSocket
|
||
|
|
||
|
extern int
|
||
|
InitIPv6Library(void);
|
||
|
|
||
|
HRESULT DPlayHelp_Init()
|
||
|
{
|
||
|
DWORD dwThreadID;
|
||
|
HRESULT hr;
|
||
|
|
||
|
// start winsock, and get the default ip addr for this system
|
||
|
hr = StartupIP();
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
return hr; // StartupIP will have printed an error
|
||
|
}
|
||
|
|
||
|
InitIPv6Library();
|
||
|
|
||
|
// get the listen socket
|
||
|
hr = GetSocket(&gsDatagramListener,SOCK_DGRAM,SERVER_DGRAM_PORT,TRUE,FALSE);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
goto ERROR_EXIT; // GetSocket will have printed an error
|
||
|
}
|
||
|
|
||
|
// get the forward socket
|
||
|
hr = GetSocket(&gsForwardSocket,SOCK_DGRAM,0,FALSE,FALSE);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
goto ERROR_EXIT; // GetSocket will have printed an error
|
||
|
}
|
||
|
|
||
|
// get us a enum sessions stream listener
|
||
|
hr = GetSocket(&gsStreamListener,SOCK_STREAM,SERVER_STREAM_PORT,FALSE,TRUE);
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
goto ERROR_EXIT; // GetSocket will have printed an error
|
||
|
}
|
||
|
|
||
|
|
||
|
ghDatagramReceiveThread = CreateThread(NULL,0,ListenThreadProc,NULL,0,&dwThreadID);
|
||
|
if (!ghDatagramReceiveThread)
|
||
|
{
|
||
|
DPF_ERR("could not create udp listen thread");
|
||
|
hr = E_FAIL;
|
||
|
goto ERROR_EXIT; // GetSocket will have printed an error
|
||
|
}
|
||
|
|
||
|
ghStreamReceiveThread = CreateThread(NULL,0,StreamReceiveThreadProc,NULL,0,&dwThreadID);
|
||
|
if (!ghStreamReceiveThread)
|
||
|
{
|
||
|
DPF_ERR("could not create tcp listen thread");
|
||
|
hr = E_FAIL;
|
||
|
goto ERROR_EXIT; // GetSocket will have printed an error
|
||
|
}
|
||
|
|
||
|
|
||
|
DPF(5,"DPLAYHELP : init succeeded");
|
||
|
gbInit = TRUE;
|
||
|
return DP_OK;
|
||
|
|
||
|
ERROR_EXIT:
|
||
|
CloseSocket(&gsDatagramListener);
|
||
|
CloseSocket(&gsForwardSocket);
|
||
|
CloseSocket(&gsStreamListener);
|
||
|
|
||
|
return hr;
|
||
|
|
||
|
} // DPlayHelp_Init
|
||
|
|
||
|
void DPlayHelp_FreeServerList()
|
||
|
{
|
||
|
LPSPNODE pNodeKill,pNodeNext;
|
||
|
|
||
|
pNodeNext = gNodeList;
|
||
|
|
||
|
// search the whole list
|
||
|
while (pNodeNext)
|
||
|
{
|
||
|
// kill this node
|
||
|
pNodeKill = pNodeNext;
|
||
|
// but first, remember what's next
|
||
|
pNodeNext = pNodeKill->pNextNode;
|
||
|
// free up the node
|
||
|
MemFree(pNodeKill);
|
||
|
}
|
||
|
|
||
|
CloseSocket(&gsDatagramListener);
|
||
|
CloseSocket(&gsForwardSocket);
|
||
|
|
||
|
// close stream receive
|
||
|
RemoveSocketFromList(gsStreamListener);
|
||
|
gbReceiveShutdown = TRUE;
|
||
|
|
||
|
// drop the lock so the threads can exit - they might be waiting on
|
||
|
// the lock for cleanup
|
||
|
LEAVE_DPLAYSVR();
|
||
|
|
||
|
// wait for the threads to go away
|
||
|
if (ghDatagramReceiveThread)
|
||
|
WaitForSingleObject(ghDatagramReceiveThread, INFINITE);
|
||
|
if (ghStreamReceiveThread)
|
||
|
WaitForSingleObject(ghStreamReceiveThread, INFINITE);
|
||
|
|
||
|
ENTER_DPLAYSVR();
|
||
|
|
||
|
if (ghDatagramReceiveThread)
|
||
|
{
|
||
|
DPF(5,"datagram receive thread exited!");
|
||
|
CloseHandle(ghDatagramReceiveThread);
|
||
|
ghDatagramReceiveThread = NULL;
|
||
|
}
|
||
|
if (ghStreamReceiveThread)
|
||
|
{
|
||
|
DPF(5,"stream receive thread exited!");
|
||
|
CloseHandle(ghStreamReceiveThread);
|
||
|
ghStreamReceiveThread = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
return ;
|
||
|
|
||
|
} // DPlayHelp_FreeServerList
|
||
|
|
||
|
|