blob: 20b3f4a9164c9cc199ff0bcfe7d5f426122c0280 [file] [log] [blame]
Vishal Bhoj82c80712015-12-15 21:13:33 +05301/** @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}