]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - apps/tidep0084.git/blob - components/common/src/stream_socket_server.c
Initial commit
[apps/tidep0084.git] / components / common / src / stream_socket_server.c
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 $
11   
12    Copyright (c) 2015, Texas Instruments Incorporated
13    All rights reserved.
14   
15    Redistribution and use in source and binary forms, with or without
16    modification, are permitted provided that the following conditions
17    are met:
18   
19    *  Redistributions of source code must retain the above copyright
20       notice, this list of conditions and the following disclaimer.
21   
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.
25   
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.
29   
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);
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)
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);
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)
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);
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)
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 ));
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)
242     /* nothing we can do */
243     (void)(pIO);
244     return (0);
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)
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     }
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)
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));
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)
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);
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)
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);
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)
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);
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  */