[DHCPCSVC] Make the DHCP client service stoppable

- Make all threads (pipe thread, adapter discovery thread and dispatcher thread) wait on the stop event.
- Close shared resources in the main (dispatcher) thread after the pipe thread and the adapter discovery thread have shut down.

This enables us to stop and restart the DHCP client properly.

CORE-14390
This commit is contained in:
Eric Kohl 2021-08-08 14:09:31 +02:00
parent f486f87796
commit 033b6639e4
7 changed files with 233 additions and 133 deletions

View file

@ -1,5 +1,10 @@
#include <rosdhcp.h>
#define NDEBUG
#include <reactos/debug.h>
extern HANDLE hAdapterStateChangedEvent;
SOCKET DhcpSocket = INVALID_SOCKET;
static LIST_ENTRY AdapterList;
static WSADATA wsd;
@ -287,14 +292,13 @@ DWORD WINAPI AdapterDiscoveryThread(LPVOID Context) {
PMIB_IFTABLE Table = (PMIB_IFTABLE) malloc(sizeof(MIB_IFTABLE));
DWORD Error, Size = sizeof(MIB_IFTABLE);
PDHCP_ADAPTER Adapter = NULL;
HANDLE AdapterStateChangedEvent = (HANDLE)Context;
HANDLE hStopEvent = (HANDLE)Context;
struct interface_info *ifi = NULL;
struct protocol *proto;
int i, AdapterCount = 0, Broadcast;
/* FIXME: Kill this thread when the service is stopped */
do {
while (TRUE)
{
DH_DbgPrint(MID_TRACE,("Getting Adapter List...\n"));
while( (Error = GetIfTable(Table, &Size, 0 )) ==
@ -346,7 +350,7 @@ DWORD WINAPI AdapterDiscoveryThread(LPVOID Context) {
got_one, &Adapter->DhclientInfo);
state_init(&Adapter->DhclientInfo);
SetEvent(AdapterStateChangedEvent);
SetEvent(hAdapterStateChangedEvent);
}
} else {
@ -444,7 +448,7 @@ DWORD WINAPI AdapterDiscoveryThread(LPVOID Context) {
ApiLock();
InsertTailList( &AdapterList, &Adapter->ListEntry );
AdapterCount++;
SetEvent(AdapterStateChangedEvent);
SetEvent(hAdapterStateChangedEvent);
ApiUnlock();
} else { free( Adapter ); Adapter = 0; }
} else { free( Adapter ); Adapter = 0; }
@ -458,43 +462,24 @@ DWORD WINAPI AdapterDiscoveryThread(LPVOID Context) {
if (Error != NO_ERROR)
break;
#else
Sleep(3000);
if (WaitForSingleObject(hStopEvent, 3000) == WAIT_OBJECT_0)
{
DPRINT("Stopping the discovery thread!\n");
break;
}
#endif
} while (TRUE);
}
DbgPrint("DHCPCSVC: Adapter discovery thread is terminating! (Error: %d)\n", Error);
if (Table)
free(Table);
DPRINT("Adapter discovery thread terminated! (Error: %d)\n", Error);
if( Table ) free( Table );
return Error;
}
HANDLE StartAdapterDiscovery(VOID) {
HANDLE ThreadHandle, EventHandle;
EventHandle = CreateEvent(NULL,
FALSE,
FALSE,
NULL);
if (EventHandle == NULL)
return NULL;
ThreadHandle = CreateThread(NULL,
0,
AdapterDiscoveryThread,
(LPVOID)EventHandle,
0,
NULL);
if (ThreadHandle == NULL)
{
CloseHandle(EventHandle);
return NULL;
}
CloseHandle(ThreadHandle);
return EventHandle;
HANDLE StartAdapterDiscovery(HANDLE hStopEvent) {
return CreateThread(NULL, 0, AdapterDiscoveryThread, (LPVOID)hStopEvent, 0, NULL);
}
void AdapterStop() {

View file

@ -13,7 +13,7 @@
static CRITICAL_SECTION ApiCriticalSection;
extern HANDLE AdapterStateChangedEvent;
extern HANDLE hAdapterStateChangedEvent;
VOID ApiInit() {
InitializeCriticalSection( &ApiCriticalSection );
@ -33,7 +33,7 @@ VOID ApiFree() {
/* This represents the service portion of the DHCP client API */
DWORD DSLeaseIpAddress( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
DWORD DSLeaseIpAddress( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req ) {
COMM_DHCP_REPLY Reply;
PDHCP_ADAPTER Adapter;
struct protocol* proto;
@ -56,16 +56,16 @@ DWORD DSLeaseIpAddress( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
Adapter->DhclientInfo.client->state = S_INIT;
state_reboot(&Adapter->DhclientInfo);
if (AdapterStateChangedEvent != NULL)
SetEvent(AdapterStateChangedEvent);
if (hAdapterStateChangedEvent != NULL)
SetEvent(hAdapterStateChangedEvent);
}
ApiUnlock();
return Send( &Reply );
return Send(CommPipe, &Reply );
}
DWORD DSQueryHWInfo( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
DWORD DSQueryHWInfo( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req ) {
COMM_DHCP_REPLY Reply;
PDHCP_ADAPTER Adapter;
@ -84,10 +84,10 @@ DWORD DSQueryHWInfo( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
ApiUnlock();
return Send( &Reply );
return Send(CommPipe, &Reply );
}
DWORD DSReleaseIpAddressLease( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
DWORD DSReleaseIpAddressLease( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req ) {
COMM_DHCP_REPLY Reply;
PDHCP_ADAPTER Adapter;
struct protocol* proto;
@ -117,16 +117,16 @@ DWORD DSReleaseIpAddressLease( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
Adapter->DhclientInfo.client->active = NULL;
Adapter->DhclientInfo.client->state = S_INIT;
if (AdapterStateChangedEvent != NULL)
SetEvent(AdapterStateChangedEvent);
if (hAdapterStateChangedEvent != NULL)
SetEvent(hAdapterStateChangedEvent);
}
ApiUnlock();
return Send( &Reply );
return Send(CommPipe, &Reply );
}
DWORD DSRenewIpAddressLease( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
DWORD DSRenewIpAddressLease( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req ) {
COMM_DHCP_REPLY Reply;
PDHCP_ADAPTER Adapter;
struct protocol* proto;
@ -138,7 +138,7 @@ DWORD DSRenewIpAddressLease( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
if( !Adapter || Adapter->DhclientState.state == S_STATIC ) {
Reply.Reply = 0;
ApiUnlock();
return Send( &Reply );
return Send(CommPipe, &Reply );
}
Reply.Reply = 1;
@ -154,15 +154,15 @@ DWORD DSRenewIpAddressLease( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
Adapter->DhclientInfo.client->state = S_INIT;
state_reboot(&Adapter->DhclientInfo);
if (AdapterStateChangedEvent != NULL)
SetEvent(AdapterStateChangedEvent);
if (hAdapterStateChangedEvent != NULL)
SetEvent(hAdapterStateChangedEvent);
ApiUnlock();
return Send( &Reply );
return Send(CommPipe, &Reply );
}
DWORD DSStaticRefreshParams( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
DWORD DSStaticRefreshParams( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req ) {
NTSTATUS Status;
COMM_DHCP_REPLY Reply;
PDHCP_ADAPTER Adapter;
@ -197,16 +197,16 @@ DWORD DSStaticRefreshParams( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
&Adapter->NteInstance );
Reply.Reply = NT_SUCCESS(Status);
if (AdapterStateChangedEvent != NULL)
SetEvent(AdapterStateChangedEvent);
if (hAdapterStateChangedEvent != NULL)
SetEvent(hAdapterStateChangedEvent);
}
ApiUnlock();
return Send( &Reply );
return Send(CommPipe, &Reply );
}
DWORD DSGetAdapterInfo( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
DWORD DSGetAdapterInfo( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req ) {
COMM_DHCP_REPLY Reply;
PDHCP_ADAPTER Adapter;
@ -240,5 +240,5 @@ DWORD DSGetAdapterInfo( PipeSendFunc Send, COMM_DHCP_REQ *Req ) {
ApiUnlock();
return Send( &Reply );
return Send(CommPipe, &Reply );
}

View file

@ -122,14 +122,6 @@ init_client(void)
inaddr_any.s_addr = INADDR_ANY;
bootp_packet_handler = do_packet;
if (PipeInit() == INVALID_HANDLE_VALUE)
{
DbgPrint("DHCPCSVC: PipeInit() failed!\n");
AdapterStop();
ApiFree();
return 0; // FALSE
}
return 1; // TRUE
}

View file

@ -41,6 +41,9 @@
#include <rosdhcp.h>
#define NDEBUG
#include <reactos/debug.h>
//#include <sys/ioctl.h>
//#include <net/if_media.h>
@ -48,7 +51,8 @@
//#include <poll.h>
extern SOCKET DhcpSocket;
HANDLE AdapterStateChangedEvent = NULL;
extern HANDLE hAdapterStateChangedEvent;
struct protocol *protocols = NULL;
struct timeout *timeouts = NULL;
static struct timeout *free_timeouts = NULL;
@ -71,12 +75,7 @@ dispatch(HANDLE hStopEvent)
HANDLE Events[3];
int EventCount = 2;
Events[0] = StartAdapterDiscovery();
if (!Events[0])
return;
AdapterStateChangedEvent = Events[0];
Events[0] = hAdapterStateChangedEvent;
Events[1] = hStopEvent;
Events[2] = WSA_INVALID_EVENT;
@ -157,6 +156,7 @@ dispatch(HANDLE hStopEvent)
else if (count == WAIT_OBJECT_0 + 1)
{
/* Stop event signalled */
DPRINT("Dispatch thread stop event!\n");
break;
}
else if (count == WAIT_OBJECT_0 + 2)
@ -183,12 +183,11 @@ dispatch(HANDLE hStopEvent)
}
} while (1);
AdapterStateChangedEvent = NULL;
CloseHandle(Events[0]);
CloseHandle(Events[1]);
WSACloseEvent(Events[2]);
ApiUnlock();
DPRINT("Dispatch thread stopped!\n");
}
void

View file

@ -11,21 +11,28 @@
#define NDEBUG
#include <reactos/debug.h>
static HANDLE CommPipe = INVALID_HANDLE_VALUE, CommThread;
DWORD CommThrId;
#define COMM_PIPE_OUTPUT_BUFFER sizeof(COMM_DHCP_REQ)
#define COMM_PIPE_INPUT_BUFFER sizeof(COMM_DHCP_REPLY)
#define COMM_PIPE_DEFAULT_TIMEOUT 1000
DWORD PipeSend( COMM_DHCP_REPLY *Reply ) {
DWORD PipeSend( HANDLE CommPipe, COMM_DHCP_REPLY *Reply ) {
DWORD Written = 0;
OVERLAPPED Overlapped = {0};
BOOL Success =
WriteFile( CommPipe,
Reply,
sizeof(*Reply),
&Written,
NULL );
&Overlapped);
if (!Success)
{
WaitForSingleObject(CommPipe, INFINITE);
Success = GetOverlappedResult(CommPipe,
&Overlapped,
&Written,
TRUE);
}
return Success ? Written : -1;
}
@ -34,10 +41,57 @@ DWORD WINAPI PipeThreadProc( LPVOID Parameter ) {
COMM_DHCP_REQ Req;
COMM_DHCP_REPLY Reply;
BOOL Result, Connected;
HANDLE Events[2];
HANDLE CommPipe;
OVERLAPPED Overlapped = {0};
DWORD dwError;
while( TRUE ) {
Connected = ConnectNamedPipe( CommPipe, NULL ) ?
TRUE : GetLastError() == ERROR_PIPE_CONNECTED;
DPRINT("PipeThreadProc(%p)\n", Parameter);
CommPipe = CreateNamedPipeW
( DHCP_PIPE_NAME,
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1,
COMM_PIPE_OUTPUT_BUFFER,
COMM_PIPE_INPUT_BUFFER,
COMM_PIPE_DEFAULT_TIMEOUT,
NULL );
if (CommPipe == INVALID_HANDLE_VALUE)
{
DbgPrint("DHCP: Could not create named pipe\n");
return FALSE;
}
Events[0] = (HANDLE)Parameter;
Events[1] = CommPipe;
while( TRUE )
{
Connected = ConnectNamedPipe(CommPipe, &Overlapped);
if (!Connected)
{
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
dwError = WaitForMultipleObjects(2, Events, FALSE, INFINITE);
DPRINT("WaitForMultipleObjects() returned %lu\n", dwError);
if (dwError == WAIT_OBJECT_0 + 1)
{
Connected = GetOverlappedResult(CommPipe,
&Overlapped,
&BytesRead,
TRUE);
}
else if (dwError == WAIT_OBJECT_0)
{
CancelIo(CommPipe);
CloseHandle(CommPipe);
CommPipe = INVALID_HANDLE_VALUE;
break;
}
}
}
if (!Connected) {
DbgPrint("DHCP: Could not connect named pipe\n");
@ -46,74 +100,75 @@ DWORD WINAPI PipeThreadProc( LPVOID Parameter ) {
break;
}
Result = ReadFile( CommPipe, &Req, sizeof(Req), &BytesRead, NULL );
Result = ReadFile(CommPipe, &Req, sizeof(Req), &BytesRead, &Overlapped);
if (!Result)
{
dwError = GetLastError();
if (dwError == ERROR_IO_PENDING)
{
dwError = WaitForMultipleObjects(2, Events, FALSE, INFINITE);
DPRINT("WaitForMultipleObjects() returned %lu\n", dwError);
if (dwError == WAIT_OBJECT_0 + 1)
{
Result = GetOverlappedResult(CommPipe,
&Overlapped,
&BytesRead,
TRUE);
}
else if (dwError == WAIT_OBJECT_0)
{
CancelIo(CommPipe);
DisconnectNamedPipe( CommPipe );
CloseHandle(CommPipe);
CommPipe = INVALID_HANDLE_VALUE;
break;
}
}
}
if( Result ) {
switch( Req.Type ) {
case DhcpReqQueryHWInfo:
DSQueryHWInfo( PipeSend, &Req );
DSQueryHWInfo( PipeSend, CommPipe, &Req );
break;
case DhcpReqLeaseIpAddress:
DSLeaseIpAddress( PipeSend, &Req );
DSLeaseIpAddress( PipeSend, CommPipe, &Req );
break;
case DhcpReqReleaseIpAddress:
DSReleaseIpAddressLease( PipeSend, &Req );
DSReleaseIpAddressLease( PipeSend, CommPipe, &Req );
break;
case DhcpReqRenewIpAddress:
DSRenewIpAddressLease( PipeSend, &Req );
DSRenewIpAddressLease( PipeSend, CommPipe, &Req );
break;
case DhcpReqStaticRefreshParams:
DSStaticRefreshParams( PipeSend, &Req );
DSStaticRefreshParams( PipeSend, CommPipe, &Req );
break;
case DhcpReqGetAdapterInfo:
DSGetAdapterInfo( PipeSend, &Req );
DSGetAdapterInfo( PipeSend, CommPipe, &Req );
break;
default:
DPRINT1("Unrecognized request type %d\n", Req.Type);
ZeroMemory( &Reply, sizeof( COMM_DHCP_REPLY ) );
Reply.Reply = 0;
PipeSend( &Reply );
PipeSend(CommPipe, &Reply );
break;
}
}
DisconnectNamedPipe( CommPipe );
}
DPRINT("Pipe thread stopped!\n");
return TRUE;
}
HANDLE PipeInit() {
CommPipe = CreateNamedPipeW
( DHCP_PIPE_NAME,
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1,
COMM_PIPE_OUTPUT_BUFFER,
COMM_PIPE_INPUT_BUFFER,
COMM_PIPE_DEFAULT_TIMEOUT,
NULL );
if( CommPipe == INVALID_HANDLE_VALUE ) {
DbgPrint("DHCP: Could not create named pipe\n");
return CommPipe;
}
CommThread = CreateThread( NULL, 0, PipeThreadProc, NULL, 0, &CommThrId );
if( !CommThread ) {
CloseHandle( CommPipe );
CommPipe = INVALID_HANDLE_VALUE;
}
return CommPipe;
}
VOID PipeDestroy() {
CloseHandle( CommPipe );
CommPipe = INVALID_HANDLE_VALUE;
HANDLE PipeInit(HANDLE hStopEvent)
{
return CreateThread( NULL, 0, PipeThreadProc, (LPVOID)hStopEvent, 0, NULL);
}

View file

@ -17,8 +17,10 @@ static WCHAR ServiceName[] = L"DHCP";
SERVICE_STATUS_HANDLE ServiceStatusHandle = 0;
SERVICE_STATUS ServiceStatus;
HANDLE hStopEvent = NULL;
HANDLE hAdapterStateChangedEvent = NULL;
static HANDLE PipeHandle = INVALID_HANDLE_VALUE;
extern SOCKET DhcpSocket;
DWORD APIENTRY
DhcpCApiInitialize(LPDWORD Version)
@ -391,6 +393,10 @@ ServiceControlHandler(DWORD dwControl,
VOID WINAPI
ServiceMain(DWORD argc, LPWSTR* argv)
{
HANDLE hPipeThread = INVALID_HANDLE_VALUE;
HANDLE hDiscoveryThread = INVALID_HANDLE_VALUE;
DWORD ret;
ServiceStatusHandle = RegisterServiceCtrlHandlerExW(ServiceName,
ServiceControlHandler,
NULL);
@ -403,18 +409,52 @@ ServiceMain(DWORD argc, LPWSTR* argv)
UpdateServiceStatus(SERVICE_START_PENDING);
/* Create the stop event */
hStopEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
hStopEvent = CreateEventW(NULL, TRUE, FALSE, NULL);
if (hStopEvent == NULL)
{
UpdateServiceStatus(SERVICE_STOPPED);
return;
}
hAdapterStateChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (hAdapterStateChangedEvent == NULL)
{
CloseHandle(hStopEvent);
UpdateServiceStatus(SERVICE_STOPPED);
return;
}
UpdateServiceStatus(SERVICE_START_PENDING);
if (!init_client())
{
DPRINT1("DHCPCSVC: init_client() failed!\n");
DbgPrint("DHCPCSVC: init_client() failed!\n");
CloseHandle(hAdapterStateChangedEvent);
CloseHandle(hStopEvent);
UpdateServiceStatus(SERVICE_STOPPED);
return;
}
UpdateServiceStatus(SERVICE_START_PENDING);
hPipeThread = PipeInit(hStopEvent);
if (hPipeThread == INVALID_HANDLE_VALUE)
{
DbgPrint("DHCPCSVC: PipeInit() failed!\n");
stop_client();
CloseHandle(hAdapterStateChangedEvent);
CloseHandle(hStopEvent);
UpdateServiceStatus(SERVICE_STOPPED);
return;
}
hDiscoveryThread = StartAdapterDiscovery(hStopEvent);
if (hDiscoveryThread == INVALID_HANDLE_VALUE)
{
DbgPrint("DHCPCSVC: StartAdapterDiscovery() failed!\n");
stop_client();
CloseHandle(hAdapterStateChangedEvent);
CloseHandle(hStopEvent);
UpdateServiceStatus(SERVICE_STOPPED);
return;
}
@ -429,8 +469,37 @@ ServiceMain(DWORD argc, LPWSTR* argv)
dispatch(hStopEvent);
DH_DbgPrint(MID_TRACE,("DHCPCSVC: DHCP service is shutting down\n"));
stop_client();
DPRINT("Wait for pipe thread to close! %p\n", hPipeThread);
if (hPipeThread != INVALID_HANDLE_VALUE)
{
DPRINT("Waiting for pipe thread\n");
ret = WaitForSingleObject(hPipeThread, 5000);
DPRINT("Done %lx\n", ret);
}
DPRINT("Wait for discovery thread to close! %p\n", hDiscoveryThread);
if (hDiscoveryThread != INVALID_HANDLE_VALUE)
{
DPRINT("Waiting for discovery thread\n");
ret = WaitForSingleObject(hDiscoveryThread, 5000);
DPRINT("Done %lx\n", ret);
}
DPRINT("Closing events!\n");
CloseHandle(hAdapterStateChangedEvent);
CloseHandle(hStopEvent);
if (DhcpSocket != INVALID_SOCKET)
closesocket(DhcpSocket);
CloseHandle(hDiscoveryThread);
CloseHandle(hPipeThread);
DPRINT("Done!\n");
UpdateServiceStatus(SERVICE_STOPPED);
}

View file

@ -76,7 +76,7 @@ typedef struct _DHCP_ADAPTER {
unsigned char recv_buf[1];
} DHCP_ADAPTER, *PDHCP_ADAPTER;
typedef DWORD (*PipeSendFunc)( COMM_DHCP_REPLY *Reply );
typedef DWORD (*PipeSendFunc)(HANDLE CommPipe, COMM_DHCP_REPLY *Reply );
#define random rand
#define srandom srand
@ -85,24 +85,24 @@ int init_client(void);
void stop_client(void);
void AdapterInit(VOID);
HANDLE StartAdapterDiscovery(VOID);
HANDLE StartAdapterDiscovery(HANDLE hStopEvent);
void AdapterStop(VOID);
extern PDHCP_ADAPTER AdapterGetFirst(VOID);
extern PDHCP_ADAPTER AdapterGetNext(PDHCP_ADAPTER);
extern PDHCP_ADAPTER AdapterFindIndex( unsigned int AdapterIndex );
extern PDHCP_ADAPTER AdapterFindInfo( struct interface_info *info );
extern PDHCP_ADAPTER AdapterFindByHardwareAddress( u_int8_t haddr[16], u_int8_t hlen );
extern HANDLE PipeInit(VOID);
extern HANDLE PipeInit(HANDLE hStopEvent);
extern VOID ApiInit(VOID);
extern VOID ApiFree(VOID);
extern VOID ApiLock(VOID);
extern VOID ApiUnlock(VOID);
extern DWORD DSQueryHWInfo( PipeSendFunc Send, COMM_DHCP_REQ *Req );
extern DWORD DSLeaseIpAddress( PipeSendFunc Send, COMM_DHCP_REQ *Req );
extern DWORD DSRenewIpAddressLease( PipeSendFunc Send, COMM_DHCP_REQ *Req );
extern DWORD DSReleaseIpAddressLease( PipeSendFunc Send, COMM_DHCP_REQ *Req );
extern DWORD DSStaticRefreshParams( PipeSendFunc Send, COMM_DHCP_REQ *Req );
extern DWORD DSGetAdapterInfo( PipeSendFunc Send, COMM_DHCP_REQ *Req );
extern DWORD DSQueryHWInfo( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req );
extern DWORD DSLeaseIpAddress( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req );
extern DWORD DSRenewIpAddressLease( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req );
extern DWORD DSReleaseIpAddressLease( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req );
extern DWORD DSStaticRefreshParams( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req );
extern DWORD DSGetAdapterInfo( PipeSendFunc Send, HANDLE CommPipe, COMM_DHCP_REQ *Req );
extern int inet_aton(const char *s, struct in_addr *addr);
int warn( char *format, ... );