/* ---------------------------------------------------------------------- Module: ULS.DLL (Service Provider) File: sputils.cpp Content: This file contains the utilities for service provider. History: 10/15/96 Chu, Lon-Chan [lonchanc] Created. Copyright (c) Microsoft Corporation 1996-1997 ---------------------------------------------------------------------- */ #include "ulsp.h" #include "spinc.h" TCHAR c_szWindowClassName[] = TEXT ("UlsLdapSp"); BOOL g_fExitNow = FALSE; HANDLE g_ahThreadWaitFor[NUM_THREAD_WAIT_FOR] = { 0 }; DWORD WINAPI ReqThread ( VOID *lParam ) { BOOL fStayInThisThread = TRUE; DWORD dwExitCode = 0; DWORD dwResult; // Start WSA for subsequent host query in this service provider // WSADATA WSAData; if (WSAStartup (MAKEWORD (1, 1), &WSAData)) { dwExitCode = ILS_E_WINSOCK; goto MyExit; } // Make sure that all the event are initialized // INT i; for (i = 0; i < NUM_THREAD_WAIT_FOR; i++) { if (g_ahThreadWaitFor[i] == NULL) { MyAssert (FALSE); dwExitCode = ILS_E_THREAD; goto MyExit; } } // Wait for events to happen!!! // do { dwResult = MsgWaitForMultipleObjects ( NUM_THREAD_WAIT_FOR, &g_ahThreadWaitFor[0], FALSE, // OR logic INFINITE, // infinite QS_ALLINPUT); // any message in queue switch (dwResult) { case WAIT_OBJECT_0 + THREAD_WAIT_FOR_REQUEST: if (g_pReqQueue != NULL) { g_pReqQueue->Schedule (); MyAssert (fStayInThisThread); } else { MyAssert (FALSE); fStayInThisThread = FALSE; } break; case WAIT_OBJECT_0 + THREAD_WAIT_FOR_EXIT: case WAIT_ABANDONED_0 + THREAD_WAIT_FOR_EXIT: case WAIT_ABANDONED_0 + THREAD_WAIT_FOR_REQUEST: case WAIT_TIMEOUT: // Exit this thread // fStayInThisThread = FALSE; break; default: // If a message in the queue, then dispatch it. // Right now, wldap32 does not have a message pump. // However, for possible update of wldap32, we need to // protect ourselves from being fried. // if (! KeepUiResponsive ()) fStayInThisThread = FALSE; break; } } while (fStayInThisThread); MyExit: if (dwExitCode != ILS_E_WINSOCK) WSACleanup (); // ExitThread (dwExitCode); return 0; } BOOL KeepUiResponsive ( VOID ) { MSG msg; while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message != WM_QUIT) { TranslateMessage (&msg); DispatchMessage (&msg); } else { PostQuitMessage ((int)msg.wParam); return FALSE; } } return TRUE; } LRESULT CALLBACK SP_WndProc ( HWND hWnd, UINT uMsg, WPARAM uParam, LPARAM lParam ) { switch (uMsg) { case WM_CREATE: break; case WM_TIMER: switch (LOWORD (uParam)) { case ID_TIMER_POLL_RESULT: if (g_pRespQueue != NULL) { // No-wait polling // LDAP_TIMEVAL PollTimeout; ZeroMemory (&PollTimeout, sizeof (PollTimeout)); // PollTimeout.tv_sec = 0; // PollTimeout.tv_usec = 0; g_pRespQueue->PollLdapResults (&PollTimeout); } else { MyAssert (FALSE); } break; default: if (LOWORD (uParam) >= KEEP_ALIVE_TIMER_BASE) { // Allocate marshall request buffer // MARSHAL_REQ *pReq = MarshalReq_Alloc (WM_ILS_REFRESH, 0, 1); if (pReq != NULL) { HRESULT hr = ILS_E_FAIL; ULONG uTimerID = LOWORD (uParam); // Fill in parameters // MarshalReq_SetParam (pReq, 0, (DWORD) uTimerID, 0); // Enter the request // if (g_pReqQueue != NULL) { hr = g_pReqQueue->Enter (pReq); } else { MyAssert (FALSE); } // Avoid timer overrun if the request is submitted successfully // if (hr == S_OK) { KillTimer (hWnd, uTimerID); } else { MemFree (pReq); } } } else { MyAssert (FALSE); } break; } // switch (LOWORD (uParam)) break; case WM_ILS_CLIENT_NEED_RELOGON: case WM_ILS_CLIENT_NETWORK_DOWN: #if 1 MyAssert (FALSE); // we should post to com directly #else { // Get the local user object // SP_CClient *pClient = (SP_CClient *) lParam; // Make sure the parent local user object is valid // if (MyIsBadWritePtr (pClient, sizeof (*pClient)) || ! pClient->IsValidObject () || ! pClient->IsRegistered ()) { MyAssert (FALSE); break; // exit } // Indicate this user object is not remotely connected to the server // pClient->SetRegLocally (); // Get the server info // SERVER_INFO *pServerInfo = pClient->GetServerInfo (); if (pServerInfo == NULL) { MyAssert (FALSE); break; // exit } // Duplicate the server name // TCHAR *pszServerName = My_strdup (pServerInfo->pszServerName); if (pszServerName == NULL) break; // exit // Notify the com layer // PostMessage (g_hWndNotify, uMsg, (WPARAM) pClient, (LPARAM) pszServerName); } #endif break; #ifdef ENABLE_MEETING_PLACE case WM_ILS_MEETING_NEED_RELOGON: case WM_ILS_MEETING_NETWORK_DOWN: #if 1 MyAssert (FALSE); // we should post to com directly #else { // Get the local user object // SP_CMeeting *pMtg = (SP_CMeeting *) lParam; // Make sure the parent local user object is valid // if (MyIsBadWritePtr (pMtg, sizeof (*pMtg)) || ! pMtg->IsValidObject () || ! pMtg->IsRegistered ()) { MyAssert (FALSE); break; // exit } // Indicate this user object is not remotely connected to the server // pMtg->SetRegLocally (); // Get the server info // SERVER_INFO *pServerInfo = pMtg->GetServerInfo (); if (pServerInfo == NULL) { MyAssert (FALSE); break; // exit } // Duplicate the server name // TCHAR *pszServerName = My_strdup (pServerInfo->pszServerName); if (pszServerName == NULL) break; // exit // Notify the com layer // PostMessage (g_hWndNotify, uMsg, (WPARAM) pMtg, (LPARAM) pszServerName); } #endif break; #endif // ENABLE_MEETING_PLACE #if 0 case WM_ILS_IP_ADDRESS_CHANGED: { // Get the local user object // SP_CClient *pClient = (SP_CClient *) lParam; // Make sure the parent local user object is valid // if (MyIsBadWritePtr (pClient, sizeof (*pClient)) || ! pClient->IsValidObject () || ! pClient->IsRegistered ()) { MyAssert (FALSE); break; // exit } // Change IP address now // pClient->UpdateIPAddress (); } break; #endif case WM_CLOSE: DestroyWindow (hWnd); break; case WM_DESTROY: g_hWndHidden = NULL; #ifdef USE_HIDDEN_THREAD PostQuitMessage (0); #endif break; default: return DefWindowProc (hWnd, uMsg, uParam, lParam); } return 0; } BOOL MyCreateWindow ( VOID ) { WNDCLASS wc; // do the stuff to create a hidden window ZeroMemory (&wc, sizeof (wc)); // wc.style = 0; wc.lpfnWndProc = (WNDPROC) SP_WndProc; // wc.cbClsExtra = 0; // wc.cbWndExtra = 0; // wc.hIcon = NULL; wc.hInstance = g_hInstance; // wc.hCursor = NULL; // wc.hbrBackground = NULL; // wc.lpszMenuName = NULL; wc.lpszClassName = c_szWindowClassName; // register the class // it is ok, if the class is already registered by another app RegisterClass (&wc); // create a window for socket notification g_hWndHidden = CreateWindow ( wc.lpszClassName, NULL, WS_POPUP, /* Window style. */ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, /* the application window is the parent. */ NULL, /* hardcoded ID */ g_hInstance, /* the application owns this window. */ NULL); /* Pointer not needed. */ return (g_hWndHidden != NULL); } HRESULT GetLocalIPAddress ( DWORD *pdwIPAddress ) { MyAssert (pdwIPAddress != NULL); // get local host name CHAR szLocalHostName[MAX_PATH]; szLocalHostName[0] = '\0'; gethostname (&szLocalHostName[0], MAX_PATH); // get the host entry by name PHOSTENT phe = gethostbyname (&szLocalHostName[0]); if (phe == NULL) return ILS_E_WINSOCK; // get info from the host entry *pdwIPAddress = *(DWORD *) phe->h_addr; return S_OK; } // guid --> string VOID GetGuidString ( GUID *pGuid, TCHAR *pszGuid ) { MyAssert (! MyIsBadWritePtr (pGuid, sizeof (GUID))); MyAssert (pszGuid != NULL); CHAR *psz = (CHAR *) pGuid; for (ULONG i = 0; i < sizeof (GUID); i++) { wsprintf (pszGuid, TEXT ("%02x"), (0x0FF & (ULONG) *psz)); pszGuid += 2; psz++; } *pszGuid = TEXT ('\0'); } // string --> guid VOID GetStringGuid ( TCHAR *pszGuid, GUID *pGuid ) { ULONG cchGuid = lstrlen (pszGuid); MyAssert (cchGuid == 2 * sizeof (GUID)); MyAssert (! MyIsBadWritePtr (pGuid, sizeof (GUID))); // Clean up target GUID structure // ZeroMemory (pGuid, sizeof (GUID)); // Translate guid string to guid // CHAR *psz = (CHAR *) pGuid; cchGuid >>= 1; for (ULONG i = 0; i < cchGuid; i++) { *psz++ = (CHAR) ((HexChar2Val (pszGuid[0]) << 4) | HexChar2Val (pszGuid[1])); pszGuid += 2; } } INT HexChar2Val ( TCHAR c ) { INT Val; if (TEXT ('0') <= c && c <= TEXT ('9')) Val = c - TEXT ('0'); else if (TEXT ('a') <= c && c <= TEXT ('f')) Val = c - TEXT ('a') + 10; else if (TEXT ('A') <= c && c <= TEXT ('F')) Val = c - TEXT ('A') + 10; else Val = 0; MyAssert (0 <= Val && Val <= 15); return Val & 0x0F; } INT DecimalChar2Val ( TCHAR c ) { INT Val; if (TEXT ('0') <= c && c <= TEXT ('9')) Val = c - TEXT ('0'); else Val = 0; MyAssert (0 <= Val && Val <= 9); return Val & 0x0F; } BOOL IsValidGuid ( GUID *pGuid ) { DWORD *pdw = (DWORD *) pGuid; return (pdw[0] != 0 || pdw[1] != 0 || pdw[2] != 0 || pdw[3] != 0); } // long --> string VOID GetLongString ( LONG Val, TCHAR *pszVal ) { MyAssert (pszVal != NULL); wsprintf (pszVal, TEXT ("%lu"), Val); } // string --> long LONG GetStringLong ( TCHAR *pszVal ) { MyAssert (pszVal != NULL); LONG Val = 0; for (INT i = 0; i < INTEGER_STRING_LENGTH && *pszVal != TEXT ('\0'); i++) { Val = 10 * Val + DecimalChar2Val (*pszVal++); } return Val; } // it is the caller's responsibility to make sure // the buffer is sufficient and // the ip address is in network order VOID GetIPAddressString ( TCHAR *pszIPAddress, DWORD dwIPAddress ) { BYTE temp[4]; *(DWORD *) &temp[0] = dwIPAddress; wsprintf (pszIPAddress, TEXT ("%u.%u.%u.%u"), (UINT) temp[0], (UINT) temp[1], (UINT) temp[2], (UINT) temp[3]); } ULONG My_lstrlen ( const TCHAR *psz ) { return ((psz != NULL) ? lstrlen (psz) : 0); } VOID My_lstrcpy ( TCHAR *pszDst, const TCHAR *pszSrc ) { if (pszDst != NULL) { if (pszSrc != NULL) { lstrcpy (pszDst, pszSrc); } else { *pszDst = TEXT ('\0'); } } } INT My_lstrcmpi ( const TCHAR *p, const TCHAR *q ) { INT retcode; if (p == q) { retcode = 0; } else if (p == NULL) { retcode = -1; } else if (q == NULL) { retcode = 1; } else { retcode = lstrcmpi (p, q); } return retcode; } TCHAR * My_strdup ( const TCHAR *pszToDup ) { TCHAR *psz = NULL; if (pszToDup != NULL) { psz = (TCHAR *) MemAlloc ((lstrlen (pszToDup) + 1) * sizeof (TCHAR)); if (psz != NULL) { lstrcpy (psz, pszToDup); } } return psz; } TCHAR * My_strchr ( const TCHAR *psz, TCHAR c ) { TCHAR *pszFound = NULL; if (psz) { while (*psz) { if (*psz == c) { pszFound = (TCHAR *) psz; break; } psz++; } } return pszFound; } BOOL My_isspace ( TCHAR ch ) { return (ch == TEXT (' ') || ch == TEXT ('\t') || ch == TEXT ('\r') || ch == TEXT ('\n')); } BOOL IsSameMemory ( const BYTE *pb1, const BYTE *pb2, DWORD cbSize ) { while (cbSize--) { if (*pb1++ != *pb2++) { return FALSE; } } return TRUE; } BYTE * MyBinDup ( const BYTE *pbToDup, ULONG cbToDup ) { BYTE *pb = NULL; if (pbToDup) { pb = (BYTE *) MemAlloc (cbToDup); if (pb) { CopyMemory (pb, pbToDup, cbToDup); } } return pb; } /* ---------- registry ------------- */ const TCHAR c_szUlsLdapSpReg[] = TEXT("Software\\Microsoft\\User Location Service\\LDAP Provider"); const TCHAR c_szResponseTimeout[] = TEXT("Response Timeout"); const TCHAR c_szResponsePollPeriod[] = TEXT("Response Poll Period"); const TCHAR c_szClientSig[] = TEXT ("Client Signature"); BOOL GetRegistrySettings ( VOID ) { // Open the LDAP Provider settings // HKEY hKey; if (RegOpenKeyEx ( HKEY_CURRENT_USER, &c_szUlsLdapSpReg[0], 0, KEY_READ, &hKey) != NOERROR) { // The folder does not exist // g_uResponseTimeout = ILS_MIN_RESP_TIMEOUT; g_uResponsePollPeriod = ILS_DEF_RESP_POLL_PERIOD; g_dwClientSig = (ULONG) -1; } else { // Get response timeout // GetRegValueLong ( hKey, &c_szResponseTimeout[0], (LONG *) &g_uResponseTimeout, ILS_DEF_RESP_TIMEOUT); // Make sure the value is within the range // if (g_uResponseTimeout < ILS_MIN_RESP_TIMEOUT) g_uResponseTimeout = ILS_MIN_RESP_TIMEOUT; // Get response poll period // GetRegValueLong ( hKey, &c_szResponsePollPeriod[0], (LONG *) &g_uResponsePollPeriod, ILS_DEF_RESP_POLL_PERIOD); // Make sure the value is within the range // if (g_uResponsePollPeriod < ILS_MIN_RESP_POLL_PERIOD) g_uResponsePollPeriod = ILS_MIN_RESP_POLL_PERIOD; // Get client signature // GetRegValueLong ( hKey, &c_szClientSig[0], (LONG *) &g_dwClientSig, (LONG) -1); RegCloseKey (hKey); } // Make sure this value is not -1 // if (g_dwClientSig == (ULONG) -1) { // The client signature does not exist. // We need to generate a new one // g_dwClientSig = GetTickCount (); // Save it back to the registry // DWORD dwDontCare; if (RegCreateKeyEx (HKEY_CURRENT_USER, &c_szUlsLdapSpReg[0], 0, TEXT (""), REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &dwDontCare) == NOERROR) { RegSetValueEx ( hKey, &c_szClientSig[0], 0, REG_DWORD, (BYTE *) &g_dwClientSig, sizeof (&g_dwClientSig)); } } return TRUE; } BOOL GetRegValueLong ( HKEY hKey, const TCHAR *pszKey, LONG *plValue, LONG lDefValue ) { MyAssert (hKey != NULL); MyAssert (pszKey != NULL); MyAssert (plValue != NULL); *plValue = lDefValue; DWORD dwType; ULONG cb; TCHAR szText[MAX_PATH]; cb = sizeof (szText); if (RegQueryValueEx ( hKey, pszKey, NULL, &dwType, (BYTE *) &szText[0], &cb) == ERROR_SUCCESS) { switch (dwType) { case REG_DWORD: case REG_BINARY: *plValue = *(LONG *) &szText[0]; break; case REG_SZ: *plValue = GetStringLong (&szText[0]); break; default: return FALSE; } } return TRUE; } /* ------- LDAP error codes ---------- */ const LONG c_LdapErrToHrShort[] = { // End of search (per AndyHe info) LDAP_PARAM_ERROR, ILS_E_PARAMETER, // Keep alive fails LDAP_NO_SUCH_OBJECT, ILS_E_NO_SUCH_OBJECT, // Logon with conflicting email name LDAP_ALREADY_EXISTS, ILS_E_NAME_CONFLICTS, LDAP_OPERATIONS_ERROR, ILS_E_LDAP_OPERATIONS_ERROR, LDAP_PROTOCOL_ERROR, ILS_E_LDAP_PROTOCOL_ERROR, LDAP_TIMELIMIT_EXCEEDED, ILS_E_LDAP_TIMELIMIT_EXCEEDED, LDAP_SIZELIMIT_EXCEEDED, ILS_E_LDAP_SIZELIMIT_EXCEEDED, LDAP_COMPARE_FALSE, ILS_E_LDAP_COMPARE_FALSE, LDAP_COMPARE_TRUE, ILS_E_LDAP_COMPARE_TRUE, LDAP_AUTH_METHOD_NOT_SUPPORTED, ILS_E_LDAP_AUTH_METHOD_NOT_SUPPORTED, LDAP_STRONG_AUTH_REQUIRED, ILS_E_LDAP_STRONG_AUTH_REQUIRED, LDAP_REFERRAL_V2, ILS_E_LDAP_REFERRAL_V2, LDAP_PARTIAL_RESULTS, ILS_E_LDAP_PARTIAL_RESULTS, LDAP_REFERRAL, ILS_E_LDAP_REFERRAL, LDAP_ADMIN_LIMIT_EXCEEDED, ILS_E_LDAP_ADMIN_LIMIT_EXCEEDED, LDAP_UNAVAILABLE_CRIT_EXTENSION,ILS_E_LDAP_UNAVAILABLE_CRIT_EXTENSION, LDAP_NO_SUCH_ATTRIBUTE, ILS_E_LDAP_NO_SUCH_ATTRIBUTE, LDAP_UNDEFINED_TYPE, ILS_E_LDAP_UNDEFINED_TYPE, LDAP_INAPPROPRIATE_MATCHING, ILS_E_LDAP_INAPPROPRIATE_MATCHING, LDAP_CONSTRAINT_VIOLATION, ILS_E_LDAP_CONSTRAINT_VIOLATION, LDAP_ATTRIBUTE_OR_VALUE_EXISTS, ILS_E_LDAP_ATTRIBUTE_OR_VALUE_EXISTS, LDAP_INVALID_SYNTAX, ILS_E_LDAP_INVALID_SYNTAX, LDAP_ALIAS_PROBLEM, ILS_E_LDAP_ALIAS_PROBLEM, LDAP_INVALID_DN_SYNTAX, ILS_E_LDAP_INVALID_DN_SYNTAX, LDAP_IS_LEAF, ILS_E_LDAP_IS_LEAF, LDAP_ALIAS_DEREF_PROBLEM, ILS_E_LDAP_ALIAS_DEREF_PROBLEM, LDAP_INAPPROPRIATE_AUTH, ILS_E_LDAP_INAPPROPRIATE_AUTH, LDAP_INVALID_CREDENTIALS, ILS_E_LDAP_INVALID_CREDENTIALS, LDAP_INSUFFICIENT_RIGHTS, ILS_E_LDAP_INSUFFICIENT_RIGHTS, LDAP_BUSY, ILS_E_LDAP_BUSY, LDAP_UNAVAILABLE, ILS_E_LDAP_UNAVAILABLE, LDAP_UNWILLING_TO_PERFORM, ILS_E_LDAP_UNWILLING_TO_PERFORM, LDAP_LOOP_DETECT, ILS_E_LDAP_LOOP_DETECT, LDAP_NAMING_VIOLATION, ILS_E_LDAP_NAMING_VIOLATION, LDAP_OBJECT_CLASS_VIOLATION, ILS_E_LDAP_OBJECT_CLASS_VIOLATION, LDAP_NOT_ALLOWED_ON_NONLEAF, ILS_E_LDAP_NOT_ALLOWED_ON_NONLEAF, LDAP_NOT_ALLOWED_ON_RDN, ILS_E_LDAP_NOT_ALLOWED_ON_RDN, LDAP_NO_OBJECT_CLASS_MODS, ILS_E_LDAP_NO_OBJECT_CLASS_MODS, LDAP_RESULTS_TOO_LARGE, ILS_E_LDAP_RESULTS_TOO_LARGE, LDAP_AFFECTS_MULTIPLE_DSAS, ILS_E_LDAP_AFFECTS_MULTIPLE_DSAS, LDAP_OTHER, ILS_E_LDAP_OTHER, LDAP_SERVER_DOWN, ILS_E_LDAP_SERVER_DOWN, LDAP_LOCAL_ERROR, ILS_E_LDAP_LOCAL_ERROR, LDAP_ENCODING_ERROR, ILS_E_LDAP_ENCODING_ERROR, LDAP_DECODING_ERROR, ILS_E_LDAP_DECODING_ERROR, LDAP_TIMEOUT, ILS_E_LDAP_TIMEOUT, LDAP_AUTH_UNKNOWN, ILS_E_LDAP_AUTH_UNKNOWN, LDAP_FILTER_ERROR, ILS_E_LDAP_FILTER_ERROR, LDAP_USER_CANCELLED, ILS_E_LDAP_USER_CANCELLED, LDAP_NO_MEMORY, ILS_E_LDAP_NO_MEMORY, }; HRESULT LdapError2Hresult ( ULONG uLdapError ) { HRESULT hr; switch (uLdapError) { case LDAP_SUCCESS: hr = S_OK; break; default: // If nothing appears to be appropriate // hr = ILS_E_SERVER_EXEC; // Go through the loop to find a matching error code // for ( INT i = 0; i < ARRAY_ELEMENTS (c_LdapErrToHrShort); i += 2) { if (c_LdapErrToHrShort[i] == (LONG) uLdapError) { hr = (HRESULT) c_LdapErrToHrShort[i+1]; break; } } MyAssert (hr != ILS_E_SERVER_EXEC); break; } return hr; }