1 /** @file 2 Implement the recvfrom API. 3 4 Copyright (c) 2011, Intel Corporation 5 All rights reserved. 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 <SocketInternals.h> 16 17 18 /** 19 Receive data from a network connection and return the remote system's address. 20 21 The recvfrom routine waits for receive data from a remote network 22 connection. This routine is typically called for SOCK_DGRAM sockets 23 when the socket is being shared by multiple remote systems and it is 24 important to get the remote system address for a response. 25 26 The 27 <a href="http://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html">POSIX</a> 28 documentation is available online. 29 30 @param [in] s Socket file descriptor returned from ::socket. 31 32 @param [in] buffer Address of a buffer to receive the data. 33 34 @param [in] length Length of the buffer in bytes. 35 36 @param [in] flags Message control flags 37 38 @param [out] address Network address to receive the remote system address 39 40 @param [in] address_len Length of the remote network address structure 41 42 @return This routine returns the number of valid bytes in the buffer, 43 zero if no data was received, and -1 when an error occurs. 44 In the case of an error, ::errno contains more details. 45 46 **/ 47 ssize_t 48 recvfrom ( 49 int s, 50 void * buffer, 51 size_t length, 52 int flags, 53 struct sockaddr * address, 54 socklen_t * address_len 55 ) 56 { 57 socklen_t ByteCount; 58 ssize_t LengthInBytes; 59 UINT8 * pData; 60 EFI_SOCKET_PROTOCOL * pSocketProtocol; 61 EFI_STATUS Status; 62 struct timeval TimeVal; 63 EFI_EVENT pTimer; 64 UINT64 Timeout; 65 ssize_t TotalBytes; 66 67 // 68 // Assume failure 69 // 70 LengthInBytes = -1; 71 72 // 73 // Locate the context for this socket 74 // 75 pSocketProtocol = BslFdToSocketProtocol ( s, NULL, &errno ); 76 if ( NULL != pSocketProtocol ) { 77 // 78 // Receive the data from the socket 79 // 80 Status = pSocketProtocol->pfnReceive ( pSocketProtocol, 81 flags, 82 length, 83 buffer, 84 (size_t *)&LengthInBytes, 85 address, 86 address_len, 87 &errno ); 88 if ( EFI_ERROR ( Status )) { 89 LengthInBytes = -1; 90 if ( EAGAIN == errno ) { 91 // 92 // Get the timeout 93 // 94 ByteCount = sizeof ( TimeVal ); 95 LengthInBytes = getsockopt ( s, 96 SOL_SOCKET, 97 SO_RCVTIMEO, 98 &TimeVal, 99 &ByteCount ); 100 if ( 0 == LengthInBytes ) { 101 // 102 // Compute the timeout 103 // 104 Timeout = TimeVal.tv_sec; 105 Timeout *= 1000 * 1000; 106 Timeout += TimeVal.tv_usec; 107 Timeout *= 10; 108 109 // 110 // The timer is only necessary if a timeout is running 111 // 112 LengthInBytes = -1; 113 Status = EFI_SUCCESS; 114 pTimer = NULL; 115 if ( 0 != Timeout ) { 116 Status = gBS->CreateEvent ( EVT_TIMER, 117 TPL_NOTIFY, 118 NULL, 119 NULL, 120 &pTimer ); 121 } 122 if ( !EFI_ERROR ( Status )) { 123 // 124 // Start the timer 125 // 126 if ( NULL != pTimer ) { 127 Status = gBS->SetTimer ( pTimer, 128 TimerRelative, 129 Timeout ); 130 } 131 if ( !EFI_ERROR ( Status )) { 132 // 133 // Loop until data is received or the timeout 134 // expires 135 // 136 TotalBytes = 0; 137 pData = (UINT8 *)buffer; 138 do { 139 // 140 // Determine if the timeout expired 141 // 142 if ( NULL != pTimer ) { 143 Status = gBS->CheckEvent ( pTimer ); 144 if ( EFI_SUCCESS == Status ) { 145 errno = ETIMEDOUT; 146 if ( 0 == TotalBytes ) { 147 TotalBytes = -1; 148 } 149 break; 150 } 151 } 152 153 // 154 // Attempt to receive some data 155 // 156 Status = pSocketProtocol->pfnReceive ( pSocketProtocol, 157 flags, 158 length, 159 pData, 160 (size_t *)&LengthInBytes, 161 address, 162 address_len, 163 &errno ); 164 if ( !EFI_ERROR ( Status )) { 165 // 166 // Account for the data received 167 // 168 TotalBytes += LengthInBytes; 169 pData += LengthInBytes; 170 length -= LengthInBytes; 171 } 172 } while ( EFI_NOT_READY == Status ); 173 LengthInBytes = TotalBytes; 174 175 // 176 // Stop the timer 177 // 178 if ( NULL != pTimer ) { 179 gBS->SetTimer ( pTimer, 180 TimerCancel, 181 0 ); 182 } 183 } 184 185 // 186 // Release the timer 187 // 188 if ( NULL != pTimer ) { 189 gBS->CloseEvent ( pTimer ); 190 } 191 } 192 } 193 } 194 } 195 } 196 197 // 198 // Return the receive data length, -1 for errors 199 // 200 return LengthInBytes; 201 } 202