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 **/
47ssize_t
48recvfrom (
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