1 /** @file
2   Routines for HttpDxe driver to perform DNS resolution based on UEFI DNS protocols.
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 "HttpDriver.h"
16 
17 /**
18   Retrieve the host address using the EFI_DNS4_PROTOCOL.
19 
20   @param[in]  HttpInstance        Pointer to HTTP_PROTOCOL instance.
21   @param[in]  HostName            Pointer to buffer containing hostname.
22   @param[out] IpAddress           On output, pointer to buffer containing IPv4 address.
23 
24   @retval EFI_SUCCESS             Operation succeeded.
25   @retval EFI_OUT_OF_RESOURCES    Failed to allocate needed resources.
26   @retval EFI_DEVICE_ERROR        An unexpected network error occurred.
27   @retval Others                  Other errors as indicated.
28 
29 **/
30 EFI_STATUS
HttpDns4(IN HTTP_PROTOCOL * HttpInstance,IN CHAR16 * HostName,OUT EFI_IPv4_ADDRESS * IpAddress)31 HttpDns4 (
32   IN     HTTP_PROTOCOL            *HttpInstance,
33   IN     CHAR16                   *HostName,
34      OUT EFI_IPv4_ADDRESS         *IpAddress
35   )
36 {
37   EFI_STATUS                      Status;
38   EFI_DNS4_PROTOCOL               *Dns4;
39   EFI_DNS4_CONFIG_DATA            Dns4CfgData;
40   EFI_DNS4_COMPLETION_TOKEN       Token;
41   BOOLEAN                         IsDone;
42   HTTP_SERVICE                    *Service;
43   EFI_HANDLE                      Dns4Handle;
44   EFI_IP4_CONFIG2_PROTOCOL        *Ip4Config2;
45   UINTN                           DnsServerListCount;
46   EFI_IPv4_ADDRESS                *DnsServerList;
47   UINTN                           DataSize;
48 
49 
50   Service = HttpInstance->Service;
51   ASSERT (Service != NULL);
52 
53   DnsServerList      = NULL;
54   DnsServerListCount = 0;
55   ZeroMem (&Token, sizeof (EFI_DNS4_COMPLETION_TOKEN));
56 
57   //
58   // Get DNS server list from EFI IPv4 Configuration II protocol.
59   //
60   Status = gBS->HandleProtocol (Service->ControllerHandle, &gEfiIp4Config2ProtocolGuid, (VOID **) &Ip4Config2);
61   if (!EFI_ERROR (Status)) {
62     //
63     // Get the required size.
64     //
65     DataSize = 0;
66     Status   = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeDnsServer, &DataSize, NULL);
67     if (Status == EFI_BUFFER_TOO_SMALL) {
68       DnsServerList = AllocatePool (DataSize);
69       if (DnsServerList == NULL) {
70         return EFI_OUT_OF_RESOURCES;
71       }
72 
73       Status   = Ip4Config2->GetData (Ip4Config2, Ip4Config2DataTypeDnsServer, &DataSize, DnsServerList);
74       if (EFI_ERROR (Status)) {
75         FreePool (DnsServerList);
76         DnsServerList = NULL;
77       } else {
78         DnsServerListCount = DataSize / sizeof (EFI_IPv4_ADDRESS);
79       }
80     }
81   }
82 
83   Dns4Handle = NULL;
84   Dns4       = NULL;
85 
86   //
87   // Create a DNS child instance and get the protocol.
88   //
89   Status = NetLibCreateServiceChild (
90              Service->ControllerHandle,
91              Service->ImageHandle,
92              &gEfiDns4ServiceBindingProtocolGuid,
93              &Dns4Handle
94              );
95   if (EFI_ERROR (Status)) {
96     goto Exit;
97   }
98 
99   Status = gBS->OpenProtocol (
100                   Dns4Handle,
101                   &gEfiDns4ProtocolGuid,
102                   (VOID **) &Dns4,
103                   Service->ImageHandle,
104                   Service->ControllerHandle,
105                   EFI_OPEN_PROTOCOL_BY_DRIVER
106                   );
107   if (EFI_ERROR (Status)) {
108     goto Exit;
109   }
110 
111   //
112   // Configure DNS4 instance for the DNS server address and protocol.
113   //
114   ZeroMem (&Dns4CfgData, sizeof (Dns4CfgData));
115   Dns4CfgData.DnsServerListCount = DnsServerListCount;
116   Dns4CfgData.DnsServerList      = DnsServerList;
117   Dns4CfgData.UseDefaultSetting  = HttpInstance->IPv4Node.UseDefaultAddress;
118   if (!Dns4CfgData.UseDefaultSetting) {
119     IP4_COPY_ADDRESS (&Dns4CfgData.StationIp, &HttpInstance->IPv4Node.LocalAddress);
120     IP4_COPY_ADDRESS (&Dns4CfgData.SubnetMask, &HttpInstance->IPv4Node.LocalSubnet);
121   }
122   Dns4CfgData.EnableDnsCache     = TRUE;
123   Dns4CfgData.Protocol           = EFI_IP_PROTO_UDP;
124   Status = Dns4->Configure (
125                    Dns4,
126                    &Dns4CfgData
127                    );
128   if (EFI_ERROR (Status)) {
129     goto Exit;
130   }
131 
132   //
133   // Create event to set the is done flag when name resolution is finished.
134   //
135   ZeroMem (&Token, sizeof (Token));
136   Status = gBS->CreateEvent (
137                   EVT_NOTIFY_SIGNAL,
138                   TPL_NOTIFY,
139                   HttpCommonNotify,
140                   &IsDone,
141                   &Token.Event
142                   );
143   if (EFI_ERROR (Status)) {
144     goto Exit;
145   }
146 
147   //
148   // Start asynchronous name resolution.
149   //
150   Token.Status = EFI_NOT_READY;
151   IsDone       = FALSE;
152   Status = Dns4->HostNameToIp (Dns4, HostName, &Token);
153   if (EFI_ERROR (Status)) {
154     goto Exit;
155   }
156 
157   while (!IsDone) {
158     Dns4->Poll (Dns4);
159   }
160 
161   //
162   // Name resolution is done, check result.
163   //
164   Status = Token.Status;
165   if (!EFI_ERROR (Status)) {
166     if (Token.RspData.H2AData == NULL) {
167       Status = EFI_DEVICE_ERROR;
168       goto Exit;
169     }
170     if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {
171       Status = EFI_DEVICE_ERROR;
172       goto Exit;
173     }
174     //
175     // We just return the first IP address from DNS protocol.
176     //
177     IP4_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);
178     Status = EFI_SUCCESS;
179   }
180 
181 Exit:
182 
183   if (Token.Event != NULL) {
184     gBS->CloseEvent (Token.Event);
185   }
186   if (Token.RspData.H2AData != NULL) {
187     if (Token.RspData.H2AData->IpList != NULL) {
188       FreePool (Token.RspData.H2AData->IpList);
189     }
190     FreePool (Token.RspData.H2AData);
191   }
192 
193   if (Dns4 != NULL) {
194     Dns4->Configure (Dns4, NULL);
195 
196     gBS->CloseProtocol (
197            Dns4Handle,
198            &gEfiDns4ProtocolGuid,
199            Service->ImageHandle,
200            Service->ControllerHandle
201            );
202   }
203 
204   if (Dns4Handle != NULL) {
205     NetLibDestroyServiceChild (
206       Service->ControllerHandle,
207       Service->ImageHandle,
208       &gEfiDns4ServiceBindingProtocolGuid,
209       Dns4Handle
210       );
211   }
212 
213   if (DnsServerList != NULL) {
214     FreePool (DnsServerList);
215   }
216 
217   return Status;
218 }
219 
220 /**
221   Retrieve the host address using the EFI_DNS6_PROTOCOL.
222 
223   @param[in]  HttpInstance        Pointer to HTTP_PROTOCOL instance.
224   @param[in]  HostName            Pointer to buffer containing hostname.
225   @param[out] IpAddress           On output, pointer to buffer containing IPv6 address.
226 
227   @retval EFI_SUCCESS             Operation succeeded.
228   @retval EFI_OUT_OF_RESOURCES    Failed to allocate needed resources.
229   @retval EFI_DEVICE_ERROR        An unexpected network error occurred.
230   @retval Others                  Other errors as indicated.
231 
232 **/
233 EFI_STATUS
HttpDns6(IN HTTP_PROTOCOL * HttpInstance,IN CHAR16 * HostName,OUT EFI_IPv6_ADDRESS * IpAddress)234 HttpDns6 (
235   IN     HTTP_PROTOCOL            *HttpInstance,
236   IN     CHAR16                   *HostName,
237      OUT EFI_IPv6_ADDRESS         *IpAddress
238   )
239 {
240   EFI_STATUS                      Status;
241   HTTP_SERVICE                    *Service;
242   EFI_DNS6_PROTOCOL               *Dns6;
243   EFI_DNS6_CONFIG_DATA            Dns6ConfigData;
244   EFI_DNS6_COMPLETION_TOKEN       Token;
245   EFI_HANDLE                      Dns6Handle;
246   EFI_IP6_CONFIG_PROTOCOL         *Ip6Config;
247   EFI_IPv6_ADDRESS                *DnsServerList;
248   UINTN                           DnsServerListCount;
249   UINTN                           DataSize;
250   BOOLEAN                         IsDone;
251 
252 
253   Service = HttpInstance->Service;
254   ASSERT (Service != NULL);
255 
256   DnsServerList       = NULL;
257   DnsServerListCount  = 0;
258   Dns6                = NULL;
259   Dns6Handle          = NULL;
260   ZeroMem (&Token, sizeof (EFI_DNS6_COMPLETION_TOKEN));
261 
262   //
263   // Get DNS server list from EFI IPv6 Configuration protocol.
264   //
265   Status = gBS->HandleProtocol (Service->ControllerHandle, &gEfiIp6ConfigProtocolGuid, (VOID **) &Ip6Config);
266   if (!EFI_ERROR (Status)) {
267     //
268     // Get the required size.
269     //
270     DataSize = 0;
271     Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, NULL);
272     if (Status == EFI_BUFFER_TOO_SMALL) {
273       DnsServerList = AllocatePool (DataSize);
274       if (DnsServerList == NULL) {
275         return EFI_OUT_OF_RESOURCES;
276       }
277 
278       Status = Ip6Config->GetData (Ip6Config, Ip6ConfigDataTypeDnsServer, &DataSize, DnsServerList);
279       if (EFI_ERROR (Status)) {
280         FreePool (DnsServerList);
281         DnsServerList = NULL;
282       } else {
283         DnsServerListCount = DataSize / sizeof (EFI_IPv6_ADDRESS);
284       }
285     }
286   }
287 
288   //
289   // Create a DNSv6 child instance and get the protocol.
290   //
291   Status = NetLibCreateServiceChild (
292              Service->ControllerHandle,
293              Service->ImageHandle,
294              &gEfiDns6ServiceBindingProtocolGuid,
295              &Dns6Handle
296              );
297   if (EFI_ERROR (Status)) {
298     goto Exit;
299   }
300 
301   Status = gBS->OpenProtocol (
302                   Dns6Handle,
303                   &gEfiDns6ProtocolGuid,
304                   (VOID **) &Dns6,
305                   Service->ImageHandle,
306                   Service->ControllerHandle,
307                   EFI_OPEN_PROTOCOL_BY_DRIVER
308                   );
309   if (EFI_ERROR (Status)) {
310     goto Exit;
311   }
312 
313   //
314   // Configure DNS6 instance for the DNS server address and protocol.
315   //
316   ZeroMem (&Dns6ConfigData, sizeof (EFI_DNS6_CONFIG_DATA));
317   Dns6ConfigData.DnsServerCount = (UINT32)DnsServerListCount;
318   Dns6ConfigData.DnsServerList  = DnsServerList;
319   Dns6ConfigData.EnableDnsCache = TRUE;
320   Dns6ConfigData.Protocol       = EFI_IP_PROTO_UDP;
321   IP6_COPY_ADDRESS (&Dns6ConfigData.StationIp, &HttpInstance->Ipv6Node.LocalAddress);
322   Status = Dns6->Configure (
323                    Dns6,
324                    &Dns6ConfigData
325                    );
326   if (EFI_ERROR (Status)) {
327     goto Exit;
328   }
329 
330   Token.Status = EFI_NOT_READY;
331   IsDone       = FALSE;
332   //
333   // Create event to set the  IsDone flag when name resolution is finished.
334   //
335   Status = gBS->CreateEvent (
336                   EVT_NOTIFY_SIGNAL,
337                   TPL_NOTIFY,
338                   HttpCommonNotify,
339                   &IsDone,
340                   &Token.Event
341                   );
342   if (EFI_ERROR (Status)) {
343     goto Exit;
344   }
345 
346   //
347   // Start asynchronous name resolution.
348   //
349   Status = Dns6->HostNameToIp (Dns6, HostName, &Token);
350   if (EFI_ERROR (Status)) {
351     goto Exit;
352   }
353 
354   while (!IsDone) {
355     Dns6->Poll (Dns6);
356   }
357 
358   //
359   // Name resolution is done, check result.
360   //
361   Status = Token.Status;
362   if (!EFI_ERROR (Status)) {
363     if (Token.RspData.H2AData == NULL) {
364       Status = EFI_DEVICE_ERROR;
365       goto Exit;
366     }
367     if (Token.RspData.H2AData->IpCount == 0 || Token.RspData.H2AData->IpList == NULL) {
368       Status = EFI_DEVICE_ERROR;
369       goto Exit;
370     }
371     //
372     // We just return the first IPv6 address from DNS protocol.
373     //
374     IP6_COPY_ADDRESS (IpAddress, Token.RspData.H2AData->IpList);
375     Status = EFI_SUCCESS;
376   }
377 
378 Exit:
379 
380   if (Token.Event != NULL) {
381     gBS->CloseEvent (Token.Event);
382   }
383   if (Token.RspData.H2AData != NULL) {
384     if (Token.RspData.H2AData->IpList != NULL) {
385       FreePool (Token.RspData.H2AData->IpList);
386     }
387     FreePool (Token.RspData.H2AData);
388   }
389 
390   if (Dns6 != NULL) {
391     Dns6->Configure (Dns6, NULL);
392 
393     gBS->CloseProtocol (
394            Dns6Handle,
395            &gEfiDns6ProtocolGuid,
396            Service->ImageHandle,
397            Service->ControllerHandle
398            );
399   }
400 
401   if (Dns6Handle != NULL) {
402     NetLibDestroyServiceChild (
403       Service->ControllerHandle,
404       Service->ImageHandle,
405       &gEfiDns6ServiceBindingProtocolGuid,
406       Dns6Handle
407       );
408   }
409 
410   if (DnsServerList != NULL) {
411     FreePool (DnsServerList);
412   }
413 
414   return Status;
415 }
416