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