1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         GPLv2+ - See COPYING in the top level directory
4  * PURPOSE:         Test for service networking
5  * PROGRAMMER:      Pierre Schweitzer
6  */
7 
8 #include "precomp.h"
9 
10 #include "svchlp.h"
11 
12 #define WIN32_NO_STATUS
13 #include <iphlpapi.h>
14 #include <winsock2.h>
15 
16 /*** Service part of the test ***/
17 
18 static SERVICE_STATUS_HANDLE status_handle;
19 
20 static void
21 report_service_status(DWORD dwCurrentState,
22                       DWORD dwWin32ExitCode,
23                       DWORD dwWaitHint)
24 {
25     BOOL res;
26     SERVICE_STATUS status;
27 
28     status.dwServiceType   = SERVICE_WIN32_OWN_PROCESS;
29     status.dwCurrentState  = dwCurrentState;
30     status.dwWin32ExitCode = dwWin32ExitCode;
31     status.dwWaitHint      = dwWaitHint;
32 
33     status.dwServiceSpecificExitCode = 0;
34     status.dwCheckPoint              = 0;
35 
36     if ( (dwCurrentState == SERVICE_START_PENDING) ||
37          (dwCurrentState == SERVICE_STOP_PENDING)  ||
38          (dwCurrentState == SERVICE_STOPPED) )
39     {
40         status.dwControlsAccepted = 0;
41     }
42     else
43     {
44         status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
45     }
46 
47 #if 0
48     if ( (dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED) )
49         status.dwCheckPoint = 0;
50     else
51         status.dwCheckPoint = dwCheckPoint++;
52 #endif
53 
54     res = SetServiceStatus(status_handle, &status);
55     service_ok(res, "SetServiceStatus(%d) failed: %lu\n", dwCurrentState, GetLastError());
56 }
57 
58 static VOID WINAPI service_handler(DWORD ctrl)
59 {
60     switch(ctrl)
61     {
62     case SERVICE_CONTROL_STOP:
63     case SERVICE_CONTROL_SHUTDOWN:
64         report_service_status(SERVICE_STOP_PENDING, NO_ERROR, 0);
65     default:
66         report_service_status(SERVICE_RUNNING, NO_ERROR, 0);
67     }
68 }
69 
70 static DWORD GetExtendedTcpTableWithAlloc(PVOID *TcpTable, BOOL Order, DWORD Family, TCP_TABLE_CLASS Class)
71 {
72     DWORD ret;
73     DWORD Size = 0;
74 
75     *TcpTable = NULL;
76 
77     ret = GetExtendedTcpTable(*TcpTable, &Size, Order, Family, Class, 0);
78     if (ret == ERROR_INSUFFICIENT_BUFFER)
79     {
80         *TcpTable = HeapAlloc(GetProcessHeap(), 0, Size);
81         if (*TcpTable == NULL)
82         {
83             return ERROR_OUTOFMEMORY;
84         }
85 
86         ret = GetExtendedTcpTable(*TcpTable, &Size, Order, Family, Class, 0);
87         if (ret != NO_ERROR)
88         {
89             HeapFree(GetProcessHeap(), 0, *TcpTable);
90             *TcpTable = NULL;
91         }
92     }
93 
94     return ret;
95 }
96 
97 static DWORD GetExtendedUdpTableWithAlloc(PVOID *UdpTable, BOOL Order, DWORD Family, UDP_TABLE_CLASS Class)
98 {
99     DWORD ret;
100     DWORD Size = 0;
101 
102     *UdpTable = NULL;
103 
104     ret = GetExtendedUdpTable(*UdpTable, &Size, Order, Family, Class, 0);
105     if (ret == ERROR_INSUFFICIENT_BUFFER)
106     {
107         *UdpTable = HeapAlloc(GetProcessHeap(), 0, Size);
108         if (*UdpTable == NULL)
109         {
110             return ERROR_OUTOFMEMORY;
111         }
112 
113         ret = GetExtendedUdpTable(*UdpTable, &Size, Order, Family, Class, 0);
114         if (ret != NO_ERROR)
115         {
116             HeapFree(GetProcessHeap(), 0, *UdpTable);
117             *UdpTable = NULL;
118         }
119     }
120 
121     return ret;
122 }
123 
124 static void
125 test_tcp(LPWSTR svc_name, DWORD service_tag)
126 {
127     SOCKET sock;
128     SOCKADDR_IN server;
129     PMIB_TCPTABLE_OWNER_MODULE TcpTableOwnerMod;
130     DWORD i, ret;
131     BOOLEAN Found;
132     DWORD Pid = GetCurrentProcessId();
133 
134     sock = socket(AF_INET, SOCK_STREAM, 0);
135     service_ok(sock != INVALID_SOCKET, "Socket creation failed!\n");
136 
137     ZeroMemory(&server, sizeof(SOCKADDR_IN));
138     server.sin_family = AF_INET;
139     server.sin_addr.s_addr = htonl(INADDR_ANY);
140     server.sin_port = htons(9876);
141 
142     ret = bind(sock, (SOCKADDR*)&server, sizeof(SOCKADDR_IN));
143     service_ok(ret != SOCKET_ERROR, "binding failed\n");
144 
145     ret = listen(sock, SOMAXCONN);
146     service_ok(ret != SOCKET_ERROR, "listening failed\n");
147 
148     ret = GetExtendedTcpTableWithAlloc((PVOID *)&TcpTableOwnerMod, TRUE, AF_INET, TCP_TABLE_OWNER_MODULE_LISTENER);
149     service_ok(ret == ERROR_SUCCESS, "GetExtendedTcpTableWithAlloc failed: %x\n", ret);
150     if (ret == ERROR_SUCCESS)
151     {
152         service_ok(TcpTableOwnerMod->dwNumEntries > 0, "No TCP connections?!\n");
153 
154         Found = FALSE;
155         for (i = 0; i < TcpTableOwnerMod->dwNumEntries; ++i)
156         {
157             if (TcpTableOwnerMod->table[i].dwState == MIB_TCP_STATE_LISTEN &&
158                 TcpTableOwnerMod->table[i].dwLocalAddr == 0 &&
159                 TcpTableOwnerMod->table[i].dwLocalPort == htons(9876) &&
160                 TcpTableOwnerMod->table[i].dwRemoteAddr == 0)
161             {
162                 Found = TRUE;
163                 break;
164             }
165         }
166 
167         service_ok(Found, "Our socket wasn't found!\n");
168         if (Found)
169         {
170             DWORD Size = 0;
171             PTCPIP_OWNER_MODULE_BASIC_INFO BasicInfo = NULL;
172 
173             service_ok(TcpTableOwnerMod->table[i].dwOwningPid == Pid, "Invalid owner\n");
174             service_ok((DWORD)(TcpTableOwnerMod->table[i].OwningModuleInfo[0]) == service_tag, "Invalid tag: %x - %x\n", (DWORD)TcpTableOwnerMod->table[i].OwningModuleInfo[0], service_tag);
175 
176             ret = GetOwnerModuleFromTcpEntry(&TcpTableOwnerMod->table[i], TCPIP_OWNER_MODULE_INFO_BASIC, BasicInfo, &Size);
177             service_ok(ret == ERROR_INSUFFICIENT_BUFFER, "GetOwnerModuleFromTcpEntry failed with: %x\n", ret);
178             if (ERROR_INSUFFICIENT_BUFFER == ERROR_INSUFFICIENT_BUFFER)
179             {
180                 BasicInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
181                 service_ok(BasicInfo != NULL, "HeapAlloc failed\n");
182 
183                 ret = GetOwnerModuleFromTcpEntry(&TcpTableOwnerMod->table[i], TCPIP_OWNER_MODULE_INFO_BASIC, BasicInfo, &Size);
184                 service_ok(ret == ERROR_SUCCESS, "GetOwnerModuleFromTcpEntry failed with: %x\n", ret);
185                 service_ok(_wcsicmp(svc_name, BasicInfo->pModulePath) == 0, "Mismatching names (%S, %S)\n", svc_name, BasicInfo->pModulePath);
186                 service_ok(_wcsicmp(svc_name, BasicInfo->pModuleName) == 0, "Mismatching names (%S, %S)\n", svc_name, BasicInfo->pModuleName);
187             }
188         }
189 
190         HeapFree(GetProcessHeap(), 0, TcpTableOwnerMod);
191     }
192 
193     closesocket(sock);
194 }
195 
196 static void
197 test_udp(LPWSTR svc_name, DWORD service_tag)
198 {
199     SOCKET sock;
200     SOCKADDR_IN server;
201     PMIB_UDPTABLE_OWNER_MODULE UdpTableOwnerMod;
202     DWORD i, ret;
203     BOOLEAN Found;
204     DWORD Pid = GetCurrentProcessId();
205 
206     sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
207     service_ok(sock != INVALID_SOCKET, "Socket creation failed!\n");
208 
209     ZeroMemory(&server, sizeof(SOCKADDR_IN));
210     server.sin_family = AF_INET;
211     server.sin_addr.s_addr = htonl(INADDR_ANY);
212     server.sin_port = htons(9876);
213 
214     ret = bind(sock, (SOCKADDR*)&server, sizeof(SOCKADDR_IN));
215     service_ok(ret != SOCKET_ERROR, "binding failed\n");
216 
217     ret = GetExtendedUdpTableWithAlloc((PVOID *)&UdpTableOwnerMod, TRUE, AF_INET, UDP_TABLE_OWNER_MODULE);
218     service_ok(ret == ERROR_SUCCESS, "GetExtendedUdpTableWithAlloc failed: %x\n", ret);
219     if (ret == ERROR_SUCCESS)
220     {
221         service_ok(UdpTableOwnerMod->dwNumEntries > 0, "No TCP connections?!\n");
222 
223         Found = FALSE;
224         for (i = 0; i < UdpTableOwnerMod->dwNumEntries; ++i)
225         {
226             if (UdpTableOwnerMod->table[i].dwLocalAddr == 0 &&
227                 UdpTableOwnerMod->table[i].dwLocalPort == htons(9876))
228             {
229                 Found = TRUE;
230                 break;
231             }
232         }
233 
234         service_ok(Found, "Our socket wasn't found!\n");
235         if (Found)
236         {
237             DWORD Size = 0;
238             PTCPIP_OWNER_MODULE_BASIC_INFO BasicInfo = NULL;
239 
240             service_ok(UdpTableOwnerMod->table[i].dwOwningPid == Pid, "Invalid owner\n");
241             service_ok((DWORD)(UdpTableOwnerMod->table[i].OwningModuleInfo[0]) == service_tag, "Invalid tag: %x - %x\n", (DWORD)UdpTableOwnerMod->table[i].OwningModuleInfo[0], service_tag);
242 
243             ret = GetOwnerModuleFromUdpEntry(&UdpTableOwnerMod->table[i], TCPIP_OWNER_MODULE_INFO_BASIC, BasicInfo, &Size);
244             service_ok(ret == ERROR_INSUFFICIENT_BUFFER, "GetOwnerModuleFromUdpEntry failed with: %x\n", ret);
245             if (ret == ERROR_INSUFFICIENT_BUFFER)
246             {
247                 BasicInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, Size);
248                 service_ok(BasicInfo != NULL, "HeapAlloc failed\n");
249 
250                 ret = GetOwnerModuleFromUdpEntry(&UdpTableOwnerMod->table[i], TCPIP_OWNER_MODULE_INFO_BASIC, BasicInfo, &Size);
251                 service_ok(ret == ERROR_SUCCESS, "GetOwnerModuleFromUdpEntry failed with: %x\n", ret);
252                 service_ok(_wcsicmp(svc_name, BasicInfo->pModulePath) == 0, "Mismatching names (%S, %S)\n", svc_name, BasicInfo->pModulePath);
253                 service_ok(_wcsicmp(svc_name, BasicInfo->pModuleName) == 0, "Mismatching names (%S, %S)\n", svc_name, BasicInfo->pModuleName);
254             }
255         }
256 
257         HeapFree(GetProcessHeap(), 0, UdpTableOwnerMod);
258     }
259 
260     closesocket(sock);
261 }
262 
263 static void WINAPI
264 service_main(DWORD dwArgc, LPWSTR* lpszArgv)
265 {
266     // SERVICE_STATUS_HANDLE status_handle;
267     PTEB Teb;
268     WSADATA wsaData;
269 
270     UNREFERENCED_PARAMETER(dwArgc);
271 
272     /* Register our service for control (lpszArgv[0] holds the service name) */
273     status_handle = RegisterServiceCtrlHandlerW(lpszArgv[0], service_handler);
274     service_ok(status_handle != NULL, "RegisterServiceCtrlHandler failed: %lu\n", GetLastError());
275     if (!status_handle)
276         return;
277 
278     /* Report SERVICE_RUNNING status */
279     report_service_status(SERVICE_RUNNING, NO_ERROR, 4000);
280 
281     /* Check our tag is not 0 */
282     Teb = NtCurrentTeb();
283     service_ok(Teb->SubProcessTag != 0, "SubProcessTag is not defined!\n");
284 
285     if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
286     {
287         skip("Failed to init WS2\n");
288         goto quit;
289     }
290 
291     test_tcp(lpszArgv[0], PtrToUlong(Teb->SubProcessTag));
292     test_udp(lpszArgv[0], PtrToUlong(Teb->SubProcessTag));
293 
294     WSACleanup();
295 quit:
296     /* Work is done */
297     report_service_status(SERVICE_STOPPED, NO_ERROR, 0);
298 }
299 
300 static BOOL start_service(PCSTR service_nameA, PCWSTR service_nameW)
301 {
302     BOOL res;
303 
304     SERVICE_TABLE_ENTRYW servtbl[] =
305     {
306         { (PWSTR)service_nameW, service_main },
307         { NULL, NULL }
308     };
309 
310     res = StartServiceCtrlDispatcherW(servtbl);
311     service_ok(res, "StartServiceCtrlDispatcherW failed: %lu\n", GetLastError());
312     return res;
313 }
314 
315 
316 /*** Tester part of the test ***/
317 
318 static void
319 my_test_server(PCSTR service_nameA,
320                PCWSTR service_nameW,
321                void *param)
322 {
323     BOOL res;
324     SC_HANDLE hSC = NULL;
325     SC_HANDLE hService = NULL;
326     SERVICE_STATUS ServiceStatus;
327 
328     /* Open the SCM */
329     hSC = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
330     if (!hSC)
331     {
332         skip("OpenSCManagerW failed with error %lu!\n", GetLastError());
333         return;
334     }
335 
336     /* First create ourselves as a service running in the default LocalSystem account */
337     hService = register_service_exW(hSC, L"ServiceNetwork", service_nameW, NULL,
338                                     SERVICE_ALL_ACCESS,
339                                     SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
340                                     SERVICE_DEMAND_START,
341                                     SERVICE_ERROR_IGNORE,
342                                     NULL, NULL, NULL,
343                                     NULL, NULL);
344     if (!hService)
345     {
346         skip("CreateServiceW failed with error %lu!\n", GetLastError());
347         goto Cleanup;
348     }
349 
350     /* Start it */
351     if (!StartServiceW(hService, 0, NULL))
352     {
353         skip("StartServiceW failed with error %lu!\n", GetLastError());
354         goto Cleanup;
355     }
356 
357     /* Wait for the service to stop by itself */
358     do
359     {
360         Sleep(100);
361         ZeroMemory(&ServiceStatus, sizeof(ServiceStatus));
362         res = QueryServiceStatus(hService, &ServiceStatus);
363     } while (res && ServiceStatus.dwCurrentState != SERVICE_STOPPED);
364     ok(res, "QueryServiceStatus failed: %lu\n", GetLastError());
365     ok(ServiceStatus.dwCurrentState == SERVICE_STOPPED, "ServiceStatus.dwCurrentState = %lx\n", ServiceStatus.dwCurrentState);
366 
367     /* Be sure the service is really stopped */
368     res = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
369     if (!res && ServiceStatus.dwCurrentState != SERVICE_STOPPED &&
370         ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING &&
371         GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
372     {
373         skip("ControlService failed with error %lu!\n", GetLastError());
374         goto Cleanup;
375     }
376 
377 #if 0
378     trace("Service stopped. Going to restart it...\n");
379 
380     /* Now change the service configuration to make it start under the NetworkService account */
381     if (!ChangeServiceConfigW(hService,
382                               SERVICE_NO_CHANGE,
383                               SERVICE_NO_CHANGE,
384                               SERVICE_NO_CHANGE,
385                               NULL, NULL, NULL, NULL,
386                               L"NT AUTHORITY\\NetworkService", L"",
387                               NULL))
388     {
389         skip("ChangeServiceConfigW failed with error %lu!\n", GetLastError());
390         goto Cleanup;
391     }
392 
393     /* Start it */
394     if (!StartServiceW(hService, 0, NULL))
395     {
396         skip("StartServiceW failed with error %lu!\n", GetLastError());
397         goto Cleanup;
398     }
399 
400     /* Wait for the service to stop by itself */
401     do
402     {
403         Sleep(100);
404         ZeroMemory(&ServiceStatus, sizeof(ServiceStatus));
405         res = QueryServiceStatus(hService, &ServiceStatus);
406     } while (res && ServiceStatus.dwCurrentState != SERVICE_STOPPED);
407     ok(res, "QueryServiceStatus failed: %lu\n", GetLastError());
408     ok(ServiceStatus.dwCurrentState == SERVICE_STOPPED, "ServiceStatus.dwCurrentState = %lx\n", ServiceStatus.dwCurrentState);
409 
410     /* Be sure the service is really stopped */
411     res = ControlService(hService, SERVICE_CONTROL_STOP, &ServiceStatus);
412     if (!res && ServiceStatus.dwCurrentState != SERVICE_STOPPED &&
413         ServiceStatus.dwCurrentState != SERVICE_STOP_PENDING &&
414         GetLastError() != ERROR_SERVICE_NOT_ACTIVE)
415     {
416         skip("ControlService failed with error %lu!\n", GetLastError());
417         goto Cleanup;
418     }
419 #endif
420 
421 Cleanup:
422     if (hService)
423     {
424         res = DeleteService(hService);
425         ok(res, "DeleteService failed: %lu\n", GetLastError());
426         CloseServiceHandle(hService);
427     }
428 
429     if (hSC)
430         CloseServiceHandle(hSC);
431 }
432 
433 START_TEST(ServiceNetwork)
434 {
435     int argc;
436     char** argv;
437 
438     /* Check whether this test is started as a separated service process */
439     argc = winetest_get_mainargs(&argv);
440     if (argc >= 3)
441     {
442         service_process(start_service, argc, argv);
443         return;
444     }
445 
446     /* We are started as the real test */
447     test_runner(my_test_server, NULL);
448     // trace("Returned from test_runner\n");
449 }
450