-
Notifications
You must be signed in to change notification settings - Fork 42
/
socket_async.c
339 lines (315 loc) · 15.1 KB
/
socket_async.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#include <stdbool.h>
#include <stdint.h>
// This file is OS-specific, and is identified by setting include directories
// in the project
#include "./inc/socket_async_os.h"
#include "./inc/socket_async.h"
#include "az_iot/c-utility/inc/azure_c_shared_utility/xlogging.h"
static int get_socket_errno(int file_descriptor)
{
int sock_errno = 0;
socklen_t optlen = sizeof(sock_errno);
getsockopt(file_descriptor, SOL_SOCKET, SO_ERROR, &sock_errno, &optlen);
return sock_errno;
}
SOCKET_ASYNC_HANDLE socket_async_create(uint32_t serverIPv4, uint16_t port,
bool is_UDP, SOCKET_ASYNC_OPTIONS_HANDLE options)
{
SOCKET_ASYNC_HANDLE result;
struct sockaddr_in sock_addr;
/* Codes_SRS_SOCKET_ASYNC_30_013: [ The is_UDP parameter shall be true for a UDP connection, and false for TCP. ]*/
int sock = socket(AF_INET, is_UDP ? SOCK_DGRAM : SOCK_STREAM, 0);
if (sock < 0)
{
/* Codes_SRS_SOCKET_ASYNC_30_010: [ If socket option creation fails, socket_async_create shall log an error and return SOCKET_ASYNC_INVALID_SOCKET. ]*/
// An essentially impossible failure, not worth logging errno()
LogError("create socket failed");
result = SOCKET_ASYNC_INVALID_SOCKET;
}
else
{
bool setopt_ok;
int setopt_return;
// None of the currently defined options apply to UDP
/* Codes_SRS_SOCKET_ASYNC_30_015: [ If is_UDP is true, the optional options parameter shall be ignored. ]*/
if (!is_UDP)
{
if (options != NULL)
{
/* Codes_SRS_SOCKET_ASYNC_30_014: [ If the optional options parameter is non-NULL and is_UDP is false, and options->keep_alive is non-negative, socket_async_create shall set the socket options to the provided options values. ]*/
if (options->keep_alive >= 0)
{
int keepAlive = 1; //enable keepalive
setopt_ok = 0 == (setopt_return = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive)));
setopt_ok = setopt_ok && 0 == (setopt_return = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&(options->keep_idle), sizeof((options->keep_idle))));
setopt_ok = setopt_ok && 0 == (setopt_return = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&(options->keep_interval), sizeof((options->keep_interval))));
setopt_ok = setopt_ok && 0 == (setopt_return = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, (void *)&(options->keep_count), sizeof((options->keep_count))));
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_015: [ If the optional options parameter is non-NULL and is_UDP is false, and options->keep_alive is negative, socket_async_create not set the socket keep-alive options. ]*/
// < 0 means use system defaults, so do nothing
setopt_ok = true;
setopt_return = 0;
}
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_017: [ If the optional options parameter is NULL and is_UDP is false, socket_async_create shall disable TCP keep-alive. ]*/
int keepAlive = 0; //disable keepalive
setopt_ok = 0 == setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
setopt_return = 0;
}
}
else
{
setopt_ok = true;
setopt_return = 0;
}
// NB: On full-sized (multi-process) systems it would be necessary to use the SO_REUSEADDR option to
// grab the socket from any earlier (dying) invocations of the process and then deal with any
// residual junk in the connection stream. This doesn't happen with embedded, so it doesn't need
// to be defended against.
if (!setopt_ok)
{
/* Codes_SRS_SOCKET_ASYNC_30_020: [ If socket option setting fails, socket_async_create shall log an error and return SOCKET_ASYNC_INVALID_SOCKET. ]*/
// setsockopt has no real possibility of failing due to the way it's being used here, so there's no need
// to spend memory trying to log the not-really-possible errno.
LogError("setsockopt failed: %d", setopt_return);
result = SOCKET_ASYNC_INVALID_SOCKET;
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_019: [ The socket returned shall be non-blocking. ]*/
// When supplied with either F_GETFL and F_SETFL parameters, the fcntl function
// does simple bit flips which have no error path, so it is not necessary to
// check for errors. (Source checked for linux and lwIP).
int originalFlags = fcntl(sock, F_GETFL, 0);
int bind_ret;
(void)fcntl(sock, F_SETFL, originalFlags | O_NONBLOCK);
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
sock_addr.sin_addr.s_addr = 0;
sock_addr.sin_port = 0; // random local port
bind_ret = bind(sock, (const struct sockaddr*)&sock_addr, sizeof(sock_addr));
if (bind_ret != 0)
{
/* Codes_SRS_SOCKET_ASYNC_30_021: [ If socket binding fails, socket_async_create shall log an error and return SOCKET_ASYNC_INVALID_SOCKET. ]*/
LogError("bind socket failed: %d", get_socket_errno(sock));
result = SOCKET_ASYNC_INVALID_SOCKET;
}
else
{
int connect_ret;
memset(&sock_addr, 0, sizeof(sock_addr));
sock_addr.sin_family = AF_INET;
/* Codes_SRS_SOCKET_ASYNC_30_011: [ The host_ipv4 parameter shall be the 32-bit IP V4 of the target server. ]*/
sock_addr.sin_addr.s_addr = serverIPv4;
/* Codes_SRS_SOCKET_ASYNC_30_012: [ The port parameter shall be the port number for the target server. ]*/
sock_addr.sin_port = htons(port);
connect_ret = connect(sock, (const struct sockaddr*)&sock_addr, sizeof(sock_addr));
if (connect_ret == -1)
{
int sockErr = get_socket_errno(sock);
if (sockErr != EINPROGRESS)
{
/* Codes_SRS_SOCKET_ASYNC_30_022: [ If socket connection fails, socket_async_create shall log an error and return SOCKET_ASYNC_INVALID_SOCKET. ]*/
LogError("Socket connect failed, not EINPROGRESS: %d", sockErr);
result = SOCKET_ASYNC_INVALID_SOCKET;
}
else
{
// This is the normally expected code path for our non-blocking socket
/* Codes_SRS_SOCKET_ASYNC_30_018: [ On success, socket_async_create shall return the created and configured SOCKET_ASYNC_HANDLE. ]*/
result = sock;
}
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_018: [ On success, socket_async_create shall return the created and configured SOCKET_ASYNC_HANDLE. ]*/
// This result would be a surprise because a non-blocking socket
// returns EINPROGRESS. But it could happen if this thread got
// blocked for a while by the system while the handshake proceeded,
// or for a UDP socket.
result = sock;
}
}
}
}
return result;
}
int socket_async_is_create_complete(SOCKET_ASYNC_HANDLE sock, bool* is_complete)
{
int result;
if (is_complete == NULL)
{
/* Codes_SRS_SOCKET_ASYNC_30_026: [ If the is_complete parameter is NULL, socket_async_is_create_complete shall log an error and return FAILURE. ]*/
LogError("is_complete is NULL");
result = __FAILURE__;
}
else
{
struct timeval tv;
int select_ret;
// Check if the socket is ready to perform a write.
fd_set writeset;
fd_set errset;
FD_ZERO(&writeset);
FD_ZERO(&errset);
FD_SET(sock, &writeset);
FD_SET(sock, &errset);
tv.tv_sec = 0;
tv.tv_sec = 0;
select_ret = select(sock + 1, NULL, &writeset, &errset, &tv);
if (select_ret < 0)
{
/* Codes_SRS_SOCKET_ASYNC_30_028: [ On failure, the is_complete value shall be set to false and socket_async_create shall return FAILURE. ]*/
LogError("Socket select failed: %d", get_socket_errno(sock));
result = __FAILURE__;
}
else
{
if (FD_ISSET(sock, &errset))
{
/* Codes_SRS_SOCKET_ASYNC_30_028: [ On failure, the is_complete value shall be set to false and socket_async_create shall return FAILURE. ]*/
LogError("Socket select errset non-empty: %d", get_socket_errno(sock));
result = __FAILURE__;
}
else if (FD_ISSET(sock, &writeset))
{
/* Codes_SRS_SOCKET_ASYNC_30_027: [ On success, the is_complete value shall be set to the completion state and socket_async_create shall return 0. ]*/
// Ready to read
result = 0;
*is_complete = true;
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_027: [ On success, the is_complete value shall be set to the completion state and socket_async_create shall return 0. ]*/
// Not ready yet
result = 0;
*is_complete = false;
}
}
}
return result;
}
int socket_async_send(SOCKET_ASYNC_HANDLE sock, const void* buffer, size_t size, size_t* sent_count)
{
int result;
if (buffer == NULL)
{
/* Codes_SRS_SOCKET_ASYNC_30_033: [ If the buffer parameter is NULL, socket_async_send shall log the error return FAILURE. ]*/
LogError("buffer is NULL");
result = __FAILURE__;
}
else
{
if (sent_count == NULL)
{
/* Codes_SRS_SOCKET_ASYNC_30_034: [ If the sent_count parameter is NULL, socket_async_send shall log the error return FAILURE. ]*/
LogError("sent_count is NULL");
result = __FAILURE__;
}
else
if (size == 0)
{
/* Codes_SRS_SOCKET_ASYNC_30_073: [ If the size parameter is 0, socket_async_send shall set sent_count to 0 and return 0. ]*/
// This behavior is not always defined by the underlying API, so we make it predictable here
*sent_count = 0;
result = 0;
}
else
{
ssize_t send_result = send(sock, buffer, size, 0);
if (send_result < 0)
{
int sock_err = get_socket_errno(sock);
if (sock_err == EAGAIN || sock_err == EWOULDBLOCK)
{
/* Codes_SRS_SOCKET_ASYNC_30_036: [ If the underlying socket is unable to accept any bytes for transmission because its buffer is full, socket_async_send shall return 0 and the sent_count parameter shall receive the value 0. ]*/
// Nothing sent, try again later
*sent_count = 0;
result = 0;
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_037: [ If socket_async_send fails unexpectedly, socket_async_send shall log the error return FAILURE. ]*/
// Something bad happened
LogError("Unexpected send error: %d", sock_err);
result = __FAILURE__;
}
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_035: [ If the underlying socket accepts one or more bytes for transmission, socket_async_send shall return 0 and the sent_count parameter shall receive the number of bytes accepted for transmission. ]*/
// Sent at least part of the message
result = 0;
*sent_count = (size_t)send_result;
}
}
}
return result;
}
int socket_async_receive(SOCKET_ASYNC_HANDLE sock, void* buffer, size_t size, size_t* received_count)
{
int result;
if (buffer == NULL)
{
/* Codes_SRS_SOCKET_ASYNC_30_052: [ If the buffer parameter is NULL, socket_async_receive shall log the error and return FAILURE. ]*/
LogError("buffer is NULL");
result = __FAILURE__;
}
else
{
if (received_count == NULL)
{
/* Codes_SRS_SOCKET_ASYNC_30_053: [ If the received_count parameter is NULL, socket_async_receive shall log the error and return FAILURE. ]*/
LogError("received_count is NULL");
result = __FAILURE__;
}
else
if (size == 0)
{
/* Codes_SRS_SOCKET_ASYNC_30_072: [ If the size parameter is 0, socket_async_receive shall log an error and return FAILURE. ]*/
LogError("size is 0");
result = __FAILURE__;
}
else
{
ssize_t recv_result = recv(sock, buffer, size, 0);
if (recv_result < 0)
{
int sock_err = get_socket_errno(sock);
if (sock_err == EAGAIN || sock_err == EWOULDBLOCK)
{
/* Codes_SRS_SOCKET_ASYNC_30_055: [ If the underlying socket has no received bytes available, socket_async_receive shall return 0 and the received_count parameter shall receive the value 0. ]*/
// Nothing received, try again later
*received_count = 0;
result = 0;
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_056: [ If the underlying socket fails unexpectedly, socket_async_receive shall log the error and return FAILURE. ]*/
// Something bad happened
LogError("Unexpected recv error: %d", sock_err);
result = __FAILURE__;
}
}
else
{
/* Codes_SRS_SOCKET_ASYNC_30_054: [ On success, the underlying socket shall set one or more received bytes into buffer, socket_async_receive shall return 0, and the received_count parameter shall receive the number of bytes received into buffer. ]*/
// Received some stuff
*received_count = (size_t)recv_result;
result = 0;
}
}
}
return result;
}
void socket_async_destroy(SOCKET_ASYNC_HANDLE sock)
{
/* Codes_SRS_SOCKET_ASYNC_30_071: [ socket_async_destroy shall call the underlying close method on the supplied socket. ]*/
close(sock);
}