xref: /reactos/base/services/dhcpcsvc/dhcpcsvc.c (revision 463784c5)
1 /*
2  * COPYRIGHT:       See COPYING in the top level directory
3  * PROJECT:         ReactOS system libraries
4  * FILE:            lib/dhcpcapi/dhcpcapi.c
5  * PURPOSE:         Client API for DHCP
6  * COPYRIGHT:       Copyright 2005 Art Yerkes <ayerkes@speakeasy.net>
7  */
8 
9 #include <rosdhcp.h>
10 #include <winsvc.h>
11 
12 #define NDEBUG
13 #include <debug.h>
14 
15 static WCHAR ServiceName[] = L"DHCP";
16 
17 SERVICE_STATUS_HANDLE ServiceStatusHandle = 0;
18 SERVICE_STATUS ServiceStatus;
19 HANDLE hStopEvent = NULL;
20 HANDLE hAdapterStateChangedEvent = NULL;
21 
22 static HANDLE PipeHandle = INVALID_HANDLE_VALUE;
23 extern SOCKET DhcpSocket;
24 
25 DWORD APIENTRY
26 DhcpCApiInitialize(LPDWORD Version)
27 {
28     DWORD PipeMode;
29 
30     /* Wait for the pipe to be available */
31     if (!WaitNamedPipeW(DHCP_PIPE_NAME, NMPWAIT_USE_DEFAULT_WAIT))
32     {
33         /* No good, we failed */
34         return GetLastError();
35     }
36 
37     /* It's available, let's try to open it */
38     PipeHandle = CreateFileW(DHCP_PIPE_NAME,
39                              GENERIC_READ | GENERIC_WRITE,
40                              FILE_SHARE_READ | FILE_SHARE_WRITE,
41                              NULL,
42                              OPEN_EXISTING,
43                              0,
44                              NULL);
45 
46     /* Check if we succeeded in opening the pipe */
47     if (PipeHandle == INVALID_HANDLE_VALUE)
48     {
49         /* We didn't */
50         return GetLastError();
51     }
52 
53     /* Change the pipe into message mode */
54     PipeMode = PIPE_READMODE_MESSAGE;
55     if (!SetNamedPipeHandleState(PipeHandle, &PipeMode, NULL, NULL))
56     {
57         /* Mode change failed */
58         CloseHandle(PipeHandle);
59         PipeHandle = INVALID_HANDLE_VALUE;
60         return GetLastError();
61     }
62     else
63     {
64         /* We're good to go */
65         *Version = 2;
66         return NO_ERROR;
67     }
68 }
69 
70 VOID APIENTRY
71 DhcpCApiCleanup(VOID)
72 {
73     CloseHandle(PipeHandle);
74     PipeHandle = INVALID_HANDLE_VALUE;
75 }
76 
77 DWORD APIENTRY
78 DhcpQueryHWInfo(DWORD AdapterIndex,
79                 PDWORD MediaType,
80                 PDWORD Mtu,
81                 PDWORD Speed)
82 {
83     COMM_DHCP_REQ Req;
84     COMM_DHCP_REPLY Reply;
85     DWORD BytesRead;
86     BOOL Result;
87 
88     ASSERT(PipeHandle != INVALID_HANDLE_VALUE);
89 
90     Req.Type = DhcpReqQueryHWInfo;
91     Req.AdapterIndex = AdapterIndex;
92 
93     Result = TransactNamedPipe(PipeHandle,
94                                &Req, sizeof(Req),
95                                &Reply, sizeof(Reply),
96                                &BytesRead, NULL);
97     if (!Result)
98     {
99         /* Pipe transaction failed */
100         return 0;
101     }
102 
103     if (Reply.Reply == 0)
104         return 0;
105 
106     *MediaType = Reply.QueryHWInfo.MediaType;
107     *Mtu = Reply.QueryHWInfo.Mtu;
108     *Speed = Reply.QueryHWInfo.Speed;
109     return 1;
110 }
111 
112 DWORD APIENTRY
113 DhcpLeaseIpAddress(DWORD AdapterIndex)
114 {
115     COMM_DHCP_REQ Req;
116     COMM_DHCP_REPLY Reply;
117     DWORD BytesRead;
118     BOOL Result;
119 
120     ASSERT(PipeHandle != INVALID_HANDLE_VALUE);
121 
122     Req.Type = DhcpReqLeaseIpAddress;
123     Req.AdapterIndex = AdapterIndex;
124 
125     Result = TransactNamedPipe(PipeHandle,
126                                &Req, sizeof(Req),
127                                &Reply, sizeof(Reply),
128                                &BytesRead, NULL);
129     if (!Result)
130     {
131         /* Pipe transaction failed */
132         return 0;
133     }
134 
135     return Reply.Reply;
136 }
137 
138 DWORD APIENTRY
139 DhcpReleaseIpAddressLease(DWORD AdapterIndex)
140 {
141     COMM_DHCP_REQ Req;
142     COMM_DHCP_REPLY Reply;
143     DWORD BytesRead;
144     BOOL Result;
145 
146     ASSERT(PipeHandle != INVALID_HANDLE_VALUE);
147 
148     Req.Type = DhcpReqReleaseIpAddress;
149     Req.AdapterIndex = AdapterIndex;
150 
151     Result = TransactNamedPipe(PipeHandle,
152                                &Req, sizeof(Req),
153                                &Reply, sizeof(Reply),
154                                &BytesRead, NULL);
155     if (!Result)
156     {
157         /* Pipe transaction failed */
158         return 0;
159     }
160 
161     return Reply.Reply;
162 }
163 
164 DWORD APIENTRY
165 DhcpRenewIpAddressLease(DWORD AdapterIndex)
166 {
167     COMM_DHCP_REQ Req;
168     COMM_DHCP_REPLY Reply;
169     DWORD BytesRead;
170     BOOL Result;
171 
172     ASSERT(PipeHandle != INVALID_HANDLE_VALUE);
173 
174     Req.Type = DhcpReqRenewIpAddress;
175     Req.AdapterIndex = AdapterIndex;
176 
177     Result = TransactNamedPipe(PipeHandle,
178                                &Req, sizeof(Req),
179                                &Reply, sizeof(Reply),
180                                &BytesRead, NULL);
181     if (!Result)
182     {
183         /* Pipe transaction failed */
184         return 0;
185     }
186 
187     return Reply.Reply;
188 }
189 
190 DWORD APIENTRY
191 DhcpStaticRefreshParams(DWORD AdapterIndex,
192                         DWORD Address,
193                         DWORD Netmask)
194 {
195     COMM_DHCP_REQ Req;
196     COMM_DHCP_REPLY Reply;
197     DWORD BytesRead;
198     BOOL Result;
199 
200     ASSERT(PipeHandle != INVALID_HANDLE_VALUE);
201 
202     Req.Type = DhcpReqStaticRefreshParams;
203     Req.AdapterIndex = AdapterIndex;
204     Req.Body.StaticRefreshParams.IPAddress = Address;
205     Req.Body.StaticRefreshParams.Netmask = Netmask;
206 
207     Result = TransactNamedPipe(PipeHandle,
208                                &Req, sizeof(Req),
209                                &Reply, sizeof(Reply),
210                                &BytesRead, NULL);
211     if (!Result)
212     {
213         /* Pipe transaction failed */
214         return 0;
215     }
216 
217     return Reply.Reply;
218 }
219 
220 /*!
221  * Set new TCP/IP parameters and notify DHCP client service of this
222  *
223  * \param[in] ServerName
224  *        NULL for local machine
225  *
226  * \param[in] AdapterName
227  *        IPHLPAPI name of adapter to change
228  *
229  * \param[in] NewIpAddress
230  *        TRUE if IP address changes
231  *
232  * \param[in] IpAddress
233  *        New IP address (network byte order)
234  *
235  * \param[in] SubnetMask
236  *        New subnet mask (network byte order)
237  *
238  * \param[in] DhcpAction
239  *        0 - don't modify
240  *        1 - enable DHCP
241  *        2 - disable DHCP
242  *
243  * \return non-zero on success
244  *
245  * \remarks Undocumented by Microsoft
246  */
247 DWORD APIENTRY
248 DhcpNotifyConfigChange(LPWSTR ServerName,
249                        LPWSTR AdapterName,
250                        BOOL NewIpAddress,
251                        DWORD IpIndex,
252                        DWORD IpAddress,
253                        DWORD SubnetMask,
254                        INT DhcpAction)
255 {
256     DPRINT1("DHCPCSVC: DhcpNotifyConfigChange not implemented yet\n");
257     return 0;
258 }
259 
260 DWORD APIENTRY
261 DhcpRequestParams(DWORD Flags,
262                   PVOID Reserved,
263                   LPWSTR AdapterName,
264                   LPDHCPCAPI_CLASSID ClassId,
265                   DHCPCAPI_PARAMS_ARRAY SendParams,
266                   DHCPCAPI_PARAMS_ARRAY RecdParams,
267                   LPBYTE Buffer,
268                   LPDWORD pSize,
269                   LPWSTR RequestIdStr)
270 {
271     UNIMPLEMENTED;
272     return 0;
273 }
274 
275 /*!
276  * Get DHCP info for an adapter
277  *
278  * \param[in] AdapterIndex
279  *        Index of the adapter (iphlpapi-style) for which info is
280  *        requested
281  *
282  * \param[out] DhcpEnabled
283  *        Returns whether DHCP is enabled for the adapter
284  *
285  * \param[out] DhcpServer
286  *        Returns DHCP server IP address (255.255.255.255 if no
287  *        server reached yet), in network byte order
288  *
289  * \param[out] LeaseObtained
290  *        Returns time at which the lease was obtained
291  *
292  * \param[out] LeaseExpires
293  *        Returns time at which the lease will expire
294  *
295  * \return non-zero on success
296  *
297  * \remarks This is a ReactOS-only routine
298  */
299 DWORD APIENTRY
300 DhcpRosGetAdapterInfo(DWORD AdapterIndex,
301                       PBOOL DhcpEnabled,
302                       PDWORD DhcpServer,
303                       time_t* LeaseObtained,
304                       time_t* LeaseExpires)
305 {
306     COMM_DHCP_REQ Req;
307     COMM_DHCP_REPLY Reply;
308     DWORD BytesRead;
309     BOOL Result;
310 
311     ASSERT(PipeHandle != INVALID_HANDLE_VALUE);
312 
313     Req.Type = DhcpReqGetAdapterInfo;
314     Req.AdapterIndex = AdapterIndex;
315 
316     Result = TransactNamedPipe(PipeHandle,
317                                &Req, sizeof(Req),
318                                &Reply, sizeof(Reply),
319                                &BytesRead, NULL);
320 
321     if (Result && Reply.Reply != 0)
322         *DhcpEnabled = Reply.GetAdapterInfo.DhcpEnabled;
323     else
324         *DhcpEnabled = FALSE;
325 
326     if (*DhcpEnabled)
327     {
328         *DhcpServer = Reply.GetAdapterInfo.DhcpServer;
329         *LeaseObtained = Reply.GetAdapterInfo.LeaseObtained;
330         *LeaseExpires = Reply.GetAdapterInfo.LeaseExpires;
331     }
332     else
333     {
334         *DhcpServer = INADDR_NONE;
335         *LeaseObtained = 0;
336         *LeaseExpires = 0;
337     }
338 
339     return Reply.Reply;
340 }
341 
342 
343 static VOID
344 UpdateServiceStatus(DWORD dwState)
345 {
346     ServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
347     ServiceStatus.dwCurrentState = dwState;
348 
349     if (dwState == SERVICE_RUNNING)
350         ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP;
351     else
352         ServiceStatus.dwControlsAccepted = 0;
353 
354     ServiceStatus.dwWin32ExitCode = 0;
355     ServiceStatus.dwServiceSpecificExitCode = 0;
356     ServiceStatus.dwCheckPoint = 0;
357 
358     if (dwState == SERVICE_START_PENDING ||
359         dwState == SERVICE_STOP_PENDING)
360         ServiceStatus.dwWaitHint = 1000;
361     else
362         ServiceStatus.dwWaitHint = 0;
363 
364     SetServiceStatus(ServiceStatusHandle,
365                      &ServiceStatus);
366 }
367 
368 static DWORD WINAPI
369 ServiceControlHandler(DWORD dwControl,
370                       DWORD dwEventType,
371                       LPVOID lpEventData,
372                       LPVOID lpContext)
373 {
374     switch (dwControl)
375     {
376         case SERVICE_CONTROL_STOP:
377         case SERVICE_CONTROL_SHUTDOWN:
378             UpdateServiceStatus(SERVICE_STOP_PENDING);
379             if (hStopEvent != NULL)
380                 SetEvent(hStopEvent);
381             return ERROR_SUCCESS;
382 
383         case SERVICE_CONTROL_INTERROGATE:
384             SetServiceStatus(ServiceStatusHandle,
385                              &ServiceStatus);
386             return ERROR_SUCCESS;
387 
388         default:
389             return ERROR_CALL_NOT_IMPLEMENTED;
390     }
391 }
392 
393 VOID WINAPI
394 ServiceMain(DWORD argc, LPWSTR* argv)
395 {
396     HANDLE hPipeThread = INVALID_HANDLE_VALUE;
397     HANDLE hDiscoveryThread = INVALID_HANDLE_VALUE;
398     DWORD ret;
399 
400     ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
401                                                         ServiceControlHandler,
402                                                         NULL);
403     if (!ServiceStatusHandle)
404     {
405         DPRINT1("DHCPCSVC: Unable to register service control handler (%lx)\n", GetLastError());
406         return;
407     }
408 
409     UpdateServiceStatus(SERVICE_START_PENDING);
410 
411     /* Create the stop event */
412     hStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
413     if (hStopEvent == NULL)
414     {
415         UpdateServiceStatus(SERVICE_STOPPED);
416         return;
417     }
418 
419     hAdapterStateChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
420     if (hAdapterStateChangedEvent == NULL)
421     {
422         CloseHandle(hStopEvent);
423         UpdateServiceStatus(SERVICE_STOPPED);
424         return;
425     }
426 
427     UpdateServiceStatus(SERVICE_START_PENDING);
428 
429     if (!init_client())
430     {
431         DbgPrint("DHCPCSVC: init_client() failed!\n");
432         CloseHandle(hAdapterStateChangedEvent);
433         CloseHandle(hStopEvent);
434         UpdateServiceStatus(SERVICE_STOPPED);
435         return;
436     }
437 
438     UpdateServiceStatus(SERVICE_START_PENDING);
439 
440     hPipeThread = PipeInit(hStopEvent);
441     if (hPipeThread == INVALID_HANDLE_VALUE)
442     {
443         DbgPrint("DHCPCSVC: PipeInit() failed!\n");
444         stop_client();
445         CloseHandle(hAdapterStateChangedEvent);
446         CloseHandle(hStopEvent);
447         UpdateServiceStatus(SERVICE_STOPPED);
448         return;
449     }
450 
451     hDiscoveryThread = StartAdapterDiscovery(hStopEvent);
452     if (hDiscoveryThread == INVALID_HANDLE_VALUE)
453     {
454         DbgPrint("DHCPCSVC: StartAdapterDiscovery() failed!\n");
455         stop_client();
456         CloseHandle(hAdapterStateChangedEvent);
457         CloseHandle(hStopEvent);
458         UpdateServiceStatus(SERVICE_STOPPED);
459         return;
460     }
461 
462     DH_DbgPrint(MID_TRACE,("DHCP Service Started\n"));
463 
464     UpdateServiceStatus(SERVICE_RUNNING);
465 
466     DH_DbgPrint(MID_TRACE,("Going into dispatch()\n"));
467     DH_DbgPrint(MID_TRACE,("DHCPCSVC: DHCP service is starting up\n"));
468 
469     dispatch(hStopEvent);
470 
471     DH_DbgPrint(MID_TRACE,("DHCPCSVC: DHCP service is shutting down\n"));
472 
473     stop_client();
474 
475     DPRINT("Wait for pipe thread to close! %p\n", hPipeThread);
476     if (hPipeThread != INVALID_HANDLE_VALUE)
477     {
478         DPRINT("Waiting for pipe thread\n");
479         ret = WaitForSingleObject(hPipeThread, 5000);
480         DPRINT("Done %lx\n", ret);
481     }
482 
483     DPRINT("Wait for discovery thread to close! %p\n", hDiscoveryThread);
484     if (hDiscoveryThread != INVALID_HANDLE_VALUE)
485     {
486         DPRINT("Waiting for discovery thread\n");
487         ret = WaitForSingleObject(hDiscoveryThread, 5000);
488         DPRINT("Done %lx\n", ret);
489     }
490 
491     DPRINT("Closing events!\n");
492     CloseHandle(hAdapterStateChangedEvent);
493     CloseHandle(hStopEvent);
494 
495     if (DhcpSocket != INVALID_SOCKET)
496         closesocket(DhcpSocket);
497 
498     CloseHandle(hDiscoveryThread);
499     CloseHandle(hPipeThread);
500 
501     DPRINT("Done!\n");
502 
503     UpdateServiceStatus(SERVICE_STOPPED);
504 }
505 
506 BOOL WINAPI
507 DllMain(HINSTANCE hinstDLL,
508         DWORD fdwReason,
509         LPVOID lpvReserved)
510 {
511     switch (fdwReason)
512     {
513         case DLL_PROCESS_ATTACH:
514             DisableThreadLibraryCalls(hinstDLL);
515             break;
516 
517         case DLL_PROCESS_DETACH:
518             break;
519     }
520 
521     return TRUE;
522 }
523 
524 /* EOF */
525