1 /*
2  * PROJECT:    ReactOS NetSh
3  * LICENSE:    GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4  * PURPOSE:    Network Shell helper dll management and support functions
5  * COPYRIGHT:  Copyright 2023 Eric Kohl <eric.kohl@reactos.org>
6  */
7 
8 /* INCLUDES *******************************************************************/
9 
10 #include "precomp.h"
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* GLOBALS ********************************************************************/
16 
17 PDLL_LIST_ENTRY pDllListHead = NULL;
18 PDLL_LIST_ENTRY pDllListTail = NULL;
19 
20 PHELPER_ENTRY pHelperListHead = NULL;
21 PHELPER_ENTRY pHelperListTail = NULL;
22 
23 PDLL_LIST_ENTRY pCurrentDll = NULL;
24 
25 /* FUNCTIONS ******************************************************************/
26 
27 static
28 VOID
29 StartHelpers(VOID)
30 {
31     PHELPER_ENTRY pHelper;
32     DWORD dwError;
33 
34     pHelper = pHelperListHead;
35     while (pHelper != NULL)
36     {
37         if (pHelper->bStarted == FALSE)
38         {
39             if (pHelper->Attributes.pfnStart)
40             {
41                 dwError = pHelper->Attributes.pfnStart(NULL, 0);
42                 if (dwError == ERROR_SUCCESS)
43                     pHelper->bStarted = TRUE;
44             }
45         }
46 
47         pHelper = pHelper->pNext;
48     }
49 }
50 
51 
52 static
53 VOID
54 RegisterHelperDll(
55     _In_ PDLL_LIST_ENTRY pEntry)
56 {
57     PWSTR pszValueName = NULL;
58     HKEY hKey;
59     DWORD dwError;
60 
61     dwError = RegCreateKeyExW(HKEY_LOCAL_MACHINE,
62                               REG_NETSH_PATH,
63                               0,
64                               NULL,
65                               REG_OPTION_NON_VOLATILE,
66                               KEY_WRITE,
67                               NULL,
68                               &hKey,
69                               NULL);
70     if (dwError == ERROR_SUCCESS)
71     {
72         RegSetValueExW(hKey,
73                        pEntry->pszValueName,
74                        0,
75                        REG_SZ,
76                        (PBYTE)pEntry->pszDllName,
77                        (wcslen(pEntry->pszDllName) + 1) * sizeof(WCHAR));
78 
79         RegCloseKey(hKey);
80     }
81 
82     HeapFree(GetProcessHeap(), 0, pszValueName);
83 }
84 
85 
86 static
87 VOID
88 FreeHelperDll(
89     _In_ PDLL_LIST_ENTRY pEntry)
90 {
91     if (pEntry->hModule)
92         FreeLibrary(pEntry->hModule);
93 
94     if (pEntry->pszValueName)
95         HeapFree(GetProcessHeap(), 0, pEntry->pszValueName);
96 
97     if (pEntry->pszShortName)
98         HeapFree(GetProcessHeap(), 0, pEntry->pszShortName);
99 
100     if (pEntry->pszDllName)
101         HeapFree(GetProcessHeap(), 0, pEntry->pszDllName);
102 
103     HeapFree(GetProcessHeap(), 0, pEntry);
104 }
105 
106 
107 static
108 DWORD
109 LoadHelperDll(
110     _In_ PWSTR pszDllName,
111     _In_ BOOL bRegister)
112 {
113     PNS_DLL_INIT_FN pInitHelperDll;
114     PDLL_LIST_ENTRY pEntry;
115     PWSTR pszStart, pszEnd;
116     BOOL bInserted = FALSE;
117     DWORD dwError;
118 
119     pEntry = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DLL_LIST_ENTRY));
120     if (pEntry == NULL)
121     {
122         return ERROR_OUTOFMEMORY;
123     }
124 
125     pEntry->pszDllName = HeapAlloc(GetProcessHeap(),
126                                    HEAP_ZERO_MEMORY,
127                                    (wcslen(pszDllName) + 1) * sizeof(WCHAR));
128     if (pEntry->pszDllName == NULL)
129     {
130         dwError = ERROR_OUTOFMEMORY;
131         goto done;
132     }
133 
134     wcscpy(pEntry->pszDllName, pszDllName);
135 
136     pszStart = wcsrchr(pszDllName, L'\\');
137     if (pszStart == NULL)
138         pszStart = pszDllName;
139 
140     pEntry->pszShortName = HeapAlloc(GetProcessHeap(),
141                                      HEAP_ZERO_MEMORY,
142                                      (wcslen(pszStart) + 1) * sizeof(WCHAR));
143     if (pEntry->pszShortName == NULL)
144     {
145         dwError = ERROR_OUTOFMEMORY;
146         goto done;
147     }
148 
149     wcscpy(pEntry->pszShortName, pszStart);
150 
151     pEntry->pszValueName = HeapAlloc(GetProcessHeap(),
152                                      HEAP_ZERO_MEMORY,
153                                      (wcslen(pEntry->pszShortName) + 1) * sizeof(WCHAR));
154     if (pEntry->pszValueName == NULL)
155     {
156         dwError = ERROR_OUTOFMEMORY;
157         goto done;
158     }
159 
160     wcscpy(pEntry->pszValueName, pEntry->pszShortName);
161 
162     pszEnd = wcsrchr(pEntry->pszValueName, L'.');
163     if (pszEnd != NULL)
164         *pszEnd = UNICODE_NULL;
165 
166     if (pDllListTail == NULL)
167     {
168         pEntry->pPrev = NULL;
169         pEntry->pNext = NULL;
170         pDllListHead = pEntry;
171         pDllListTail = pEntry;
172     }
173     else
174     {
175         pEntry->pPrev = NULL;
176         pEntry->pNext = pDllListHead;
177         pDllListHead->pPrev = pEntry;
178         pDllListHead = pEntry;
179     }
180 
181     bInserted = TRUE;
182 
183     pEntry->hModule = LoadLibraryW(pEntry->pszDllName);
184     if (pEntry->hModule == NULL)
185     {
186         dwError = GetLastError();
187         DPRINT1("Could not load the helper dll %S (Error: %lu)\n", pEntry->pszDllName, dwError);
188         goto done;
189     }
190 
191     pInitHelperDll = (PNS_DLL_INIT_FN)GetProcAddress(pEntry->hModule, "InitHelperDll");
192     if (pInitHelperDll == NULL)
193     {
194         dwError = GetLastError();
195         DPRINT1("Could not find 'InitHelperDll' (Error: %lu)\n", dwError);
196         goto done;
197     }
198 
199     pCurrentDll = pEntry;
200     dwError = pInitHelperDll(5, NULL);
201     pCurrentDll = NULL;
202 
203     DPRINT1("InitHelperDll returned %lu\n", dwError);
204     if (dwError != ERROR_SUCCESS)
205     {
206         DPRINT1("Call to InitHelperDll failed (Error: %lu)\n", dwError);
207         goto done;
208     }
209 
210 //    if (pEntry->Attributes.pfnStart)
211 //        pEntry->Attributes.pfnStart(NULL, 0);
212 
213     if (bRegister)
214         RegisterHelperDll(pEntry);
215 
216 done:
217     if (dwError != ERROR_SUCCESS)
218     {
219         if (bInserted)
220         {
221             if (pEntry->pPrev != NULL)
222                 pEntry->pPrev->pNext = pEntry->pNext;
223             if (pEntry->pNext != NULL)
224                 pEntry->pNext->pPrev = pEntry->pPrev;
225             if (pDllListTail == pEntry)
226                 pDllListTail = pEntry->pPrev;
227             if (pDllListHead == pEntry)
228                 pDllListHead = pEntry->pNext;
229             pEntry->pPrev = NULL;
230             pEntry->pNext = NULL;
231         }
232 
233         FreeHelperDll(pEntry);
234     }
235 
236     return dwError;
237 }
238 
239 
240 VOID
241 LoadHelpers(VOID)
242 {
243     PWSTR pszNameBuffer = NULL;
244     PWSTR pszValueBuffer = NULL;
245     HKEY hKey;
246     DWORD dwValueCount, dwMaxNameLength, dwMaxValueLength;
247     DWORD dwNameLength, dwValueLength, dwType;
248     DWORD dwIndex, dwError;
249 
250     DPRINT1("LoadHelpers()\n");
251 
252     dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
253                             REG_NETSH_PATH,
254                             0,
255                             KEY_READ,
256                             &hKey);
257     if (dwError != ERROR_SUCCESS)
258         return;
259 
260     dwError = RegQueryInfoKeyW(hKey,
261                                NULL,
262                                NULL,
263                                NULL,
264                                NULL,
265                                NULL,
266                                NULL,
267                                &dwValueCount,
268                                &dwMaxNameLength,
269                                &dwMaxValueLength,
270                                NULL,
271                                NULL);
272     if (dwError != ERROR_SUCCESS)
273         goto done;
274 
275     pszNameBuffer = HeapAlloc(GetProcessHeap(), 0,
276                               (dwMaxNameLength + 1) * sizeof(WCHAR));
277     if (pszNameBuffer == NULL)
278         goto done;
279 
280     pszValueBuffer = HeapAlloc(GetProcessHeap(), 0,
281                                dwMaxValueLength + sizeof(WCHAR));
282     if (pszValueBuffer == NULL)
283         goto done;
284 
285     for (dwIndex = 0; dwIndex < dwValueCount; dwIndex++)
286     {
287         dwNameLength = dwMaxNameLength + 1;
288         dwValueLength = dwMaxValueLength + sizeof(WCHAR);
289         dwError = RegEnumValueW(hKey,
290                                 dwIndex,
291                                 pszNameBuffer,
292                                 &dwNameLength,
293                                 NULL,
294                                 &dwType,
295                                 (PBYTE)pszValueBuffer,
296                                 &dwValueLength);
297         if (dwError != ERROR_SUCCESS)
298             break;
299 
300         DPRINT1("Dll: %S --> %S  %lu\n", pszNameBuffer, pszValueBuffer, dwError);
301         LoadHelperDll(pszValueBuffer, FALSE);
302     }
303 
304 done:
305     if (pszValueBuffer)
306         HeapFree(GetProcessHeap(), 0, pszValueBuffer);
307 
308     if (pszNameBuffer)
309         HeapFree(GetProcessHeap(), 0, pszNameBuffer);
310 
311     RegCloseKey(hKey);
312 
313     StartHelpers();
314 }
315 
316 
317 VOID
318 UnloadHelpers(VOID)
319 {
320     PDLL_LIST_ENTRY pEntry;
321 
322     while (pDllListHead != NULL)
323     {
324         pEntry = pDllListHead;
325         pDllListHead = pEntry->pNext;
326 
327 //        if (pEntry->Attributes.pfnStop)
328 //            pEntry->Attributes.pfnStop(0);
329 
330         FreeHelperDll(pEntry);
331     }
332 
333     pDllListTail = NULL;
334 }
335 
336 
337 PHELPER_ENTRY
338 FindHelper(
339     _In_ const GUID *pguidHelper)
340 {
341     PHELPER_ENTRY pHelper;
342 
343     pHelper = pHelperListHead;
344     while (pHelper != NULL)
345     {
346         if (IsEqualGUID(pguidHelper, &pHelper->Attributes.guidHelper))
347             return pHelper;
348 
349         pHelper = pHelper->pNext;
350     }
351 
352     return NULL;
353 }
354 
355 
356 DWORD
357 WINAPI
358 RegisterHelper(
359     _In_ const GUID *pguidParentHelper,
360     _In_ const NS_HELPER_ATTRIBUTES *pHelperAttributes)
361 {
362     PHELPER_ENTRY pHelper = NULL, pParentHelper;
363     DWORD dwError = ERROR_SUCCESS;
364 
365     DPRINT("RegisterHelper(%p %p)\n", pguidParentHelper, pHelperAttributes);
366 
367     if (FindHelper(&pHelperAttributes->guidHelper) != NULL)
368     {
369         DPRINT1("The Helper has already been registered!\n");
370         return 1;
371     }
372 
373     pHelper = (PHELPER_ENTRY)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HELPER_ENTRY));
374     if (pHelper == NULL)
375     {
376         dwError = ERROR_OUTOFMEMORY;
377         goto done;
378     }
379 
380     CopyMemory(&pHelper->Attributes, pHelperAttributes, sizeof(NS_HELPER_ATTRIBUTES));
381     pHelper->pDllEntry = pCurrentDll;
382     DPRINT("pHelper->pDllEntry: %p\n", pHelper->pDllEntry);
383 
384     if (pguidParentHelper == NULL)
385     {
386         if (pHelperListTail == NULL)
387         {
388             pHelperListHead = pHelper;
389             pHelperListTail = pHelper;
390         }
391         else
392         {
393             pHelper->pNext = pHelperListHead;
394             pHelperListHead->pPrev = pHelper;
395             pHelperListHead = pHelper;
396         }
397     }
398     else
399     {
400         pParentHelper = FindHelper(&pHelperAttributes->guidHelper);
401         if (pParentHelper == NULL)
402             return ERROR_INVALID_PARAMETER;
403 
404         if (pParentHelper->pSubHelperHead == NULL && pParentHelper->pSubHelperTail == NULL)
405         {
406             pParentHelper->pSubHelperHead = pHelper;
407             pParentHelper->pSubHelperTail = pHelper;
408         }
409         else
410         {
411             pHelper->pPrev = pParentHelper->pSubHelperTail;
412             pParentHelper->pSubHelperTail->pNext = pHelper;
413             pParentHelper->pSubHelperTail = pHelper;
414         }
415     }
416 
417 done:
418 
419     return dwError;
420 }
421 
422 
423 DWORD
424 WINAPI
425 AddHelperCommand(
426     LPCWSTR pwszMachine,
427     LPWSTR *ppwcArguments,
428     DWORD dwCurrentIndex,
429     DWORD dwArgCount,
430     DWORD dwFlags,
431     LPCVOID pvData,
432     BOOL *pbDone)
433 {
434     DWORD dwError = ERROR_SUCCESS;
435 
436     DPRINT("AddHelperCommand()\n");
437 
438     if (dwArgCount == 2)
439     {
440 //        ConResPrintf(StdErr, IDS_INVALID_SYNTAX);
441 //        ConResPrintf(StdErr, IDS_HLP_ADD_HELPER_EX);
442         return 1;
443     }
444 
445     dwError = LoadHelperDll(ppwcArguments[2], TRUE);
446     if (dwError != ERROR_SUCCESS)
447         return dwError;
448 
449     StartHelpers();
450 
451     return ERROR_SUCCESS;
452 }
453 
454 
455 DWORD
456 WINAPI
457 DeleteHelperCommand(
458     LPCWSTR pwszMachine,
459     LPWSTR *ppwcArguments,
460     DWORD dwCurrentIndex,
461     DWORD dwArgCount,
462     DWORD dwFlags,
463     LPCVOID pvData,
464     BOOL *pbDone)
465 {
466     PDLL_LIST_ENTRY pEntry;
467     HKEY hKey;
468     DWORD dwError;
469 
470     DPRINT("DeleteHelper()\n");
471 
472     if (dwArgCount == 2)
473     {
474 //        ConResPrintf(StdErr, IDS_INVALID_SYNTAX);
475 //        ConResPrintf(StdErr, IDS_HLP_DEL_HELPER_EX);
476         return 1;
477     }
478 
479     pEntry = pDllListHead;
480     while (pEntry != NULL)
481     {
482         if (wcscmp(pEntry->pszShortName, ppwcArguments[2]) == 0)
483         {
484             DPRINT1("remove %S\n", pEntry->pszShortName);
485 
486             if (pEntry->pPrev != NULL)
487                 pEntry->pPrev->pNext = pEntry->pNext;
488             if (pEntry->pNext != NULL)
489                 pEntry->pNext->pPrev = pEntry->pPrev;
490             if (pDllListTail == pEntry)
491                 pDllListTail = pEntry->pPrev;
492             if (pDllListHead == pEntry)
493                 pDllListHead = pEntry->pNext;
494             pEntry->pPrev = NULL;
495             pEntry->pNext = NULL;
496 
497             dwError = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
498                                     REG_NETSH_PATH,
499                                     0,
500                                     KEY_WRITE,
501                                     &hKey);
502             if (dwError == ERROR_SUCCESS)
503             {
504                 RegDeleteValue(hKey, pEntry->pszValueName);
505                 RegCloseKey(hKey);
506             }
507 
508             FreeHelperDll(pEntry);
509 
510             return 1;
511         }
512 
513         pEntry = pEntry->pNext;
514     }
515 
516     return ERROR_SUCCESS;
517 }
518 
519 
520 static
521 VOID
522 PrintSubContext(
523     _In_ PCONTEXT_ENTRY pParentContext,
524     _In_ DWORD dwLevel)
525 {
526     PCONTEXT_ENTRY pContext;
527     PHELPER_ENTRY pHelper;
528     WCHAR szPrefix[22];
529     DWORD i;
530 
531     if (pParentContext == NULL)
532         return;
533 
534     pContext = pParentContext->pSubContextHead;
535     while (pContext != NULL)
536     {
537         pHelper = FindHelper(&pContext->Guid);
538         if (pHelper != NULL)
539         {
540             if (dwLevel > 10)
541                 dwLevel = 10;
542 
543             for (i = 0; i < dwLevel * 2; i++)
544                 szPrefix[i] = L' ';
545             szPrefix[i] = UNICODE_NULL;
546 
547             ConPrintf(StdOut, L"{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}  %-16s  %s%s\n",
548                 pHelper->Attributes.guidHelper.Data1,
549                 pHelper->Attributes.guidHelper.Data2,
550                 pHelper->Attributes.guidHelper.Data3,
551                 pHelper->Attributes.guidHelper.Data4[0],
552                 pHelper->Attributes.guidHelper.Data4[1],
553                 pHelper->Attributes.guidHelper.Data4[2],
554                 pHelper->Attributes.guidHelper.Data4[3],
555                 pHelper->Attributes.guidHelper.Data4[4],
556                 pHelper->Attributes.guidHelper.Data4[5],
557                 pHelper->Attributes.guidHelper.Data4[6],
558                 pHelper->Attributes.guidHelper.Data4[7],
559                 pHelper->pDllEntry->pszShortName,
560                 szPrefix,
561                 pContext->pszContextName);
562         }
563 
564         PrintSubContext(pContext, dwLevel + 1);
565 
566         pContext = pContext->pNext;
567     }
568 }
569 
570 
571 DWORD
572 WINAPI
573 ShowHelperCommand(
574     LPCWSTR pwszMachine,
575     LPWSTR *ppwcArguments,
576     DWORD dwCurrentIndex,
577     DWORD dwArgCount,
578     DWORD dwFlags,
579     LPCVOID pvData,
580     BOOL *pbDone)
581 {
582     DPRINT("ShowHelperCommand()\n");
583 
584     ConPrintf(StdOut, L"Helper GUID                             DLL Name          Command\n");
585     ConPrintf(StdOut, L"--------------------------------------  ----------------  --------\n");
586 
587     if (pRootContext == NULL)
588         return ERROR_SUCCESS;
589 
590     PrintSubContext(pRootContext, 0);
591 
592     return ERROR_SUCCESS;
593 }
594