xref: /reactos/sdk/lib/apisets/apisets.c (revision 0b366ea1)
1 /*
2  * PROJECT:     ReactOS apisets
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     Resolving the apiset to a ReactOS system dll
5  * COPYRIGHT:   Copyright 2024 Mark Jansen <mark.jansen@reactos.org>
6  */
7 
8 #include <ndk/umtypes.h>
9 #include <ndk/rtlfuncs.h>
10 #include "apisetsp.h"
11 
12 
13 const ULONGLONG API_ = (ULONGLONG)0x2D004900500041; /// L"API-"
14 const ULONGLONG EXT_ = (ULONGLONG)0x2D005400580045; /// L"EXT-";
15 
16 WORD PrefixSize = sizeof(L"api-") - sizeof(WCHAR);
17 WORD ExtensionSize = sizeof(L".dll") - sizeof(WCHAR);
18 
19 
20 // The official prototype according to the Windows kit 8.1 is:
21 //NTSTATUS
22 //ApiSetResolveToHost (
23 //    _In_ PCAPI_SET_NAMESPACE_ARRAY Schema,
24 //    _In_ PCUNICODE_STRING FileNameIn,
25 //    _In_opt_ PCUNICODE_STRING ParentName,
26 //    _Out_ PBOOLEAN Resolved,
27 //    _Out_ PUNICODE_STRING HostBinary
28 //    );
29 
30 
31 NTSTATUS
32 ApiSetResolveToHost(
33     _In_ DWORD ApisetVersion,
34     _In_ PCUNICODE_STRING ApiToResolve,
35     _Out_ PBOOLEAN Resolved,
36     _Out_ PUNICODE_STRING Output
37 )
38 {
39     if (ApiToResolve->Length < PrefixSize)
40     {
41         *Resolved = FALSE;
42         return STATUS_SUCCESS;
43     }
44 
45     // Grab the first four chars from the string, converting the first 3 to uppercase
46     PWSTR ApiSetNameBuffer = ApiToResolve->Buffer;
47     ULONGLONG ApiSetNameBufferPrefix = ((ULONGLONG *)ApiSetNameBuffer)[0] & 0xFFFFFFDFFFDFFFDF;
48     // Check if it matches either 'api-' or 'ext-'
49     if (!(ApiSetNameBufferPrefix == API_ || ApiSetNameBufferPrefix == EXT_))
50     {
51         *Resolved = FALSE;
52         return STATUS_SUCCESS;
53     }
54 
55     // If there is an extension, cut it off (we store apisets without extension)
56     UNICODE_STRING Tmp = *ApiToResolve;
57     const WCHAR *Extension = Tmp.Buffer + (Tmp.Length - ExtensionSize) / sizeof(WCHAR);
58     if (!_wcsnicmp(Extension, L".dll", ExtensionSize / sizeof(WCHAR)))
59         Tmp.Length -= ExtensionSize;
60 
61     // Binary search the apisets
62     // Ideally we should use bsearch here, but that drags in another dependency and we do not want that here.
63     LONG UBnd = g_ApisetsCount - 1;
64     LONG LBnd = 0;
65     while (LBnd <= UBnd)
66     {
67         LONG Index = (UBnd - LBnd) / 2 + LBnd;
68 
69         LONG result = RtlCompareUnicodeString(&Tmp, &g_Apisets[Index].Name, TRUE);
70         if (result == 0)
71         {
72             // Check if this version is included
73             if (g_Apisets[Index].dwOsVersions & ApisetVersion)
74             {
75                 // Return a static string (does not have to be freed)
76                 *Resolved = TRUE;
77                 *Output = g_Apisets[Index].Target;
78             }
79             return STATUS_SUCCESS;
80         }
81         else if (result < 0)
82         {
83             UBnd = Index - 1;
84         }
85         else
86         {
87             LBnd = Index + 1;
88         }
89     }
90     *Resolved = FALSE;
91     return STATUS_SUCCESS;
92 }
93