]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - apps/tidep0084.git/blob - components/common/src/stream_socket_client.c
caf139dd69df8cd8868a341087a10a2ebf615893
[apps/tidep0084.git] / components / common / src / stream_socket_client.c
1 /******************************************************************************
2  @file stream_socket_client.c
4  @brief TIMAC 2.0 API Socket client abstraction API
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: 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 #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 #include <unistd.h>
62 #endif
63 #if defined(_MSC_VER)
64 #include "winsock2.h"
65 #include "ws2tcpip.h"
66 #endif
68 #define _STREAM_SOCKET_PRIVATE 1450384394
69 #include "stream_socket_private.h"
71 #include "errno.h"
72 #include <stdio.h>
73 #include <stdlib.h>
74 #include <string.h>
76 #if defined(_MSC_VER)
77 #undef gai_strerror /* we want only the ascii version here */
78 #define gai_strerror gai_strerrorA
79 #endif
81 /*!
82  * @brief [private] method handler for STREAM_WrBytes() for the client socket.
83  * @param pIO - the io stream
84  * @param pBytes - data buffer
85  * @param nbytes - number of bytes to transfer
86  * @param mSecs_timeout - timeout period in milliseconds for the operation
87  *
88  * @return negative on error, otherwise 0..actual transfered
89  */
90 static int socket_client_wr(struct io_stream *pIO,
91                              const void *pBytes,
92                              size_t nbytes,
93                              int mSecs_timeout)
94 {
95     struct linux_socket *pS;
96     struct unix_fdrw rw;
98     /* get our internal form */
99     pS = _stream_socket_h2ps((intptr_t)(pIO), 'c');
100     if(pS == NULL)
101     {
102         return (-1);
103     }
105     /* setup for error messages */
106     pS->err_action = "write()";
108     /* house keeping */
109     if(!(pS->is_connected))
110     {
111         _stream_socket_error(pS, "not connected", 0, "");
112         return (-1);
113     }
115     /* use the common code */
116     memset(&(rw), 0, sizeof(rw));
118     rw.is_connected  = true;
119     rw.rw            = 'w';
120     rw.fd            = pS->h;
121     rw.fifo_handle   = 0;
122     rw.log_prefix    = "client-wr";
123     rw.type          = 's';
124     rw.log_why       = LOG_DBG_SOCKET;
125     rw.c_bytes       = pBytes;
126     rw.v_bytes       = NULL;
127     rw.n_done        = 0;
128     rw.n_todo        = nbytes;
129     rw.mSecs_timeout = mSecs_timeout;
131     return (UNIX_fdRw(&rw));
134 /*!
135  * @brief [private] method handler for STREAM_RdBytes() for the client socket.
136  * @param pIO - the io stream
137  * @param pBytes - data buffer
138  * @param nbytes - number of bytes to transfer
139  * @param mSecs_timeout - timeout period in milliseconds for the operation
140  *
141  * @return negative on error, otherwise 0..actual transfered
142  */
143 static int socket_client_rd(struct io_stream *pIO,
144                              void *pBytes,
145                              size_t nbytes,
146                              int mSecs_timeout)
148     int r;
149     struct unix_fdrw rw;
150     struct linux_socket *pS;
152     /* get our internal representation */
153     pS = _stream_socket_h2ps((intptr_t)(pIO), 'c');
154     if(pS == NULL)
155     {
156         return (-1);
157     }
159     /* for error messages */
160     pS->err_action = "read()";
162     /* house keeping */
163     if(!(pS->is_connected))
164     {
165         _stream_socket_error(pS, "not connected", 0, "");
166         return (-1);
167     }
169     /* use the common code */
170     memset(&(rw), 0, sizeof(rw));
172     rw.is_connected  = true;
173     rw.rw            = 'r';
174     rw.fd            = pS->h;
175     rw.fifo_handle   = 0;
176     rw.log_prefix    = "client-rd";
177     rw.log_why       = LOG_DBG_SOCKET;
178     rw.type          = 's';
179     rw.c_bytes       = NULL;
180     rw.v_bytes       = pBytes;
181     rw.n_done        = 0;
182     rw.n_todo        = nbytes;
183     rw.mSecs_timeout = mSecs_timeout;
185     r = (UNIX_fdRw(&rw));
186     pS->is_connected = rw.is_connected;
187     return r;
190 /*!
191  * @brief [private] method handler for STREAM_RxAvail() for the client socket.
192  * @param pIO - the io stream
193  *
194  * @return boolean true if data is readable
195  */
196 static bool socket_client_poll(struct io_stream *pIO, int mSec_timeout)
198     struct linux_socket *pS;
200     pS = _stream_socket_io2ps(pIO, 'c');
201     if(pS == NULL)
202     {
203         return (false);
204     }
205     return (_stream_socket_poll(pS, mSec_timeout));
208 /*!
209  * @brief [private] method handler for the STREAM_Flush() for the client socket
210  * @param pIO - the io stream
211  * @return In this case, the function always returns 0.
212  */
213 static void socket_client_close(struct io_stream *pIO)
215     struct linux_socket *pS;
217     pS = _stream_socket_io2ps(pIO, 'c');
218     if(pS)
219     {
220         _stream_socket_close(pS);
221     }
224 /*!
225  * @brief [private] method handler for the STREAM_Flush() for the client socket
226  * @param pIO - the io stream
227  * @return In this case, the function always returns 0.
228  */
229 static int socket_client_flush(struct io_stream *pIO)
231     /* nothing we can do */
232     (void)(pIO);
233     return (0);
236 /*!
237  * @var socket_client_funcs
238  * @brief [private] Method table for the client sockets.
239  */
240 static const struct io_stream_funcs socket_client_funcs = {
241     .name = "socket-client",
242     .wr_fn = socket_client_wr,
243     .rd_fn = socket_client_rd,
244     .close_fn = socket_client_close,
245     .poll_fn  = socket_client_poll,
246     .flush_fn = socket_client_flush
247 };
249 /*
250  * Create a client socket.
251  *
252  * Public function defined in stream_socket.h
253  */
254 intptr_t SOCKET_CLIENT_create(struct socket_cfg *pCFG)
256     struct linux_socket *pS;
258     /* sanity checks */
259     /* these are required for client */
260     if((pCFG->host == NULL) || (pCFG->service == NULL))
261     {
262         LOG_printf(LOG_ERROR, "socket_client: create bad host/service\n");
263         return (0);
264     }
265     if(pCFG->ascp != 'c')
266     {
267         LOG_printf(LOG_ERROR, "socket_client: incorrect cfg type\n");
268         return (0);
269     }
271     /* common initialization code */
272     pS = _stream_socket_create(pCFG, &socket_client_funcs);
273     if(pS == NULL)
274     {
275         return (0);
276     }
277     else
278     {
279         return ((intptr_t)(pS->pParent));
280     }
283 /*
284  * Connect a stream socket
285  *
286  * Public function defined in stream_socket.h
287  */
288 int SOCKET_CLIENT_connect(intptr_t h)
290     struct linux_socket *pS;
291     struct addrinfo hints;
292     struct addrinfo *result;
293     struct addrinfo *rp;
294     int r;
296     pS = _stream_socket_h2ps(h,'c');
297     if(pS == NULL)
298     {
299         return (-1);
300     }
302     /* close if already open & ignore failures */
303     _stream_socket_close(pS);
305     /* reset error & connection state. */
306     pS->pParent->is_error = false;
307     pS->is_connected      = false;
308     pS->err_action        = "connect()";
310     /* setup for getaddrinfo() */
311     memset((void *)(&hints), 0, sizeof(hints));
312     hints.ai_family   = AF_UNSPEC;    /* Allow IPv4 or IPv6 */
313     hints.ai_socktype = SOCK_STREAM;  /* simple streaming socket */
314     hints.ai_flags    = 0;
315     hints.ai_protocol = 0;            /* Any protocol */
317     /* Parse the address info */
318     r = getaddrinfo(pS->cfg.host, pS->cfg.service, &(hints), &result);
319     if(r != 0)
320     {
321         _stream_socket_error(pS, "getaddrinfo()", r,gai_strerror(r));
322         return (-1);
323     }
325     /* try each address result */
326     rp = result;
327     for(rp = result ; rp != NULL ; rp = rp->ai_next)
328     {
329         /* what INET should we use? */
330 #define _support_inet4 _bit0
331 #define _support_inet6 _bit1
332         switch (pS->cfg.inet_4or6)
333         {
334         case 0:
335             r = (_support_inet4 | _support_inet6);
336             break;
337         case 4:
338             r = (_support_inet4);
339             break;
340         case 6:
341             r = (_support_inet6);
342             break;
343         }
345         if(rp->ai_family == AF_INET6)
346         {
347             if(0 == (r & _support_inet6))
348             {
349                 /* disallowed */
350                 continue;
351             }
352         }
354         if(rp->ai_family == AF_INET)
355         {
356             if(0 == (r & _support_inet4))
357             {
358                 /* disallowed */
359                 continue;
360             }
361         }
362 #undef _support_inet4
363 #undef _support_inet6
365         /* reset incase changed below */
366         pS->pParent->is_error = false;
367         pS->h = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
368         if(pS->h < 0)
369         {
370             /* skip */
371             _stream_socket_error(pS, "socket()", _socket_errno(), NULL);
372             continue;
373         }
375         /* mark the socket as reusable. */
376         r = _stream_socket_reuse(pS);
377         if(r < 0)
378         {
379         next_socket:
380             _stream_socket_close(pS);
381             continue;
382         }
384         /* bind to specific interface if requested */
385         r = _stream_socket_bind_to_device(pS);
386         if(r < 0)
387         {
388             goto next_socket;
389         }
391         /* go for it! */
392         r = connect(pS->h, rp->ai_addr, (socklen_t)(rp->ai_addrlen));
393         if(r == -1)
394         {
395             _stream_socket_error(pS, "connect()", _socket_errno(), NULL);
396             goto next_socket;
397         }
398         /* Great Success :-) */
399         break;
400     }
402     /* release the addresses we have */
403     freeaddrinfo(result);
404     result = NULL;
406     /* did anything work/ */
407     if(pS->h < 0)
408     {
409         _stream_socket_error(pS, "client-nomore", 0, "");
410         return (-1);
411     }
412     /* Great Success :-) */
413     pS->is_connected = true;
414     LOG_printf(LOG_DBG_SOCKET,
415                 "client: (connection=%d) Connect success\n",
416                 pS->connection_id);
417     return (0);
420 /*
421  *  ========================================
422  *  Texas Instruments Micro Controller Style
423  *  ========================================
424  *  Local Variables:
425  *  mode: c
426  *  c-file-style: "bsd"
427  *  tab-width: 4
428  *  c-basic-offset: 4
429  *  indent-tabs-mode: nil
430  *  End:
431  *  vim:set  filetype=c tabstop=4 shiftwidth=4 expandtab=true
432  */