1 /******************************************************************************
2 @file stream_socket_server.c
4 @brief TIMAC 2.0 API STREAM implimentation file for a server socket.
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: July 14, 2016 (2.00.00.30)$
44 *****************************************************************************/
46 #include "compiler.h"
47 #include "stream.h"
48 #include "stream_socket.h"
50 #include "log.h"
51 #include "timer.h"
52 #include "void_ptr.h"
53 #include "unix_fdrw.h"
55 #include <sys/types.h>
56 #if defined(__linux__)
57 #include <sys/socket.h>
58 #include <netdb.h>
59 #include <unistd.h>
60 #include <arpa/inet.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 <string.h>
71 #include <errno.h>
73 #if defined(_MSC_VER)
74 #undef gai_strerror
75 #define gai_strerror gai_strerrorA
76 #endif
78 /*
79 * @brief code to check for disconnects
80 * @param pS - the socket to check
81 * @param pRW - the read write structure we are using.
82 *
83 * Goal is to set pS->is_connected and is_error
84 */
85 static void disconnect_check(struct linux_socket *pS, struct unix_fdrw *pRW)
86 {
87 if(pRW->is_error)
88 {
89 pS->pParent->is_error = true;
90 LOG_printf(LOG_ERROR, "socket: (connection=%d) reporting error up\n",
91 pS->connection_id);
92 return;
93 }
95 if(pRW->is_connected)
96 {
97 return;
98 }
100 pS->pParent->is_error = true;
101 pS->is_connected = false;
103 LOG_printf(LOG_DBG_SOCKET,
104 "socket: (connection=%d) disconnect\n",
105 pS->connection_id);
106 }
108 /*!
109 * @brief [private] method handler for STREAM_WrBytes() for the server socket.
110 * @param pIO - the io stream
111 * @param pBytes - data buffer
112 * @param nbytes - number of bytes to transfer
113 * @param mSecs_timeout - timeout period in milliseconds for the operation
114 *
115 * @return negative on error, otherwise 0..actual transfered
116 */
117 static int socket_server_wr(struct io_stream *pIO,
118 const void *pBytes,
119 size_t nbytes, int mSecs_timeout)
120 {
121 struct linux_socket *pS;
122 struct unix_fdrw rw;
123 int r;
125 /* socket must be in the accepted state */
126 pS = _stream_socket_io2ps(pIO, 'a');
127 if(pS == NULL)
128 {
129 return (-1);
130 }
132 pS->err_action = "write()";
134 if(!(pS->is_connected))
135 {
136 _stream_socket_error(pS, "not-connected", 0, "");
137 return (-1);
138 }
140 /* use the common unix handle write code */
141 memset(&(rw), 0, sizeof(rw));
143 rw.is_connected = true;
144 rw.type = 's';
145 rw.rw = 'w';
146 rw.fd = pS->h;
147 rw.fifo_handle = 0;
148 rw.log_prefix = "server-wr";
149 rw.log_why = LOG_DBG_SOCKET;
150 rw.c_bytes = pBytes;
151 rw.v_bytes = NULL;
152 rw.n_done = 0;
153 rw.n_todo = nbytes;
154 rw.mSecs_timeout = mSecs_timeout;
156 r = UNIX_fdRw(&rw);
158 disconnect_check(pS,&rw);
160 return (r);
161 }
163 /*!
164 * @brief [private] method handler for STREAM_RdBytes() for the server socket.
165 * @param pIO - the io stream
166 * @param pBytes - data buffer
167 * @param nbytes - number of bytes to transfer
168 * @param mSecs_timeout - timeout period in milliseconds for the operation
169 *
170 * @return negative on error, otherwise 0..actual transfered
171 */
172 static int socket_server_rd(struct io_stream *pIO,
173 void *pBytes,
174 size_t nbytes,
175 int mSecs_timeout)
176 {
177 struct linux_socket *pS;
178 struct unix_fdrw rw;
179 int r;
181 /* socket must be in the accepted state */
182 pS = _stream_socket_io2ps(pIO, 'a');
183 if(pS == NULL)
184 {
185 return (-1);
186 }
187 pS->err_action = "read()";
189 if(!(pS->is_connected))
190 {
191 _stream_socket_error(pS, "not-connected", 0, "");
192 return (-1);
193 }
195 memset(&(rw), 0, sizeof(rw));
197 rw.type = 's';
198 rw.is_connected = true;
199 rw.rw = 'r';
200 rw.fd = pS->h;
201 rw.fifo_handle = 0;
202 rw.log_prefix = "server-rd";
203 rw.log_why = LOG_DBG_SOCKET;
204 rw.c_bytes = NULL;
205 rw.v_bytes = pBytes;
206 rw.n_done = 0;
207 rw.n_todo = nbytes;
208 rw.mSecs_timeout = mSecs_timeout;
210 r = UNIX_fdRw(&rw);
212 disconnect_check(pS,&rw);
214 return (r);
215 }
217 /*!
218 * @brief [private] method handler for STREAM_RxAvail() for the server socket.
219 * @param pIO - the io stream
220 *
221 * @return boolean true if data is readable
222 */
223 static bool socket_server_poll(struct io_stream *pIO, int mSec_timeout)
224 {
225 struct linux_socket *pS;
227 pS = _stream_socket_io2ps(pIO, 's');
228 if(pS == NULL)
229 {
230 return (false);
231 }
232 return (_stream_socket_poll(pS, mSec_timeout ));
233 }
235 /*!
236 * @brief [private] method handler for the STREAM_Flush() for the server socket
237 * @param pIO - the io stream
238 * @return In this case, the function always returns 0.
239 */
240 static int socket_server_flush(struct io_stream *pIO)
241 {
242 /* nothing we can do */
243 (void)(pIO);
244 return (0);
245 }
247 /*!
248 * @brief [private] method handler for the STREAM_Close() for the server socket
249 * @param pIO - the io stream
250 * @returns void
251 */
252 static void socket_server_close(struct io_stream *pIO)
253 {
254 struct linux_socket *pS;
255 /* this could be an accepted socket */
256 /* or it could be the socket we are listening on */
257 /* THUS: Type code = don't care */
258 pS = _stream_socket_io2ps(pIO, 0);
259 if(pS)
260 {
261 _stream_socket_close(pS);
262 }
263 }
265 /*!
266 * @var socket_server_funcs
267 * @brief [private] Method table for the server sockets.
268 */
269 static const struct io_stream_funcs socket_server_funcs = {
270 .name = "socket-server",
271 .wr_fn = socket_server_wr,
272 .rd_fn = socket_server_rd,
273 .close_fn = socket_server_close,
274 .poll_fn = socket_server_poll,
275 .flush_fn = socket_server_flush
276 };
278 /*
279 * Create a server socket
280 *
281 * Public function defined in stream_socket.h
282 */
283 intptr_t SOCKET_SERVER_create(struct socket_cfg *pCFG)
284 {
285 struct addrinfo hints;
286 struct addrinfo *results;
287 struct addrinfo *rp;
288 struct linux_socket *pS;
289 const char *host;
290 int r;
292 /* the HOST setting is optional */
293 /* If it is BLANK or NULL, we bind to anything */
294 /* otherwise we bind to a specific IP address */
295 /* (in case the host has more then 1 IP address) */
297 /* The host setting is "optional" for the server */
298 /* The service (port number) is manditory */
299 if((pCFG->service == NULL))
300 {
301 LOG_printf(LOG_ERROR, "socket-server: create() bad service\n");
302 return (0);
303 }
305 /* sanity check */
306 if(pCFG->ascp != 's')
307 {
308 LOG_printf(LOG_ERROR, "socket_server: incorrect cfg type\n");
309 return (0);
310 }
312 /* basic configuration */
313 pS = _stream_socket_create(pCFG, &socket_server_funcs);
314 if(pS == NULL)
315 {
316 return (0);
317 }
319 /* house keeping for errors */
320 pS->err_action = "server-create";
322 /* we are binding to a specific ip address? */
323 host = pS->cfg.host;
324 if(host)
325 {
326 if(0 == strlen(host))
327 {
328 /* allow user to specify "" as blank. */
329 host = NULL;
330 }
331 }
333 /* do our lookup */
334 memset(&hints, 0, sizeof(hints));
335 switch (pS->cfg.inet_4or6)
336 {
337 default:
338 BUG_HERE("Unsupported\n");
339 break;
340 case 0:
341 hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
342 break;
343 case 4:
344 hints.ai_family = AF_INET; /* only IPv4 */
345 break;
346 case 6:
347 hints.ai_family = AF_INET; /* only IPv6 */
348 break;
349 }
350 hints.ai_socktype = SOCK_STREAM; /* simple streaming socket */
352 /* are we binding to a specific interface/address? */
353 if(host)
354 {
355 hints.ai_flags = 0;
356 }
357 else
358 {
359 /* See the discussion about "bind()" here */
360 /* and the use of the AI_PASSIVE flag */
361 /* http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#bind */
362 hints.ai_flags = AI_PASSIVE;
363 }
365 /* lookup the stuff */
366 r = getaddrinfo(host, pS->cfg.service, &hints, &results);
367 if(r != 0)
368 {
369 _stream_socket_error(pS, "getaddrinfo()", r, gai_strerror(r));
370 _stream_socket_destroy(pS);
371 return (0);
372 }
374 /* go through our results until we are successful.. */
375 pS->h = -1;
376 for(rp = results ; rp != NULL ; rp = rp->ai_next)
377 {
378 /* what INET should we use? */
379 switch (pS->cfg.inet_4or6)
380 {
381 case 0:
382 r = 3;
383 break;
384 case 4:
385 r = 1;
386 break;
387 case 6:
388 r = 2;
389 break;
390 }
392 if(rp->ai_family == AF_INET6)
393 {
394 if(0 == (r & 2))
395 {
396 /* disallowed */
397 continue;
398 }
399 }
401 if(rp->ai_family == AF_INET)
402 {
403 if(0 == (r & 1))
404 {
405 /* disallowed */
406 continue;
407 }
408 }
410 pS->pParent->is_error = false;
411 pS->h = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
412 if(pS->h < 0)
413 {
414 /* skip */
415 _stream_socket_error(pS, "socket()", _socket_errno(), NULL);
416 continue;
417 }
419 r = _stream_socket_reuse(pS);
420 if(r < 0)
421 {
422 /* Hmm it did not work? clean up and try next */
423 next_socket:
424 _stream_socket_close(pS);
425 pS->h = -1;
426 continue;
427 }
429 /* bind to specific interface if requested */
430 r = _stream_socket_bind_to_device(pS);
431 if(r < 0)
432 {
433 goto next_socket;
434 }
436 /* Bind to the address/port we desire */
437 r = bind(pS->h, rp->ai_addr, (socklen_t)(rp->ai_addrlen));
438 if(r != 0)
439 {
440 _stream_socket_error(pS, "bind()", _socket_errno(), NULL);
441 goto next_socket;
442 }
443 /* great success! */
444 break;
445 }
447 /* release the results list */
448 freeaddrinfo(results);
450 /* success? */
451 if(pS->h < 0)
452 {
453 _stream_socket_error(pS, "server-nomore", 0, "");
454 _stream_socket_destroy(pS);
455 return (0);
456 }
458 /* Indicate our success */
459 LOG_printf(LOG_DBG_SOCKET,
460 "socket(server:%s) ready to accept\n",
461 pS->cfg.service);
463 return (STREAM_structToH(pS->pParent));
465 }
467 /*
468 * Public function to set a server socket to listen mode
469 *
470 * Public function defined in stream_socket.h
471 */
472 int SOCKET_SERVER_listen(intptr_t h)
473 {
474 struct linux_socket *pS;
475 int r;
477 /* Find our internal representation */
478 pS = _stream_socket_h2ps(h,'s');
479 if(pS == NULL)
480 {
481 return (-1);
482 }
484 /* tell the socket to listen */
485 r = listen(pS->h, pS->cfg.server_backlog);
486 if(r != 0)
487 {
488 pS->err_action = "listen()";
489 _stream_socket_error(pS, "listen-fail", _socket_errno(), NULL);
490 r = -1;
491 }
492 /* we are now a listening socket. */
493 pS->cfg.ascp = 'l';
494 return (r);
495 }
497 /* Convert a struct sockaddr address to a string, IPv4 and IPv6: */
498 static const char *get_ip_str(const struct sockaddr *sa, char *s, size_t maxlen)
499 {
500 #if defined(__linux__)
501 const struct sockaddr_in *SA4 = (((const struct sockaddr_in *)sa));
502 const struct sockaddr_in6 *SA6 = (((const struct sockaddr_in6 *)sa));
504 #endif
505 #if defined(_MSC_VER)
506 struct sockaddr_in *SA4 = (((struct sockaddr_in *)sa));
507 struct sockaddr_in6 *SA6 = (((struct sockaddr_in6 *)sa));
508 #endif
510 switch(sa->sa_family)
511 {
512 case AF_INET:
513 inet_ntop(AF_INET, &(SA4->sin_addr), s, maxlen);
514 break;
516 case AF_INET6:
517 inet_ntop(AF_INET6, &(SA6->sin6_addr),s, maxlen);
518 break;
520 default:
521 strncpy(s, "Unknown AF", maxlen);
522 break;
523 }
525 return (s);
526 }
528 /*
529 * Public function for server sockets to accept a connection
530 *
531 * Public function defined in stream_socket.h
532 */
533 int SOCKET_SERVER_accept(intptr_t *h, intptr_t hListener, int mSec_timeout)
534 {
535 struct linux_socket *pSL;
536 struct linux_socket *pSA;
537 struct unix_fdrw rw;
538 int r;
540 /* make sure this is not valid. */
541 *h = 0;
543 /* translate to our internal form */
544 pSL = _stream_socket_h2ps(hListener, 'l');
545 if(pSL == NULL)
546 {
547 return (-1);
548 }
550 memset(&rw, 0, sizeof(rw));
551 rw.is_connected = true;
552 rw.fd = pSL->h;
553 rw.type = 's';
554 rw.rw = 'r';
555 rw.log_prefix = "sock-accept";
556 rw.log_why = LOG_DBG_SOCKET;
557 rw.fifo_handle = 0;
558 rw.mSecs_timeout = mSec_timeout;
560 /* is there somebody knocking waiting to be accepted? */
561 r = POLL_readable(&rw);
562 if(r < 0)
563 {
564 _stream_socket_error(pSL, "poll", _socket_errno(), NULL);
565 return (r);
566 }
568 if(r == 0)
569 {
570 /* Nobody is there */
571 /* we accepted 0 connections */
572 return (0);
573 }
575 /* init the new accepted socket */
576 pSA = _stream_socket_create(&(pSL->cfg) , &socket_server_funcs);
577 if(!pSA)
578 {
579 return (-1);
580 }
582 /* this is our accepted socket. */
583 pSA->cfg.ascp = 'a';
585 /* update our error message */
586 pSL->err_action = "accept";
588 /* setup for the accept call */
589 pSA->other_len = sizeof(pSA->other);
590 memset((void *)(&pSA->other), 0, sizeof(pSA->other));
592 /* Accept our new connection */
593 pSA->other_len = sizeof(pSA->other);
594 pSA->h = accept(pSL->h,
595 (struct sockaddr *)&(pSA->other),
596 &(pSA->other_len));
598 /* did something go wrong? */
599 if(pSA->h < 0)
600 {
601 _stream_socket_error(pSA, "accept-fail", _socket_errno(), NULL);
602 _stream_socket_close(pSA);
603 _stream_socket_destroy(pSA);
604 return (-1);
605 }
607 /* print a debug log about the connection */
608 if(LOG_test(LOG_DBG_SOCKET))
609 {
610 /* Technique is from here: */
611 /* http://beej.us/guide/bgnet/output/html/multipage/getpeernameman.html */
613 /* ip6 strings are the *larger* */
614 /* of IPNET_ADDRSTRLEN(ie: v4) */
615 /* or IPNET6_ADDRSTRLEN(ie: v6) */
616 /* so we use the ipnet6 version */
617 char ipstr[ INET6_ADDRSTRLEN ];
618 int port;
620 /* provide a dummy name incase something goes wrong below */
621 strcpy(ipstr, "unknown");
622 /* deal with both IPv4 and IPv6: */
623 port = -1;
624 switch(pSA->other.ss_family)
625 {
626 case AF_INET:
627 port = ((struct sockaddr_in *)(&(pSA->other)))->sin_port;
628 break;
629 case AF_INET6:
630 port = ((struct sockaddr_in6 *)(&(pSA->other)))->sin6_port;
631 break;
632 }
633 get_ip_str((struct sockaddr *)(&(pSA->other)), ipstr, sizeof(ipstr));
635 /* convert port endian: */
636 port = ntohs((u_short)port);
637 LOG_printf(LOG_DBG_SOCKET, "socket(server:%s) new cid: %d, h: %d\n",
638 pSA->cfg.service,
639 pSA->connection_id,
640 (int)(pSA->h));
641 LOG_printf(LOG_DBG_SOCKET, "socket(server:%s) peer: %s, port %d\n",
642 pSA->cfg.service,
643 ipstr,
644 port);
645 }
647 /* mark connection as accepted */
648 pSA->cfg.ascp = 'a';
649 pSA->is_connected = true;
650 *h = STREAM_structToH(pSA->pParent);
651 /* we accepted 1 connection */
652 return (1);
653 }
655 /*
656 * ========================================
657 * Texas Instruments Micro Controller Style
658 * ========================================
659 * Local Variables:
660 * mode: c
661 * c-file-style: "bsd"
662 * tab-width: 4
663 * c-basic-offset: 4
664 * indent-tabs-mode: nil
665 * End:
666 * vim:set filetype=c tabstop=4 shiftwidth=4 expandtab=true
667 */