[apps/tidep0084.git] / tutorials / generic_sensor_tutorial / tutorial / SensorToCloud / components / common / src / stream_socket_private.c
1 /******************************************************************************
2 @file stream_socket_private.c
4 @brief TIMAC 2.0 API Socket abstraction shared code (server & client)
6 Group: WCS LPC
7 $Target Devices: Linux: AM335x, Embedded Devices: CC1310, CC1350$
9 ******************************************************************************
10 $License: BSD3 2016 $
12 Copyright (c) 2015, Texas Instruments Incorporated
13 All rights reserved.
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions
17 are met:
19 * Redistributions of source code must retain the above copyright
20 notice, this list of conditions and the following disclaimer.
22 * Redistributions in binary form must reproduce the above copyright
23 notice, this list of conditions and the following disclaimer in the
24 documentation and/or other materials provided with the distribution.
26 * Neither the name of Texas Instruments Incorporated nor the names of
27 its contributors may be used to endorse or promote products derived
28 from this software without specific prior written permission.
30 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
31 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
32 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
35 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
36 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
37 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
38 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
39 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
40 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 ******************************************************************************
42 $Release Name: TI-15.4Stack Linux x64 SDK$
43 $Release Date: Jun 28, 2017 (2.02.00.03)$
44 *****************************************************************************/
46 #include "compiler.h"
47 #include "stream.h"
48 #include "stream_socket.h"
49 #include "log.h"
50 #include "timer.h"
51 #include "void_ptr.h"
52 #include "unix_fdrw.h"
54 #include <sys/types.h>
55 #if defined(__linux__)
56 #include <sys/socket.h>
57 #include <netdb.h>
58 #include <unistd.h>
59 #include <arpa/inet.h>
60 #include <unistd.h>
61 #endif
62 #if defined(_MSC_VER)
63 #include "winsock2.h"
64 #include "ws2tcpip.h"
65 #endif
67 #define _STREAM_SOCKET_PRIVATE 1450384394
68 #include "stream_socket_private.h"
70 #include "errno.h"
71 #include <stdio.h>
72 #include <stdlib.h>
73 #include <string.h>
74 #include "hlos_specific.h"
76 int _socket_errno(void)
77 {
78 int e;
79 #if defined(__linux__)
80 e = errno;
81 #endif
82 #if defined(_MSC_VER)
83 e = WSAGetLastError();
84 #endif
85 return (e);
86 }
88 /*!
89 * @var socket_test
90 * @brief [private] Used to verify a linux socket pointer
91 * Also answer to live, universe, and everything..
92 */
93 static const int socket_test = 42;
95 /*!
96 * @brief Release memory in the 'cfg' structure.
97 */
98 static void _stream_socket_cfg_free(struct linux_socket *pS)
99 {
100 /* release what we got */
101 if(pS->cfg.host)
102 {
103 free_const((const void *)(pS->cfg.host));
104 }
105 if(pS->cfg.service)
106 {
107 free_const((const void *)(pS->cfg.service));
108 }
109 if(pS->cfg.device_binding)
110 {
111 free_const((const void *)(pS->cfg.device_binding));
112 }
114 /* clear pointers */
115 pS->cfg.host = NULL;
116 pS->cfg.service = NULL;
117 pS->cfg.device_binding = NULL;
118 }
120 /*!
121 * @brief Copy (and reallocate as needed) a socket configuration structure.
122 */
123 static int _stream_socket_cfg_copy(struct linux_socket *pS,
124 const struct socket_cfg *pCFG)
125 {
126 int r;
128 /* assume success */
129 r = 0;
131 /* Bulk copy */
132 pS->cfg = *pCFG;
134 /* If host was specified, make our own copy */
135 if(pS->cfg.host)
136 {
137 pS->cfg.host = strdup(pS->cfg.host);
138 if(pS->cfg.host == NULL)
139 {
140 r = -1;
141 }
142 }
144 /* same for service */
145 if(pS->cfg.service)
146 {
147 pS->cfg.service = strdup(pS->cfg.service);
148 if(pS->cfg.service == NULL)
149 {
150 r = -1;
151 }
152 }
154 /* same for finding */
155 if(pS->cfg.device_binding)
156 {
157 pS->cfg.device_binding = strdup(pS->cfg.device_binding);
158 if(pS->cfg.device_binding == NULL)
159 {
160 r = -1;
161 }
162 }
164 /* if something went wrong... cleanup allocation */
165 if(r < 0)
166 {
167 /* something is wrong... something failed */
168 LOG_printf(LOG_ERROR, "socket-cfg-copy no memory\n");
170 _stream_socket_cfg_free(pS);
171 }
172 /* return Great Success */
173 return (r);
174 }
176 /*
177 * pseudo-private function to create a linux socket structure
178 * Used by both client and server sockets.
179 *
180 * Pseudo-private function defined in stream_socket_private.h
181 */
182 struct linux_socket *_stream_socket_create(const struct socket_cfg *pCFG,
183 const struct io_stream_funcs *pFuncs)
184 {
185 /* this is the source of our connection ids it must be static. */
186 static int connection_counter;
188 struct linux_socket *pS;
189 int r;
191 switch (pCFG->inet_4or6)
192 {
193 case 0: /* pick randomly */
194 case 4: /* specifically inet4 */
195 case 6: /* specifically inet6 */
196 break;
197 default:
198 LOG_printf(LOG_ERROR, "invalid inet_4or6\n");
199 return (0);
200 }
202 pS = calloc(1, sizeof(*pS));
203 if(pS == NULL)
204 {
205 LOG_printf(LOG_ERROR, "socket-client no memory\n");
206 return (pS);
207 }
209 _ATOMIC_global_lock();
210 connection_counter++;
211 pS->connection_id = connection_counter;
212 _ATOMIC_global_unlock();
214 pS->test_ptr = &(socket_test);
215 pS->pParent = STREAM_createPrivate(pFuncs, (intptr_t)(pS));
216 if(pS->pParent == NULL)
217 {
218 memset((void *)(pS), 0, sizeof(*pS));
219 return (NULL);
220 }
222 pS->h = -1;
223 r = _stream_socket_cfg_copy(pS, pCFG);
224 if(r < 0)
225 {
226 STREAM_destroyPrivate(pS->pParent);
227 free((void *)(pS));
228 pS = NULL;
229 }
230 else
231 {
232 /* UPDATE internal pointers */
233 pS->is_connected = false;
234 pS->err_action = "client-create";
235 }
237 return (pS);
238 }
240 /*
241 * Determine if a socket is connected or not.
242 *
243 * Public function defined in stream_socket.h
244 */
245 bool STREAM_SOCKET_isConnected(intptr_t h)
246 {
247 struct linux_socket *pS;
248 bool answer;
250 answer = false;
251 pS = _stream_socket_h2ps(h,0);
252 if(pS)
253 {
254 answer = pS->is_connected;
255 }
256 return (answer);
257 }
259 /*
260 * Destroy a socket created by various SOCKET_<name>_create() calls
261 *
262 * Public function defined in stream_socket.h
263 */
264 void SOCKET_destroy(intptr_t h)
265 {
266 if(h)
267 {
268 _stream_socket_destroy(_stream_socket_h2ps(h,0));
269 }
270 }
272 /*
273 * Pseudo-private destroy function for sockets
274 * Shared by both client and server socket code.
275 *
276 * Pseudo-private function defined in stream_socket_private.h
277 */
278 void _stream_socket_destroy(struct linux_socket *pS)
279 {
280 if(pS == NULL)
281 {
282 return;
283 }
284 if(pS->h > 0)
285 {
286 _stream_socket_close(pS);
287 }
288 if(pS->pParent)
289 {
290 STREAM_destroyPrivate(pS->pParent);
291 pS->pParent = NULL;
292 }
293 _stream_socket_cfg_free(pS);
295 memset((void *)(pS), 0, sizeof(*pS));
296 }
298 /*
299 * Mark a socket as a reusable socket (avoids address in use errors)
300 * Pseudo-private function shared across socket types
301 *
302 * Pseudo-private function defined in stream_socket_private
303 */
304 int _stream_socket_reuse(struct linux_socket *pS)
305 {
306 int r,f,v;
308 /* Mark as reuseable, avoid EADDRINUSE errors */
309 /* http://linux.die.net/man/7/socket */
310 /* http://linux.die.net/man/2/setsockopt */
311 f = SO_REUSEADDR;
312 #if defined(SO_REUSEPORT)
313 /* http://stackoverflow.com/questions/14388706/socket-options-so-reuseaddr-and-so-reuseport-how-do-they-differ-do-they-mean-t */
314 /* post Linux 3.9 */
315 f |= SO_REUSEPORT;
316 #endif
317 v = 1;
318 r = setsockopt(pS->h, SOL_SOCKET, f, (const void *)(&v), sizeof(v));
319 if(r < 0)
320 {
321 _stream_socket_error(pS, "setsocketopt(SO_REUSEADDR)", errno, NULL);
322 }
323 return (r);
324 }
326 /*
327 * Bind this socket to a specific device, ie: 'eth0' vrs 'eth1'
328 * Pseudo-private to socket implimentations.
329 *
330 * Pseudo-private function defined in stream_socket_private
331 */
332 int _stream_socket_bind_to_device(struct linux_socket *pS)
333 {
334 size_t l;
335 int r;
337 /* accept *NO* binding */
338 if(pS->cfg.device_binding == NULL)
339 {
340 return (0);
341 }
342 /* or blank binding */
343 l = strlen(pS->cfg.device_binding);
344 if(l == 0)
345 {
346 return (0);
347 }
348 #if defined(_MSC_VER)
349 /* not supported on windows */
350 r = 0;
351 #else
352 /* http://linux.die.net/man/7/socket */
353 /* http://linux.die.net/man/2/setsockopt */
354 r = setsockopt(pS->h,
355 SOL_SOCKET,
356 SO_BINDTODEVICE,
357 ((const void *)(pS->cfg.device_binding)),
358 strlen(pS->cfg.device_binding));
359 #endif
360 if(r < 0)
361 {
362 _stream_socket_error(pS, "bind()", _socket_errno(),NULL);
363 }
364 return (r);
365 }
367 /*
368 * Print an error message with this socket and mark the stream as in error
369 *
370 * Pseudo-private function defined in stream_socket_private.h
371 */
372 void _stream_socket_error(struct linux_socket *pS,
373 const char *msg1,
374 int errnum,
375 const char *msg2)
376 {
377 const char *host;
379 host = pS->cfg.host;
380 if(host == NULL)
381 {
382 host = "(null-host)";
383 }
384 pS->pParent->is_error = true;
385 if(msg2 == NULL)
386 {
387 msg2 = "";
388 if(errnum != 0)
389 {
390 #if defined(__linux__)
391 msg2 = strerror(errnum);
392 #endif
393 #if defined(_MSC_VER)
394 msg2 = gai_strerrorA(errnum);
395 #endif
396 }
397 }
398 LOG_printf(LOG_ERROR, "socket(%c,%s:%s) %s %s %d %s\n",
399 pS->cfg.ascp,
400 host,
401 pS->cfg.service,
402 pS->err_action,
403 msg1,
404 errnum,
405 msg2);
406 }
408 /*
409 * Pseudo-private socket function that converts a stream handle
410 * into a socket structure pointer
411 *
412 * Pseudo-private function defined in stream_socket_private.h
413 */
414 struct linux_socket *_stream_socket_io2ps(struct io_stream *pIO, int typecode)
415 {
416 struct linux_socket *pS;
417 pS = (struct linux_socket *)(pIO->opaque_ptr);
419 if(pS == NULL)
420 {
421 bad:
422 LOG_printf(LOG_ERROR, "not a socket handle: %p (it is a: %s)\n",
423 (void *)(pIO), pIO->pFuncs->name );
424 return (NULL);
425 }
427 if(pS->test_ptr != &(socket_test))
428 {
429 goto bad;
430 }
432 if(typecode)
433 {
434 if(pS->cfg.ascp != typecode)
435 {
436 pS->err_action = "get-type";
437 _stream_socket_error(pS, "wrong-socket-type", 0, "");
438 /* return failure */
439 pS = NULL;
440 }
441 }
442 return (pS);
443 }
445 /*
446 * convert a handle into a linux socket pointer
447 * Shared by both client and server socket code
448 *
449 * Pseudo-private function defined in stream_socket_private.h
450 */
451 struct linux_socket *_stream_socket_h2ps(intptr_t h, int typecode)
452 {
453 struct io_stream *pIO;
455 pIO = STREAM_hToStruct(h);
456 if(pIO == NULL)
457 {
458 return (NULL);
459 }
461 return (_stream_socket_io2ps(pIO, typecode));
462 }
464 /*
465 * Return true if this handle is a socket handle
466 *
467 * Public function defined in stream_socket.h
468 */
469 bool STREAM_isSocket(intptr_t h)
470 {
471 struct io_stream *pIO;
472 struct linux_socket *pS;
474 pIO = STREAM_hToStruct(h);
475 if(pIO == NULL)
476 {
477 return (false);
478 }
479 pS = (struct linux_socket *)(pIO->opaque_ptr);
481 if(pS->test_ptr == &(socket_test))
482 {
483 return (true);
484 }
485 else
486 {
487 return (false);
488 }
489 }
491 /*
492 * Psuedo-private function to close a socket.
493 * Shared between client and server sockets
494 *
495 * Pseudo-private function defined in stream_socket_private.h
496 */
497 void _stream_socket_close(struct linux_socket *pS)
498 {
499 pS->is_connected = false;
500 /* clear the error because we closed the connection */
501 pS->pParent->is_error = false;
503 /* close it */
504 if(pS->h >= 0)
505 {
506 LOG_printf(LOG_DBG_SOCKET, "socket_close(%c,%s:%s)\n",
507 pS->cfg.ascp,
508 pS->cfg.host ? pS->cfg.host : "server",
509 pS->cfg.service);
510 #if defined(_MSC_VER)
511 closesocket(pS->h);
512 #endif
513 #if defined(__linux__)
514 close(pS->h);
515 #endif
516 pS->h = -1;
517 if(pS->h != -1)
518 {
519 BUG_HERE("need to close this\n");
520 }
521 }
522 }
524 /*
525 * Pseudo private function to poll (read) a socket
526 * Shared between client and server sockets.
527 *
528 * Pseudo-private function defined in stream_socket_private.h
529 */
530 bool _stream_socket_poll(struct linux_socket *pS, int mSecs_timeout)
531 {
532 int r;
534 /* assume ok to poll */
535 r = 0;
537 pS->err_action = "poll";
538 switch(pS->cfg.ascp)
539 {
540 default:
541 _stream_socket_error(pS, "not-valid-poll-state", 0, "");
542 r = -1;
543 break;
544 case 'c':
545 if(!(pS->is_connected))
546 {
547 r = -1;
548 _stream_socket_error(pS, "not connected", 0, "");
549 }
550 break;
551 case 's':
552 _stream_socket_error(pS, "not-listen-state", 0, "");
553 r = -1;
554 break;
555 case 'l':
556 /* we can poll */
557 break;
558 case 'a':
559 /* yes we can poll! */
560 break;
561 }
562 if(r < 0)
563 {
564 _stream_socket_error(pS, "poll bad socket type", 0, "");
565 r = -1;
566 }
567 if(r >= 0)
568 {
569 struct unix_fdrw rw;
570 memset(&rw, 0, sizeof(rw));
571 rw.is_connected = true;
572 rw.fd = pS->h;
573 rw.rw = 'r';
574 rw.log_prefix = "socket-poll";
575 rw.log_why = LOG_DBG_SOCKET;
576 rw.mSecs_timeout = mSecs_timeout;
577 rw.type = 's';
578 r = POLL_readable(&rw);
579 if(r < 0)
580 {
581 _stream_socket_error(pS,"poll error", 0, "");
582 }
583 }
585 if(r > 0)
586 {
587 return (true);
588 }
589 else
590 {
591 return (false);
592 }
593 }
595 /*
596 * ========================================
597 * Texas Instruments Micro Controller Style
598 * ========================================
599 * Local Variables:
600 * mode: c
601 * c-file-style: "bsd"
602 * tab-width: 4
603 * c-basic-offset: 4
604 * indent-tabs-mode: nil
605 * End:
606 * vim:set filetype=c tabstop=4 shiftwidth=4 expandtab=true
607 */