Home | History | Annotate | Download | only in BsdSocketLib
      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