1 /** @file
2 Functions implementation related with DHCPv4/v6 for DNS driver.
3 
4 Copyright (c) 2015, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "DnsImpl.h"
16 
17 /**
18   This function initialize the DHCP4 message instance.
19 
20   This function will pad each item of dhcp4 message packet.
21 
22   @param  Seed             Pointer to the message instance of the DHCP4 packet.
23   @param  InterfaceInfo    Pointer to the EFI_IP4_CONFIG2_INTERFACE_INFO instance.
24 
25 **/
26 VOID
DnsInitSeedPacket(OUT EFI_DHCP4_PACKET * Seed,IN EFI_IP4_CONFIG2_INTERFACE_INFO * InterfaceInfo)27 DnsInitSeedPacket (
28   OUT EFI_DHCP4_PACKET               *Seed,
29   IN  EFI_IP4_CONFIG2_INTERFACE_INFO *InterfaceInfo
30   )
31 {
32   EFI_DHCP4_HEADER           *Header;
33 
34   //
35   // Get IfType and HwAddressSize from SNP mode data.
36   //
37   Seed->Size            = sizeof (EFI_DHCP4_PACKET);
38   Seed->Length          = sizeof (Seed->Dhcp4);
39   Header                = &Seed->Dhcp4.Header;
40   ZeroMem (Header, sizeof (EFI_DHCP4_HEADER));
41   Header->OpCode        = DHCP4_OPCODE_REQUEST;
42   Header->HwType        = InterfaceInfo->IfType;
43   Header->HwAddrLen     = (UINT8) InterfaceInfo->HwAddressSize;
44   CopyMem (Header->ClientHwAddr, &(InterfaceInfo->HwAddress), Header->HwAddrLen);
45 
46   Seed->Dhcp4.Magik     = DHCP4_MAGIC;
47   Seed->Dhcp4.Option[0] = DHCP4_TAG_EOP;
48 }
49 
50 /**
51   The common notify function.
52 
53   @param[in]  Event   The event signaled.
54   @param[in]  Context The context.
55 
56 **/
57 VOID
58 EFIAPI
DhcpCommonNotify(IN EFI_EVENT Event,IN VOID * Context)59 DhcpCommonNotify (
60   IN EFI_EVENT  Event,
61   IN VOID       *Context
62   )
63 {
64   if ((Event == NULL) || (Context == NULL)) {
65     return ;
66   }
67 
68   *((BOOLEAN *) Context) = TRUE;
69 }
70 
71 /**
72   Parse the ACK to get required information
73 
74   @param  Dhcp4            The DHCP4 protocol.
75   @param  Packet           Packet waiting for parse.
76   @param  DnsServerInfor   The required Dns4 server information.
77 
78   @retval EFI_SUCCESS           The DNS information is got from the DHCP ACK.
79   @retval EFI_NO_MAPPING        DHCP failed to acquire address and other information.
80   @retval EFI_DEVICE_ERROR      Other errors as indicated.
81   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
82 
83 **/
84 EFI_STATUS
ParseDhcp4Ack(IN EFI_DHCP4_PROTOCOL * Dhcp4,IN EFI_DHCP4_PACKET * Packet,IN DNS4_SERVER_INFOR * DnsServerInfor)85 ParseDhcp4Ack (
86   IN EFI_DHCP4_PROTOCOL         *Dhcp4,
87   IN EFI_DHCP4_PACKET           *Packet,
88   IN DNS4_SERVER_INFOR          *DnsServerInfor
89   )
90 {
91   EFI_STATUS              Status;
92   UINT32                  OptionCount;
93   EFI_DHCP4_PACKET_OPTION **OptionList;
94   UINT32                  ServerCount;
95   EFI_IPv4_ADDRESS        *ServerList;
96   UINT32                  Index;
97   UINT32                  Count;
98 
99   ServerCount = 0;
100   ServerList = NULL;
101 
102   OptionCount = 0;
103   OptionList  = NULL;
104 
105   Status      = Dhcp4->Parse (Dhcp4, Packet, &OptionCount, OptionList);
106   if (Status != EFI_BUFFER_TOO_SMALL) {
107     return EFI_DEVICE_ERROR;
108   }
109 
110   OptionList = AllocatePool (OptionCount * sizeof (EFI_DHCP4_PACKET_OPTION *));
111   if (OptionList == NULL) {
112     return EFI_OUT_OF_RESOURCES;
113   }
114 
115   Status = Dhcp4->Parse (Dhcp4, Packet, &OptionCount, OptionList);
116   if (EFI_ERROR (Status)) {
117     gBS->FreePool (OptionList);
118     return EFI_DEVICE_ERROR;
119   }
120 
121   Status = EFI_NOT_FOUND;
122 
123   for (Index = 0; Index < OptionCount; Index++) {
124     //
125     // Get DNS server addresses
126     //
127     if (OptionList[Index]->OpCode == DHCP4_TAG_DNS_SERVER) {
128 
129       if (((OptionList[Index]->Length & 0x3) != 0) || (OptionList[Index]->Length == 0)) {
130         Status = EFI_DEVICE_ERROR;
131         break;
132       }
133 
134       ServerCount = OptionList[Index]->Length/4;
135       ServerList = AllocatePool (ServerCount * sizeof (EFI_IPv4_ADDRESS));
136       if (ServerList == NULL) {
137         return EFI_OUT_OF_RESOURCES;
138       }
139 
140       for(Count=0; Count < ServerCount; Count++){
141         CopyMem (ServerList + Count, &OptionList[Index]->Data[4 * Count], sizeof (EFI_IPv4_ADDRESS));
142       }
143 
144       *(DnsServerInfor->ServerCount) = ServerCount;
145       DnsServerInfor->ServerList     = ServerList;
146 
147       Status = EFI_SUCCESS;
148     }
149   }
150 
151   gBS->FreePool (OptionList);
152 
153   return Status;
154 }
155 
156 /**
157   EFI_DHCP6_INFO_CALLBACK is provided by the consumer of the EFI DHCPv6 Protocol
158   instance to intercept events that occurs in the DHCPv6 Information Request
159   exchange process.
160 
161   @param  This                  Pointer to the EFI_DHCP6_PROTOCOL instance that
162                                 is used to configure this  callback function.
163   @param  Context               Pointer to the context that is initialized in
164                                 the EFI_DHCP6_PROTOCOL.InfoRequest().
165   @param  Packet                Pointer to Reply packet that has been received.
166                                 The EFI DHCPv6 Protocol instance is responsible
167                                 for freeing the buffer.
168 
169   @retval EFI_SUCCESS           The DNS information is got from the DHCP ACK.
170   @retval EFI_DEVICE_ERROR      Other errors as indicated.
171   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
172 **/
173 EFI_STATUS
174 EFIAPI
ParseDhcp6Ack(IN EFI_DHCP6_PROTOCOL * This,IN VOID * Context,IN EFI_DHCP6_PACKET * Packet)175 ParseDhcp6Ack (
176   IN EFI_DHCP6_PROTOCOL          *This,
177   IN VOID                        *Context,
178   IN EFI_DHCP6_PACKET            *Packet
179   )
180 {
181   EFI_STATUS                  Status;
182   UINT32                      OptionCount;
183   EFI_DHCP6_PACKET_OPTION     **OptionList;
184   DNS6_SERVER_INFOR           *DnsServerInfor;
185   UINT32                      ServerCount;
186   EFI_IPv6_ADDRESS            *ServerList;
187   UINT32                      Index;
188   UINT32                      Count;
189 
190   OptionCount = 0;
191   ServerCount = 0;
192   ServerList  = NULL;
193 
194   Status      = This->Parse (This, Packet, &OptionCount, NULL);
195   if (Status != EFI_BUFFER_TOO_SMALL) {
196     return EFI_DEVICE_ERROR;
197   }
198 
199   OptionList = AllocateZeroPool (OptionCount * sizeof (EFI_DHCP6_PACKET_OPTION *));
200   if (OptionList == NULL) {
201     return EFI_OUT_OF_RESOURCES;
202   }
203 
204   Status = This->Parse (This, Packet, &OptionCount, OptionList);
205   if (EFI_ERROR (Status)) {
206     gBS->FreePool (OptionList);
207     return EFI_DEVICE_ERROR;
208   }
209 
210   DnsServerInfor = (DNS6_SERVER_INFOR *) Context;
211 
212   for (Index = 0; Index < OptionCount; Index++) {
213     OptionList[Index]->OpCode = NTOHS (OptionList[Index]->OpCode);
214     OptionList[Index]->OpLen  = NTOHS (OptionList[Index]->OpLen);
215 
216     //
217     // Get DNS server addresses from this reply packet.
218     //
219     if (OptionList[Index]->OpCode == DHCP6_TAG_DNS_SERVER) {
220 
221       if (((OptionList[Index]->OpLen & 0xf) != 0) || (OptionList[Index]->OpLen == 0)) {
222         Status = EFI_DEVICE_ERROR;
223         gBS->FreePool (OptionList);
224         return Status;
225       }
226 
227       ServerCount = OptionList[Index]->OpLen/16;
228       ServerList = AllocatePool (ServerCount * sizeof (EFI_IPv6_ADDRESS));
229       if (ServerList == NULL) {
230         gBS->FreePool (OptionList);
231         return EFI_OUT_OF_RESOURCES;
232       }
233 
234       for(Count=0; Count < ServerCount; Count++){
235         CopyMem (ServerList + Count, &OptionList[Index]->Data[16 * Count], sizeof (EFI_IPv6_ADDRESS));
236       }
237 
238       *(DnsServerInfor->ServerCount) = ServerCount;
239       DnsServerInfor->ServerList     = ServerList;
240     }
241   }
242 
243   gBS->FreePool (OptionList);
244 
245   return Status;
246 
247 }
248 
249 /**
250   Parse the DHCP ACK to get Dns4 server information.
251 
252   @param  Instance         The DNS instance.
253   @param  DnsServerCount   Retrieved Dns4 server Ip count.
254   @param  DnsServerList    Retrieved Dns4 server Ip list.
255 
256   @retval EFI_SUCCESS           The Dns4 information is got from the DHCP ACK.
257   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
258   @retval EFI_NO_MEDIA          There was a media error.
259   @retval Others                Other errors as indicated.
260 
261 **/
262 EFI_STATUS
GetDns4ServerFromDhcp4(IN DNS_INSTANCE * Instance,OUT UINT32 * DnsServerCount,OUT EFI_IPv4_ADDRESS ** DnsServerList)263 GetDns4ServerFromDhcp4 (
264   IN  DNS_INSTANCE               *Instance,
265   OUT UINT32                     *DnsServerCount,
266   OUT EFI_IPv4_ADDRESS           **DnsServerList
267   )
268 {
269   EFI_STATUS                          Status;
270   EFI_HANDLE                          Image;
271   EFI_HANDLE                          Controller;
272   BOOLEAN                             MediaPresent;
273   EFI_HANDLE                          MnpChildHandle;
274   EFI_MANAGED_NETWORK_PROTOCOL        *Mnp;
275   EFI_MANAGED_NETWORK_CONFIG_DATA     MnpConfigData;
276   EFI_HANDLE                          Dhcp4Handle;
277   EFI_DHCP4_PROTOCOL                  *Dhcp4;
278   EFI_IP4_CONFIG2_PROTOCOL            *Ip4Config2;
279   UINTN                               DataSize;
280   VOID                                *Data;
281   EFI_IP4_CONFIG2_INTERFACE_INFO      *InterfaceInfo;
282   EFI_DHCP4_PACKET                    SeedPacket;
283   EFI_DHCP4_PACKET_OPTION             *ParaList[2];
284   DNS4_SERVER_INFOR                   DnsServerInfor;
285 
286   EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN    Token;
287   BOOLEAN                             IsDone;
288   UINTN                               Index;
289 
290   Image                      = Instance->Service->ImageHandle;
291   Controller                 = Instance->Service->ControllerHandle;
292 
293   MnpChildHandle             = NULL;
294   Mnp                        = NULL;
295 
296   Dhcp4Handle                = NULL;
297   Dhcp4                      = NULL;
298 
299   Ip4Config2                 = NULL;
300   DataSize                   = 0;
301   Data                       = NULL;
302   InterfaceInfo              = NULL;
303 
304   ZeroMem ((UINT8 *) ParaList, sizeof (ParaList));
305 
306   ZeroMem (&MnpConfigData, sizeof (EFI_MANAGED_NETWORK_CONFIG_DATA));
307 
308   ZeroMem (&DnsServerInfor, sizeof (DNS4_SERVER_INFOR));
309 
310   ZeroMem (&Token, sizeof (EFI_DHCP4_TRANSMIT_RECEIVE_TOKEN));
311 
312   DnsServerInfor.ServerCount = DnsServerCount;
313 
314   IsDone = FALSE;
315 
316   //
317   // Check media.
318   //
319   MediaPresent = TRUE;
320   NetLibDetectMedia (Controller, &MediaPresent);
321   if (!MediaPresent) {
322     return EFI_NO_MEDIA;
323   }
324 
325   //
326   // Create a Mnp child instance, get the protocol and config for it.
327   //
328   Status = NetLibCreateServiceChild (
329              Controller,
330              Image,
331              &gEfiManagedNetworkServiceBindingProtocolGuid,
332              &MnpChildHandle
333              );
334   if (EFI_ERROR (Status)) {
335     return Status;
336   }
337 
338   Status = gBS->OpenProtocol (
339                   MnpChildHandle,
340                   &gEfiManagedNetworkProtocolGuid,
341                   (VOID **) &Mnp,
342                   Image,
343                   Controller,
344                   EFI_OPEN_PROTOCOL_BY_DRIVER
345                   );
346   if (EFI_ERROR (Status)) {
347     goto ON_EXIT;
348   }
349 
350   MnpConfigData.ReceivedQueueTimeoutValue = 0;
351   MnpConfigData.TransmitQueueTimeoutValue = 0;
352   MnpConfigData.ProtocolTypeFilter        = IP4_ETHER_PROTO;
353   MnpConfigData.EnableUnicastReceive      = TRUE;
354   MnpConfigData.EnableMulticastReceive    = TRUE;
355   MnpConfigData.EnableBroadcastReceive    = TRUE;
356   MnpConfigData.EnablePromiscuousReceive  = FALSE;
357   MnpConfigData.FlushQueuesOnReset        = TRUE;
358   MnpConfigData.EnableReceiveTimestamps   = FALSE;
359   MnpConfigData.DisableBackgroundPolling  = FALSE;
360 
361   Status = Mnp->Configure(Mnp, &MnpConfigData);
362   if (EFI_ERROR (Status)) {
363     goto ON_EXIT;
364   }
365 
366   //
367   // Create a DHCP4 child instance and get the protocol.
368   //
369   Status = NetLibCreateServiceChild (
370              Controller,
371              Image,
372              &gEfiDhcp4ServiceBindingProtocolGuid,
373              &Dhcp4Handle
374              );
375   if (EFI_ERROR (Status)) {
376     goto ON_EXIT;
377   }
378 
379   Status = gBS->OpenProtocol (
380                   Dhcp4Handle,
381                   &gEfiDhcp4ProtocolGuid,
382                   (VOID **) &Dhcp4,
383                   Image,
384                   Controller,
385                   EFI_OPEN_PROTOCOL_BY_DRIVER
386                   );
387   if (EFI_ERROR (Status)) {
388     goto ON_EXIT;
389   }
390 
391   //
392   // Get Ip4Config2 instance info.
393   //
394   Status = gBS->HandleProtocol (Controller, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2);
395   if (EFI_ERROR (Status)) {
396     goto ON_EXIT;
397   }
398 
399   Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeInterfaceInfo, &DataSize, Data);
400   if (EFI_ERROR (Status) && Status != EFI_BUFFER_TOO_SMALL) {
401     goto ON_EXIT;
402   }
403 
404   Data = AllocateZeroPool (DataSize);
405   if (Data == NULL) {
406     Status = EFI_OUT_OF_RESOURCES;
407     goto ON_EXIT;
408   }
409 
410   Status = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeInterfaceInfo, &DataSize, Data);
411   if (EFI_ERROR (Status)) {
412     goto ON_EXIT;
413   }
414 
415   InterfaceInfo = (EFI_IP4_CONFIG2_INTERFACE_INFO *)Data;
416 
417   //
418   // Build required Token.
419   //
420   Status = gBS->CreateEvent (
421                   EVT_NOTIFY_SIGNAL,
422                   TPL_NOTIFY,
423                   DhcpCommonNotify,
424                   &IsDone,
425                   &Token.CompletionEvent
426                   );
427   if (EFI_ERROR (Status)) {
428     goto ON_EXIT;
429   }
430 
431   SetMem (&Token.RemoteAddress, sizeof (EFI_IPv4_ADDRESS), 0xff);
432 
433   Token.RemotePort = 67;
434 
435   Token.ListenPointCount = 1;
436 
437   Token.ListenPoints = AllocateZeroPool (Token.ListenPointCount * sizeof (EFI_DHCP4_LISTEN_POINT));
438   if (Token.ListenPoints == NULL) {
439     Status = EFI_OUT_OF_RESOURCES;
440     goto ON_EXIT;
441   }
442 
443   if (Instance->Dns4CfgData.UseDefaultSetting) {
444     CopyMem (&(Token.ListenPoints[0].ListenAddress), &(InterfaceInfo->StationAddress), sizeof (EFI_IPv4_ADDRESS));
445     CopyMem (&(Token.ListenPoints[0].SubnetMask), &(InterfaceInfo->SubnetMask), sizeof (EFI_IPv4_ADDRESS));
446   } else {
447     CopyMem (&(Token.ListenPoints[0].ListenAddress), &(Instance->Dns4CfgData.StationIp), sizeof (EFI_IPv4_ADDRESS));
448     CopyMem (&(Token.ListenPoints[0].SubnetMask), &(Instance->Dns4CfgData.SubnetMask), sizeof (EFI_IPv4_ADDRESS));
449   }
450 
451   Token.ListenPoints[0].ListenPort = 68;
452 
453   Token.TimeoutValue = DNS_TIME_TO_GETMAP;
454 
455   DnsInitSeedPacket (&SeedPacket, InterfaceInfo);
456 
457   ParaList[0] = AllocateZeroPool (sizeof (EFI_DHCP4_PACKET_OPTION));
458   if (ParaList[0] == NULL) {
459     Status = EFI_OUT_OF_RESOURCES;
460     goto ON_EXIT;
461   }
462 
463   ParaList[0]->OpCode  = DHCP4_TAG_TYPE;
464   ParaList[0]->Length  = 1;
465   ParaList[0]->Data[0] = DHCP4_MSG_INFORM;
466 
467   ParaList[1] = AllocateZeroPool (sizeof (EFI_DHCP4_PACKET_OPTION));
468   if (ParaList[1] == NULL) {
469     Status = EFI_OUT_OF_RESOURCES;
470     goto ON_EXIT;
471   }
472 
473   ParaList[1]->OpCode  = DHCP4_TAG_PARA_LIST;
474   ParaList[1]->Length  = 1;
475   ParaList[1]->Data[0] = DHCP4_TAG_DNS_SERVER;
476 
477   Status = Dhcp4->Build (Dhcp4, &SeedPacket, 0, NULL, 2, ParaList, &Token.Packet);
478 
479   Token.Packet->Dhcp4.Header.Xid      = HTONL(NET_RANDOM (NetRandomInitSeed ()));
480 
481   Token.Packet->Dhcp4.Header.Reserved = HTONS ((UINT16)0x8000);
482 
483   if (Instance->Dns4CfgData.UseDefaultSetting) {
484     CopyMem (&(Token.Packet->Dhcp4.Header.ClientAddr), &(InterfaceInfo->StationAddress), sizeof (EFI_IPv4_ADDRESS));
485   } else {
486     CopyMem (&(Token.Packet->Dhcp4.Header.ClientAddr), &(Instance->Dns4CfgData.StationIp), sizeof (EFI_IPv4_ADDRESS));
487   }
488 
489   CopyMem (Token.Packet->Dhcp4.Header.ClientHwAddr, &(InterfaceInfo->HwAddress), InterfaceInfo->HwAddressSize);
490 
491   Token.Packet->Dhcp4.Header.HwAddrLen = (UINT8)(InterfaceInfo->HwAddressSize);
492 
493   //
494   // TransmitReceive Token
495   //
496   Status = Dhcp4->TransmitReceive (Dhcp4, &Token);
497   if (EFI_ERROR (Status)) {
498     goto ON_EXIT;
499   }
500 
501   //
502   // Poll the packet
503   //
504   do {
505     Status = Mnp->Poll (Mnp);
506   } while (!IsDone);
507 
508   //
509   // Parse the ACK to get required information if received done.
510   //
511   if (IsDone && !EFI_ERROR (Token.Status)) {
512     for (Index = 0; Index < Token.ResponseCount; Index++) {
513       Status = ParseDhcp4Ack (Dhcp4, &Token.ResponseList[Index], &DnsServerInfor);
514       if (!EFI_ERROR (Status)) {
515         break;
516       }
517     }
518 
519     *DnsServerList = DnsServerInfor.ServerList;
520   } else {
521     Status = Token.Status;
522   }
523 
524 ON_EXIT:
525 
526   if (Data != NULL) {
527     FreePool (Data);
528   }
529 
530   for (Index = 0; Index < 2; Index++) {
531     if (ParaList[Index] != NULL) {
532       FreePool (ParaList[Index]);
533     }
534   }
535 
536   if (Token.ListenPoints) {
537     FreePool (Token.ListenPoints);
538   }
539 
540   if (Token.Packet) {
541     FreePool (Token.Packet);
542   }
543 
544   if (Token.ResponseList != NULL) {
545     FreePool (Token.ResponseList);
546   }
547 
548   if (Token.CompletionEvent != NULL) {
549     gBS->CloseEvent (Token.CompletionEvent);
550   }
551 
552   if (Dhcp4 != NULL) {
553     Dhcp4->Stop (Dhcp4);
554     Dhcp4->Configure (Dhcp4, NULL);
555 
556     gBS->CloseProtocol (
557            Dhcp4Handle,
558            &gEfiDhcp4ProtocolGuid,
559            Image,
560            Controller
561            );
562   }
563 
564   if (Dhcp4Handle != NULL) {
565     NetLibDestroyServiceChild (
566       Controller,
567       Image,
568       &gEfiDhcp4ServiceBindingProtocolGuid,
569       Dhcp4Handle
570       );
571   }
572 
573   if (Mnp != NULL) {
574     Mnp->Configure (Mnp, NULL);
575 
576     gBS->CloseProtocol (
577            MnpChildHandle,
578            &gEfiManagedNetworkProtocolGuid,
579            Image,
580            Controller
581            );
582   }
583 
584   NetLibDestroyServiceChild (
585     Controller,
586     Image,
587     &gEfiManagedNetworkServiceBindingProtocolGuid,
588     MnpChildHandle
589     );
590 
591   return Status;
592 }
593 
594 /**
595   Parse the DHCP ACK to get Dns6 server information.
596 
597   @param  Image            The handle of the driver image.
598   @param  Controller       The handle of the controller.
599   @param  DnsServerCount   Retrieved Dns6 server Ip count.
600   @param  DnsServerList    Retrieved Dns6 server Ip list.
601 
602   @retval EFI_SUCCESS           The Dns6 information is got from the DHCP ACK.
603   @retval EFI_OUT_OF_RESOURCES  Failed to allocate memory.
604   @retval EFI_NO_MEDIA          There was a media error.
605   @retval Others                Other errors as indicated.
606 
607 **/
608 EFI_STATUS
GetDns6ServerFromDhcp6(IN EFI_HANDLE Image,IN EFI_HANDLE Controller,OUT UINT32 * DnsServerCount,OUT EFI_IPv6_ADDRESS ** DnsServerList)609 GetDns6ServerFromDhcp6 (
610   IN  EFI_HANDLE                 Image,
611   IN  EFI_HANDLE                 Controller,
612   OUT UINT32                     *DnsServerCount,
613   OUT EFI_IPv6_ADDRESS           **DnsServerList
614   )
615 {
616   EFI_HANDLE                Dhcp6Handle;
617   EFI_DHCP6_PROTOCOL        *Dhcp6;
618   EFI_STATUS                Status;
619   EFI_STATUS                TimerStatus;
620   EFI_DHCP6_PACKET_OPTION   *Oro;
621   EFI_DHCP6_RETRANSMISSION  InfoReqReXmit;
622   EFI_EVENT                 Timer;
623   BOOLEAN                   MediaPresent;
624   DNS6_SERVER_INFOR         DnsServerInfor;
625 
626   Dhcp6Handle = NULL;
627   Dhcp6       = NULL;
628   Oro         = NULL;
629   Timer       = NULL;
630 
631   ZeroMem (&DnsServerInfor, sizeof (DNS6_SERVER_INFOR));
632 
633   DnsServerInfor.ServerCount = DnsServerCount;
634 
635   //
636   // Check media status before doing DHCP.
637   //
638   MediaPresent = TRUE;
639   NetLibDetectMedia (Controller, &MediaPresent);
640   if (!MediaPresent) {
641     return EFI_NO_MEDIA;
642   }
643 
644   //
645   // Create a DHCP6 child instance and get the protocol.
646   //
647   Status = NetLibCreateServiceChild (
648              Controller,
649              Image,
650              &gEfiDhcp6ServiceBindingProtocolGuid,
651              &Dhcp6Handle
652              );
653   if (EFI_ERROR (Status)) {
654     return Status;
655   }
656 
657   Status = gBS->OpenProtocol (
658                   Dhcp6Handle,
659                   &gEfiDhcp6ProtocolGuid,
660                   (VOID **) &Dhcp6,
661                   Image,
662                   Controller,
663                   EFI_OPEN_PROTOCOL_BY_DRIVER
664                   );
665   if (EFI_ERROR (Status)) {
666     goto ON_EXIT;
667   }
668 
669   Oro = AllocateZeroPool (sizeof (EFI_DHCP6_PACKET_OPTION) + 1);
670   if (Oro == NULL) {
671     Status = EFI_OUT_OF_RESOURCES;
672     goto ON_EXIT;
673   }
674 
675   //
676   // Ask the server to reply with DNS options.
677   // All members in EFI_DHCP6_PACKET_OPTION are in network order.
678   //
679   Oro->OpCode  = HTONS (DHCP6_TAG_DNS_REQUEST);
680   Oro->OpLen   = HTONS (2);
681   Oro->Data[1] = DHCP6_TAG_DNS_SERVER;
682 
683   InfoReqReXmit.Irt = 4;
684   InfoReqReXmit.Mrc = 1;
685   InfoReqReXmit.Mrt = 10;
686   InfoReqReXmit.Mrd = 30;
687 
688   Status = Dhcp6->InfoRequest (
689                     Dhcp6,
690                     TRUE,
691                     Oro,
692                     0,
693                     NULL,
694                     &InfoReqReXmit,
695                     NULL,
696                     ParseDhcp6Ack,
697                     &DnsServerInfor
698                     );
699   if (Status == EFI_NO_MAPPING) {
700     Status = gBS->CreateEvent (EVT_TIMER, TPL_CALLBACK, NULL, NULL, &Timer);
701     if (EFI_ERROR (Status)) {
702       goto ON_EXIT;
703     }
704 
705     Status = gBS->SetTimer (
706                     Timer,
707                     TimerRelative,
708                     DNS_TIME_TO_GETMAP * TICKS_PER_SECOND
709                     );
710 
711     if (EFI_ERROR (Status)) {
712       goto ON_EXIT;
713     }
714 
715     do {
716       TimerStatus = gBS->CheckEvent (Timer);
717       if (!EFI_ERROR (TimerStatus)) {
718         Status = Dhcp6->InfoRequest (
719                           Dhcp6,
720                           TRUE,
721                           Oro,
722                           0,
723                           NULL,
724                           &InfoReqReXmit,
725                           NULL,
726                           ParseDhcp6Ack,
727                           &DnsServerInfor
728                           );
729       }
730     } while (TimerStatus == EFI_NOT_READY);
731   }
732 
733   *DnsServerList  = DnsServerInfor.ServerList;
734 
735 ON_EXIT:
736 
737   if (Oro != NULL) {
738     FreePool (Oro);
739   }
740 
741   if (Timer != NULL) {
742     gBS->CloseEvent (Timer);
743   }
744 
745   if (Dhcp6 != NULL) {
746     gBS->CloseProtocol (
747            Dhcp6Handle,
748            &gEfiDhcp6ProtocolGuid,
749            Image,
750            Controller
751            );
752   }
753 
754   NetLibDestroyServiceChild (
755     Controller,
756     Image,
757     &gEfiDhcp6ServiceBindingProtocolGuid,
758     Dhcp6Handle
759     );
760 
761   return Status;
762 
763 }
764 
765