/*========================================================================== * * Copyright (C) 1995-1997 Microsoft Corporation. All Rights Reserved. * * File: dplaysvr.c * Content: dplay winsock shared .exe - allows multiple apps to share * a single winsock port * History: * Date By Reason * ==== == ====== * 2/10/97 andyco created it from ddhelp * 29-jan-98 sohailm added support for stream enum sessions * 1/12/2000 aarono added support for rsip * 11/29/2000 aarono B#228292: prefix bug initialize path in WinMain() * ***************************************************************************/ #ifdef WINNT #ifdef DBG #undef DEBUG #define DEBUG #endif #endif #include #include "dplaysvr.h" #include "newdpf.h" #include "memalloc.h" #include "dphelp.h" #if USE_RSIP #include "rsip.h" #elif USE_NATHELP #include "dpnathlp.h" #endif #if USE_RSIP BOOL bRsip; #endif #if USE_NATHELP extern BOOL natGetCapsUpdate(VOID); extern BOOL natInit(VOID); extern VOID natFini(VOID); extern HRESULT natRegisterUDPPort(DWORD port); extern IDirectPlayNATHelp *g_pINatHelp; #endif HANDLE hInstApp; BOOL bNoCallbacks; CRITICAL_SECTION gcsCritSection; // the crit section we take in winmain // this is a global so dphelp can take it before // forwarding enum requests that come in on its // receive thread (manbugs 3907) int gnCSCount; // dplaysvr lock count /* * Externs */ extern RECEIVELIST gReceiveList; extern FDS gReadfds; // we watch every dplay process so when it exits we // make sure it cleaned up... typedef struct _PROCESSDATA { struct _PROCESSDATA *link; DWORD pid; } PROCESSDATA, *LPPROCESSDATA; LPPROCESSDATA lpProcessList; // list of all processes that are registered // with us //********************************************************************** // Globals //********************************************************************** BOOL g_fDaclInited = FALSE; SECURITY_ATTRIBUTES g_sa; BYTE g_abSD[SECURITY_DESCRIPTOR_MIN_LENGTH]; PSECURITY_ATTRIBUTES g_psa = NULL; PACL g_pEveryoneACL = NULL; /* * ThreadProc * * Open a process and wait for it to terminate */ DWORD WINAPI ThreadProc( LPVOID *pdata ) { HANDLE hproc; DWORD rc; LPPROCESSDATA ppd; LPPROCESSDATA curr; LPPROCESSDATA prev; PROCESSDATA pd; DPHELPDATA hd; ppd = (LPPROCESSDATA) pdata; /* * get a handle to the process that attached to DDRAW */ DPF( 2, "Watchdog thread started for pid %08lx", ppd->pid ); hproc = OpenProcess( PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, ppd->pid ); if( hproc == NULL ) { DPF( 1, "OpenProcess for %08lx failed!", ppd->pid ); ExitThread( 0 ); } /* * wait for process to die */ rc = WaitForSingleObject( hproc, INFINITE ); if( rc == WAIT_FAILED ) { DPF( 1, "Wait for process %08lx failed", ppd->pid ); CloseHandle( hproc ); ExitThread( 0 ); } /* * remove process from the list of watched processes */ ENTER_DPLAYSVR(); pd = *ppd; curr = lpProcessList; prev = NULL; while( curr != NULL ) { if( curr == ppd ) { if( prev == NULL ) { lpProcessList = curr->link; } else { prev->link = curr->link; } DPF( 2, "PID %08lx removed from list", ppd->pid ); MemFree( curr ); break; } prev = curr; curr = curr->link; } if( bNoCallbacks ) { DPF( 1, "No callbacks allowed: leaving thread early" ); LEAVE_DPLAYSVR(); CloseHandle( hproc ); ExitThread( 0 ); } // clean up! memset(&hd,0,sizeof(hd)); hd.pid = pd.pid; DPlayHelp_DeleteServer(&hd,TRUE); LEAVE_DPLAYSVR(); CloseHandle( hproc ); ExitThread( 0 ); return 0; } /* ThreadProc */ /* * MainWndProc */ LONG_PTR __stdcall MainWndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { switch(message) { case WM_ENDSESSION: /* * shoot ourselves in the head */ if( lParam == FALSE ) { DPF( 3, "WM_ENDSESSION" ); ENTER_DPLAYSVR(); DPF( 1, "Setting NO CALLBACKS" ); bNoCallbacks = TRUE; LEAVE_DPLAYSVR(); } else { DPF( 3, "User logging off" ); } break; } return DefWindowProc(hWnd, message, wParam, lParam); } /* MainWndProc */ /* * WindowThreadProc */ void WindowThreadProc( LPVOID pdata ) { static char szClassName[] = "DPlayHelpWndClass"; WNDCLASS cls; MSG msg; HWND hwnd; /* * build class and create window */ cls.lpszClassName = szClassName; cls.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); cls.hInstance = hInstApp; cls.hIcon = NULL; cls.hCursor = NULL; cls.lpszMenuName = NULL; cls.style = 0; cls.lpfnWndProc = MainWndProc; cls.cbWndExtra = 0; cls.cbClsExtra = 0; if( !RegisterClass( &cls ) ) { DPF( 1, "RegisterClass FAILED!" ); ExitThread( 0 ); } hwnd = CreateWindow( szClassName, szClassName, WS_POPUP, 0, 0, 0, 0, NULL, NULL, hInstApp, NULL); if( hwnd == NULL ) { DPF( 1, "No monitor window!" ); ExitThread( 0 ); } /* * pump the messages */ while( GetMessage( &msg, NULL, 0, 0 ) ) { TranslateMessage( &msg ); DispatchMessage( &msg ); } DPF( 1, "Exiting WindowThreadProc" ); ExitThread( 1 ); } /* WindowThreadProc */ // // called by by DPlayHelp_AddServer when we get a new process attached. // we wait for the process to go away, and then make sure it cleaned // all its registered servers up. // void WatchNewPid(LPDPHELPDATA phd) { LPPROCESSDATA ppd; BOOL found; DWORD tid; DPF( 1, "watching new pid" ); ENTER_DPLAYSVR(); ppd = lpProcessList; found = FALSE; while( ppd != NULL ) { if( ppd->pid == phd->pid ) { DPF( 2, "Have thread for process %08lx already", phd->pid ); found = TRUE; break; } ppd = ppd->link; } /* * couldn't find anyone waiting on this process, so create * a brand spanking new thread */ if( !found ) { DPF( 2, "Allocating new thread for process %08lx",phd->pid ); ppd = MemAlloc( sizeof( PROCESSDATA ) ); if( ppd != NULL ) { HANDLE h; ppd->link = lpProcessList; lpProcessList = ppd; ppd->pid = phd->pid; h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) ThreadProc, (LPVOID)ppd, 0, (LPDWORD)&tid); if( h != NULL ) { DPF( 2, "Thread %08lx created",tid); CloseHandle( h ); } else { #ifdef DEBUG DPF( 0, "COULD NOT CREATE HELPER THREAD FOR PID %08lx", phd->pid ); DebugBreak(); //_asm int 3; #endif } } else { #ifdef DEBUG DPF( 0, "OUT OF MEMORY CREATING HELPER THREAD FOR PID %08lx", phd->pid ); DebugBreak(); //_asm int 3; #endif } } LEAVE_DPLAYSVR(); } // WatchNewPid typedef DWORD (WINAPI *PFNREGISTERSERVICE)(DWORD,DWORD); // nt's winbase.h doesn't have these constants - we need them // so we can compile. taken from \proj\dev\inc\winbase.h #ifndef RSP_UNREGISTER_SERVICE #define RSP_UNREGISTER_SERVICE 0x00000000 #endif #ifndef RSP_SIMPLE_SERVICE #define RSP_SIMPLE_SERVICE 0x00000001 #endif // on Win95, we want to call RegisterServiceProcess // but, it's not available on NT, so we can't import it directly // here we try to find it dynamically in kernel32. if we find it, // we call it, otherwise we assume we're on NT and it's not avaible void MakeMeService() { HANDLE hLib; PFNREGISTERSERVICE pfnRegisterServiceProcess; hLib = LoadLibrary("kernel32.dll"); if (!hLib) { // wacky! DPF(1,"could not load library kernel32 to register service proc"); return; } pfnRegisterServiceProcess = (PFNREGISTERSERVICE)GetProcAddress(hLib,"RegisterServiceProcess"); if (!pfnRegisterServiceProcess) { // this is expected on NT DPF(3,"could not register service process - expected on NT"); FreeLibrary(hLib); return ; } pfnRegisterServiceProcess( 0, RSP_SIMPLE_SERVICE ); FreeLibrary(hLib); return ; } // MakeMeService // on Win95, we want to call RegisterServiceProcess to Unregister // (see MakeMeService) void StopServiceProcess() { HANDLE hLib; PFNREGISTERSERVICE pfnRegisterServiceProcess; hLib = LoadLibrary("kernel32.dll"); if (!hLib) { // wacky! DPF(1,"could not load library kernel32 to register service proc"); return; } pfnRegisterServiceProcess = (PFNREGISTERSERVICE)GetProcAddress(hLib,"RegisterServiceProcess"); if (!pfnRegisterServiceProcess) { // this is expected on NT DPF(3,"could not unregister service process - not avail - not tragic"); FreeLibrary(hLib); return ; } // unregistered! pfnRegisterServiceProcess( 0, RSP_UNREGISTER_SERVICE ); FreeLibrary(hLib); return ; } // StopServiceProcess //********************************************************************** // ------------------------------ // DNGetNullDacl - Get a SECURITY_ATTRIBUTE structure that specifies a // NULL DACL which is accessible by all users. // Taken from IDirectPlay8 code base. // // Entry: Nothing // // Exit: PSECURITY_ATTRIBUTES // ------------------------------ #undef DPF_MODNAME #define DPF_MODNAME "DNGetNullDacl" PSECURITY_ATTRIBUTES DNGetNullDacl() { PSID psidEveryone = NULL; SID_IDENTIFIER_AUTHORITY siaWorld = SECURITY_WORLD_SID_AUTHORITY; DWORD dwAclSize; // This is done to make this function independent of DNOSIndirectionInit so that the debug // layer can call it before the indirection layer is initialized. if (!g_fDaclInited) { if (!InitializeSecurityDescriptor((SECURITY_DESCRIPTOR*)g_abSD, SECURITY_DESCRIPTOR_REVISION)) { DPF(0, "Failed to initialize security descriptor" ); goto Error; } // Create SID for the Everyone group. if (!AllocateAndInitializeSid(&siaWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidEveryone)) { DPF(0, "Failed to allocate Everyone SID" ); goto Error; } dwAclSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(psidEveryone) - sizeof(DWORD); // Allocate the ACL, this won't be a tracked allocation and we will let process cleanup destroy it g_pEveryoneACL = (PACL)HeapAlloc(GetProcessHeap(), 0, dwAclSize); if (g_pEveryoneACL == NULL) { DPF(0, "Failed to allocate ACL buffer" ); goto Error; } // Intialize the ACL. if (!InitializeAcl(g_pEveryoneACL, dwAclSize, ACL_REVISION)) { DPF(0, "Failed to initialize ACL" ); goto Error; } // Add the ACE. if (!AddAccessAllowedAce(g_pEveryoneACL, ACL_REVISION, GENERIC_ALL, psidEveryone)) { DPF(0, "Failed to add ACE to ACL" ); goto Error; } // We no longer need the SID that was allocated. FreeSid(psidEveryone); psidEveryone = NULL; // Add the ACL to the security descriptor.. if (!SetSecurityDescriptorDacl((SECURITY_DESCRIPTOR*)g_abSD, TRUE, g_pEveryoneACL, FALSE)) { DPF(0, "Failed to add ACL to security descriptor" ); goto Error; } g_sa.nLength = sizeof(SECURITY_ATTRIBUTES); g_sa.lpSecurityDescriptor = g_abSD; g_sa.bInheritHandle = FALSE; g_psa = &g_sa; g_fDaclInited = TRUE; } Error: if (psidEveryone) { FreeSid(psidEveryone); psidEveryone = NULL; } return g_psa; } //********************************************************************** /* * WinMain */ int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { DWORD tid; DWORD rc; OSVERSIONINFOA VersionInfo; BOOL fUseGlobalNamespace; HANDLE hstartevent; HANDLE hstartupevent; HANDLE hmutex; HANDLE hackevent; LPDPHELPDATA phd; HANDLE hsharedmem; HANDLE h; char szSystemDir[MAX_PATH*sizeof(WCHAR)]; DWORD tWait; #if (USE_RSIP || USE_NATHELP) DWORD tLast; DWORD tNow; #define RSIP_RENEW_TEST_INTERVAL 60000 #endif DPFINIT(); DPF( 2, "*** dplaysvr STARTED, PID=%08lx ***", GetCurrentProcessId() ); if( !MemInit() ) { DPF( 1, "Could not init memory manager" ); return 0; } /* * Set our working directory to the system directory. * This prevents us from holding network connections open * forever if the first DirectDraw app that we run is across * a network connection. */ memset(szSystemDir, 0, sizeof(WCHAR)); GetSystemDirectory(szSystemDir, sizeof(szSystemDir)); SetCurrentDirectory(szSystemDir); // Determine if we're running on NT. memset(&VersionInfo, 0, sizeof(VersionInfo)); VersionInfo.dwOSVersionInfoSize = sizeof(VersionInfo); if (GetVersionExA(&VersionInfo)) { if (VersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) { DPF(2, "Running on NT version %u.%u.%u, using global namespace.", VersionInfo.dwMajorVersion, VersionInfo.dwMinorVersion, VersionInfo.dwBuildNumber); fUseGlobalNamespace = TRUE; } else { DPF(2, "Running on 9x version %u.%u.%u, not using global namespace.", VersionInfo.dwMajorVersion, VersionInfo.dwMinorVersion, LOWORD(VersionInfo.dwBuildNumber)); fUseGlobalNamespace = FALSE; } } else { rc = GetLastError(); DPF(0, "Could not determine OS version (err = %u), assuming global namespace not needed.", rc); fUseGlobalNamespace = FALSE; } // try to register ourselves as a service so user can't see us // in task list MakeMeService(); #if 0 // andyco - not sure if we need this... /* * We must guarantee that DPHELP unloads after the last ddraw app, * since ctrl-alt-del may have happened while an app held the ddraw * lock, and DPHELP needs to clean up orphaned cheap ddraw mutex * locks. */ if ( ! SetProcessShutdownParameters(0x100,SHUTDOWN_NORETRY) ) { DPF(0,"dplaysvr could not set itself to shutdown last!"); } #endif hInstApp = hInstance; /* * create startup event */ if (fUseGlobalNamespace) { hstartupevent = CreateEvent( DNGetNullDacl(), TRUE, FALSE, "Global\\" DPHELP_STARTUP_EVENT_NAME ); } else { hstartupevent = CreateEvent( NULL, TRUE, FALSE, DPHELP_STARTUP_EVENT_NAME ); } if( hstartupevent == NULL ) { DPF( 1, "Could not create startup event!" ); return 0; } /* * create shared memory area */ if (fUseGlobalNamespace) { hsharedmem = CreateFileMapping( INVALID_HANDLE_VALUE, DNGetNullDacl(), PAGE_READWRITE, 0, sizeof( DPHELPDATA ), "Global\\" DPHELP_SHARED_NAME ); } else { hsharedmem = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof( DPHELPDATA ), DPHELP_SHARED_NAME ); } if( hsharedmem == NULL ) { DPF( 1, "Could not create file mapping!" ); return 0; } /* * create mutex for people who want to use the shared memory area */ if (fUseGlobalNamespace) { hmutex = CreateMutex( DNGetNullDacl(), FALSE, "Global\\" DPHELP_MUTEX_NAME ); } else { hmutex = CreateMutex( NULL, FALSE, DPHELP_MUTEX_NAME ); } if( hmutex == NULL ) { DPF( 1, "Could not create mutex " DPHELP_MUTEX_NAME ); CloseHandle( hsharedmem ); return 0; } /* * create events */ if (fUseGlobalNamespace) { hstartevent = CreateEvent( DNGetNullDacl(), FALSE, FALSE, "Global\\" DPHELP_EVENT_NAME ); } else { hstartevent = CreateEvent( NULL, FALSE, FALSE, DPHELP_EVENT_NAME ); } if( hstartevent == NULL ) { DPF( 1, "Could not create event " DPHELP_EVENT_NAME ); CloseHandle( hmutex ); CloseHandle( hsharedmem ); return 0; } if (fUseGlobalNamespace) { hackevent = CreateEvent( DNGetNullDacl(), FALSE, FALSE, "Global\\" DPHELP_ACK_EVENT_NAME ); } else { hackevent = CreateEvent( NULL, FALSE, FALSE, DPHELP_ACK_EVENT_NAME ); } if( hackevent == NULL ) { DPF( 1, "Could not create event " DPHELP_ACK_EVENT_NAME ); CloseHandle( hmutex ); CloseHandle( hsharedmem ); CloseHandle( hstartevent ); return 0; } /* * Create window so we can get messages */ h = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) WindowThreadProc, NULL, 0, (LPDWORD)&tid ); if( h == NULL ) { DPF( 1, "Create of WindowThreadProc FAILED!" ); CloseHandle( hackevent ); CloseHandle( hmutex ); CloseHandle( hsharedmem ); CloseHandle( hstartevent ); return 0; } CloseHandle( h ); /* * serialize access to us */ INIT_DPLAYSVR_CSECT(); if (!gbIPStarted) { rc = StartupIP(); if (FAILED(rc)) { DPF_ERR("dphelp : could not init wsock ! not adding server"); return (rc); } } #if USE_RSIP bRsip=rsipInit(); if(bRsip){ rsipListenPort(FALSE, SERVER_DGRAM_PORT, NULL, NULL); } #endif #if USE_NATHELP natInit(); if(g_pINatHelp){ natRegisterUDPPort(SERVER_DGRAM_PORT); } #endif /* * let invoker and anyone else who comes along know we exist */ SetEvent( hstartupevent ); tLast=tNow=timeGetTime(); /* * loop forever, processing requests */ while( 1 ) { wait: tWait=(tLast+RSIP_RENEW_TEST_INTERVAL)-tNow; if((int)tWait < 0){ tWait=0; } ASSERT(!(tWait &0x80000000)); /* * wait to be notified of a request */ DPF( 1, "Waiting for next request" ); rc = WaitForSingleObject( hstartevent, tWait ); #if (USE_RSIP || USE_NATHELP) tNow=timeGetTime(); if(rc==WAIT_TIMEOUT) { tLast=tNow; #if USE_RSIP if(bRsip) {rsipPortExtend(tNow);} #elif USE_NATHELP if(g_pINatHelp){natGetCapsUpdate();} #endif goto wait; } #endif if( rc == WAIT_FAILED ) { DPF( 1, "Wait FAILED!!!" ); continue; } ENTER_DPLAYSVR(); phd = (LPDPHELPDATA) MapViewOfFile( hsharedmem, FILE_MAP_ALL_ACCESS, 0, 0, 0 ); if( phd == NULL ) { DPF( 1, "Could not create view of file!" ); LEAVE_DPLAYSVR(); continue; } /* * find out what we need to do */ switch( phd->req ) { case DPHELPREQ_SUICIDE: DPF( 1, "DPHELPREQ_SUICIDE" ); #if USE_RSIP if(bRsip){ rsipFini(); } #endif #if USE_NATHELP if(g_pINatHelp){ natFini(); } #endif DPlayHelp_FreeServerList(); SetEvent( hackevent ); CloseHandle( hmutex ); UnmapViewOfFile( phd ); CloseHandle( hsharedmem ); CloseHandle( hstartevent ); if (gReceiveList.pConnection) { MemFree(gReceiveList.pConnection); } if (gReadfds.pfdbigset) { MemFree(gReadfds.pfdbigset); } FINI_DPLAYSVR_CSECT(); // This should be done after functions that use a Dacl will no longer be // called (CreateMutex, CreateFile, etc). if (g_pEveryoneACL) { HeapFree(GetProcessHeap(), 0, g_pEveryoneACL); g_pEveryoneACL = NULL; g_psa = NULL; g_fDaclInited = FALSE; } #ifdef DEBUG MemState(); #endif DPF( 3, "Good Night Gracie" ); TerminateProcess( GetCurrentProcess(), 0 ); break; case DPHELPREQ_RETURNHELPERPID: DPF( 2, "DDHELPREQ_RETURNHELPERPID" ); phd->pid = GetCurrentProcessId(); break; case DPHELPREQ_DPLAYADDSERVER: DPF( 2, "DPHELPREQ_DPLAYADDSERVER" ); phd->hr = DPlayHelp_AddServer(phd); #if USE_RSIP if(!bRsip){ bRsip=rsipInit(); if(bRsip){ rsipListenPort(FALSE, SERVER_DGRAM_PORT, NULL, NULL); } } #endif #if USE_NATHELP if(!g_pINatHelp){ natInit(); if(g_pINatHelp){ natRegisterUDPPort(SERVER_DGRAM_PORT); } } #endif break; case DPHELPREQ_DPLAYDELETESERVER: DPF( 2, "DPHELPREQ_DPLAYDELETESERVER" ); DPlayHelp_DeleteServer(phd,FALSE); break; default: DPF( 1, "helper - Unknown Request???" ); break; } /* * let caller know we've got the news */ UnmapViewOfFile( phd ); SetEvent( hackevent ); LEAVE_DPLAYSVR(); } StopServiceProcess(); } /* WinMain */