VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/NetworkPkg/HttpBootDxe/HttpBootClient.c@ 80721

Last change on this file since 80721 was 80721, checked in by vboxsync, 5 years ago

Devices/EFI/FirmwareNew: Start upgrade process to edk2-stable201908 (compiles on Windows and works to some extent), bugref:4643

  • Property svn:eol-style set to native
File size: 38.7 KB
Line 
1/** @file
2 Implementation of the boot file download function.
3
4Copyright (c) 2015 - 2018, Intel Corporation. All rights reserved.<BR>
5(C) Copyright 2016 Hewlett Packard Enterprise Development LP<BR>
6SPDX-License-Identifier: BSD-2-Clause-Patent
7
8**/
9
10#include "HttpBootDxe.h"
11
12/**
13 Update the device path node to include the boot resource information.
14
15 @param[in] Private The pointer to the driver's private data.
16
17 @retval EFI_SUCCESS Device patch successfully updated.
18 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources.
19 @retval Others Unexpected error happened.
20
21**/
22EFI_STATUS
23HttpBootUpdateDevicePath (
24 IN HTTP_BOOT_PRIVATE_DATA *Private
25 )
26{
27 EFI_DEV_PATH *Node;
28 EFI_DEVICE_PATH_PROTOCOL *TmpIpDevicePath;
29 EFI_DEVICE_PATH_PROTOCOL *TmpDnsDevicePath;
30 EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
31 UINTN Length;
32 EFI_STATUS Status;
33
34 TmpIpDevicePath = NULL;
35 TmpDnsDevicePath = NULL;
36
37 //
38 // Update the IP node with DHCP assigned information.
39 //
40 if (!Private->UsingIpv6) {
41 Node = AllocateZeroPool (sizeof (IPv4_DEVICE_PATH));
42 if (Node == NULL) {
43 return EFI_OUT_OF_RESOURCES;
44 }
45 Node->Ipv4.Header.Type = MESSAGING_DEVICE_PATH;
46 Node->Ipv4.Header.SubType = MSG_IPv4_DP;
47 SetDevicePathNodeLength (Node, sizeof (IPv4_DEVICE_PATH));
48 CopyMem (&Node->Ipv4.LocalIpAddress, &Private->StationIp, sizeof (EFI_IPv4_ADDRESS));
49 Node->Ipv4.RemotePort = Private->Port;
50 Node->Ipv4.Protocol = EFI_IP_PROTO_TCP;
51 Node->Ipv4.StaticIpAddress = FALSE;
52 CopyMem (&Node->Ipv4.GatewayIpAddress, &Private->GatewayIp, sizeof (EFI_IPv4_ADDRESS));
53 CopyMem (&Node->Ipv4.SubnetMask, &Private->SubnetMask, sizeof (EFI_IPv4_ADDRESS));
54 } else {
55 Node = AllocateZeroPool (sizeof (IPv6_DEVICE_PATH));
56 if (Node == NULL) {
57 return EFI_OUT_OF_RESOURCES;
58 }
59 Node->Ipv6.Header.Type = MESSAGING_DEVICE_PATH;
60 Node->Ipv6.Header.SubType = MSG_IPv6_DP;
61 SetDevicePathNodeLength (Node, sizeof (IPv6_DEVICE_PATH));
62 Node->Ipv6.PrefixLength = IP6_PREFIX_LENGTH;
63 Node->Ipv6.RemotePort = Private->Port;
64 Node->Ipv6.Protocol = EFI_IP_PROTO_TCP;
65 Node->Ipv6.IpAddressOrigin = 0;
66 CopyMem (&Node->Ipv6.LocalIpAddress, &Private->StationIp.v6, sizeof (EFI_IPv6_ADDRESS));
67 CopyMem (&Node->Ipv6.RemoteIpAddress, &Private->ServerIp.v6, sizeof (EFI_IPv6_ADDRESS));
68 CopyMem (&Node->Ipv6.GatewayIpAddress, &Private->GatewayIp.v6, sizeof (EFI_IPv6_ADDRESS));
69 }
70
71 TmpIpDevicePath = AppendDevicePathNode (Private->ParentDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
72 FreePool (Node);
73 if (TmpIpDevicePath == NULL) {
74 return EFI_OUT_OF_RESOURCES;
75 }
76
77 //
78 // Update the DNS node with DNS server IP list if existed.
79 //
80 if (Private->DnsServerIp != NULL) {
81 Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6) + Private->DnsServerCount * sizeof (EFI_IP_ADDRESS);
82 Node = AllocatePool (Length);
83 if (Node == NULL) {
84 FreePool (TmpIpDevicePath);
85 return EFI_OUT_OF_RESOURCES;
86 }
87 Node->DevPath.Type = MESSAGING_DEVICE_PATH;
88 Node->DevPath.SubType = MSG_DNS_DP;
89 SetDevicePathNodeLength (Node, Length);
90 Node->Dns.IsIPv6 = Private->UsingIpv6 ? 0x01 : 0x00;
91 CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL) + sizeof (Node->Dns.IsIPv6), Private->DnsServerIp, Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
92
93 TmpDnsDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
94 FreePool (Node);
95 FreePool (TmpIpDevicePath);
96 TmpIpDevicePath = NULL;
97 if (TmpDnsDevicePath == NULL) {
98 return EFI_OUT_OF_RESOURCES;
99 }
100 }
101
102 //
103 // Update the URI node with the boot file URI.
104 //
105 Length = sizeof (EFI_DEVICE_PATH_PROTOCOL) + AsciiStrSize (Private->BootFileUri);
106 Node = AllocatePool (Length);
107 if (Node == NULL) {
108 if (TmpIpDevicePath != NULL) {
109 FreePool (TmpIpDevicePath);
110 }
111 if (TmpDnsDevicePath != NULL) {
112 FreePool (TmpDnsDevicePath);
113 }
114 return EFI_OUT_OF_RESOURCES;
115 }
116 Node->DevPath.Type = MESSAGING_DEVICE_PATH;
117 Node->DevPath.SubType = MSG_URI_DP;
118 SetDevicePathNodeLength (Node, Length);
119 CopyMem ((UINT8*) Node + sizeof (EFI_DEVICE_PATH_PROTOCOL), Private->BootFileUri, AsciiStrSize (Private->BootFileUri));
120
121 if (TmpDnsDevicePath != NULL) {
122 NewDevicePath = AppendDevicePathNode (TmpDnsDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
123 FreePool (TmpDnsDevicePath);
124 } else {
125 ASSERT (TmpIpDevicePath != NULL);
126 NewDevicePath = AppendDevicePathNode (TmpIpDevicePath, (EFI_DEVICE_PATH_PROTOCOL*) Node);
127 FreePool (TmpIpDevicePath);
128 }
129 FreePool (Node);
130 if (NewDevicePath == NULL) {
131 return EFI_OUT_OF_RESOURCES;
132 }
133
134 if (!Private->UsingIpv6) {
135 //
136 // Reinstall the device path protocol of the child handle.
137 //
138 Status = gBS->ReinstallProtocolInterface (
139 Private->Ip4Nic->Controller,
140 &gEfiDevicePathProtocolGuid,
141 Private->Ip4Nic->DevicePath,
142 NewDevicePath
143 );
144 if (EFI_ERROR (Status)) {
145 return Status;
146 }
147
148 FreePool (Private->Ip4Nic->DevicePath);
149 Private->Ip4Nic->DevicePath = NewDevicePath;
150 } else {
151 //
152 // Reinstall the device path protocol of the child handle.
153 //
154 Status = gBS->ReinstallProtocolInterface (
155 Private->Ip6Nic->Controller,
156 &gEfiDevicePathProtocolGuid,
157 Private->Ip6Nic->DevicePath,
158 NewDevicePath
159 );
160 if (EFI_ERROR (Status)) {
161 return Status;
162 }
163 FreePool (Private->Ip6Nic->DevicePath);
164 Private->Ip6Nic->DevicePath = NewDevicePath;
165 }
166
167 return EFI_SUCCESS;
168}
169
170/**
171 Parse the boot file URI information from the selected Dhcp4 offer packet.
172
173 @param[in] Private The pointer to the driver's private data.
174
175 @retval EFI_SUCCESS Successfully parsed out all the boot information.
176 @retval Others Failed to parse out the boot information.
177
178**/
179EFI_STATUS
180HttpBootDhcp4ExtractUriInfo (
181 IN HTTP_BOOT_PRIVATE_DATA *Private
182 )
183{
184 HTTP_BOOT_DHCP4_PACKET_CACHE *SelectOffer;
185 HTTP_BOOT_DHCP4_PACKET_CACHE *HttpOffer;
186 UINT32 SelectIndex;
187 UINT32 ProxyIndex;
188 UINT32 DnsServerIndex;
189 EFI_DHCP4_PACKET_OPTION *Option;
190 EFI_STATUS Status;
191
192 ASSERT (Private != NULL);
193 ASSERT (Private->SelectIndex != 0);
194 SelectIndex = Private->SelectIndex - 1;
195 ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
196
197 DnsServerIndex = 0;
198
199 Status = EFI_SUCCESS;
200
201 //
202 // SelectOffer contains the IP address configuration and name server configuration.
203 // HttpOffer contains the boot file URL.
204 //
205 SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp4;
206 if (Private->FilePathUri == NULL) {
207 //
208 // In Corporate environment, we need a HttpOffer.
209 //
210 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||
211 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||
212 (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
213 HttpOffer = SelectOffer;
214 } else {
215 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
216 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
217 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp4;
218 }
219 Private->BootFileUriParser = HttpOffer->UriParser;
220 Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_BOOTFILE]->Data;
221 } else {
222 //
223 // In Home environment the BootFileUri comes from the FilePath.
224 //
225 Private->BootFileUriParser = Private->FilePathUriParser;
226 Private->BootFileUri = Private->FilePathUri;
227 }
228
229 //
230 // Check the URI scheme.
231 //
232 Status = HttpBootCheckUriScheme (Private->BootFileUri);
233 if (EFI_ERROR (Status)) {
234 DEBUG ((EFI_D_ERROR, "HttpBootDhcp4ExtractUriInfo: %r.\n", Status));
235 if (Status == EFI_INVALID_PARAMETER) {
236 AsciiPrint ("\n Error: Invalid URI address.\n");
237 } else if (Status == EFI_ACCESS_DENIED) {
238 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
239 }
240 return Status;
241 }
242
243 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||
244 (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||
245 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {
246 Option = SelectOffer->OptList[HTTP_BOOT_DHCP4_TAG_INDEX_DNS_SERVER];
247 ASSERT (Option != NULL);
248
249 //
250 // Record the Dns Server address list.
251 //
252 Private->DnsServerCount = (Option->Length) / sizeof (EFI_IPv4_ADDRESS);
253
254 Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
255 if (Private->DnsServerIp == NULL) {
256 return EFI_OUT_OF_RESOURCES;
257 }
258
259 for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) {
260 CopyMem (&(Private->DnsServerIp[DnsServerIndex].v4), &(((EFI_IPv4_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv4_ADDRESS));
261 }
262
263 //
264 // Configure the default DNS server if server assigned.
265 //
266 Status = HttpBootRegisterIp4Dns (
267 Private,
268 Option->Length,
269 Option->Data
270 );
271 if (EFI_ERROR (Status)) {
272 FreePool (Private->DnsServerIp);
273 Private->DnsServerIp = NULL;
274 return Status;
275 }
276 }
277
278 //
279 // Extract the port from URL, and use default HTTP port 80 if not provided.
280 //
281 Status = HttpUrlGetPort (
282 Private->BootFileUri,
283 Private->BootFileUriParser,
284 &Private->Port
285 );
286 if (EFI_ERROR (Status) || Private->Port == 0) {
287 Private->Port = 80;
288 }
289
290 //
291 // All boot informations are valid here.
292 //
293
294 //
295 // Update the device path to include the boot resource information.
296 //
297 Status = HttpBootUpdateDevicePath (Private);
298 if (EFI_ERROR (Status) && Private->DnsServerIp != NULL) {
299 FreePool (Private->DnsServerIp);
300 Private->DnsServerIp = NULL;
301 }
302
303 return Status;
304}
305
306/**
307 Parse the boot file URI information from the selected Dhcp6 offer packet.
308
309 @param[in] Private The pointer to the driver's private data.
310
311 @retval EFI_SUCCESS Successfully parsed out all the boot information.
312 @retval Others Failed to parse out the boot information.
313
314**/
315EFI_STATUS
316HttpBootDhcp6ExtractUriInfo (
317 IN HTTP_BOOT_PRIVATE_DATA *Private
318 )
319{
320 HTTP_BOOT_DHCP6_PACKET_CACHE *SelectOffer;
321 HTTP_BOOT_DHCP6_PACKET_CACHE *HttpOffer;
322 UINT32 SelectIndex;
323 UINT32 ProxyIndex;
324 UINT32 DnsServerIndex;
325 EFI_DHCP6_PACKET_OPTION *Option;
326 EFI_IPv6_ADDRESS IpAddr;
327 CHAR8 *HostName;
328 UINTN HostNameSize;
329 CHAR16 *HostNameStr;
330 EFI_STATUS Status;
331
332 ASSERT (Private != NULL);
333 ASSERT (Private->SelectIndex != 0);
334 SelectIndex = Private->SelectIndex - 1;
335 ASSERT (SelectIndex < HTTP_BOOT_OFFER_MAX_NUM);
336
337 DnsServerIndex = 0;
338
339 Status = EFI_SUCCESS;
340 HostName = NULL;
341 //
342 // SelectOffer contains the IP address configuration and name server configuration.
343 // HttpOffer contains the boot file URL.
344 //
345 SelectOffer = &Private->OfferBuffer[SelectIndex].Dhcp6;
346 if (Private->FilePathUri == NULL) {
347 //
348 // In Corporate environment, we need a HttpOffer.
349 //
350 if ((SelectOffer->OfferType == HttpOfferTypeDhcpIpUri) ||
351 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns) ||
352 (SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns)) {
353 HttpOffer = SelectOffer;
354 } else {
355 ASSERT (Private->SelectProxyType != HttpOfferTypeMax);
356 ProxyIndex = Private->OfferIndex[Private->SelectProxyType][0];
357 HttpOffer = &Private->OfferBuffer[ProxyIndex].Dhcp6;
358 }
359 Private->BootFileUriParser = HttpOffer->UriParser;
360 Private->BootFileUri = (CHAR8*) HttpOffer->OptList[HTTP_BOOT_DHCP6_IDX_BOOT_FILE_URL]->Data;
361 } else {
362 //
363 // In Home environment the BootFileUri comes from the FilePath.
364 //
365 Private->BootFileUriParser = Private->FilePathUriParser;
366 Private->BootFileUri = Private->FilePathUri;
367 }
368
369 //
370 // Check the URI scheme.
371 //
372 Status = HttpBootCheckUriScheme (Private->BootFileUri);
373 if (EFI_ERROR (Status)) {
374 DEBUG ((EFI_D_ERROR, "HttpBootDhcp6ExtractUriInfo: %r.\n", Status));
375 if (Status == EFI_INVALID_PARAMETER) {
376 AsciiPrint ("\n Error: Invalid URI address.\n");
377 } else if (Status == EFI_ACCESS_DENIED) {
378 AsciiPrint ("\n Error: Access forbidden, only HTTPS connection is allowed.\n");
379 }
380 return Status;
381 }
382
383 //
384 // Set the Local station address to IP layer.
385 //
386 Status = HttpBootSetIp6Address (Private);
387 if (EFI_ERROR (Status)) {
388 return Status;
389 }
390
391 //
392 // Register the IPv6 gateway address to the network device.
393 //
394 Status = HttpBootSetIp6Gateway (Private);
395 if (EFI_ERROR (Status)) {
396 return Status;
397 }
398
399 if ((SelectOffer->OfferType == HttpOfferTypeDhcpNameUriDns) ||
400 (SelectOffer->OfferType == HttpOfferTypeDhcpDns) ||
401 (SelectOffer->OfferType == HttpOfferTypeDhcpIpUriDns)) {
402 Option = SelectOffer->OptList[HTTP_BOOT_DHCP6_IDX_DNS_SERVER];
403 ASSERT (Option != NULL);
404
405 //
406 // Record the Dns Server address list.
407 //
408 Private->DnsServerCount = HTONS (Option->OpLen) / sizeof (EFI_IPv6_ADDRESS);
409
410 Private->DnsServerIp = AllocateZeroPool (Private->DnsServerCount * sizeof (EFI_IP_ADDRESS));
411 if (Private->DnsServerIp == NULL) {
412 return EFI_OUT_OF_RESOURCES;
413 }
414
415 for (DnsServerIndex = 0; DnsServerIndex < Private->DnsServerCount; DnsServerIndex++) {
416 CopyMem (&(Private->DnsServerIp[DnsServerIndex].v6), &(((EFI_IPv6_ADDRESS *) Option->Data)[DnsServerIndex]), sizeof (EFI_IPv6_ADDRESS));
417 }
418
419 //
420 // Configure the default DNS server if server assigned.
421 //
422 Status = HttpBootSetIp6Dns (
423 Private,
424 HTONS (Option->OpLen),
425 Option->Data
426 );
427 if (EFI_ERROR (Status)) {
428 goto Error;
429 }
430 }
431
432 //
433 // Extract the HTTP server Ip from URL. This is used to Check route table
434 // whether can send message to HTTP Server Ip through the GateWay.
435 //
436 Status = HttpUrlGetIp6 (
437 Private->BootFileUri,
438 Private->BootFileUriParser,
439 &IpAddr
440 );
441
442 if (EFI_ERROR (Status)) {
443 //
444 // The Http server address is expressed by Name Ip, so perform DNS resolution
445 //
446 Status = HttpUrlGetHostName (
447 Private->BootFileUri,
448 Private->BootFileUriParser,
449 &HostName
450 );
451 if (EFI_ERROR (Status)) {
452 goto Error;
453 }
454
455 HostNameSize = AsciiStrSize (HostName);
456 HostNameStr = AllocateZeroPool (HostNameSize * sizeof (CHAR16));
457 if (HostNameStr == NULL) {
458 Status = EFI_OUT_OF_RESOURCES;
459 goto Error;
460 }
461
462 AsciiStrToUnicodeStrS (HostName, HostNameStr, HostNameSize);
463
464 if (HostName != NULL) {
465 FreePool (HostName);
466 }
467
468 Status = HttpBootDns (Private, HostNameStr, &IpAddr);
469 FreePool (HostNameStr);
470 if (EFI_ERROR (Status)) {
471 AsciiPrint ("\n Error: Could not retrieve the host address from DNS server.\n");
472 goto Error;
473 }
474 }
475
476 CopyMem (&Private->ServerIp.v6, &IpAddr, sizeof (EFI_IPv6_ADDRESS));
477
478 //
479 // Extract the port from URL, and use default HTTP port 80 if not provided.
480 //
481 Status = HttpUrlGetPort (
482 Private->BootFileUri,
483 Private->BootFileUriParser,
484 &Private->Port
485 );
486 if (EFI_ERROR (Status) || Private->Port == 0) {
487 Private->Port = 80;
488 }
489
490 //
491 // All boot informations are valid here.
492 //
493
494 //
495 // Update the device path to include the boot resource information.
496 //
497 Status = HttpBootUpdateDevicePath (Private);
498 if (EFI_ERROR (Status)) {
499 goto Error;
500 }
501
502 return Status;
503
504Error:
505 if (Private->DnsServerIp != NULL) {
506 FreePool (Private->DnsServerIp);
507 Private->DnsServerIp = NULL;
508 }
509
510 return Status;
511}
512
513
514/**
515 Discover all the boot information for boot file.
516
517 @param[in, out] Private The pointer to the driver's private data.
518
519 @retval EFI_SUCCESS Successfully obtained all the boot information .
520 @retval Others Failed to retrieve the boot information.
521
522**/
523EFI_STATUS
524HttpBootDiscoverBootInfo (
525 IN OUT HTTP_BOOT_PRIVATE_DATA *Private
526 )
527{
528 EFI_STATUS Status;
529
530 //
531 // Start D.O.R.A/S.A.R.R exchange to acquire station ip address and
532 // other Http boot information.
533 //
534 Status = HttpBootDhcp (Private);
535 if (EFI_ERROR (Status)) {
536 return Status;
537 }
538
539 if (!Private->UsingIpv6) {
540 Status = HttpBootDhcp4ExtractUriInfo (Private);
541 } else {
542 Status = HttpBootDhcp6ExtractUriInfo (Private);
543 }
544
545 return Status;
546}
547
548/**
549 HttpIo Callback function which will be invoked when specified HTTP_IO_CALLBACK_EVENT happened.
550
551 @param[in] EventType Indicate the Event type that occurs in the current callback.
552 @param[in] Message HTTP message which will be send to, or just received from HTTP server.
553 @param[in] Context The Callback Context pointer.
554
555 @retval EFI_SUCCESS Tells the HttpIo to continue the HTTP process.
556 @retval Others Tells the HttpIo to abort the current HTTP process.
557**/
558EFI_STATUS
559EFIAPI
560HttpBootHttpIoCallback (
561 IN HTTP_IO_CALLBACK_EVENT EventType,
562 IN EFI_HTTP_MESSAGE *Message,
563 IN VOID *Context
564 )
565{
566 HTTP_BOOT_PRIVATE_DATA *Private;
567 EFI_STATUS Status;
568 Private = (HTTP_BOOT_PRIVATE_DATA *) Context;
569 if (Private->HttpBootCallback != NULL) {
570 Status = Private->HttpBootCallback->Callback (
571 Private->HttpBootCallback,
572 EventType == HttpIoRequest ? HttpBootHttpRequest : HttpBootHttpResponse,
573 EventType == HttpIoRequest ? FALSE : TRUE,
574 sizeof (EFI_HTTP_MESSAGE),
575 (VOID *) Message
576 );
577 return Status;
578 }
579 return EFI_SUCCESS;
580}
581
582/**
583 Create a HttpIo instance for the file download.
584
585 @param[in] Private The pointer to the driver's private data.
586
587 @retval EFI_SUCCESS Successfully created.
588 @retval Others Failed to create HttpIo.
589
590**/
591EFI_STATUS
592HttpBootCreateHttpIo (
593 IN HTTP_BOOT_PRIVATE_DATA *Private
594 )
595{
596 HTTP_IO_CONFIG_DATA ConfigData;
597 EFI_STATUS Status;
598 EFI_HANDLE ImageHandle;
599
600 ASSERT (Private != NULL);
601
602 ZeroMem (&ConfigData, sizeof (HTTP_IO_CONFIG_DATA));
603 if (!Private->UsingIpv6) {
604 ConfigData.Config4.HttpVersion = HttpVersion11;
605 ConfigData.Config4.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
606 IP4_COPY_ADDRESS (&ConfigData.Config4.LocalIp, &Private->StationIp.v4);
607 IP4_COPY_ADDRESS (&ConfigData.Config4.SubnetMask, &Private->SubnetMask.v4);
608 ImageHandle = Private->Ip4Nic->ImageHandle;
609 } else {
610 ConfigData.Config6.HttpVersion = HttpVersion11;
611 ConfigData.Config6.RequestTimeOut = HTTP_BOOT_REQUEST_TIMEOUT;
612 IP6_COPY_ADDRESS (&ConfigData.Config6.LocalIp, &Private->StationIp.v6);
613 ImageHandle = Private->Ip6Nic->ImageHandle;
614 }
615
616 Status = HttpIoCreateIo (
617 ImageHandle,
618 Private->Controller,
619 Private->UsingIpv6 ? IP_VERSION_6 : IP_VERSION_4,
620 &ConfigData,
621 HttpBootHttpIoCallback,
622 (VOID *) Private,
623 &Private->HttpIo
624 );
625 if (EFI_ERROR (Status)) {
626 return Status;
627 }
628
629 Private->HttpCreated = TRUE;
630 return EFI_SUCCESS;
631}
632
633/**
634 Release all the resource of a cache item.
635
636 @param[in] Cache The pointer to the cache item.
637
638**/
639VOID
640HttpBootFreeCache (
641 IN HTTP_BOOT_CACHE_CONTENT *Cache
642 )
643{
644 UINTN Index;
645 LIST_ENTRY *Entry;
646 LIST_ENTRY *NextEntry;
647 HTTP_BOOT_ENTITY_DATA *EntityData;
648
649 if (Cache != NULL) {
650 //
651 // Free the request data
652 //
653 if (Cache->RequestData != NULL) {
654 if (Cache->RequestData->Url != NULL) {
655 FreePool (Cache->RequestData->Url);
656 }
657 FreePool (Cache->RequestData);
658 }
659
660 //
661 // Free the response header
662 //
663 if (Cache->ResponseData != NULL) {
664 if (Cache->ResponseData->Headers != NULL) {
665 for (Index = 0; Index < Cache->ResponseData->HeaderCount; Index++) {
666 FreePool (Cache->ResponseData->Headers[Index].FieldName);
667 FreePool (Cache->ResponseData->Headers[Index].FieldValue);
668 }
669 FreePool (Cache->ResponseData->Headers);
670 }
671 }
672
673 //
674 // Free the response body
675 //
676 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Cache->EntityDataList) {
677 EntityData = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_ENTITY_DATA, Link);
678 if (EntityData->Block != NULL) {
679 FreePool (EntityData->Block);
680 }
681 RemoveEntryList (&EntityData->Link);
682 FreePool (EntityData);
683 }
684
685 FreePool (Cache);
686 }
687}
688
689/**
690 Clean up all cached data.
691
692 @param[in] Private The pointer to the driver's private data.
693
694**/
695VOID
696HttpBootFreeCacheList (
697 IN HTTP_BOOT_PRIVATE_DATA *Private
698 )
699{
700 LIST_ENTRY *Entry;
701 LIST_ENTRY *NextEntry;
702 HTTP_BOOT_CACHE_CONTENT *Cache;
703
704 NET_LIST_FOR_EACH_SAFE (Entry, NextEntry, &Private->CacheList) {
705 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
706 RemoveEntryList (&Cache->Link);
707 HttpBootFreeCache (Cache);
708 }
709}
710
711/**
712 Get the file content from cached data.
713
714 @param[in] Private The pointer to the driver's private data.
715 @param[in] Uri Uri of the file to be retrieved from cache.
716 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
717 code of EFI_SUCCESS, the amount of data transferred to
718 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
719 the size of Buffer required to retrieve the requested file.
720 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
721 then the size of the requested file is returned in
722 BufferSize.
723 @param[out] ImageType The image type of the downloaded file.
724
725 @retval EFI_SUCCESS Successfully created.
726 @retval Others Failed to create HttpIo.
727
728**/
729EFI_STATUS
730HttpBootGetFileFromCache (
731 IN HTTP_BOOT_PRIVATE_DATA *Private,
732 IN CHAR16 *Uri,
733 IN OUT UINTN *BufferSize,
734 OUT UINT8 *Buffer,
735 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
736 )
737{
738 LIST_ENTRY *Entry;
739 LIST_ENTRY *Entry2;
740 HTTP_BOOT_CACHE_CONTENT *Cache;
741 HTTP_BOOT_ENTITY_DATA *EntityData;
742 UINTN CopyedSize;
743
744 if (Uri == NULL || BufferSize == NULL || Buffer == NULL || ImageType == NULL) {
745 return EFI_INVALID_PARAMETER;
746 }
747
748 NET_LIST_FOR_EACH (Entry, &Private->CacheList) {
749 Cache = NET_LIST_USER_STRUCT (Entry, HTTP_BOOT_CACHE_CONTENT, Link);
750 //
751 // Compare the URI to see whether we already have a cache for this file.
752 //
753 if ((Cache->RequestData != NULL) &&
754 (Cache->RequestData->Url != NULL) &&
755 (StrCmp (Uri, Cache->RequestData->Url) == 0)) {
756 //
757 // Hit in cache, record image type.
758 //
759 *ImageType = Cache->ImageType;
760
761 //
762 // Check buffer size.
763 //
764 if (*BufferSize < Cache->EntityLength) {
765 *BufferSize = Cache->EntityLength;
766 return EFI_BUFFER_TOO_SMALL;
767 }
768
769 //
770 // Fill data to buffer.
771 //
772 CopyedSize = 0;
773 NET_LIST_FOR_EACH (Entry2, &Cache->EntityDataList) {
774 EntityData = NET_LIST_USER_STRUCT (Entry2, HTTP_BOOT_ENTITY_DATA, Link);
775 if (*BufferSize > CopyedSize) {
776 CopyMem (
777 Buffer + CopyedSize,
778 EntityData->DataStart,
779 MIN (EntityData->DataLength, *BufferSize - CopyedSize)
780 );
781 CopyedSize += MIN (EntityData->DataLength, *BufferSize - CopyedSize);
782 }
783 }
784 *BufferSize = CopyedSize;
785 return EFI_SUCCESS;
786 }
787 }
788
789 return EFI_NOT_FOUND;
790}
791
792/**
793 A callback function to intercept events during message parser.
794
795 This function will be invoked during HttpParseMessageBody() with various events type. An error
796 return status of the callback function will cause the HttpParseMessageBody() aborted.
797
798 @param[in] EventType Event type of this callback call.
799 @param[in] Data A pointer to data buffer.
800 @param[in] Length Length in bytes of the Data.
801 @param[in] Context Callback context set by HttpInitMsgParser().
802
803 @retval EFI_SUCCESS Continue to parser the message body.
804 @retval Others Abort the parse.
805
806**/
807EFI_STATUS
808EFIAPI
809HttpBootGetBootFileCallback (
810 IN HTTP_BODY_PARSE_EVENT EventType,
811 IN CHAR8 *Data,
812 IN UINTN Length,
813 IN VOID *Context
814 )
815{
816 HTTP_BOOT_CALLBACK_DATA *CallbackData;
817 HTTP_BOOT_ENTITY_DATA *NewEntityData;
818 EFI_STATUS Status;
819 EFI_HTTP_BOOT_CALLBACK_PROTOCOL *HttpBootCallback;
820
821 //
822 // We only care about the entity data.
823 //
824 if (EventType != BodyParseEventOnData) {
825 return EFI_SUCCESS;
826 }
827
828 CallbackData = (HTTP_BOOT_CALLBACK_DATA *) Context;
829 HttpBootCallback = CallbackData->Private->HttpBootCallback;
830 if (HttpBootCallback != NULL) {
831 Status = HttpBootCallback->Callback (
832 HttpBootCallback,
833 HttpBootHttpEntityBody,
834 TRUE,
835 (UINT32)Length,
836 Data
837 );
838 if (EFI_ERROR (Status)) {
839 return Status;
840 }
841 }
842 //
843 // Copy data if caller has provided a buffer.
844 //
845 if (CallbackData->BufferSize > CallbackData->CopyedSize) {
846 CopyMem (
847 CallbackData->Buffer + CallbackData->CopyedSize,
848 Data,
849 MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize)
850 );
851 CallbackData->CopyedSize += MIN (Length, CallbackData->BufferSize - CallbackData->CopyedSize);
852 }
853
854 //
855 // The caller doesn't provide a buffer, save the block into cache list.
856 //
857 if (CallbackData->Cache != NULL) {
858 NewEntityData = AllocatePool (sizeof (HTTP_BOOT_ENTITY_DATA));
859 if (NewEntityData == NULL) {
860 return EFI_OUT_OF_RESOURCES;
861 }
862 if (CallbackData->NewBlock) {
863 NewEntityData->Block = CallbackData->Block;
864 CallbackData->Block = NULL;
865 }
866 NewEntityData->DataLength = Length;
867 NewEntityData->DataStart = (UINT8*) Data;
868 InsertTailList (&CallbackData->Cache->EntityDataList, &NewEntityData->Link);
869 }
870 return EFI_SUCCESS;
871}
872
873/**
874 This function download the boot file by using UEFI HTTP protocol.
875
876 @param[in] Private The pointer to the driver's private data.
877 @param[in] HeaderOnly Only request the response header, it could save a lot of time if
878 the caller only want to know the size of the requested file.
879 @param[in, out] BufferSize On input the size of Buffer in bytes. On output with a return
880 code of EFI_SUCCESS, the amount of data transferred to
881 Buffer. On output with a return code of EFI_BUFFER_TOO_SMALL,
882 the size of Buffer required to retrieve the requested file.
883 @param[out] Buffer The memory buffer to transfer the file to. IF Buffer is NULL,
884 then the size of the requested file is returned in
885 BufferSize.
886 @param[out] ImageType The image type of the downloaded file.
887
888 @retval EFI_SUCCESS The file was loaded.
889 @retval EFI_INVALID_PARAMETER BufferSize is NULL or Buffer Size is not NULL but Buffer is NULL.
890 @retval EFI_OUT_OF_RESOURCES Could not allocate needed resources
891 @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to read the current directory entry.
892 BufferSize has been updated with the size needed to complete
893 the request.
894 @retval Others Unexpected error happened.
895
896**/
897EFI_STATUS
898HttpBootGetBootFile (
899 IN HTTP_BOOT_PRIVATE_DATA *Private,
900 IN BOOLEAN HeaderOnly,
901 IN OUT UINTN *BufferSize,
902 OUT UINT8 *Buffer,
903 OUT HTTP_BOOT_IMAGE_TYPE *ImageType
904 )
905{
906 EFI_STATUS Status;
907 EFI_HTTP_STATUS_CODE StatusCode;
908 CHAR8 *HostName;
909 EFI_HTTP_REQUEST_DATA *RequestData;
910 HTTP_IO_RESPONSE_DATA *ResponseData;
911 HTTP_IO_RESPONSE_DATA ResponseBody;
912 HTTP_IO *HttpIo;
913 HTTP_IO_HEADER *HttpIoHeader;
914 VOID *Parser;
915 HTTP_BOOT_CALLBACK_DATA Context;
916 UINTN ContentLength;
917 HTTP_BOOT_CACHE_CONTENT *Cache;
918 UINT8 *Block;
919 UINTN UrlSize;
920 CHAR16 *Url;
921 BOOLEAN IdentityMode;
922 UINTN ReceivedSize;
923
924 ASSERT (Private != NULL);
925 ASSERT (Private->HttpCreated);
926
927 if (BufferSize == NULL || ImageType == NULL) {
928 return EFI_INVALID_PARAMETER;
929 }
930
931 if (*BufferSize != 0 && Buffer == NULL) {
932 return EFI_INVALID_PARAMETER;
933 }
934
935 //
936 // First, check whether we already cached the requested Uri.
937 //
938 UrlSize = AsciiStrSize (Private->BootFileUri);
939 Url = AllocatePool (UrlSize * sizeof (CHAR16));
940 if (Url == NULL) {
941 return EFI_OUT_OF_RESOURCES;
942 }
943 AsciiStrToUnicodeStrS (Private->BootFileUri, Url, UrlSize);
944 if (!HeaderOnly && Buffer != NULL) {
945 Status = HttpBootGetFileFromCache (Private, Url, BufferSize, Buffer, ImageType);
946 if (Status != EFI_NOT_FOUND) {
947 FreePool (Url);
948 return Status;
949 }
950 }
951
952 //
953 // Not found in cache, try to download it through HTTP.
954 //
955
956 //
957 // 1. Create a temp cache item for the requested URI if caller doesn't provide buffer.
958 //
959 Cache = NULL;
960 if ((!HeaderOnly) && (*BufferSize == 0)) {
961 Cache = AllocateZeroPool (sizeof (HTTP_BOOT_CACHE_CONTENT));
962 if (Cache == NULL) {
963 Status = EFI_OUT_OF_RESOURCES;
964 goto ERROR_1;
965 }
966 Cache->ImageType = ImageTypeMax;
967 InitializeListHead (&Cache->EntityDataList);
968 }
969
970 //
971 // 2. Send HTTP request message.
972 //
973
974 //
975 // 2.1 Build HTTP header for the request, 3 header is needed to download a boot file:
976 // Host
977 // Accept
978 // User-Agent
979 //
980 HttpIoHeader = HttpBootCreateHeader (3);
981 if (HttpIoHeader == NULL) {
982 Status = EFI_OUT_OF_RESOURCES;
983 goto ERROR_2;
984 }
985
986 //
987 // Add HTTP header field 1: Host
988 //
989 HostName = NULL;
990 Status = HttpUrlGetHostName (
991 Private->BootFileUri,
992 Private->BootFileUriParser,
993 &HostName
994 );
995 if (EFI_ERROR (Status)) {
996 goto ERROR_3;
997 }
998 Status = HttpBootSetHeader (
999 HttpIoHeader,
1000 HTTP_HEADER_HOST,
1001 HostName
1002 );
1003 FreePool (HostName);
1004 if (EFI_ERROR (Status)) {
1005 goto ERROR_3;
1006 }
1007
1008 //
1009 // Add HTTP header field 2: Accept
1010 //
1011 Status = HttpBootSetHeader (
1012 HttpIoHeader,
1013 HTTP_HEADER_ACCEPT,
1014 "*/*"
1015 );
1016 if (EFI_ERROR (Status)) {
1017 goto ERROR_3;
1018 }
1019
1020 //
1021 // Add HTTP header field 3: User-Agent
1022 //
1023 Status = HttpBootSetHeader (
1024 HttpIoHeader,
1025 HTTP_HEADER_USER_AGENT,
1026 HTTP_USER_AGENT_EFI_HTTP_BOOT
1027 );
1028 if (EFI_ERROR (Status)) {
1029 goto ERROR_3;
1030 }
1031
1032 //
1033 // 2.2 Build the rest of HTTP request info.
1034 //
1035 RequestData = AllocatePool (sizeof (EFI_HTTP_REQUEST_DATA));
1036 if (RequestData == NULL) {
1037 Status = EFI_OUT_OF_RESOURCES;
1038 goto ERROR_3;
1039 }
1040 RequestData->Method = HeaderOnly ? HttpMethodHead : HttpMethodGet;
1041 RequestData->Url = Url;
1042
1043 //
1044 // 2.3 Record the request info in a temp cache item.
1045 //
1046 if (Cache != NULL) {
1047 Cache->RequestData = RequestData;
1048 }
1049
1050 //
1051 // 2.4 Send out the request to HTTP server.
1052 //
1053 HttpIo = &Private->HttpIo;
1054 Status = HttpIoSendRequest (
1055 HttpIo,
1056 RequestData,
1057 HttpIoHeader->HeaderCount,
1058 HttpIoHeader->Headers,
1059 0,
1060 NULL
1061 );
1062 if (EFI_ERROR (Status)) {
1063 goto ERROR_4;
1064 }
1065
1066 //
1067 // 3. Receive HTTP response message.
1068 //
1069
1070 //
1071 // 3.1 First step, use zero BodyLength to only receive the response headers.
1072 //
1073 ResponseData = AllocateZeroPool (sizeof(HTTP_IO_RESPONSE_DATA));
1074 if (ResponseData == NULL) {
1075 Status = EFI_OUT_OF_RESOURCES;
1076 goto ERROR_4;
1077 }
1078 Status = HttpIoRecvResponse (
1079 &Private->HttpIo,
1080 TRUE,
1081 ResponseData
1082 );
1083 if (EFI_ERROR (Status) || EFI_ERROR (ResponseData->Status)) {
1084 if (EFI_ERROR (ResponseData->Status)) {
1085 StatusCode = HttpIo->RspToken.Message->Data.Response->StatusCode;
1086 HttpBootPrintErrorMessage (StatusCode);
1087 Status = ResponseData->Status;
1088 }
1089 goto ERROR_5;
1090 }
1091
1092 //
1093 // Check the image type according to server's response.
1094 //
1095 Status = HttpBootCheckImageType (
1096 Private->BootFileUri,
1097 Private->BootFileUriParser,
1098 ResponseData->HeaderCount,
1099 ResponseData->Headers,
1100 ImageType
1101 );
1102 if (EFI_ERROR (Status)) {
1103 goto ERROR_5;
1104 }
1105
1106 //
1107 // 3.2 Cache the response header.
1108 //
1109 if (Cache != NULL) {
1110 Cache->ResponseData = ResponseData;
1111 Cache->ImageType = *ImageType;
1112 }
1113
1114 //
1115 // 3.3 Init a message-body parser from the header information.
1116 //
1117 Parser = NULL;
1118 Context.NewBlock = FALSE;
1119 Context.Block = NULL;
1120 Context.CopyedSize = 0;
1121 Context.Buffer = Buffer;
1122 Context.BufferSize = *BufferSize;
1123 Context.Cache = Cache;
1124 Context.Private = Private;
1125 Status = HttpInitMsgParser (
1126 HeaderOnly ? HttpMethodHead : HttpMethodGet,
1127 ResponseData->Response.StatusCode,
1128 ResponseData->HeaderCount,
1129 ResponseData->Headers,
1130 HttpBootGetBootFileCallback,
1131 (VOID*) &Context,
1132 &Parser
1133 );
1134 if (EFI_ERROR (Status)) {
1135 goto ERROR_6;
1136 }
1137
1138 //
1139 // 3.4 Continue to receive and parse message-body if needed.
1140 //
1141 Block = NULL;
1142 if (!HeaderOnly) {
1143 //
1144 // 3.4.1, check whether we are in identity transfer-coding.
1145 //
1146 ContentLength = 0;
1147 Status = HttpGetEntityLength (Parser, &ContentLength);
1148 if (!EFI_ERROR (Status)) {
1149 IdentityMode = TRUE;
1150 } else {
1151 IdentityMode = FALSE;
1152 }
1153
1154 //
1155 // 3.4.2, start the message-body download, the identity and chunked transfer-coding
1156 // is handled in different path here.
1157 //
1158 ZeroMem (&ResponseBody, sizeof (HTTP_IO_RESPONSE_DATA));
1159 if (IdentityMode) {
1160 //
1161 // In identity transfer-coding there is no need to parse the message body,
1162 // just download the message body to the user provided buffer directly.
1163 //
1164 ReceivedSize = 0;
1165 while (ReceivedSize < ContentLength) {
1166 ResponseBody.Body = (CHAR8*) Buffer + ReceivedSize;
1167 ResponseBody.BodyLength = *BufferSize - ReceivedSize;
1168 Status = HttpIoRecvResponse (
1169 &Private->HttpIo,
1170 FALSE,
1171 &ResponseBody
1172 );
1173 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {
1174 if (EFI_ERROR (ResponseBody.Status)) {
1175 Status = ResponseBody.Status;
1176 }
1177 goto ERROR_6;
1178 }
1179 ReceivedSize += ResponseBody.BodyLength;
1180 if (Private->HttpBootCallback != NULL) {
1181 Status = Private->HttpBootCallback->Callback (
1182 Private->HttpBootCallback,
1183 HttpBootHttpEntityBody,
1184 TRUE,
1185 (UINT32)ResponseBody.BodyLength,
1186 ResponseBody.Body
1187 );
1188 if (EFI_ERROR (Status)) {
1189 goto ERROR_6;
1190 }
1191 }
1192 }
1193 } else {
1194 //
1195 // In "chunked" transfer-coding mode, so we need to parse the received
1196 // data to get the real entity content.
1197 //
1198 Block = NULL;
1199 while (!HttpIsMessageComplete (Parser)) {
1200 //
1201 // Allocate a buffer in Block to hold the message-body.
1202 // If caller provides a buffer, this Block will be reused in every HttpIoRecvResponse().
1203 // Otherwise a buffer, the buffer in Block will be cached and we should allocate a new before
1204 // every HttpIoRecvResponse().
1205 //
1206 if (Block == NULL || Context.BufferSize == 0) {
1207 Block = AllocatePool (HTTP_BOOT_BLOCK_SIZE);
1208 if (Block == NULL) {
1209 Status = EFI_OUT_OF_RESOURCES;
1210 goto ERROR_6;
1211 }
1212 Context.NewBlock = TRUE;
1213 Context.Block = Block;
1214 } else {
1215 Context.NewBlock = FALSE;
1216 }
1217
1218 ResponseBody.Body = (CHAR8*) Block;
1219 ResponseBody.BodyLength = HTTP_BOOT_BLOCK_SIZE;
1220 Status = HttpIoRecvResponse (
1221 &Private->HttpIo,
1222 FALSE,
1223 &ResponseBody
1224 );
1225 if (EFI_ERROR (Status) || EFI_ERROR (ResponseBody.Status)) {
1226 if (EFI_ERROR (ResponseBody.Status)) {
1227 Status = ResponseBody.Status;
1228 }
1229 goto ERROR_6;
1230 }
1231
1232 //
1233 // Parse the new received block of the message-body, the block will be saved in cache.
1234 //
1235 Status = HttpParseMessageBody (
1236 Parser,
1237 ResponseBody.BodyLength,
1238 ResponseBody.Body
1239 );
1240 if (EFI_ERROR (Status)) {
1241 goto ERROR_6;
1242 }
1243 }
1244 }
1245 }
1246
1247 //
1248 // 3.5 Message-body receive & parse is completed, we should be able to get the file size now.
1249 //
1250 Status = HttpGetEntityLength (Parser, &ContentLength);
1251 if (EFI_ERROR (Status)) {
1252 goto ERROR_6;
1253 }
1254
1255 if (*BufferSize < ContentLength) {
1256 Status = EFI_BUFFER_TOO_SMALL;
1257 } else {
1258 Status = EFI_SUCCESS;
1259 }
1260 *BufferSize = ContentLength;
1261
1262 //
1263 // 4. Save the cache item to driver's cache list and return.
1264 //
1265 if (Cache != NULL) {
1266 Cache->EntityLength = ContentLength;
1267 InsertTailList (&Private->CacheList, &Cache->Link);
1268 }
1269
1270 if (Parser != NULL) {
1271 HttpFreeMsgParser (Parser);
1272 }
1273
1274 return Status;
1275
1276ERROR_6:
1277 if (Parser != NULL) {
1278 HttpFreeMsgParser (Parser);
1279 }
1280 if (Context.Block != NULL) {
1281 FreePool (Context.Block);
1282 }
1283 HttpBootFreeCache (Cache);
1284
1285ERROR_5:
1286 if (ResponseData != NULL) {
1287 FreePool (ResponseData);
1288 }
1289ERROR_4:
1290 if (RequestData != NULL) {
1291 FreePool (RequestData);
1292 }
1293ERROR_3:
1294 HttpBootFreeHeader (HttpIoHeader);
1295ERROR_2:
1296 if (Cache != NULL) {
1297 FreePool (Cache);
1298 }
1299ERROR_1:
1300 if (Url != NULL) {
1301 FreePool (Url);
1302 }
1303
1304 return Status;
1305}
1306
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette