765 lines
21 KiB
C
765 lines
21 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
|
||
*/
|
||
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_inet_ntoa g_inet_ntoa;
|
||
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)
|
||
{
|
||
SOCKADDR_IN * pin = (SOCKADDR_IN *)psockaddr;
|
||
|
||
DPF(nLevel,"%s af = AF_INET : address = %s : port = %d\n",pStr,
|
||
g_inet_ntoa(pin->sin_addr),g_htons(pin->sin_port));
|
||
|
||
} // DebugPrintAddr
|
||
|
||
#undef DPF_MODNAME
|
||
#define DPF_MODNAME "DebugPrintSocket"
|
||
|
||
void DebugPrintSocket(UINT level,LPSTR pStr,SOCKET * pSock)
|
||
{
|
||
SOCKADDR sockaddr;
|
||
int addrlen=sizeof(sockaddr);
|
||
|
||
g_getsockname(*pSock,&sockaddr,&addrlen);
|
||
DEBUGPRINTADDR(level,pStr,&sockaddr);
|
||
|
||
}
|
||
|
||
#endif // debug
|
||
|
||
// this is called every time we add a new server node to our list...
|
||
HRESULT GetDefaultHostAddr(DWORD * puHostAddr)
|
||
{
|
||
|
||
// 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...
|
||
|
||
*puHostAddr = 0x0100007f; // loopback 127.0.0.1 (<bleeping> little-endian)
|
||
|
||
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.sin_family = AF_INET;
|
||
|
||
// find the default ip to use w/ this host
|
||
hr = GetDefaultHostAddr(&(pNode->sockaddr.sin_addr.s_addr));
|
||
if (FAILED(hr))
|
||
{
|
||
DPF_ERR("could not get host IP address");
|
||
MemFree(pNode);
|
||
return (DPERR_UNAVAILABLE);
|
||
}
|
||
|
||
pNode->sockaddr.sin_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.sin_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 IP_SetAddr(LPVOID pmsg,SOCKADDR_IN * paddrSrc)
|
||
{
|
||
LPSOCKADDR_IN paddrDest; // tempo variable, makes casting less ugly
|
||
LPMESSAGEHEADER phead;
|
||
|
||
phead = (LPMESSAGEHEADER)pmsg;
|
||
|
||
paddrDest = (SOCKADDR_IN *)&(phead->sockaddr);
|
||
// poke the new ip addr into the message header
|
||
paddrDest->sin_addr.s_addr = paddrSrc->sin_addr.s_addr;
|
||
|
||
return;
|
||
|
||
} // IP_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_IN * psockaddr)
|
||
{
|
||
LPSPNODE pNode = gNodeList;
|
||
UINT addrlen = sizeof(SOCKADDR_IN);
|
||
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
|
||
IP_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
|
||
|
||
//
|
||
// 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;
|
||
INT addrlen=sizeof(SOCKADDR);
|
||
SOCKADDR sockaddr; // the from address
|
||
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;
|
||
}
|
||
while (1)
|
||
{
|
||
err = g_recvfrom(gsDatagramListener,pBuffer,dwBufSize,0,&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_IN *)&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_inet_ntoa = (cb_inet_ntoa) GetProcAddress(hWinsock, "inet_ntoa");
|
||
if (!g_inet_ntoa)
|
||
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_IN sockaddr;
|
||
UINT err;
|
||
SOCKET sNew;
|
||
|
||
sNew = g_socket( AF_INET, type, 0);
|
||
if (INVALID_SOCKET == sNew)
|
||
{
|
||
goto ERROR_EXIT;
|
||
}
|
||
|
||
// set up the sockaddr to bind to
|
||
sockaddr.sin_family = PF_INET;
|
||
sockaddr.sin_addr.s_addr = INADDR_ANY;
|
||
sockaddr.sin_port = port;
|
||
|
||
// do the bind
|
||
if( SOCKET_ERROR == g_bind( sNew, (LPSOCKADDR)&sockaddr, sizeof(sockaddr) ) )
|
||
{
|
||
goto ERROR_EXIT;
|
||
}
|
||
|
||
if (bBroadcast)
|
||
{
|
||
BOOL bTrue = TRUE;
|
||
|
||
if( SOCKET_ERROR == g_setsockopt( sNew,SOL_SOCKET,SO_BROADCAST,(char FAR *)&bTrue,
|
||
sizeof(bTrue) ) )
|
||
{
|
||
err = g_WSAGetLastError();
|
||
DPF(0," dphelp - create - could not set broadcast err = %d\n",err);
|
||
// not really tragic, since for AF_INET it's not required to set broadcast
|
||
// b4 receiving w/ MS winsock...
|
||
}
|
||
}
|
||
|
||
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
|
||
|
||
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
|
||
}
|
||
|
||
// 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
|
||
|
||
|
||
|