/** Process Packet Too Big message sent by a router in response to a packet that it cannot forward because the packet is larger than the MTU of outgoing link. Since this driver already uses IPv6 minimum link MTU as the maximum packet size, if Packet Too Big message is still received, do not reduce the packet size, but rather include a Fragment header in the subsequent packets. @param[in] IpSb The IP service that received the packet. @param[in] Head The IP head of the ICMPv6 error packet. @param[in] Packet The content of the ICMPv6 error with the IP head removed. @retval EFI_SUCCESS The ICMPv6 error processed successfully. @retval EFI_OUT_OF_RESOURCES Failed to finish the operation due to lack of resource. @retval EFI_NOT_FOUND The packet too big message is not sent to us. **/ EFI_STATUS Ip6ProcessPacketTooBig ( IN IP6_SERVICE *IpSb, IN EFI_IP6_HEADER *Head, IN NET_BUF *Packet ) { IP6_ICMP_ERROR_HEAD Icmp; UINT32 Mtu; IP6_ROUTE_ENTRY *RouteEntry; EFI_IPv6_ADDRESS *DestAddress; NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); Mtu = NTOHL (Icmp.Fourth); DestAddress = &Icmp.IpHead.DestinationAddress; if (Mtu < IP6_MIN_LINK_MTU) { // // Normally the multicast address is considered to be on-link and not recorded // in route table. Here it is added into the table since the MTU information // need be recorded. // if (IP6_IS_MULTICAST (DestAddress)) { RouteEntry = Ip6CreateRouteEntry (DestAddress, 128, NULL); if (RouteEntry == NULL) { NetbufFree (Packet); return EFI_OUT_OF_RESOURCES; } RouteEntry->Flag = IP6_DIRECT_ROUTE | IP6_PACKET_TOO_BIG; InsertHeadList (&IpSb->RouteTable->RouteArea[128], &RouteEntry->Link); IpSb->RouteTable->TotalNum++; } else { RouteEntry = Ip6FindRouteEntry (IpSb->RouteTable, DestAddress, NULL); if (RouteEntry == NULL) { NetbufFree (Packet); return EFI_NOT_FOUND; } RouteEntry->Flag = RouteEntry->Flag | IP6_PACKET_TOO_BIG; Ip6FreeRouteEntry (RouteEntry); } } NetbufFree (Packet); return EFI_SUCCESS; }
/** Process the ICMPv6 error packet, and deliver the packet to upper layer. @param[in] IpSb The IP service that received the packet. @param[in] Head The IP head of the ICMPv6 error packet. @param[in] Packet The content of the ICMPv6 error with the IP head removed. @retval EFI_SUCCESS The ICMPv6 error processed successfully. @retval EFI_INVALID_PARAMETER The packet is invalid. @retval Others Failed to process the packet. **/ EFI_STATUS Ip6ProcessIcmpError ( IN IP6_SERVICE *IpSb, IN EFI_IP6_HEADER *Head, IN NET_BUF *Packet ) { IP6_ICMP_ERROR_HEAD Icmp; // // Check the validity of the packet // if (Packet->TotalSize < sizeof (Icmp)) { goto DROP; } NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); if (Icmp.Head.Type == ICMP_V6_PACKET_TOO_BIG) { return Ip6ProcessPacketTooBig (IpSb, Head, Packet); } // // Notify the upper-layer process that an ICMPv6 eror message is received. // IP6_GET_CLIP_INFO (Packet)->Status = EFI_ICMP_ERROR; return Ip6Demultiplex (IpSb, Head, Packet); DROP: NetbufFree (Packet); Packet = NULL; return EFI_INVALID_PARAMETER; }
/** Handle the ICMPv6 packet. First validate the message format, then, according to the message types, process it as an informational packet or an error packet. @param[in] IpSb The IP service that received the packet. @param[in] Head The IP head of the ICMPv6 packet. @param[in] Packet The content of the ICMPv6 packet with IP head removed. @retval EFI_INVALID_PARAMETER The packet is malformated. @retval EFI_SUCCESS The ICMPv6 message successfully processed. @retval Others Failed to handle the ICMPv6 packet. **/ EFI_STATUS Ip6IcmpHandle ( IN IP6_SERVICE *IpSb, IN EFI_IP6_HEADER *Head, IN NET_BUF *Packet ) { IP6_ICMP_HEAD Icmp; UINT16 PseudoCheckSum; UINT16 CheckSum; // // Check the validity of the incoming packet. // if (Packet->TotalSize < sizeof (Icmp)) { goto DROP; } NetbufCopy (Packet, 0, sizeof (Icmp), (UINT8 *) &Icmp); // // Make sure checksum is valid. // PseudoCheckSum = NetIp6PseudoHeadChecksum ( &Head->SourceAddress, &Head->DestinationAddress, IP6_ICMP, Packet->TotalSize ); CheckSum = (UINT16) ~NetAddChecksum (PseudoCheckSum, NetbufChecksum (Packet)); if (CheckSum != 0) { goto DROP; } // // According to the packet type, call corresponding process // if (Icmp.Type <= ICMP_V6_ERROR_MAX) { return Ip6ProcessIcmpError (IpSb, Head, Packet); } else { return Ip6ProcessIcmpInformation (IpSb, Head, Packet); } DROP: NetbufFree (Packet); return EFI_INVALID_PARAMETER; }
/** The input process routine for MTFTP upload. @param UdpPacket The received MTFTP packet. @param EndPoint The local/remote access point @param IoStatus The result of the packet receiving @param Context Opaque parameter for the callback, which is the MTFTP session. **/ VOID EFIAPI Mtftp4WrqInput ( IN NET_BUF *UdpPacket, IN UDP_END_POINT *EndPoint, IN EFI_STATUS IoStatus, IN VOID *Context ) { MTFTP4_PROTOCOL *Instance; EFI_MTFTP4_PACKET *Packet; BOOLEAN Completed; EFI_STATUS Status; UINT32 Len; UINT16 Opcode; Instance = (MTFTP4_PROTOCOL *) Context; NET_CHECK_SIGNATURE (Instance, MTFTP4_PROTOCOL_SIGNATURE); Completed = FALSE; Packet = NULL; Status = EFI_SUCCESS; if (EFI_ERROR (IoStatus)) { Status = IoStatus; goto ON_EXIT; } ASSERT (UdpPacket != NULL); if (UdpPacket->TotalSize < MTFTP4_OPCODE_LEN) { goto ON_EXIT; } // // Client send initial request to server's listening port. Server // will select a UDP port to communicate with the client. // if (EndPoint->RemotePort != Instance->ConnectedPort) { if (Instance->ConnectedPort != 0) { goto ON_EXIT; } else { Instance->ConnectedPort = EndPoint->RemotePort; } } // // Copy the MTFTP packet to a continuous buffer if it isn't already so. // Len = UdpPacket->TotalSize; if (UdpPacket->BlockOpNum > 1) { Packet = AllocatePool (Len); if (Packet == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ON_EXIT; } NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); } else { Packet = (EFI_MTFTP4_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); ASSERT (Packet != NULL); } Opcode = NTOHS (Packet->OpCode); // // Call the user's CheckPacket if provided. Abort the transmission // if CheckPacket returns an EFI_ERROR code. // if ((Instance->Token->CheckPacket != NULL) && ((Opcode == EFI_MTFTP4_OPCODE_OACK) || (Opcode == EFI_MTFTP4_OPCODE_ERROR))) { Status = Instance->Token->CheckPacket ( &Instance->Mtftp4, Instance->Token, (UINT16) Len, Packet ); if (EFI_ERROR (Status)) { // // Send an error message to the server to inform it // if (Opcode != EFI_MTFTP4_OPCODE_ERROR) { Mtftp4SendError ( Instance, EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, (UINT8 *) "User aborted the transfer" ); } Status = EFI_ABORTED; goto ON_EXIT; } } switch (Opcode) { case EFI_MTFTP4_OPCODE_ACK: if (Len != MTFTP4_OPCODE_LEN + MTFTP4_BLKNO_LEN) { goto ON_EXIT; } Status = Mtftp4WrqHandleAck (Instance, Packet, Len, &Completed); break; case EFI_MTFTP4_OPCODE_OACK: if (Len <= MTFTP4_OPCODE_LEN) { goto ON_EXIT; } Status = Mtftp4WrqHandleOack (Instance, Packet, Len, &Completed); break; case EFI_MTFTP4_OPCODE_ERROR: Status = EFI_TFTP_ERROR; break; default: break; } ON_EXIT: // // Free the resources, then if !EFI_ERROR (Status) and not completed, // restart the receive, otherwise end the session. // if ((Packet != NULL) && (UdpPacket->BlockOpNum > 1)) { FreePool (Packet); } if (UdpPacket != NULL) { NetbufFree (UdpPacket); } if (!EFI_ERROR (Status) && !Completed) { Status = UdpIoRecvDatagram (Instance->UnicastPort, Mtftp4WrqInput, Instance, 0); } // // Status may have been updated by UdpIoRecvDatagram // if (EFI_ERROR (Status) || Completed) { Mtftp4CleanOperation (Instance, Status); } }
/** Build then send a MTFTP data packet for the MTFTP upload session. @param Instance The MTFTP upload session. @param BlockNum The block number to send. @retval EFI_OUT_OF_RESOURCES Failed to build the packet. @retval EFI_ABORTED The consumer of this child directs to abort the transmission by return an error through PacketNeeded. @retval EFI_SUCCESS The data is sent. **/ EFI_STATUS Mtftp4WrqSendBlock ( IN OUT MTFTP4_PROTOCOL *Instance, IN UINT16 BlockNum ) { EFI_MTFTP4_PACKET *Packet; EFI_MTFTP4_TOKEN *Token; NET_BUF *UdpPacket; EFI_STATUS Status; UINT16 DataLen; UINT8 *DataBuf; UINT64 Start; // // Allocate a buffer to hold the user data // UdpPacket = NetbufAlloc (Instance->BlkSize + MTFTP4_DATA_HEAD_LEN); if (UdpPacket == NULL) { return EFI_OUT_OF_RESOURCES; } Packet = (EFI_MTFTP4_PACKET *) NetbufAllocSpace (UdpPacket, MTFTP4_DATA_HEAD_LEN, FALSE); ASSERT (Packet != NULL); Packet->Data.OpCode = HTONS (EFI_MTFTP4_OPCODE_DATA); Packet->Data.Block = HTONS (BlockNum); // // Read the block from either the buffer or PacketNeeded callback // Token = Instance->Token; DataLen = Instance->BlkSize; if (Token->Buffer != NULL) { Start = MultU64x32 (BlockNum - 1, Instance->BlkSize); if (Token->BufferSize < Start + Instance->BlkSize) { DataLen = (UINT16) (Token->BufferSize - Start); Instance->LastBlock = BlockNum; Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum); } if (DataLen > 0) { NetbufAllocSpace (UdpPacket, DataLen, FALSE); CopyMem (Packet->Data.Data, (UINT8 *) Token->Buffer + Start, DataLen); } } else { // // Get data from PacketNeeded // DataBuf = NULL; Status = Token->PacketNeeded ( &Instance->Mtftp4, Token, &DataLen, (VOID **) &DataBuf ); if (EFI_ERROR (Status) || (DataLen > Instance->BlkSize)) { if (DataBuf != NULL) { FreePool (DataBuf); } if (UdpPacket != NULL) { NetbufFree (UdpPacket); } Mtftp4SendError ( Instance, EFI_MTFTP4_ERRORCODE_REQUEST_DENIED, (UINT8 *) "User aborted the transfer" ); return EFI_ABORTED; } if (DataLen < Instance->BlkSize) { Instance->LastBlock = BlockNum; Mtftp4SetLastBlockNum (&Instance->Blocks, BlockNum); } if (DataLen > 0) { NetbufAllocSpace (UdpPacket, DataLen, FALSE); CopyMem (Packet->Data.Data, DataBuf, DataLen); FreePool (DataBuf); } } return Mtftp4SendPacket (Instance, UdpPacket); }
/** Connect one TLS session by finishing the TLS handshake process. @param[in] HttpInstance The HTTP instance private data. @param[in] Timeout The time to wait for connection done. @retval EFI_SUCCESS The TLS session is established. @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. @retval EFI_ABORTED TLS session state is incorrect. @retval Others Other error as indicated. **/ EFI_STATUS EFIAPI TlsConnectSession ( IN HTTP_PROTOCOL *HttpInstance, IN EFI_EVENT Timeout ) { EFI_STATUS Status; UINT8 *BufferOut; UINTN BufferOutSize; NET_BUF *PacketOut; UINT8 *DataOut; NET_BUF *Pdu; UINT8 *BufferIn; UINTN BufferInSize; UINT8 *GetSessionDataBuffer; UINTN GetSessionDataBufferSize; BufferOut = NULL; PacketOut = NULL; DataOut = NULL; Pdu = NULL; BufferIn = NULL; // // Initialize TLS state. // HttpInstance->TlsSessionState = EfiTlsSessionNotStarted; Status = HttpInstance->Tls->SetSessionData ( HttpInstance->Tls, EfiTlsSessionState, &(HttpInstance->TlsSessionState), sizeof (EFI_TLS_SESSION_STATE) ); if (EFI_ERROR (Status)) { return Status; } // // Create ClientHello // BufferOutSize = DEF_BUF_LEN; BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, NULL, 0, BufferOut, &BufferOutSize ); if (Status == EFI_BUFFER_TOO_SMALL) { FreePool (BufferOut); BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, NULL, 0, BufferOut, &BufferOutSize ); } if (EFI_ERROR (Status)) { FreePool (BufferOut); return Status; } // // Transmit ClientHello // PacketOut = NetbufAlloc ((UINT32) BufferOutSize); DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); if (DataOut == NULL) { FreePool (BufferOut); return EFI_OUT_OF_RESOURCES; } CopyMem (DataOut, BufferOut, BufferOutSize); Status = TlsCommonTransmit (HttpInstance, PacketOut); FreePool (BufferOut); NetbufFree (PacketOut); if (EFI_ERROR (Status)) { return Status; } while(HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring && \ ((Timeout == NULL) || EFI_ERROR (gBS->CheckEvent (Timeout)))) { // // Receive one TLS record. // Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout); if (EFI_ERROR (Status)) { return Status; } BufferInSize = Pdu->TotalSize; BufferIn = AllocateZeroPool (BufferInSize); if (BufferIn == NULL) { NetbufFree (Pdu); Status = EFI_OUT_OF_RESOURCES; return Status; } NetbufCopy (Pdu, 0, (UINT32)BufferInSize, BufferIn); NetbufFree (Pdu); // // Handle Receive data. // BufferOutSize = DEF_BUF_LEN; BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, BufferIn, BufferInSize, BufferOut, &BufferOutSize ); if (Status == EFI_BUFFER_TOO_SMALL) { FreePool (BufferOut); BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { FreePool (BufferIn); Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, BufferIn, BufferInSize, BufferOut, &BufferOutSize ); } FreePool (BufferIn); if (EFI_ERROR (Status)) { FreePool (BufferOut); return Status; } if (BufferOutSize != 0) { // // Transmit the response packet. // PacketOut = NetbufAlloc ((UINT32) BufferOutSize); DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); if (DataOut == NULL) { FreePool (BufferOut); return EFI_OUT_OF_RESOURCES; } CopyMem (DataOut, BufferOut, BufferOutSize); Status = TlsCommonTransmit (HttpInstance, PacketOut); NetbufFree (PacketOut); if (EFI_ERROR (Status)) { FreePool (BufferOut); return Status; } } FreePool (BufferOut); // // Get the session state, then decide whether need to continue handle received packet. // GetSessionDataBufferSize = DEF_BUF_LEN; GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); if (GetSessionDataBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->GetSessionData ( HttpInstance->Tls, EfiTlsSessionState, GetSessionDataBuffer, &GetSessionDataBufferSize ); if (Status == EFI_BUFFER_TOO_SMALL) { FreePool (GetSessionDataBuffer); GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); if (GetSessionDataBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->GetSessionData ( HttpInstance->Tls, EfiTlsSessionState, GetSessionDataBuffer, &GetSessionDataBufferSize ); } if (EFI_ERROR (Status)) { FreePool(GetSessionDataBuffer); return Status; } ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE)); HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer; FreePool (GetSessionDataBuffer); if(HttpInstance->TlsSessionState == EfiTlsSessionError) { return EFI_ABORTED; } } if (HttpInstance->TlsSessionState != EfiTlsSessionDataTransferring) { Status = EFI_ABORTED; } return Status; }
/** Receive one fragment decrypted from one TLS record. @param[in] HttpInstance Pointer to HTTP_PROTOCOL structure. @param[in, out] Fragment The received Fragment. @param[in] Timeout The time to wait for connection done. @retval EFI_SUCCESS One fragment is received. @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. @retval EFI_ABORTED Something wrong decryption the message. @retval Others Other errors as indicated. **/ EFI_STATUS EFIAPI HttpsReceive ( IN HTTP_PROTOCOL *HttpInstance, IN OUT NET_FRAGMENT *Fragment, IN EFI_EVENT Timeout ) { EFI_STATUS Status; NET_BUF *Pdu; TLS_RECORD_HEADER RecordHeader; UINT8 *BufferIn; UINTN BufferInSize; NET_FRAGMENT TempFragment; UINT8 *BufferOut; UINTN BufferOutSize; NET_BUF *PacketOut; UINT8 *DataOut; UINT8 *GetSessionDataBuffer; UINTN GetSessionDataBufferSize; Status = EFI_SUCCESS; Pdu = NULL; BufferIn = NULL; BufferInSize = 0; BufferOut = NULL; BufferOutSize = 0; PacketOut = NULL; DataOut = NULL; GetSessionDataBuffer = NULL; GetSessionDataBufferSize = 0; // // Receive only one TLS record // Status = TlsReceiveOnePdu (HttpInstance, &Pdu, Timeout); if (EFI_ERROR (Status)) { return Status; } BufferInSize = Pdu->TotalSize; BufferIn = AllocateZeroPool (BufferInSize); if (BufferIn == NULL) { Status = EFI_OUT_OF_RESOURCES; NetbufFree (Pdu); return Status; } NetbufCopy (Pdu, 0, (UINT32) BufferInSize, BufferIn); NetbufFree (Pdu); // // Handle Receive data. // RecordHeader = *(TLS_RECORD_HEADER *) BufferIn; if ((RecordHeader.ContentType == TlsContentTypeApplicationData) && (RecordHeader.Version.Major == 0x03) && (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR || RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) ) { // // Decrypt Packet. // Status = TlsProcessMessage ( HttpInstance, BufferIn, BufferInSize, EfiTlsDecrypt, &TempFragment ); FreePool (BufferIn); if (EFI_ERROR (Status)) { if (Status == EFI_ABORTED) { // // Something wrong decryption the message. // BuildResponsePacket() will be called to generate Error Alert message and send it out. // BufferOutSize = DEF_BUF_LEN; BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, NULL, 0, BufferOut, &BufferOutSize ); if (Status == EFI_BUFFER_TOO_SMALL) { FreePool (BufferOut); BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, NULL, 0, BufferOut, &BufferOutSize ); } if (EFI_ERROR (Status)) { FreePool(BufferOut); return Status; } if (BufferOutSize != 0) { PacketOut = NetbufAlloc ((UINT32)BufferOutSize); DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); if (DataOut == NULL) { FreePool (BufferOut); return EFI_OUT_OF_RESOURCES; } CopyMem (DataOut, BufferOut, BufferOutSize); Status = TlsCommonTransmit (HttpInstance, PacketOut); NetbufFree (PacketOut); } FreePool(BufferOut); if (EFI_ERROR (Status)) { return Status; } return EFI_ABORTED; } return Status; } // // Parsing buffer. // ASSERT (((TLS_RECORD_HEADER *) (TempFragment.Bulk))->ContentType == TlsContentTypeApplicationData); BufferInSize = ((TLS_RECORD_HEADER *) (TempFragment.Bulk))->Length; BufferIn = AllocateZeroPool (BufferInSize); if (BufferIn == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } CopyMem (BufferIn, TempFragment.Bulk + sizeof (TLS_RECORD_HEADER), BufferInSize); // // Free the buffer in TempFragment. // FreePool (TempFragment.Bulk); } else if ((RecordHeader.ContentType == TlsContentTypeAlert) && (RecordHeader.Version.Major == 0x03) && (RecordHeader.Version.Minor == TLS10_PROTOCOL_VERSION_MINOR || RecordHeader.Version.Minor == TLS11_PROTOCOL_VERSION_MINOR || RecordHeader.Version.Minor == TLS12_PROTOCOL_VERSION_MINOR) ) { BufferOutSize = DEF_BUF_LEN; BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { FreePool (BufferIn); Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, BufferIn, BufferInSize, BufferOut, &BufferOutSize ); if (Status == EFI_BUFFER_TOO_SMALL) { FreePool (BufferOut); BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { FreePool (BufferIn); Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, BufferIn, BufferInSize, BufferOut, &BufferOutSize ); } FreePool (BufferIn); if (EFI_ERROR (Status)) { FreePool (BufferOut); return Status; } if (BufferOutSize != 0) { PacketOut = NetbufAlloc ((UINT32) BufferOutSize); DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); if (DataOut == NULL) { FreePool (BufferOut); return EFI_OUT_OF_RESOURCES; } CopyMem (DataOut, BufferOut, BufferOutSize); Status = TlsCommonTransmit (HttpInstance, PacketOut); NetbufFree (PacketOut); } FreePool (BufferOut); // // Get the session state. // GetSessionDataBufferSize = DEF_BUF_LEN; GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); if (GetSessionDataBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->GetSessionData ( HttpInstance->Tls, EfiTlsSessionState, GetSessionDataBuffer, &GetSessionDataBufferSize ); if (Status == EFI_BUFFER_TOO_SMALL) { FreePool (GetSessionDataBuffer); GetSessionDataBuffer = AllocateZeroPool (GetSessionDataBufferSize); if (GetSessionDataBuffer == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->GetSessionData ( HttpInstance->Tls, EfiTlsSessionState, GetSessionDataBuffer, &GetSessionDataBufferSize ); } if (EFI_ERROR (Status)) { FreePool (GetSessionDataBuffer); return Status; } ASSERT(GetSessionDataBufferSize == sizeof (EFI_TLS_SESSION_STATE)); HttpInstance->TlsSessionState = *(EFI_TLS_SESSION_STATE *) GetSessionDataBuffer; FreePool (GetSessionDataBuffer); if(HttpInstance->TlsSessionState == EfiTlsSessionError) { DEBUG ((EFI_D_ERROR, "TLS Session State Error!\n")); return EFI_ABORTED; } BufferIn = NULL; BufferInSize = 0; } Fragment->Bulk = BufferIn; Fragment->Len = (UINT32) BufferInSize; return Status; }
/** Close the TLS session and send out the close notification message. @param[in] HttpInstance The HTTP instance private data. @retval EFI_SUCCESS The TLS session is closed. @retval EFI_INVALID_PARAMETER HttpInstance is NULL. @retval EFI_OUT_OF_RESOURCES Can't allocate memory resources. @retval Others Other error as indicated. **/ EFI_STATUS EFIAPI TlsCloseSession ( IN HTTP_PROTOCOL *HttpInstance ) { EFI_STATUS Status; UINT8 *BufferOut; UINTN BufferOutSize; NET_BUF *PacketOut; UINT8 *DataOut; Status = EFI_SUCCESS; BufferOut = NULL; PacketOut = NULL; DataOut = NULL; if (HttpInstance == NULL) { return EFI_INVALID_PARAMETER; } HttpInstance->TlsSessionState = EfiTlsSessionClosing; Status = HttpInstance->Tls->SetSessionData ( HttpInstance->Tls, EfiTlsSessionState, &(HttpInstance->TlsSessionState), sizeof (EFI_TLS_SESSION_STATE) ); if (EFI_ERROR (Status)) { return Status; } BufferOutSize = DEF_BUF_LEN; BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, NULL, 0, BufferOut, &BufferOutSize ); if (Status == EFI_BUFFER_TOO_SMALL) { FreePool (BufferOut); BufferOut = AllocateZeroPool (BufferOutSize); if (BufferOut == NULL) { Status = EFI_OUT_OF_RESOURCES; return Status; } Status = HttpInstance->Tls->BuildResponsePacket ( HttpInstance->Tls, NULL, 0, BufferOut, &BufferOutSize ); } if (EFI_ERROR (Status)) { FreePool (BufferOut); return Status; } PacketOut = NetbufAlloc ((UINT32) BufferOutSize); DataOut = NetbufAllocSpace (PacketOut, (UINT32) BufferOutSize, NET_BUF_TAIL); if (DataOut == NULL) { FreePool (BufferOut); return EFI_OUT_OF_RESOURCES; } CopyMem (DataOut, BufferOut, BufferOutSize); Status = TlsCommonTransmit (HttpInstance, PacketOut); FreePool (BufferOut); NetbufFree (PacketOut); return Status; }
/** Deliver the received data block to the user, which can be saved in the user provide buffer or through the CheckPacket callback. @param[in] Instance The pointer to the Mtftp6 instance. @param[in] Packet The pointer to the received packet. @param[in] Len The packet length. @param[out] UdpPacket The net buf of the received packet. @retval EFI_SUCCESS The data was saved successfully. @retval EFI_ABORTED The user tells to abort by return an error through CheckPacket. @retval EFI_BUFFER_TOO_SMALL The user's buffer is too small, and buffer length is updated to the actual buffer size needed. **/ EFI_STATUS Mtftp6RrqSaveBlock ( IN MTFTP6_INSTANCE *Instance, IN EFI_MTFTP6_PACKET *Packet, IN UINT32 Len, OUT NET_BUF **UdpPacket ) { EFI_MTFTP6_TOKEN *Token; EFI_STATUS Status; UINT16 Block; UINT64 Start; UINT32 DataLen; UINT64 TotalBlock; BOOLEAN Completed; Completed = FALSE; Token = Instance->Token; Block = NTOHS (Packet->Data.Block); DataLen = Len - MTFTP6_DATA_HEAD_LEN; // // This is the last block, save the block num // if (DataLen < Instance->BlkSize) { Completed = TRUE; Instance->LastBlk = Block; Mtftp6SetLastBlockNum (&Instance->BlkList, Block); } // // Remove this block number from the file hole. If Mtftp6RemoveBlockNum // returns EFI_NOT_FOUND, the block has been saved, don't save it again. // Note that : For bigger files, allowing the block counter to roll over // to accept transfers of unlimited size. So TotalBlock is memorised as // continuous block counter. // Status = Mtftp6RemoveBlockNum (&Instance->BlkList, Block, Completed, &TotalBlock); if (Status == EFI_NOT_FOUND) { return EFI_SUCCESS; } else if (EFI_ERROR (Status)) { return Status; } if (Token->CheckPacket != NULL) { // // Callback to the check packet routine with the received packet. // Status = Token->CheckPacket (&Instance->Mtftp6, Token, (UINT16) Len, Packet); if (EFI_ERROR (Status)) { // // Free the received packet before send new packet in ReceiveNotify, // since the Udp6Io might need to be reconfigured. // NetbufFree (*UdpPacket); *UdpPacket = NULL; // // Send the Mtftp6 error message if user aborted the current session. // Mtftp6SendError ( Instance, EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, (UINT8 *) "User aborted download" ); return EFI_ABORTED; } } if (Token->Buffer != NULL) { Start = MultU64x32 (TotalBlock - 1, Instance->BlkSize); if (Start + DataLen <= Token->BufferSize) { CopyMem ((UINT8 *) Token->Buffer + Start, Packet->Data.Data, DataLen); // // Update the file size when received the last block // if ((Instance->LastBlk == Block) && Completed) { Token->BufferSize = Start + DataLen; } } else if (Instance->LastBlk != 0) { // // Don't save the data if the buffer is too small, return // EFI_BUFFER_TOO_SMALL if received the last packet. This // will give a accurate file length. // Token->BufferSize = Start + DataLen; // // Free the received packet before send new packet in ReceiveNotify, // since the udpio might need to be reconfigured. // NetbufFree (*UdpPacket); *UdpPacket = NULL; // // Send the Mtftp6 error message if no enough buffer. // Mtftp6SendError ( Instance, EFI_MTFTP6_ERRORCODE_DISK_FULL, (UINT8 *) "User provided memory block is too small" ); return EFI_BUFFER_TOO_SMALL; } } return EFI_SUCCESS; }
/** The packet process callback for Mtftp6 download. @param[in] UdpPacket The pointer to the packet received. @param[in] UdpEpt The pointer to the Udp6 access point. @param[in] IoStatus The status from Udp6 instance. @param[in] Context The pointer to the context. **/ VOID EFIAPI Mtftp6RrqInput ( IN NET_BUF *UdpPacket, IN UDP_END_POINT *UdpEpt, IN EFI_STATUS IoStatus, IN VOID *Context ) { MTFTP6_INSTANCE *Instance; EFI_MTFTP6_PACKET *Packet; BOOLEAN IsCompleted; BOOLEAN IsMcast; EFI_STATUS Status; UINT16 Opcode; UINT32 TotalNum; UINT32 Len; Instance = (MTFTP6_INSTANCE *) Context; NET_CHECK_SIGNATURE (Instance, MTFTP6_INSTANCE_SIGNATURE); Status = EFI_SUCCESS; Packet = NULL; IsCompleted = FALSE; IsMcast = FALSE; TotalNum = 0; // // Return error status if Udp6 instance failed to receive. // if (EFI_ERROR (IoStatus)) { Status = IoStatus; goto ON_EXIT; } ASSERT (UdpPacket != NULL); if (UdpPacket->TotalSize < MTFTP6_OPCODE_LEN) { goto ON_EXIT; } // // Find the port this packet is from to restart receive correctly. // if (CompareMem ( Ip6Swap128 (&UdpEpt->LocalAddr.v6), &Instance->McastIp, sizeof (EFI_IPv6_ADDRESS) ) == 0) { IsMcast = TRUE; } else { IsMcast = FALSE; } // // Client send initial request to server's listening port. Server // will select a UDP port to communicate with the client. The server // is required to use the same port as RemotePort to multicast the // data. // if (UdpEpt->RemotePort != Instance->ServerDataPort) { if (Instance->ServerDataPort != 0) { goto ON_EXIT; } else { // // For the subsequent exchange of requests, reconfigure the udpio as // (serverip, serverport, localip, localport). // Ususally, the client set serverport as 0 to receive and reset it // once the first packet arrives to send ack. // Instance->ServerDataPort = UdpEpt->RemotePort; } } // // Copy the MTFTP packet to a continuous buffer if it isn't already so. // Len = UdpPacket->TotalSize; TotalNum = UdpPacket->BlockOpNum; if (TotalNum > 1) { Packet = AllocateZeroPool (Len); if (Packet == NULL) { Status = EFI_OUT_OF_RESOURCES; goto ON_EXIT; } NetbufCopy (UdpPacket, 0, Len, (UINT8 *) Packet); } else { Packet = (EFI_MTFTP6_PACKET *) NetbufGetByte (UdpPacket, 0, NULL); ASSERT (Packet != NULL); } Opcode = NTOHS (Packet->OpCode); // // Callback to the user's CheckPacket if provided. Abort the transmission // if CheckPacket returns an EFI_ERROR code. // if ((Instance->Token->CheckPacket != NULL) && (Opcode == EFI_MTFTP6_OPCODE_OACK || Opcode == EFI_MTFTP6_OPCODE_ERROR) ) { Status = Instance->Token->CheckPacket ( &Instance->Mtftp6, Instance->Token, (UINT16) Len, Packet ); if (EFI_ERROR (Status)) { // // Send an error message to the server to inform it // if (Opcode != EFI_MTFTP6_OPCODE_ERROR) { // // Free the received packet before send new packet in ReceiveNotify, // since the udpio might need to be reconfigured. // NetbufFree (UdpPacket); UdpPacket = NULL; // // Send the Mtftp6 error message if user aborted the current session. // Mtftp6SendError ( Instance, EFI_MTFTP6_ERRORCODE_REQUEST_DENIED, (UINT8 *) "User aborted the transfer" ); } Status = EFI_ABORTED; goto ON_EXIT; } } // // Switch the process routines by the operation code. // switch (Opcode) { case EFI_MTFTP6_OPCODE_DATA: if ((Len > (UINT32) (MTFTP6_DATA_HEAD_LEN + Instance->BlkSize)) || (Len < (UINT32) MTFTP6_DATA_HEAD_LEN)) { goto ON_EXIT; } // // Handle the data packet of Rrq. // Status = Mtftp6RrqHandleData ( Instance, Packet, Len, &UdpPacket, &IsCompleted ); break; case EFI_MTFTP6_OPCODE_OACK: if (IsMcast || Len <= MTFTP6_OPCODE_LEN) { goto ON_EXIT; } // // Handle the Oack packet of Rrq. // Status = Mtftp6RrqHandleOack ( Instance, Packet, Len, &UdpPacket, &IsCompleted ); break; default: // // Drop and return eror if received error message. // Status = EFI_TFTP_ERROR; break; } ON_EXIT: // // Free the resources, then if !EFI_ERROR (Status), restart the // receive, otherwise end the session. // if (Packet != NULL && TotalNum > 1) { FreePool (Packet); } if (UdpPacket != NULL) { NetbufFree (UdpPacket); } if (!EFI_ERROR (Status) && !IsCompleted) { if (IsMcast) { Status = UdpIoRecvDatagram ( Instance->McastUdpIo, Mtftp6RrqInput, Instance, 0 ); } else { Status = UdpIoRecvDatagram ( Instance->UdpIo, Mtftp6RrqInput, Instance, 0 ); } } // // Clean up the current session if failed to continue. // if (EFI_ERROR (Status) || IsCompleted) { Mtftp6OperationClean (Instance, Status); } }
/** Process the OACK packet for Rrq. @param[in] Instance The pointer to the Mtftp6 instance. @param[in] Packet The pointer to the received packet. @param[in] Len The length of the packet. @param[out] UdpPacket The net buf of received packet. @param[out] IsCompleted If TRUE, the download has been completed. Otherwise, the download has not been completed. @retval EFI_DEVICE_ERROR Failed to create/start a multicast Udp6 child. @retval EFI_TFTP_ERROR An error happened during the process. @retval EFI_SUCCESS The OACK packet successfully processed. **/ EFI_STATUS Mtftp6RrqHandleOack ( IN MTFTP6_INSTANCE *Instance, IN EFI_MTFTP6_PACKET *Packet, IN UINT32 Len, OUT NET_BUF **UdpPacket, OUT BOOLEAN *IsCompleted ) { EFI_MTFTP6_OPTION *Options; UINT32 Count; MTFTP6_EXT_OPTION_INFO ExtInfo; EFI_STATUS Status; INTN Expected; *IsCompleted = FALSE; // // If already started the master download, don't change the // setting. Master download always succeeds. // Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); ASSERT (Expected != -1); if (Instance->IsMaster && Expected != 1) { return EFI_SUCCESS; } ZeroMem (&ExtInfo, sizeof (MTFTP6_EXT_OPTION_INFO)); // // Parse the options in the packet. // Status = Mtftp6ParseStart (Packet, Len, &Count, &Options); if (EFI_ERROR (Status)) { return Status; } // // Parse the extensive options in the packet. // Status = Mtftp6ParseExtensionOption (Options, Count, FALSE, &ExtInfo); if (EFI_ERROR (Status) || !Mtftp6RrqOackValid (Instance, &ExtInfo, &Instance->ExtInfo)) { // // Don't send an ERROR packet if the error is EFI_OUT_OF_RESOURCES. // if (Status != EFI_OUT_OF_RESOURCES) { // // Free the received packet before send new packet in ReceiveNotify, // since the udpio might need to be reconfigured. // NetbufFree (*UdpPacket); *UdpPacket = NULL; // // Send the Mtftp6 error message if invalid packet. // Mtftp6SendError ( Instance, EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, (UINT8 *) "Mal-formated OACK packet" ); } return EFI_TFTP_ERROR; } if ((ExtInfo.BitMap & MTFTP6_OPT_MCAST_BIT) != 0) { // // Save the multicast info. Always update the Master, only update the // multicast IP address, block size, timeoute at the first time. If IP // address is updated, create a UDP child to receive the multicast. // Instance->IsMaster = ExtInfo.IsMaster; if (NetIp6IsUnspecifiedAddr (&Instance->McastIp)) { if (NetIp6IsUnspecifiedAddr (&ExtInfo.McastIp) || ExtInfo.McastPort == 0) { // // Free the received packet before send new packet in ReceiveNotify, // since the udpio might need to be reconfigured. // NetbufFree (*UdpPacket); *UdpPacket = NULL; // // Send the Mtftp6 error message if invalid multi-cast setting. // Mtftp6SendError ( Instance, EFI_MTFTP6_ERRORCODE_ILLEGAL_OPERATION, (UINT8 *) "Illegal multicast setting" ); return EFI_TFTP_ERROR; } // // Create a UDP child then start receive the multicast from it. // CopyMem ( &Instance->McastIp, &ExtInfo.McastIp, sizeof (EFI_IP_ADDRESS) ); Instance->McastPort = ExtInfo.McastPort; Instance->McastUdpIo = UdpIoCreateIo ( Instance->Service->Controller, Instance->Service->Image, Mtftp6RrqConfigMcastUdpIo, UDP_IO_UDP6_VERSION, Instance ); if (Instance->McastUdpIo == NULL) { return EFI_DEVICE_ERROR; } Status = UdpIoRecvDatagram ( Instance->McastUdpIo, Mtftp6RrqInput, Instance, 0 ); if (EFI_ERROR (Status)) { // // Free the received packet before send new packet in ReceiveNotify, // since the udpio might need to be reconfigured. // NetbufFree (*UdpPacket); *UdpPacket = NULL; // // Send the Mtftp6 error message if failed to create Udp6Io to receive. // Mtftp6SendError ( Instance, EFI_MTFTP6_ERRORCODE_ACCESS_VIOLATION, (UINT8 *) "Failed to create socket to receive multicast packet" ); return Status; } // // Update the parameters used. // if (ExtInfo.BlkSize != 0) { Instance->BlkSize = ExtInfo.BlkSize; } if (ExtInfo.Timeout != 0) { Instance->Timeout = ExtInfo.Timeout; } } } else { Instance->IsMaster = TRUE; if (ExtInfo.BlkSize != 0) { Instance->BlkSize = ExtInfo.BlkSize; } if (ExtInfo.Timeout != 0) { Instance->Timeout = ExtInfo.Timeout; } } // // Free the received packet before send new packet in ReceiveNotify, // since the udpio might need to be reconfigured. // NetbufFree (*UdpPacket); *UdpPacket = NULL; // // Send an ACK to (Expected - 1) which is 0 for unicast download, // or tell the server we want to receive the Expected block. // return Mtftp6RrqSendAck (Instance, (UINT16) (Expected - 1)); }
/** Process the received data packets. It will save the block then send back an ACK if it is active. @param[in] Instance The pointer to the Mtftp6 instance. @param[in] Packet The pointer to the received packet. @param[in] Len The length of the packet. @param[out] UdpPacket The net buf of received packet. @param[out] IsCompleted If TRUE, the download has been completed. Otherwise, the download has not been completed. @retval EFI_SUCCESS The data packet was successfully processed. @retval EFI_ABORTED The download was aborted by the user. @retval EFI_BUFFER_TOO_SMALL The user-provided buffer is too small. **/ EFI_STATUS Mtftp6RrqHandleData ( IN MTFTP6_INSTANCE *Instance, IN EFI_MTFTP6_PACKET *Packet, IN UINT32 Len, OUT NET_BUF **UdpPacket, OUT BOOLEAN *IsCompleted ) { EFI_STATUS Status; UINT16 BlockNum; INTN Expected; *IsCompleted = FALSE; BlockNum = NTOHS (Packet->Data.Block); Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); ASSERT (Expected >= 0); // // If we are active and received an unexpected packet, retransmit // the last ACK then restart receiving. If we are passive, save // the block. // if (Instance->IsMaster && (Expected != BlockNum)) { // // Free the received packet before send new packet in ReceiveNotify, // since the udpio might need to be reconfigured. // NetbufFree (*UdpPacket); *UdpPacket = NULL; Mtftp6TransmitPacket (Instance, Instance->LastPacket); return EFI_SUCCESS; } Status = Mtftp6RrqSaveBlock (Instance, Packet, Len, UdpPacket); if (EFI_ERROR (Status)) { return Status; } // // Reset the passive client's timer whenever it received a valid data packet. // if (!Instance->IsMaster) { Instance->PacketToLive = Instance->Timeout * 2; } // // Check whether we have received all the blocks. Send the ACK if we // are active (unicast client or master client for multicast download). // If we have received all the blocks, send an ACK even if we are passive // to tell the server that we are done. // Expected = Mtftp6GetNextBlockNum (&Instance->BlkList); if (Instance->IsMaster || Expected < 0) { if (Expected < 0) { // // If we are passive client, then the just received Block maybe // isn't the last block. We need to send an ACK to the last block // to inform the server that we are done. If we are active client, // the Block == Instance->LastBlock. // BlockNum = Instance->LastBlk; *IsCompleted = TRUE; } else { BlockNum = (UINT16) (Expected - 1); } // // Free the received packet before send new packet in ReceiveNotify, // since the udpio might need to be reconfigured. // NetbufFree (*UdpPacket); *UdpPacket = NULL; Mtftp6RrqSendAck (Instance, BlockNum); } return EFI_SUCCESS; }
/** Reply an ICMPv6 echo request. @param[in] IpSb The IP service that received the packet. @param[in] Head The IP head of the ICMPv6 informational message. @param[in] Packet The content of the ICMPv6 message with the IP head removed. @retval EFI_OUT_OF_RESOURCES Failed to allocate resources. @retval EFI_SUCCESS Successfully answered the ICMPv6 Echo request. @retval Others Failed to answer the ICMPv6 Echo request. **/ EFI_STATUS Ip6IcmpReplyEcho ( IN IP6_SERVICE *IpSb, IN EFI_IP6_HEADER *Head, IN NET_BUF *Packet ) { IP6_ICMP_INFORMATION_HEAD *Icmp; NET_BUF *Data; EFI_STATUS Status; EFI_IP6_HEADER ReplyHead; Status = EFI_OUT_OF_RESOURCES; // // make a copy the packet, it is really a bad idea to // send the MNP's buffer back to MNP. // Data = NetbufDuplicate (Packet, NULL, IP6_MAX_HEADLEN); if (Data == NULL) { goto Exit; } // // Change the ICMP type to echo reply, exchange the source // and destination, then send it. The source is updated to // use specific destination. See RFC1122. SRR/RR option // update is omitted. // Icmp = (IP6_ICMP_INFORMATION_HEAD *) NetbufGetByte (Data, 0, NULL); if (Icmp == NULL) { NetbufFree (Data); goto Exit; } Icmp->Head.Type = ICMP_V6_ECHO_REPLY; Icmp->Head.Checksum = 0; // // Generate the IPv6 basic header // If the Echo Reply is a response to a Echo Request sent to one of the node's unicast address, // the Source address of the Echo Reply must be the same address. // ZeroMem (&ReplyHead, sizeof (EFI_IP6_HEADER)); ReplyHead.PayloadLength = HTONS ((UINT16) (Packet->TotalSize)); ReplyHead.NextHeader = IP6_ICMP; ReplyHead.HopLimit = IpSb->CurHopLimit; IP6_COPY_ADDRESS (&ReplyHead.DestinationAddress, &Head->SourceAddress); if (Ip6IsOneOfSetAddress (IpSb, &Head->DestinationAddress, NULL, NULL)) { IP6_COPY_ADDRESS (&ReplyHead.SourceAddress, &Head->DestinationAddress); } // // If source is unspecified, Ip6Output will select a source for us // Status = Ip6Output ( IpSb, NULL, NULL, Data, &ReplyHead, NULL, 0, Ip6SysPacketSent, NULL ); Exit: NetbufFree (Packet); return Status; }