1/*
2Copyright (c) 2012, Broadcom Europe Ltd
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright
8 notice, this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12 * Neither the name of the copyright holder nor the
13 names of its contributors may be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
20DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28#include <stdio.h>
29#include <stdlib.h>
30
31#include "containers/containers.h"
32#include "containers/core/containers_common.h"
33#include "containers/core/containers_logging.h"
34#include "net_sockets.h"
35#include "net_sockets_priv.h"
36
37/*****************************************************************************/
38
39struct vc_container_net_tag
40{
41 /** The underlying socket */
42 SOCKET_T socket;
43 /** Last error raised on the socket instance. */
44 vc_container_net_status_t status;
45 /** Simple socket type */
46 vc_container_net_type_t type;
47 /** Socket address, used for sending datagrams. */
48 union {
49 struct sockaddr_storage storage;
50 struct sockaddr sa;
51 struct sockaddr_in in;
52 struct sockaddr_in6 in6;
53 } to_addr;
54 /** Number of bytes in to_addr that have been filled. */
55 SOCKADDR_LEN_T to_addr_len;
56 /** Maximum size of datagrams. */
57 size_t max_datagram_size;
58 /** Timeout to use when reading from a socket. INFINITE_TIMEOUT_MS waits forever. */
59 uint32_t read_timeout_ms;
60};
61
62/*****************************************************************************/
63static void socket_clear_address(struct sockaddr *p_addr)
64{
65 switch (p_addr->sa_family)
66 {
67 case AF_INET:
68 {
69 struct sockaddr_in *p_addr_v4 = (struct sockaddr_in *)p_addr;
70
71 memset(&p_addr_v4->sin_addr, 0, sizeof(p_addr_v4->sin_addr));
72 }
73 break;
74 case AF_INET6:
75 {
76 struct sockaddr_in6 *p_addr_v6 = (struct sockaddr_in6 *)p_addr;
77
78 memset(&p_addr_v6->sin6_addr, 0, sizeof(p_addr_v6->sin6_addr));
79 }
80 break;
81 default:
82 /* Invalid or unsupported address family */
83 vc_container_assert(0);
84 }
85}
86
87/*****************************************************************************/
88static vc_container_net_status_t socket_set_read_buffer_size(VC_CONTAINER_NET_T *p_ctx,
89 uint32_t buffer_size)
90{
91 int result;
92 const SOCKOPT_CAST_T optptr = (const SOCKOPT_CAST_T)&buffer_size;
93
94 result = setsockopt(p_ctx->socket, SOL_SOCKET, SO_RCVBUF, optptr, sizeof(buffer_size));
95
96 if (result == SOCKET_ERROR)
97 return vc_container_net_private_last_error();
98
99 return VC_CONTAINER_NET_SUCCESS;
100}
101
102/*****************************************************************************/
103static vc_container_net_status_t socket_set_read_timeout_ms(VC_CONTAINER_NET_T *p_ctx,
104 uint32_t timeout_ms)
105{
106 p_ctx->read_timeout_ms = timeout_ms;
107 return VC_CONTAINER_NET_SUCCESS;
108}
109
110/*****************************************************************************/
111static bool socket_wait_for_data( VC_CONTAINER_NET_T *p_ctx, uint32_t timeout_ms )
112{
113 int result;
114 fd_set set;
115 struct timeval tv;
116
117 if (timeout_ms == INFINITE_TIMEOUT_MS)
118 return true;
119
120 FD_ZERO(&set);
121 FD_SET(p_ctx->socket, &set);
122 tv.tv_sec = timeout_ms / 1000;
123 tv.tv_usec = (timeout_ms - tv.tv_sec * 1000) * 1000;
124 result = select(p_ctx->socket + 1, &set, NULL, NULL, &tv);
125
126 if (result == SOCKET_ERROR)
127 p_ctx->status = vc_container_net_private_last_error();
128 else
129 p_ctx->status = VC_CONTAINER_NET_SUCCESS;
130
131 return (result == 1);
132}
133
134/*****************************************************************************/
135VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port,
136 vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status )
137{
138 VC_CONTAINER_NET_T *p_ctx;
139 struct addrinfo hints, *info, *p;
140 int result;
141 vc_container_net_status_t status;
142 SOCKET_T sock = INVALID_SOCKET;
143
144 status = vc_container_net_private_init();
145 if (status != VC_CONTAINER_NET_SUCCESS)
146 {
147 LOG_ERROR(NULL, "vc_container_net_open: platform initialization failure: %d", status);
148 if (p_status)
149 *p_status = status;
150 return NULL;
151 }
152
153 p_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T));
154 if (!p_ctx)
155 {
156 if (p_status)
157 *p_status = VC_CONTAINER_NET_ERROR_NO_MEMORY;
158
159 LOG_ERROR(NULL, "vc_container_net_open: malloc fail for VC_CONTAINER_NET_T");
160 vc_container_net_private_deinit();
161 return NULL;
162 }
163
164 /* Initialize the net socket instance structure */
165 memset(p_ctx, 0, sizeof(*p_ctx));
166 p_ctx->socket = INVALID_SOCKET;
167 if (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM)
168 p_ctx->type = address ? STREAM_CLIENT : STREAM_SERVER;
169 else
170 p_ctx->type = address ? DATAGRAM_SENDER : DATAGRAM_RECEIVER;
171
172 /* Create the address info linked list from the data provided */
173 memset(&hints, 0, sizeof(hints));
174 switch (flags & VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK)
175 {
176 case 0:
177 hints.ai_family = AF_UNSPEC;
178 break;
179 case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4:
180 hints.ai_family = AF_INET;
181 break;
182 case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6:
183 hints.ai_family = AF_INET6;
184 break;
185 default:
186 status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
187 LOG_ERROR(NULL, "vc_container_net_open: invalid address forcing flag");
188 goto error;
189 }
190 hints.ai_socktype = (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM) ? SOCK_STREAM : SOCK_DGRAM;
191
192 result = getaddrinfo(address, port, &hints, &info);
193 if (result)
194 {
195 status = vc_container_net_private_last_error();
196 LOG_ERROR(NULL, "vc_container_net_open: unable to get address info: %d", status);
197 goto error;
198 }
199
200 /* Not all address infos may be useable. Search for one that is by skipping any
201 * that provoke errors. */
202 for(p = info; (p != NULL) && (sock == INVALID_SOCKET) ; p = p->ai_next)
203 {
204 sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
205 if (sock == INVALID_SOCKET)
206 {
207 status = vc_container_net_private_last_error();
208 continue;
209 }
210
211 switch (p_ctx->type)
212 {
213 case STREAM_CLIENT:
214 /* Simply connect to the given address/port */
215 if (connect(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
216 status = vc_container_net_private_last_error();
217 break;
218
219 case DATAGRAM_SENDER:
220 /* Nothing further to do */
221 break;
222
223 case STREAM_SERVER:
224 /* Try to avoid socket reuse timing issues on TCP server sockets */
225 vc_container_net_private_set_reusable(sock, true);
226
227 /* Allow any source address */
228 socket_clear_address(p->ai_addr);
229
230 if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
231 status = vc_container_net_private_last_error();
232 break;
233
234 case DATAGRAM_RECEIVER:
235 /* Allow any source address */
236 socket_clear_address(p->ai_addr);
237
238 if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
239 status = vc_container_net_private_last_error();
240 break;
241 }
242
243 if (status == VC_CONTAINER_NET_SUCCESS)
244 {
245 /* Save addressing information for later use */
246 p_ctx->to_addr_len = p->ai_addrlen;
247 memcpy(&p_ctx->to_addr, p->ai_addr, p->ai_addrlen);
248 } else {
249 vc_container_net_private_close(sock); /* Try next entry in list */
250 sock = INVALID_SOCKET;
251 }
252 }
253
254 freeaddrinfo(info);
255
256 if (sock == INVALID_SOCKET)
257 {
258 LOG_ERROR(NULL, "vc_container_net_open: failed to open socket: %d", status);
259 goto error;
260 }
261
262 p_ctx->socket = sock;
263 p_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(sock);
264 p_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS;
265
266 if (p_status)
267 *p_status = VC_CONTAINER_NET_SUCCESS;
268
269 return p_ctx;
270
271error:
272 if (p_status)
273 *p_status = status;
274 (void)vc_container_net_close(p_ctx);
275 return NULL;
276}
277
278/*****************************************************************************/
279vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx )
280{
281 if (!p_ctx)
282 return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
283
284 if (p_ctx->socket != INVALID_SOCKET)
285 {
286 vc_container_net_private_close(p_ctx->socket);
287 p_ctx->socket = INVALID_SOCKET;
288 }
289 free(p_ctx);
290
291 vc_container_net_private_deinit();
292
293 return VC_CONTAINER_NET_SUCCESS;
294}
295
296/*****************************************************************************/
297vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx )
298{
299 if (!p_ctx)
300 return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
301 return p_ctx->status;
302}
303
304/*****************************************************************************/
305size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size )
306{
307 int result = 0;
308
309 if (!p_ctx)
310 return 0;
311
312 if (!buffer)
313 {
314 p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
315 return 0;
316 }
317
318 p_ctx->status = VC_CONTAINER_NET_SUCCESS;
319
320 switch (p_ctx->type)
321 {
322 case STREAM_CLIENT:
323 case STREAM_SERVER:
324 /* Receive data from the stream */
325 if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms))
326 {
327 result = recv(p_ctx->socket, buffer, (int)size, 0);
328 if (!result)
329 p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
330 } else
331 p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT;
332 break;
333
334 case DATAGRAM_RECEIVER:
335 {
336 /* Receive the packet */
337 /* FIXME Potential for data loss, as rest of packet will be lost if buffer was not large enough */
338 if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms))
339 {
340 result = recvfrom(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, &p_ctx->to_addr_len);
341 if (!result)
342 p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
343 } else
344 p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT;
345 }
346 break;
347
348 default: /* DATAGRAM_SENDER */
349 p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
350 result = 0;
351 break;
352 }
353
354 if (result == SOCKET_ERROR)
355 {
356 p_ctx->status = vc_container_net_private_last_error();
357 result = 0;
358 }
359
360 return (size_t)result;
361}
362
363/*****************************************************************************/
364size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size )
365{
366 int result;
367
368 if (!p_ctx)
369 return 0;
370
371 if (!buffer)
372 {
373 p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
374 return 0;
375 }
376
377 p_ctx->status = VC_CONTAINER_NET_SUCCESS;
378
379 switch (p_ctx->type)
380 {
381 case STREAM_CLIENT:
382 case STREAM_SERVER:
383 /* Send data to the stream */
384 result = send(p_ctx->socket, buffer, (int)size, 0);
385 break;
386
387 case DATAGRAM_SENDER:
388 /* Send the datagram */
389
390 if (size > p_ctx->max_datagram_size)
391 size = p_ctx->max_datagram_size;
392
393 result = sendto(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, p_ctx->to_addr_len);
394 break;
395
396 default: /* DATAGRAM_RECEIVER */
397 p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
398 result = 0;
399 break;
400 }
401
402 if (result == SOCKET_ERROR)
403 {
404 p_ctx->status = vc_container_net_private_last_error();
405 result = 0;
406 }
407
408 return (size_t)result;
409}
410
411/*****************************************************************************/
412vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections )
413{
414 if (!p_ctx)
415 return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
416
417 p_ctx->status = VC_CONTAINER_NET_SUCCESS;
418
419 if (p_ctx->type == STREAM_SERVER)
420 {
421 if (listen(p_ctx->socket, maximum_connections) == SOCKET_ERROR)
422 p_ctx->status = vc_container_net_private_last_error();
423 } else {
424 p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
425 }
426
427 return p_ctx->status;
428}
429
430/*****************************************************************************/
431vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx )
432{
433 VC_CONTAINER_NET_T *p_client_ctx = NULL;
434
435 if (!p_server_ctx)
436 return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
437
438 if (!pp_client_ctx)
439 return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
440
441 *pp_client_ctx = NULL;
442
443 if (p_server_ctx->type != STREAM_SERVER)
444 {
445 p_server_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
446 goto error;
447 }
448
449 p_client_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T));
450 if (!p_client_ctx)
451 {
452 p_server_ctx->status = VC_CONTAINER_NET_ERROR_NO_MEMORY;
453 goto error;
454 }
455
456 /* Initialise the new context with the address information from the server context */
457 memset(p_client_ctx, 0, sizeof(*p_client_ctx));
458 memcpy(&p_client_ctx->to_addr, &p_server_ctx->to_addr, p_server_ctx->to_addr_len);
459 p_client_ctx->to_addr_len = p_server_ctx->to_addr_len;
460
461 p_client_ctx->socket = accept(p_server_ctx->socket, &p_client_ctx->to_addr.sa, &p_client_ctx->to_addr_len);
462
463 if (p_client_ctx->socket == INVALID_SOCKET)
464 {
465 p_server_ctx->status = vc_container_net_private_last_error();
466 goto error;
467 }
468
469 /* Need to bump up the initialisation count, as a new context has been created */
470 p_server_ctx->status = vc_container_net_private_init();
471 if (p_server_ctx->status != VC_CONTAINER_NET_SUCCESS)
472 goto error;
473
474 p_client_ctx->type = STREAM_CLIENT;
475 p_client_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(p_client_ctx->socket);
476 p_client_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS;
477 p_client_ctx->status = VC_CONTAINER_NET_SUCCESS;
478
479 *pp_client_ctx = p_client_ctx;
480 return VC_CONTAINER_NET_SUCCESS;
481
482error:
483 if (p_client_ctx)
484 free(p_client_ctx);
485 return p_server_ctx->status;
486}
487
488/*****************************************************************************/
489bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx )
490{
491 if (!p_ctx)
492 return false;
493
494 if (p_ctx->type == DATAGRAM_SENDER)
495 {
496 p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
497 return false;
498 }
499
500 return socket_wait_for_data(p_ctx, 0);
501}
502
503/*****************************************************************************/
504size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx )
505{
506 return p_ctx ? p_ctx->max_datagram_size : 0;
507}
508
509/*****************************************************************************/
510static vc_container_net_status_t translate_getnameinfo_error( int error )
511{
512 switch (error)
513 {
514 case EAI_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN;
515 case EAI_FAIL: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND;
516 case EAI_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
517 case EAI_NONAME: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND;
518
519 /* All other errors are unexpected, so just map to a general purpose error code. */
520 default:
521 return VC_CONTAINER_NET_ERROR_GENERAL;
522 }
523}
524
525/*****************************************************************************/
526vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len )
527{
528 int result;
529
530 if (!p_ctx)
531 return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
532
533 if (p_ctx->socket == INVALID_SOCKET)
534 p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED;
535 else if (!name || !name_len)
536 p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
537 else if ((result = getnameinfo(&p_ctx->to_addr.sa, p_ctx->to_addr_len, name, name_len, NULL, 0, 0)) != 0)
538 p_ctx->status = translate_getnameinfo_error(result);
539 else
540 p_ctx->status = VC_CONTAINER_NET_SUCCESS;
541
542 return p_ctx->status;
543}
544
545/*****************************************************************************/
546vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port )
547{
548 if (!p_ctx)
549 return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
550
551 if (p_ctx->socket == INVALID_SOCKET)
552 p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED;
553 else if (!port)
554 p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
555 else
556 {
557 p_ctx->status = VC_CONTAINER_NET_SUCCESS;
558 switch (p_ctx->to_addr.sa.sa_family)
559 {
560 case AF_INET:
561 *port = ntohs(p_ctx->to_addr.in.sin_port);
562 break;
563 case AF_INET6:
564 *port = ntohs(p_ctx->to_addr.in6.sin6_port);
565 break;
566 default:
567 /* Highly unexepcted address family! */
568 p_ctx->status = VC_CONTAINER_NET_ERROR_GENERAL;
569 }
570 }
571
572 return p_ctx->status;
573}
574
575/*****************************************************************************/
576vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx,
577 vc_container_net_control_t operation,
578 va_list args)
579{
580 vc_container_net_status_t status;
581
582 switch (operation)
583 {
584 case VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE:
585 status = socket_set_read_buffer_size(p_ctx, va_arg(args, uint32_t));
586 break;
587 case VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS:
588 status = socket_set_read_timeout_ms(p_ctx, va_arg(args, uint32_t));
589 break;
590 default:
591 status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
592 }
593
594 return status;
595}
596
597/*****************************************************************************/
598uint32_t vc_container_net_to_host( uint32_t value )
599{
600 return ntohl(value);
601}
602
603/*****************************************************************************/
604uint32_t vc_container_net_from_host( uint32_t value )
605{
606 return htonl(value);
607}
608
609/*****************************************************************************/
610uint16_t vc_container_net_to_host_16( uint16_t value )
611{
612 return ntohs(value);
613}
614
615/*****************************************************************************/
616uint16_t vc_container_net_from_host_16( uint16_t value )
617{
618 return htons(value);
619}
620