• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
recvfrom(int s,void * buffer,size_t length,int flags,struct sockaddr * address,socklen_t * address_len)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