reactos/base/services/dhcpcsvc/dhcp/adapter.c
Eric Kohl 033b6639e4 [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
2021-08-08 14:10:25 +02:00

577 lines
18 KiB
C

#include <rosdhcp.h>
#define NDEBUG
#include <reactos/debug.h>
extern HANDLE hAdapterStateChangedEvent;
SOCKET DhcpSocket = INVALID_SOCKET;
static LIST_ENTRY AdapterList;
static WSADATA wsd;
PCHAR *GetSubkeyNames( PCHAR MainKeyName, PCHAR Append ) {
int i = 0;
DWORD Error;
HKEY MainKey;
PCHAR *Out, OutKeyName;
SIZE_T CharTotal = 0, AppendLen = 1 + strlen(Append);
DWORD MaxSubKeyLen = 0, MaxSubKeys = 0;
Error = RegOpenKey( HKEY_LOCAL_MACHINE, MainKeyName, &MainKey );
if( Error ) return NULL;
Error = RegQueryInfoKey
( MainKey,
NULL, NULL, NULL,
&MaxSubKeys, &MaxSubKeyLen,
NULL, NULL, NULL, NULL, NULL, NULL );
MaxSubKeyLen++;
DH_DbgPrint(MID_TRACE,("MaxSubKeys: %d, MaxSubKeyLen %d\n",
MaxSubKeys, MaxSubKeyLen));
CharTotal = (sizeof(PCHAR) + MaxSubKeyLen + AppendLen) * (MaxSubKeys + 1);
DH_DbgPrint(MID_TRACE,("AppendLen: %d, CharTotal: %d\n",
AppendLen, CharTotal));
Out = (CHAR**) malloc( CharTotal );
OutKeyName = ((PCHAR)&Out[MaxSubKeys+1]);
if( !Out ) { RegCloseKey( MainKey ); return NULL; }
i = 0;
do {
Out[i] = OutKeyName;
Error = RegEnumKey( MainKey, i, OutKeyName, MaxSubKeyLen );
if( !Error ) {
strcat( OutKeyName, Append );
DH_DbgPrint(MID_TRACE,("[%d]: %s\n", i, OutKeyName));
OutKeyName += strlen(OutKeyName) + 1;
i++;
} else Out[i] = 0;
} while( Error == ERROR_SUCCESS );
RegCloseKey( MainKey );
return Out;
}
PCHAR RegReadString( HKEY Root, PCHAR Subkey, PCHAR Value ) {
PCHAR SubOut = NULL;
DWORD SubOutLen = 0, Error = 0;
HKEY ValueKey = NULL;
DH_DbgPrint(MID_TRACE,("Looking in %x:%s:%s\n", Root, Subkey, Value ));
if( Subkey && strlen(Subkey) ) {
if( RegOpenKey( Root, Subkey, &ValueKey ) != ERROR_SUCCESS )
goto regerror;
} else ValueKey = Root;
DH_DbgPrint(MID_TRACE,("Got Key %x\n", ValueKey));
if( (Error = RegQueryValueEx( ValueKey, Value, NULL, NULL,
(LPBYTE)SubOut, &SubOutLen )) != ERROR_SUCCESS )
goto regerror;
DH_DbgPrint(MID_TRACE,("Value %s has size %d\n", Value, SubOutLen));
if( !(SubOut = (CHAR*) malloc(SubOutLen)) )
goto regerror;
if( (Error = RegQueryValueEx( ValueKey, Value, NULL, NULL,
(LPBYTE)SubOut, &SubOutLen )) != ERROR_SUCCESS )
goto regerror;
DH_DbgPrint(MID_TRACE,("Value %s is %s\n", Value, SubOut));
goto cleanup;
regerror:
if( SubOut ) { free( SubOut ); SubOut = NULL; }
cleanup:
if( ValueKey && ValueKey != Root ) {
DH_DbgPrint(MID_TRACE,("Closing key %x\n", ValueKey));
RegCloseKey( ValueKey );
}
DH_DbgPrint(MID_TRACE,("Returning %x with error %d\n", SubOut, Error));
return SubOut;
}
HKEY FindAdapterKey( PDHCP_ADAPTER Adapter ) {
int i = 0;
PCHAR EnumKeyName =
"SYSTEM\\CurrentControlSet\\Control\\Class\\"
"{4D36E972-E325-11CE-BFC1-08002BE10318}";
PCHAR TargetKeyNameStart =
"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
PCHAR TargetKeyName = NULL;
PCHAR *EnumKeysLinkage = GetSubkeyNames( EnumKeyName, "\\Linkage" );
PCHAR *EnumKeysTop = GetSubkeyNames( EnumKeyName, "" );
PCHAR RootDevice = NULL;
HKEY EnumKey, OutKey = NULL;
DWORD Error = ERROR_SUCCESS;
if( !EnumKeysLinkage || !EnumKeysTop ) goto cleanup;
Error = RegOpenKey( HKEY_LOCAL_MACHINE, EnumKeyName, &EnumKey );
if( Error ) goto cleanup;
for( i = 0; EnumKeysLinkage[i]; i++ ) {
RootDevice = RegReadString
( EnumKey, EnumKeysLinkage[i], "RootDevice" );
if( RootDevice &&
!strcmp( RootDevice, Adapter->DhclientInfo.name ) ) {
TargetKeyName =
(CHAR*) malloc( strlen( TargetKeyNameStart ) +
strlen( RootDevice ) + 1);
if( !TargetKeyName ) goto cleanup;
sprintf( TargetKeyName, "%s%s",
TargetKeyNameStart, RootDevice );
Error = RegCreateKeyExA( HKEY_LOCAL_MACHINE, TargetKeyName, 0, NULL, 0, KEY_READ, NULL, &OutKey, NULL );
break;
} else {
free( RootDevice ); RootDevice = 0;
}
}
cleanup:
if( RootDevice ) free( RootDevice );
if( EnumKeysLinkage ) free( EnumKeysLinkage );
if( EnumKeysTop ) free( EnumKeysTop );
if( TargetKeyName ) free( TargetKeyName );
return OutKey;
}
BOOL PrepareAdapterForService( PDHCP_ADAPTER Adapter ) {
HKEY AdapterKey;
DWORD Error = ERROR_SUCCESS, DhcpEnabled, Length = sizeof(DWORD);
Adapter->DhclientState.config = &Adapter->DhclientConfig;
strncpy(Adapter->DhclientInfo.name, (char*)Adapter->IfMib.bDescr,
sizeof(Adapter->DhclientInfo.name));
AdapterKey = FindAdapterKey( Adapter );
if( AdapterKey )
{
Error = RegQueryValueEx(AdapterKey, "EnableDHCP", NULL, NULL, (LPBYTE)&DhcpEnabled, &Length);
if (Error != ERROR_SUCCESS || Length != sizeof(DWORD))
DhcpEnabled = 1;
CloseHandle(AdapterKey);
}
else
{
/* DHCP enabled by default */
DhcpEnabled = 1;
}
if( !DhcpEnabled ) {
/* Non-automatic case */
DbgPrint("DHCPCSVC: Adapter Name: [%s] (static)\n", Adapter->DhclientInfo.name);
Adapter->DhclientState.state = S_STATIC;
} else {
/* Automatic case */
DbgPrint("DHCPCSVC: Adapter Name: [%s] (dynamic)\n", Adapter->DhclientInfo.name);
Adapter->DhclientInfo.client->state = S_INIT;
}
return TRUE;
}
void AdapterInit() {
WSAStartup(0x0101,&wsd);
InitializeListHead( &AdapterList );
}
int
InterfaceConnected(const MIB_IFROW* IfEntry)
{
if (IfEntry->dwOperStatus == IF_OPER_STATUS_CONNECTED ||
IfEntry->dwOperStatus == IF_OPER_STATUS_OPERATIONAL)
return 1;
DH_DbgPrint(MID_TRACE,("Interface %d is down\n", IfEntry->dwIndex));
return 0;
}
BOOL
IsReconnectHackNeeded(PDHCP_ADAPTER Adapter, const MIB_IFROW* IfEntry)
{
struct protocol *proto;
PIP_ADAPTER_INFO AdapterInfo, Orig;
DWORD Size, Ret;
char *ZeroAddress = "0.0.0.0";
proto = find_protocol_by_adapter(&Adapter->DhclientInfo);
if (Adapter->DhclientInfo.client->state == S_BOUND && !proto)
return FALSE;
if (Adapter->DhclientInfo.client->state != S_BOUND &&
Adapter->DhclientInfo.client->state != S_STATIC)
return FALSE;
ApiUnlock();
Orig = AdapterInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO));
Size = sizeof(IP_ADAPTER_INFO);
if (!AdapterInfo)
{
ApiLock();
return FALSE;
}
Ret = GetAdaptersInfo(AdapterInfo, &Size);
if (Ret == ERROR_BUFFER_OVERFLOW)
{
HeapFree(GetProcessHeap(), 0, AdapterInfo);
AdapterInfo = HeapAlloc(GetProcessHeap(), 0, Size);
if (!AdapterInfo)
{
ApiLock();
return FALSE;
}
if (GetAdaptersInfo(AdapterInfo, &Size) != NO_ERROR)
{
ApiLock();
return FALSE;
}
Orig = AdapterInfo;
for (; AdapterInfo != NULL; AdapterInfo = AdapterInfo->Next)
{
if (AdapterInfo->Index == IfEntry->dwIndex)
break;
}
if (AdapterInfo == NULL)
{
HeapFree(GetProcessHeap(), 0, Orig);
ApiLock();
return FALSE;
}
}
else if (Ret != NO_ERROR)
{
HeapFree(GetProcessHeap(), 0, Orig);
ApiLock();
return FALSE;
}
if (!strcmp(AdapterInfo->IpAddressList.IpAddress.String, ZeroAddress))
{
HeapFree(GetProcessHeap(), 0, Orig);
ApiLock();
return TRUE;
}
else
{
HeapFree(GetProcessHeap(), 0, Orig);
ApiLock();
return FALSE;
}
}
/*
* XXX Figure out the way to bind a specific adapter to a socket.
*/
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 hStopEvent = (HANDLE)Context;
struct interface_info *ifi = NULL;
struct protocol *proto;
int i, AdapterCount = 0, Broadcast;
while (TRUE)
{
DH_DbgPrint(MID_TRACE,("Getting Adapter List...\n"));
while( (Error = GetIfTable(Table, &Size, 0 )) ==
ERROR_INSUFFICIENT_BUFFER ) {
DH_DbgPrint(MID_TRACE,("Error %d, New Buffer Size: %d\n", Error, Size));
free( Table );
Table = (PMIB_IFTABLE) malloc( Size );
}
if( Error != NO_ERROR )
{
/* HACK: We are waiting until TCP/IP starts */
Sleep(2000);
continue;
}
DH_DbgPrint(MID_TRACE,("Got Adapter List (%d entries)\n", Table->dwNumEntries));
for( i = Table->dwNumEntries - 1; i >= 0; i-- ) {
DH_DbgPrint(MID_TRACE,("Getting adapter %d attributes\n",
Table->table[i].dwIndex));
ApiLock();
if ((Adapter = AdapterFindByHardwareAddress(Table->table[i].bPhysAddr, Table->table[i].dwPhysAddrLen)))
{
proto = find_protocol_by_adapter(&Adapter->DhclientInfo);
/* This is an existing adapter */
if (InterfaceConnected(&Table->table[i])) {
/* We're still active so we stay in the list */
ifi = &Adapter->DhclientInfo;
/* This is a hack because IP helper API sucks */
if (IsReconnectHackNeeded(Adapter, &Table->table[i]))
{
/* This handles a disconnect/reconnect */
if (proto)
remove_protocol(proto);
Adapter->DhclientInfo.client->state = S_INIT;
/* These are already invalid since the media state change */
Adapter->RouterMib.dwForwardNextHop = 0;
Adapter->NteContext = 0;
add_protocol(Adapter->DhclientInfo.name,
Adapter->DhclientInfo.rfdesc,
got_one, &Adapter->DhclientInfo);
state_init(&Adapter->DhclientInfo);
SetEvent(hAdapterStateChangedEvent);
}
} else {
if (proto)
remove_protocol(proto);
/* We've lost our link so out we go */
RemoveEntryList(&Adapter->ListEntry);
free(Adapter);
}
ApiUnlock();
continue;
}
ApiUnlock();
Adapter = (DHCP_ADAPTER*) calloc( sizeof( DHCP_ADAPTER ) + Table->table[i].dwMtu, 1 );
if( Adapter && Table->table[i].dwType == MIB_IF_TYPE_ETHERNET && InterfaceConnected(&Table->table[i])) {
memcpy( &Adapter->IfMib, &Table->table[i],
sizeof(Adapter->IfMib) );
Adapter->DhclientInfo.client = &Adapter->DhclientState;
Adapter->DhclientInfo.rbuf = Adapter->recv_buf;
Adapter->DhclientInfo.rbuf_max = Table->table[i].dwMtu;
Adapter->DhclientInfo.rbuf_len =
Adapter->DhclientInfo.rbuf_offset = 0;
memcpy(Adapter->DhclientInfo.hw_address.haddr,
Adapter->IfMib.bPhysAddr,
Adapter->IfMib.dwPhysAddrLen);
Adapter->DhclientInfo.hw_address.hlen = Adapter->IfMib.dwPhysAddrLen;
/* I'm not sure where else to set this, but
some DHCP servers won't take a zero.
We checked the hardware type earlier in
the if statement. */
Adapter->DhclientInfo.hw_address.htype = HTYPE_ETHER;
if( DhcpSocket == INVALID_SOCKET ) {
DhcpSocket =
Adapter->DhclientInfo.rfdesc =
Adapter->DhclientInfo.wfdesc =
socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
if (DhcpSocket != INVALID_SOCKET) {
/* Allow broadcast on this socket */
Broadcast = 1;
setsockopt(DhcpSocket,
SOL_SOCKET,
SO_BROADCAST,
(const char *)&Broadcast,
sizeof(Broadcast));
Adapter->ListenAddr.sin_family = AF_INET;
Adapter->ListenAddr.sin_port = htons(LOCAL_PORT);
Adapter->BindStatus =
(bind( Adapter->DhclientInfo.rfdesc,
(struct sockaddr *)&Adapter->ListenAddr,
sizeof(Adapter->ListenAddr) ) == 0) ?
0 : WSAGetLastError();
} else {
error("socket() failed: %d\n", WSAGetLastError());
}
} else {
Adapter->DhclientInfo.rfdesc =
Adapter->DhclientInfo.wfdesc = DhcpSocket;
}
Adapter->DhclientConfig.timeout = DHCP_PANIC_TIMEOUT;
Adapter->DhclientConfig.initial_interval = DHCP_DISCOVER_INTERVAL;
Adapter->DhclientConfig.retry_interval = DHCP_DISCOVER_INTERVAL;
Adapter->DhclientConfig.select_interval = 1;
Adapter->DhclientConfig.reboot_timeout = DHCP_REBOOT_TIMEOUT;
Adapter->DhclientConfig.backoff_cutoff = DHCP_BACKOFF_MAX;
Adapter->DhclientState.interval =
Adapter->DhclientConfig.retry_interval;
if( PrepareAdapterForService( Adapter ) ) {
Adapter->DhclientInfo.next = ifi;
ifi = &Adapter->DhclientInfo;
read_client_conf(&Adapter->DhclientInfo);
if (Adapter->DhclientInfo.client->state == S_INIT)
{
add_protocol(Adapter->DhclientInfo.name,
Adapter->DhclientInfo.rfdesc,
got_one, &Adapter->DhclientInfo);
state_init(&Adapter->DhclientInfo);
}
ApiLock();
InsertTailList( &AdapterList, &Adapter->ListEntry );
AdapterCount++;
SetEvent(hAdapterStateChangedEvent);
ApiUnlock();
} else { free( Adapter ); Adapter = 0; }
} else { free( Adapter ); Adapter = 0; }
if( !Adapter )
DH_DbgPrint(MID_TRACE,("Adapter %d was rejected\n",
Table->table[i].dwIndex));
}
#if 0
Error = NotifyAddrChange(NULL, NULL);
if (Error != NO_ERROR)
break;
#else
if (WaitForSingleObject(hStopEvent, 3000) == WAIT_OBJECT_0)
{
DPRINT("Stopping the discovery thread!\n");
break;
}
#endif
}
if (Table)
free(Table);
DPRINT("Adapter discovery thread terminated! (Error: %d)\n", Error);
return Error;
}
HANDLE StartAdapterDiscovery(HANDLE hStopEvent) {
return CreateThread(NULL, 0, AdapterDiscoveryThread, (LPVOID)hStopEvent, 0, NULL);
}
void AdapterStop() {
PLIST_ENTRY ListEntry;
PDHCP_ADAPTER Adapter;
ApiLock();
while( !IsListEmpty( &AdapterList ) ) {
ListEntry = (PLIST_ENTRY)RemoveHeadList( &AdapterList );
Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
free( Adapter );
}
ApiUnlock();
WSACleanup();
}
PDHCP_ADAPTER AdapterFindIndex( unsigned int indx ) {
PDHCP_ADAPTER Adapter;
PLIST_ENTRY ListEntry;
for( ListEntry = AdapterList.Flink;
ListEntry != &AdapterList;
ListEntry = ListEntry->Flink ) {
Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
if( Adapter->IfMib.dwIndex == indx ) return Adapter;
}
return NULL;
}
PDHCP_ADAPTER AdapterFindName( const WCHAR *name ) {
PDHCP_ADAPTER Adapter;
PLIST_ENTRY ListEntry;
for( ListEntry = AdapterList.Flink;
ListEntry != &AdapterList;
ListEntry = ListEntry->Flink ) {
Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
if( !wcsicmp( Adapter->IfMib.wszName, name ) ) return Adapter;
}
return NULL;
}
PDHCP_ADAPTER AdapterFindInfo( struct interface_info *ip ) {
PDHCP_ADAPTER Adapter;
PLIST_ENTRY ListEntry;
for( ListEntry = AdapterList.Flink;
ListEntry != &AdapterList;
ListEntry = ListEntry->Flink ) {
Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
if( ip == &Adapter->DhclientInfo ) return Adapter;
}
return NULL;
}
PDHCP_ADAPTER AdapterFindByHardwareAddress( u_int8_t haddr[16], u_int8_t hlen ) {
PDHCP_ADAPTER Adapter;
PLIST_ENTRY ListEntry;
for(ListEntry = AdapterList.Flink;
ListEntry != &AdapterList;
ListEntry = ListEntry->Flink) {
Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
if (Adapter->DhclientInfo.hw_address.hlen == hlen &&
!memcmp(Adapter->DhclientInfo.hw_address.haddr,
haddr,
hlen)) return Adapter;
}
return NULL;
}
PDHCP_ADAPTER AdapterGetFirst() {
if( IsListEmpty( &AdapterList ) ) return NULL; else {
return CONTAINING_RECORD
( AdapterList.Flink, DHCP_ADAPTER, ListEntry );
}
}
PDHCP_ADAPTER AdapterGetNext( PDHCP_ADAPTER This )
{
if( This->ListEntry.Flink == &AdapterList ) return NULL;
return CONTAINING_RECORD
( This->ListEntry.Flink, DHCP_ADAPTER, ListEntry );
}
void if_register_send(struct interface_info *ip) {
}
void if_register_receive(struct interface_info *ip) {
}