xref: /reactos/base/services/dhcpcsvc/dhcp/adapter.c (revision c2c66aff)
1 #include <rosdhcp.h>
2 
3 SOCKET DhcpSocket = INVALID_SOCKET;
4 static LIST_ENTRY AdapterList;
5 static WSADATA wsd;
6 
7 PCHAR *GetSubkeyNames( PCHAR MainKeyName, PCHAR Append ) {
8     int i = 0;
9     DWORD Error;
10     HKEY MainKey;
11     PCHAR *Out, OutKeyName;
12     DWORD CharTotal = 0, AppendLen = 1 + strlen(Append);
13     DWORD MaxSubKeyLen = 0, MaxSubKeys = 0;
14 
15     Error = RegOpenKey( HKEY_LOCAL_MACHINE, MainKeyName, &MainKey );
16 
17     if( Error ) return NULL;
18 
19     Error = RegQueryInfoKey
20         ( MainKey,
21           NULL, NULL, NULL,
22           &MaxSubKeys, &MaxSubKeyLen,
23           NULL, NULL, NULL, NULL, NULL, NULL );
24 
25     MaxSubKeyLen++;
26     DH_DbgPrint(MID_TRACE,("MaxSubKeys: %d, MaxSubKeyLen %d\n",
27                            MaxSubKeys, MaxSubKeyLen));
28 
29     CharTotal = (sizeof(PCHAR) + MaxSubKeyLen + AppendLen) * (MaxSubKeys + 1);
30 
31     DH_DbgPrint(MID_TRACE,("AppendLen: %d, CharTotal: %d\n",
32                            AppendLen, CharTotal));
33 
34     Out = (CHAR**) malloc( CharTotal );
35     OutKeyName = ((PCHAR)&Out[MaxSubKeys+1]);
36 
37     if( !Out ) { RegCloseKey( MainKey ); return NULL; }
38 
39     i = 0;
40     do {
41         Out[i] = OutKeyName;
42         Error = RegEnumKey( MainKey, i, OutKeyName, MaxSubKeyLen );
43         if( !Error ) {
44             strcat( OutKeyName, Append );
45             DH_DbgPrint(MID_TRACE,("[%d]: %s\n", i, OutKeyName));
46             OutKeyName += strlen(OutKeyName) + 1;
47             i++;
48         } else Out[i] = 0;
49     } while( Error == ERROR_SUCCESS );
50 
51     RegCloseKey( MainKey );
52 
53     return Out;
54 }
55 
56 PCHAR RegReadString( HKEY Root, PCHAR Subkey, PCHAR Value ) {
57     PCHAR SubOut = NULL;
58     DWORD SubOutLen = 0, Error = 0;
59     HKEY  ValueKey = NULL;
60 
61     DH_DbgPrint(MID_TRACE,("Looking in %x:%s:%s\n", Root, Subkey, Value ));
62 
63     if( Subkey && strlen(Subkey) ) {
64         if( RegOpenKey( Root, Subkey, &ValueKey ) != ERROR_SUCCESS )
65             goto regerror;
66     } else ValueKey = Root;
67 
68     DH_DbgPrint(MID_TRACE,("Got Key %x\n", ValueKey));
69 
70     if( (Error = RegQueryValueEx( ValueKey, Value, NULL, NULL,
71                                   (LPBYTE)SubOut, &SubOutLen )) != ERROR_SUCCESS )
72         goto regerror;
73 
74     DH_DbgPrint(MID_TRACE,("Value %s has size %d\n", Value, SubOutLen));
75 
76     if( !(SubOut = (CHAR*) malloc(SubOutLen)) )
77         goto regerror;
78 
79     if( (Error = RegQueryValueEx( ValueKey, Value, NULL, NULL,
80                                   (LPBYTE)SubOut, &SubOutLen )) != ERROR_SUCCESS )
81         goto regerror;
82 
83     DH_DbgPrint(MID_TRACE,("Value %s is %s\n", Value, SubOut));
84 
85     goto cleanup;
86 
87 regerror:
88     if( SubOut ) { free( SubOut ); SubOut = NULL; }
89 cleanup:
90     if( ValueKey && ValueKey != Root ) {
91         DH_DbgPrint(MID_TRACE,("Closing key %x\n", ValueKey));
92         RegCloseKey( ValueKey );
93     }
94 
95     DH_DbgPrint(MID_TRACE,("Returning %x with error %d\n", SubOut, Error));
96 
97     return SubOut;
98 }
99 
100 HKEY FindAdapterKey( PDHCP_ADAPTER Adapter ) {
101     int i = 0;
102     PCHAR EnumKeyName =
103         "SYSTEM\\CurrentControlSet\\Control\\Class\\"
104         "{4D36E972-E325-11CE-BFC1-08002BE10318}";
105     PCHAR TargetKeyNameStart =
106         "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\";
107     PCHAR TargetKeyName = NULL;
108     PCHAR *EnumKeysLinkage = GetSubkeyNames( EnumKeyName, "\\Linkage" );
109     PCHAR *EnumKeysTop     = GetSubkeyNames( EnumKeyName, "" );
110     PCHAR RootDevice = NULL;
111     HKEY EnumKey, OutKey = NULL;
112     DWORD Error = ERROR_SUCCESS;
113 
114     if( !EnumKeysLinkage || !EnumKeysTop ) goto cleanup;
115 
116     Error = RegOpenKey( HKEY_LOCAL_MACHINE, EnumKeyName, &EnumKey );
117 
118     if( Error ) goto cleanup;
119 
120     for( i = 0; EnumKeysLinkage[i]; i++ ) {
121         RootDevice = RegReadString
122             ( EnumKey, EnumKeysLinkage[i], "RootDevice" );
123 
124         if( RootDevice &&
125             !strcmp( RootDevice, Adapter->DhclientInfo.name ) ) {
126             TargetKeyName =
127                 (CHAR*) malloc( strlen( TargetKeyNameStart ) +
128                         strlen( RootDevice ) + 1);
129             if( !TargetKeyName ) goto cleanup;
130             sprintf( TargetKeyName, "%s%s",
131                      TargetKeyNameStart, RootDevice );
132             Error = RegCreateKeyExA( HKEY_LOCAL_MACHINE, TargetKeyName, 0, NULL, 0, KEY_READ, NULL, &OutKey, NULL );
133             break;
134         } else {
135             free( RootDevice ); RootDevice = 0;
136         }
137     }
138 
139 cleanup:
140     if( RootDevice ) free( RootDevice );
141     if( EnumKeysLinkage ) free( EnumKeysLinkage );
142     if( EnumKeysTop ) free( EnumKeysTop );
143     if( TargetKeyName ) free( TargetKeyName );
144 
145     return OutKey;
146 }
147 
148 BOOL PrepareAdapterForService( PDHCP_ADAPTER Adapter ) {
149     HKEY AdapterKey;
150     DWORD Error = ERROR_SUCCESS, DhcpEnabled, Length = sizeof(DWORD);
151 
152     Adapter->DhclientState.config = &Adapter->DhclientConfig;
153     strncpy(Adapter->DhclientInfo.name, (char*)Adapter->IfMib.bDescr,
154             sizeof(Adapter->DhclientInfo.name));
155 
156     AdapterKey = FindAdapterKey( Adapter );
157     if( AdapterKey )
158     {
159         Error = RegQueryValueEx(AdapterKey, "EnableDHCP", NULL, NULL, (LPBYTE)&DhcpEnabled, &Length);
160 
161         if (Error != ERROR_SUCCESS || Length != sizeof(DWORD))
162             DhcpEnabled = 1;
163 
164         CloseHandle(AdapterKey);
165     }
166     else
167     {
168         /* DHCP enabled by default */
169         DhcpEnabled = 1;
170     }
171 
172     if( !DhcpEnabled ) {
173         /* Non-automatic case */
174         DbgPrint("DHCPCSVC: Adapter Name: [%s] (static)\n", Adapter->DhclientInfo.name);
175 
176         Adapter->DhclientState.state = S_STATIC;
177     } else {
178         /* Automatic case */
179         DbgPrint("DHCPCSVC: Adapter Name: [%s] (dynamic)\n", Adapter->DhclientInfo.name);
180 
181 	Adapter->DhclientInfo.client->state = S_INIT;
182     }
183 
184     return TRUE;
185 }
186 
187 void AdapterInit() {
188     WSAStartup(0x0101,&wsd);
189 
190     InitializeListHead( &AdapterList );
191 }
192 
193 int
194 InterfaceConnected(const MIB_IFROW* IfEntry)
195 {
196     if (IfEntry->dwOperStatus == IF_OPER_STATUS_CONNECTED ||
197         IfEntry->dwOperStatus == IF_OPER_STATUS_OPERATIONAL)
198         return 1;
199 
200     DH_DbgPrint(MID_TRACE,("Interface %d is down\n", IfEntry->dwIndex));
201     return 0;
202 }
203 
204 BOOL
205 IsReconnectHackNeeded(PDHCP_ADAPTER Adapter, const MIB_IFROW* IfEntry)
206 {
207     struct protocol *proto;
208     PIP_ADAPTER_INFO AdapterInfo, Orig;
209     DWORD Size, Ret;
210     char *ZeroAddress = "0.0.0.0";
211 
212     proto = find_protocol_by_adapter(&Adapter->DhclientInfo);
213 
214     if (Adapter->DhclientInfo.client->state == S_BOUND && !proto)
215         return FALSE;
216 
217     if (Adapter->DhclientInfo.client->state != S_BOUND &&
218         Adapter->DhclientInfo.client->state != S_STATIC)
219         return FALSE;
220 
221     ApiUnlock();
222 
223     Orig = AdapterInfo = HeapAlloc(GetProcessHeap(), 0, sizeof(IP_ADAPTER_INFO));
224     Size = sizeof(IP_ADAPTER_INFO);
225     if (!AdapterInfo)
226     {
227         ApiLock();
228         return FALSE;
229     }
230 
231     Ret = GetAdaptersInfo(AdapterInfo, &Size);
232     if (Ret == ERROR_BUFFER_OVERFLOW)
233     {
234         HeapFree(GetProcessHeap(), 0, AdapterInfo);
235         AdapterInfo = HeapAlloc(GetProcessHeap(), 0, Size);
236         if (!AdapterInfo)
237         {
238             ApiLock();
239             return FALSE;
240         }
241 
242         if (GetAdaptersInfo(AdapterInfo, &Size) != NO_ERROR)
243         {
244             ApiLock();
245             return FALSE;
246         }
247 
248         Orig = AdapterInfo;
249         for (; AdapterInfo != NULL; AdapterInfo = AdapterInfo->Next)
250         {
251             if (AdapterInfo->Index == IfEntry->dwIndex)
252                 break;
253         }
254 
255         if (AdapterInfo == NULL)
256         {
257             HeapFree(GetProcessHeap(), 0, Orig);
258             ApiLock();
259             return FALSE;
260         }
261     }
262     else if (Ret != NO_ERROR)
263     {
264         HeapFree(GetProcessHeap(), 0, Orig);
265         ApiLock();
266         return FALSE;
267     }
268 
269     if (!strcmp(AdapterInfo->IpAddressList.IpAddress.String, ZeroAddress))
270     {
271         HeapFree(GetProcessHeap(), 0, Orig);
272         ApiLock();
273         return TRUE;
274     }
275     else
276     {
277         HeapFree(GetProcessHeap(), 0, Orig);
278         ApiLock();
279         return FALSE;
280     }
281 }
282 
283 /*
284  * XXX Figure out the way to bind a specific adapter to a socket.
285  */
286 DWORD WINAPI AdapterDiscoveryThread(LPVOID Context) {
287     PMIB_IFTABLE Table = (PMIB_IFTABLE) malloc(sizeof(MIB_IFTABLE));
288     DWORD Error, Size = sizeof(MIB_IFTABLE);
289     PDHCP_ADAPTER Adapter = NULL;
290     HANDLE AdapterStateChangedEvent = (HANDLE)Context;
291     struct interface_info *ifi = NULL;
292     struct protocol *proto;
293     int i, AdapterCount = 0, Broadcast;
294 
295     /* FIXME: Kill this thread when the service is stopped */
296 
297     do {
298        DH_DbgPrint(MID_TRACE,("Getting Adapter List...\n"));
299 
300        while( (Error = GetIfTable(Table, &Size, 0 )) ==
301                ERROR_INSUFFICIENT_BUFFER ) {
302            DH_DbgPrint(MID_TRACE,("Error %d, New Buffer Size: %d\n", Error, Size));
303            free( Table );
304            Table = (PMIB_IFTABLE) malloc( Size );
305        }
306 
307        if( Error != NO_ERROR )
308        {
309            /* HACK: We are waiting until TCP/IP starts */
310            Sleep(2000);
311            continue;
312        }
313 
314        DH_DbgPrint(MID_TRACE,("Got Adapter List (%d entries)\n", Table->dwNumEntries));
315 
316        for( i = Table->dwNumEntries - 1; i >= 0; i-- ) {
317             DH_DbgPrint(MID_TRACE,("Getting adapter %d attributes\n",
318                                    Table->table[i].dwIndex));
319 
320             ApiLock();
321 
322             if ((Adapter = AdapterFindByHardwareAddress(Table->table[i].bPhysAddr, Table->table[i].dwPhysAddrLen)))
323             {
324                 proto = find_protocol_by_adapter(&Adapter->DhclientInfo);
325 
326                 /* This is an existing adapter */
327                 if (InterfaceConnected(&Table->table[i])) {
328                     /* We're still active so we stay in the list */
329                     ifi = &Adapter->DhclientInfo;
330 
331                     /* This is a hack because IP helper API sucks */
332                     if (IsReconnectHackNeeded(Adapter, &Table->table[i]))
333                     {
334                         /* This handles a disconnect/reconnect */
335 
336                         if (proto)
337                             remove_protocol(proto);
338                         Adapter->DhclientInfo.client->state = S_INIT;
339 
340                         /* These are already invalid since the media state change */
341                         Adapter->RouterMib.dwForwardNextHop = 0;
342                         Adapter->NteContext = 0;
343 
344                         add_protocol(Adapter->DhclientInfo.name,
345                                      Adapter->DhclientInfo.rfdesc,
346                                      got_one, &Adapter->DhclientInfo);
347                         state_init(&Adapter->DhclientInfo);
348 
349                         SetEvent(AdapterStateChangedEvent);
350                     }
351 
352                 } else {
353                     if (proto)
354                         remove_protocol(proto);
355 
356                     /* We've lost our link so out we go */
357                     RemoveEntryList(&Adapter->ListEntry);
358                     free(Adapter);
359                 }
360 
361                 ApiUnlock();
362 
363                 continue;
364             }
365 
366             ApiUnlock();
367 
368             Adapter = (DHCP_ADAPTER*) calloc( sizeof( DHCP_ADAPTER ) + Table->table[i].dwMtu, 1 );
369 
370             if( Adapter && Table->table[i].dwType == MIB_IF_TYPE_ETHERNET && InterfaceConnected(&Table->table[i])) {
371                 memcpy( &Adapter->IfMib, &Table->table[i],
372                         sizeof(Adapter->IfMib) );
373                 Adapter->DhclientInfo.client = &Adapter->DhclientState;
374                 Adapter->DhclientInfo.rbuf = Adapter->recv_buf;
375                 Adapter->DhclientInfo.rbuf_max = Table->table[i].dwMtu;
376                 Adapter->DhclientInfo.rbuf_len =
377                     Adapter->DhclientInfo.rbuf_offset = 0;
378                 memcpy(Adapter->DhclientInfo.hw_address.haddr,
379                        Adapter->IfMib.bPhysAddr,
380                        Adapter->IfMib.dwPhysAddrLen);
381                 Adapter->DhclientInfo.hw_address.hlen = Adapter->IfMib.dwPhysAddrLen;
382 
383                 /* I'm not sure where else to set this, but
384                    some DHCP servers won't take a zero.
385                    We checked the hardware type earlier in
386                    the if statement. */
387                 Adapter->DhclientInfo.hw_address.htype = HTYPE_ETHER;
388 
389                 if( DhcpSocket == INVALID_SOCKET ) {
390                     DhcpSocket =
391                         Adapter->DhclientInfo.rfdesc =
392                         Adapter->DhclientInfo.wfdesc =
393                         socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
394 
395                     if (DhcpSocket != INVALID_SOCKET) {
396 
397 						/* Allow broadcast on this socket */
398 						Broadcast = 1;
399 						setsockopt(DhcpSocket,
400 								   SOL_SOCKET,
401 								   SO_BROADCAST,
402 								   (const char *)&Broadcast,
403 								   sizeof(Broadcast));
404 
405                         Adapter->ListenAddr.sin_family = AF_INET;
406                         Adapter->ListenAddr.sin_port = htons(LOCAL_PORT);
407                         Adapter->BindStatus =
408                              (bind( Adapter->DhclientInfo.rfdesc,
409                                     (struct sockaddr *)&Adapter->ListenAddr,
410                                     sizeof(Adapter->ListenAddr) ) == 0) ?
411                              0 : WSAGetLastError();
412                     } else {
413                         error("socket() failed: %d\n", WSAGetLastError());
414                     }
415                 } else {
416                     Adapter->DhclientInfo.rfdesc =
417                         Adapter->DhclientInfo.wfdesc = DhcpSocket;
418                 }
419 
420                 Adapter->DhclientConfig.timeout = DHCP_PANIC_TIMEOUT;
421                 Adapter->DhclientConfig.initial_interval = DHCP_DISCOVER_INTERVAL;
422                 Adapter->DhclientConfig.retry_interval = DHCP_DISCOVER_INTERVAL;
423                 Adapter->DhclientConfig.select_interval = 1;
424                 Adapter->DhclientConfig.reboot_timeout = DHCP_REBOOT_TIMEOUT;
425                 Adapter->DhclientConfig.backoff_cutoff = DHCP_BACKOFF_MAX;
426                 Adapter->DhclientState.interval =
427                     Adapter->DhclientConfig.retry_interval;
428 
429                 if( PrepareAdapterForService( Adapter ) ) {
430                     Adapter->DhclientInfo.next = ifi;
431                     ifi = &Adapter->DhclientInfo;
432 
433                     read_client_conf(&Adapter->DhclientInfo);
434 
435                     if (Adapter->DhclientInfo.client->state == S_INIT)
436                     {
437                         add_protocol(Adapter->DhclientInfo.name,
438                                      Adapter->DhclientInfo.rfdesc,
439                                      got_one, &Adapter->DhclientInfo);
440 
441                         state_init(&Adapter->DhclientInfo);
442                     }
443 
444                     ApiLock();
445                     InsertTailList( &AdapterList, &Adapter->ListEntry );
446                     AdapterCount++;
447                     SetEvent(AdapterStateChangedEvent);
448                     ApiUnlock();
449                 } else { free( Adapter ); Adapter = 0; }
450             } else { free( Adapter ); Adapter = 0; }
451 
452             if( !Adapter )
453                 DH_DbgPrint(MID_TRACE,("Adapter %d was rejected\n",
454                                        Table->table[i].dwIndex));
455         }
456 #if 0
457         Error = NotifyAddrChange(NULL, NULL);
458         if (Error != NO_ERROR)
459             break;
460 #else
461         Sleep(3000);
462 #endif
463     } while (TRUE);
464 
465     DbgPrint("DHCPCSVC: Adapter discovery thread is terminating! (Error: %d)\n", Error);
466 
467     if( Table ) free( Table );
468     return Error;
469 }
470 
471 HANDLE StartAdapterDiscovery(VOID) {
472     HANDLE ThreadHandle, EventHandle;
473 
474     EventHandle = CreateEvent(NULL,
475                               FALSE,
476                               FALSE,
477                               NULL);
478 
479     if (EventHandle == NULL)
480         return NULL;
481 
482     ThreadHandle = CreateThread(NULL,
483                                 0,
484                                 AdapterDiscoveryThread,
485                                 (LPVOID)EventHandle,
486                                 0,
487                                 NULL);
488 
489     if (ThreadHandle == NULL)
490     {
491         CloseHandle(EventHandle);
492         return NULL;
493     }
494 
495     CloseHandle(ThreadHandle);
496 
497     return EventHandle;
498 }
499 
500 void AdapterStop() {
501     PLIST_ENTRY ListEntry;
502     PDHCP_ADAPTER Adapter;
503     ApiLock();
504     while( !IsListEmpty( &AdapterList ) ) {
505         ListEntry = (PLIST_ENTRY)RemoveHeadList( &AdapterList );
506         Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
507         free( Adapter );
508     }
509     ApiUnlock();
510     WSACleanup();
511 }
512 
513 PDHCP_ADAPTER AdapterFindIndex( unsigned int indx ) {
514     PDHCP_ADAPTER Adapter;
515     PLIST_ENTRY ListEntry;
516 
517     for( ListEntry = AdapterList.Flink;
518          ListEntry != &AdapterList;
519          ListEntry = ListEntry->Flink ) {
520         Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
521         if( Adapter->IfMib.dwIndex == indx ) return Adapter;
522     }
523 
524     return NULL;
525 }
526 
527 PDHCP_ADAPTER AdapterFindName( const WCHAR *name ) {
528     PDHCP_ADAPTER Adapter;
529     PLIST_ENTRY ListEntry;
530 
531     for( ListEntry = AdapterList.Flink;
532          ListEntry != &AdapterList;
533          ListEntry = ListEntry->Flink ) {
534         Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
535         if( !wcsicmp( Adapter->IfMib.wszName, name ) ) return Adapter;
536     }
537 
538     return NULL;
539 }
540 
541 PDHCP_ADAPTER AdapterFindInfo( struct interface_info *ip ) {
542     PDHCP_ADAPTER Adapter;
543     PLIST_ENTRY ListEntry;
544 
545     for( ListEntry = AdapterList.Flink;
546          ListEntry != &AdapterList;
547          ListEntry = ListEntry->Flink ) {
548         Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
549         if( ip == &Adapter->DhclientInfo ) return Adapter;
550     }
551 
552     return NULL;
553 }
554 
555 PDHCP_ADAPTER AdapterFindByHardwareAddress( u_int8_t haddr[16], u_int8_t hlen ) {
556     PDHCP_ADAPTER Adapter;
557     PLIST_ENTRY ListEntry;
558 
559     for(ListEntry = AdapterList.Flink;
560         ListEntry != &AdapterList;
561         ListEntry = ListEntry->Flink) {
562        Adapter = CONTAINING_RECORD( ListEntry, DHCP_ADAPTER, ListEntry );
563        if (Adapter->DhclientInfo.hw_address.hlen == hlen &&
564            !memcmp(Adapter->DhclientInfo.hw_address.haddr,
565                   haddr,
566                   hlen)) return Adapter;
567     }
568 
569     return NULL;
570 }
571 
572 PDHCP_ADAPTER AdapterGetFirst() {
573     if( IsListEmpty( &AdapterList ) ) return NULL; else {
574         return CONTAINING_RECORD
575             ( AdapterList.Flink, DHCP_ADAPTER, ListEntry );
576     }
577 }
578 
579 PDHCP_ADAPTER AdapterGetNext( PDHCP_ADAPTER This )
580 {
581     if( This->ListEntry.Flink == &AdapterList ) return NULL;
582     return CONTAINING_RECORD
583         ( This->ListEntry.Flink, DHCP_ADAPTER, ListEntry );
584 }
585 
586 void if_register_send(struct interface_info *ip) {
587 
588 }
589 
590 void if_register_receive(struct interface_info *ip) {
591 }
592