Note - possibly need to set send timeout as well
[profile/ivi/smartdevicelink.git] / SDL_Core / src / components / transport_manager / src / tcp / tcp_client_listener.cc
1 /**
2  * \file tcp_client_listener.cc
3  * \brief TcpClientListener class source file.
4  *
5  * Copyright (c) 2013, Ford Motor Company
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following
16  * disclaimer in the documentation and/or other materials provided with the
17  * distribution.
18  *
19  * Neither the name of the Ford Motor Company nor the names of its contributors
20  * may be used to endorse or promote products derived from this software
21  * without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35
36 #include "transport_manager/tcp/tcp_client_listener.h"
37
38 #include <memory.h>
39 #include <signal.h>
40 #include <errno.h>
41 #include <arpa/inet.h>
42 #include <unistd.h>
43 #include <sys/types.h>
44 #include <sys/sysctl.h>
45 #include <sys/socket.h>
46 #ifdef __linux__
47 #  include <linux/tcp.h>
48 #else  // __linux__
49 #  include <sys/time.h>
50 #  include <netinet/in.h>
51 #  include <netinet/tcp.h>
52 //#  include <netinet/tcp_var.h>
53 #endif  // __linux__
54
55 #include "transport_manager/transport_adapter/transport_adapter_controller.h"
56 #include "transport_manager/tcp/tcp_device.h"
57 #include "transport_manager/tcp/tcp_socket_connection.h"
58
59 namespace transport_manager {
60 namespace transport_adapter {
61
62 TcpClientListener::TcpClientListener(TransportAdapterController* controller,
63                                      const uint16_t port)
64     : port_(port),
65       controller_(controller),
66       thread_(),
67       socket_(-1),
68       thread_started_(false),
69       shutdown_requested_(false),
70       thread_stop_requested_(false) {
71 }
72
73 void* tcpClientListenerThread(void* data) {
74   TcpClientListener* tcpClientListener = static_cast<TcpClientListener*>(data);
75   assert(tcpClientListener != 0);
76   tcpClientListener->Thread();
77   return 0;
78 }
79
80 TransportAdapter::Error TcpClientListener::Init() {
81   return TransportAdapter::OK;
82 }
83
84 void TcpClientListener::Terminate() {
85   shutdown_requested_ = true;
86   if (TransportAdapter::OK != StopListening()) {
87     LOG4CXX_ERROR(logger_, "Cannot stop listening TCP");
88   }
89 }
90
91 bool TcpClientListener::IsInitialised() const {
92   return true;
93 }
94
95 TcpClientListener::~TcpClientListener() {
96   LOG4CXX_INFO(logger_, "destructor");
97 }
98
99 void TcpClientListener::Thread() {
100   LOG4CXX_INFO(logger_, "Tcp client listener thread started");
101
102   while (false == thread_stop_requested_) {
103     sockaddr_in client_address;
104     socklen_t client_address_size = sizeof(client_address);
105     const int connection_fd = accept(socket_,
106                                      (struct sockaddr*) &client_address,
107                                      &client_address_size);
108     if (thread_stop_requested_) break;
109
110     if (connection_fd < 0) {
111       LOG4CXX_ERROR_WITH_ERRNO(logger_, "accept() failed");
112       continue;
113     }
114
115     if (AF_INET != client_address.sin_family) {
116       LOG4CXX_ERROR(logger_, "Address of connected client is invalid");
117       continue;
118     }
119
120     char device_name[32];
121     strncpy(device_name, inet_ntoa(client_address.sin_addr),
122             sizeof(device_name) / sizeof(device_name[0]));
123     LOG4CXX_INFO(logger_, "Connected client " << device_name);
124
125     int yes = 1;
126     int keepidle = 3;  // 3 seconds to disconnection detecting
127     int keepcnt = 5;
128     int keepintvl = 1;
129     int user_timeout = 7000;  // milliseconds
130 #ifdef __linux__
131     setsockopt(connection_fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes));
132     setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle));
133     setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));
134     setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
135     setsockopt(connection_fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout, sizeof(user_timeout));
136 #elif OS_MACOSX
137     setsockopt(connection_fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes));
138     setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPALIVE, &keepidle, sizeof(keepidle));
139     setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt));
140     setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl));
141     setsockopt(connection_fd, IPPROTO_TCP, SO_RCVTIMEO, &user_timeout, sizeof(user_timeout));
142     // NOTE: may need to set SO_SNDTIMEO
143 #elif __QNX__ 
144     // TODO (KKolodiy): Out of order!
145     const int kMidLength = 4;
146     int mib[kMidLength];
147     timeval tval;
148
149     mib[0] = CTL_NET;
150     mib[1] = AF_INET;
151     mib[2] = IPPROTO_TCP;
152     mib[3] = TCPCTL_KEEPIDLE;
153     sysctl(mib, kMidLength, NULL, NULL, &keepidle, sizeof(keepidle));
154
155     mib[0] = CTL_NET;
156     mib[1] = AF_INET;
157     mib[2] = IPPROTO_TCP;
158     mib[3] = TCPCTL_KEEPCNT;
159     sysctl(mib, kMidLength, NULL, NULL, &keepcnt, sizeof(keepcnt));
160
161     mib[0] = CTL_NET;
162     mib[1] = AF_INET;
163     mib[2] = IPPROTO_TCP;
164     mib[3] = TCPCTL_KEEPINTVL;
165     sysctl(mib, kMidLength, NULL, NULL, &keepintvl, sizeof(keepintvl));
166
167     memset(&tval, sizeof(tval), 0);
168     tval.tv_sec = keepidle;
169     setsockopt(connection_fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes));
170     setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPALIVE, &tval, sizeof(tval));
171 #endif  // __QNX__
172
173     TcpDevice* tcp_device = new TcpDevice(client_address.sin_addr.s_addr, device_name);
174     DeviceSptr device = controller_->AddDevice(tcp_device);
175     tcp_device = static_cast<TcpDevice*>(device.get());
176     const ApplicationHandle app_handle = tcp_device->AddIncomingApplication(
177         connection_fd);
178
179     TcpSocketConnection* connection(
180         new TcpSocketConnection(device->unique_device_id(), app_handle,
181                                 controller_));
182     connection->set_socket(connection_fd);
183     const TransportAdapter::Error error = connection->Start();
184     if (error != TransportAdapter::OK) {
185       delete connection;
186     }
187   }
188
189   LOG4CXX_INFO(logger_, "Tcp client listener thread finished");
190 }
191
192 TransportAdapter::Error TcpClientListener::StartListening() {
193   if (thread_started_)
194     return TransportAdapter::BAD_STATE;
195
196   socket_ = socket(AF_INET, SOCK_STREAM, 0);
197
198   if (-1 == socket_) {
199     LOG4CXX_ERROR_WITH_ERRNO(logger_, "Failed to create socket");
200     return TransportAdapter::FAIL;
201   }
202
203   sockaddr_in server_address;
204   memset(&server_address, 0, sizeof(server_address));
205   server_address.sin_family = AF_INET;
206   server_address.sin_port = htons(port_);
207   server_address.sin_addr.s_addr = INADDR_ANY;
208
209   int optval = 1;
210   setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
211
212   if (0 != bind(socket_, (sockaddr*) &server_address, sizeof(server_address))) {
213     LOG4CXX_ERROR_WITH_ERRNO(logger_, "bind() failed");
214     return TransportAdapter::FAIL;
215   }
216
217   if (0 != listen(socket_, 128)) {
218     LOG4CXX_ERROR_WITH_ERRNO(logger_, "listen() failed");
219     return TransportAdapter::FAIL;
220   }
221
222   const int thread_start_error = pthread_create(&thread_, 0,
223                                                 &tcpClientListenerThread, this);
224   if (0 == thread_start_error) {
225     thread_started_ = true;
226     LOG4CXX_INFO(logger_, "Tcp client listener thread started");
227   } else {
228     LOG4CXX_ERROR(
229         logger_,
230         "Tcp client listener thread start failed, error code "
231             << thread_start_error);
232     return TransportAdapter::FAIL;
233   }
234   return TransportAdapter::OK;
235 }
236
237 TransportAdapter::Error TcpClientListener::StopListening() {
238   if (!thread_started_)
239     return TransportAdapter::BAD_STATE;
240
241   thread_stop_requested_ = true;
242   int byebyesocket = socket(AF_INET, SOCK_STREAM, 0);
243   sockaddr_in server_address;
244   memset(&server_address, 0, sizeof(server_address));
245   server_address.sin_family = AF_INET;
246   server_address.sin_port = htons(port_);
247   server_address.sin_addr.s_addr = INADDR_ANY;
248   connect(byebyesocket, (sockaddr*)&server_address, sizeof(server_address));
249   shutdown(byebyesocket, SHUT_RDWR);
250   close(byebyesocket);
251   pthread_join(thread_, 0);
252   LOG4CXX_INFO(logger_, "Tcp client listener thread terminated");
253   close(socket_);
254   socket_ = -1;
255   thread_started_ = false;
256   thread_stop_requested_ = false;
257   return TransportAdapter::OK;
258 }
259
260 }  // namespace transport_adapter
261 }  // namespace transport_manager