diff options
Diffstat (limited to 'SDL_Core/src/components/transport_manager/src/tcp')
6 files changed, 1028 insertions, 0 deletions
diff --git a/SDL_Core/src/components/transport_manager/src/tcp/dnssd_service_browser.cc b/SDL_Core/src/components/transport_manager/src/tcp/dnssd_service_browser.cc new file mode 100644 index 000000000..799348cfb --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/tcp/dnssd_service_browser.cc @@ -0,0 +1,326 @@ +/** + * \file dnssd_service_browser.cc + * \brief DnssdServiceBrowser class source file. + * + * Copyright (c) 2013, Ford Motor Company + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <algorithm> +#include <map> + +#include "transport_manager/transport_adapter/transport_adapter_impl.h" +#include "transport_manager/tcp/tcp_device.h" +#include "transport_manager/tcp/dnssd_service_browser.h" + +namespace transport_manager { + +namespace transport_adapter { + +bool operator==(const DnssdServiceRecord& a, const DnssdServiceRecord& b) { + return a.name == b.name && a.type == b.type && a.interface == b.interface + && a.protocol == b.protocol && a.domain_name == b.domain_name; +} + +void DnssdServiceBrowser::Terminate() { + if (0 != avahi_threaded_poll_) + avahi_threaded_poll_stop(avahi_threaded_poll_); + if (0 != avahi_service_browser_) + avahi_service_browser_free(avahi_service_browser_); + if (0 != avahi_client_) + avahi_client_free(avahi_client_); + if (0 != avahi_threaded_poll_) + avahi_threaded_poll_free(avahi_threaded_poll_); +} + +bool DnssdServiceBrowser::IsInitialised() const { + return initialised_; +} + +DnssdServiceBrowser::DnssdServiceBrowser(TransportAdapterController* controller) + : controller_(controller), + avahi_service_browser_(0), + avahi_threaded_poll_(0), + avahi_client_(0), + service_records_(), + mutex_(), + initialised_(false) { + pthread_mutex_init(&mutex_, 0); +} + +DnssdServiceBrowser::~DnssdServiceBrowser() { + pthread_mutex_destroy(&mutex_); +} + +void DnssdServiceBrowser::OnClientConnected() { + initialised_ = true; + LOG4CXX_ERROR(logger_, "AvahiClient ready"); +} + +void DnssdServiceBrowser::OnClientFailure() { + const int avahi_errno = avahi_client_errno(avahi_client_); + if (avahi_errno == AVAHI_ERR_DISCONNECTED) { + LOG4CXX_INFO(logger_, "AvahiClient disconnected"); + CreateAvahiClientAndBrowser(); + } else { + LOG4CXX_ERROR(logger_, + "AvahiClient failure: " << avahi_strerror(avahi_errno)); + } +} + +void AvahiClientCallback(AvahiClient *avahi_client, + AvahiClientState avahi_client_state, void* data) { + DnssdServiceBrowser* dnssd_service_browser = + static_cast<DnssdServiceBrowser*>(data); + + switch (avahi_client_state) { + case AVAHI_CLIENT_S_RUNNING: + dnssd_service_browser->OnClientConnected(); + break; + case AVAHI_CLIENT_FAILURE: + dnssd_service_browser->OnClientFailure(); + break; + } +} + +void AvahiServiceBrowserCallback(AvahiServiceBrowser *avahi_service_browser, + AvahiIfIndex interface, AvahiProtocol protocol, + AvahiBrowserEvent event, const char *name, + const char *type, const char *domain, + AvahiLookupResultFlags flags, void* data) { + DnssdServiceBrowser* dnssd_service_browser = + static_cast<DnssdServiceBrowser*>(data); + + int avahi_errno; + switch (event) { + case AVAHI_BROWSER_FAILURE: + avahi_errno = avahi_client_errno( + avahi_service_browser_get_client(avahi_service_browser)); + LOG4CXX_ERROR( + logger_, + "AvahiServiceBrowser failure: " << avahi_strerror(avahi_errno)); + break; + + case AVAHI_BROWSER_NEW: + dnssd_service_browser->AddService(interface, protocol, name, type, + domain); + break; + + case AVAHI_BROWSER_REMOVE: + dnssd_service_browser->RemoveService(interface, protocol, name, type, + domain); + break; + + case AVAHI_BROWSER_ALL_FOR_NOW: + case AVAHI_BROWSER_CACHE_EXHAUSTED: + break; + } +} + +void DnssdServiceBrowser::ServiceResolved( + const DnssdServiceRecord& service_record) { + pthread_mutex_lock(&mutex_); + ServiceRecords::iterator service_record_it = std::find( + service_records_.begin(), service_records_.end(), service_record); + if (service_record_it != service_records_.end()) { + *service_record_it = service_record; + } + DeviceVector device_vector = PrepareDeviceVector(); + controller_->SearchDeviceDone(device_vector); + pthread_mutex_unlock(&mutex_); +} + +void DnssdServiceBrowser::ServiceResolveFailed( + const DnssdServiceRecord& service_record) { + LOG4CXX_ERROR(logger_, + "AvahiServiceResolver failure for: " << service_record.name); + pthread_mutex_lock(&mutex_); + ServiceRecords::iterator service_record_it = std::find( + service_records_.begin(), service_records_.end(), service_record); + if (service_record_it != service_records_.end()) { + service_records_.erase(service_record_it); + } + pthread_mutex_unlock(&mutex_); +} + +void AvahiServiceResolverCallback(AvahiServiceResolver* avahi_service_resolver, + AvahiIfIndex interface, + AvahiProtocol protocol, + AvahiResolverEvent event, const char* name, + const char* type, const char* domain, + const char* host_name, + const AvahiAddress* avahi_address, + uint16_t port, AvahiStringList* txt, + AvahiLookupResultFlags flags, void *data) { + DnssdServiceBrowser* dnssd_service_browser = + static_cast<DnssdServiceBrowser*>(data); + + DnssdServiceRecord service_record; + service_record.interface = interface; + service_record.protocol = protocol; + service_record.domain_name = domain; + service_record.host_name = host_name; + service_record.name = name; + service_record.type = type; + switch (event) { + case AVAHI_RESOLVER_FOUND: + service_record.addr = avahi_address->data.ipv4.address; + service_record.port = port; + dnssd_service_browser->ServiceResolved(service_record); + break; + case AVAHI_RESOLVER_FAILURE: + dnssd_service_browser->ServiceResolveFailed(service_record); + break; + } + + avahi_service_resolver_free(avahi_service_resolver); +} + +TransportAdapter::Error DnssdServiceBrowser::CreateAvahiClientAndBrowser() { + if (0 != avahi_service_browser_) + avahi_service_browser_free(avahi_service_browser_); + if (0 != avahi_client_) + avahi_client_free(avahi_client_); + + int avahi_error; + avahi_client_ = avahi_client_new( + avahi_threaded_poll_get(avahi_threaded_poll_), AVAHI_CLIENT_NO_FAIL, + AvahiClientCallback, this, &avahi_error); + if (0 == avahi_client_) { + LOG4CXX_ERROR( + logger_, + "Failed to create AvahiClient: " << avahi_strerror(avahi_error)); + return TransportAdapter::FAIL; + } + + pthread_mutex_lock(&mutex_); + service_records_.clear(); + pthread_mutex_unlock(&mutex_); + + avahi_service_browser_ = avahi_service_browser_new( + avahi_client_, AVAHI_IF_UNSPEC, /* TODO use only required iface */ + AVAHI_PROTO_INET, DNSSD_DEFAULT_SERVICE_TYPE, NULL, /* use default domain */ + static_cast<AvahiLookupFlags>(0), AvahiServiceBrowserCallback, this); + + return TransportAdapter::OK; +} + +TransportAdapter::Error DnssdServiceBrowser::Init() { + avahi_threaded_poll_ = avahi_threaded_poll_new(); + if (0 == avahi_threaded_poll_) { + LOG4CXX_ERROR(logger_, "Failed to create AvahiThreadedPoll"); + return TransportAdapter::FAIL; + } + + const TransportAdapter::Error err = CreateAvahiClientAndBrowser(); + if (err != TransportAdapter::OK) { + return err; + } + + const int poll_start_status = avahi_threaded_poll_start(avahi_threaded_poll_); + if (poll_start_status != 0) { + LOG4CXX_ERROR(logger_, "Failed to start AvahiThreadedPoll"); + return TransportAdapter::FAIL; + } + + return TransportAdapter::OK; +} + +TransportAdapter::Error DnssdServiceBrowser::Scan() { + return TransportAdapter::NOT_SUPPORTED; +} + +void DnssdServiceBrowser::AddService(AvahiIfIndex interface, + AvahiProtocol protocol, const char* name, + const char* type, const char* domain) { + DnssdServiceRecord record; + record.interface = interface; + record.protocol = protocol; + record.domain_name = domain; + record.name = name; + record.type = type; + + pthread_mutex_lock(&mutex_); + if (service_records_.end() + == std::find(service_records_.begin(), service_records_.end(), record)) { + service_records_.push_back(record); + AvahiServiceResolver* avahi_service_resolver = avahi_service_resolver_new( + avahi_client_, interface, protocol, name, type, domain, + AVAHI_PROTO_INET, static_cast<AvahiLookupFlags>(0), + AvahiServiceResolverCallback, this); + } + pthread_mutex_unlock(&mutex_); +} + +void DnssdServiceBrowser::RemoveService(AvahiIfIndex interface, + AvahiProtocol protocol, + const char* name, const char* type, + const char* domain) { + DnssdServiceRecord record; + record.interface = interface; + record.protocol = protocol; + record.name = name; + record.type = type; + record.domain_name = domain; + + pthread_mutex_lock(&mutex_); + service_records_.erase( + std::remove(service_records_.begin(), service_records_.end(), record), + service_records_.end()); + pthread_mutex_unlock(&mutex_); +} + +DeviceVector DnssdServiceBrowser::PrepareDeviceVector() const { + std::map<uint32_t, TcpDevice*> devices; + for (ServiceRecords::const_iterator it = service_records_.begin(); + it != service_records_.end(); ++it) { + const DnssdServiceRecord& service_record = *it; + if (service_record.host_name.empty()) continue; + if (devices[service_record.addr] == 0) { + devices[service_record.addr] = new TcpDevice(service_record.addr, + service_record.host_name); + } + if (devices[service_record.addr] != 0) { + devices[service_record.addr]->AddDiscoveredApplication( + service_record.port); + } + } + DeviceVector device_vector; + device_vector.reserve(devices.size()); + for (std::map<uint32_t, TcpDevice*>::const_iterator it = devices.begin(); + it != devices.end(); ++it) { + device_vector.push_back(DeviceSptr(it->second)); + } + return device_vector; +} + +} // namespace +} // namespace + diff --git a/SDL_Core/src/components/transport_manager/src/tcp/tcp_client_listener.cc b/SDL_Core/src/components/transport_manager/src/tcp/tcp_client_listener.cc new file mode 100644 index 000000000..5180129ad --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/tcp/tcp_client_listener.cc @@ -0,0 +1,254 @@ +/** + * \file tcp_client_listener.cc + * \brief TcpClientListener class source file. + * + * Copyright (c) 2013, Ford Motor Company + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "transport_manager/tcp/tcp_client_listener.h" + +#include <memory.h> +#include <signal.h> +#include <errno.h> +#include <arpa/inet.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/socket.h> +#ifdef __linux__ +# include <linux/tcp.h> +#else // __linux__ +# include <sys/time.h> +# include <netinet/in.h> +# include <netinet/tcp.h> +# include <netinet/tcp_var.h> +#endif // __linux__ + +#include "transport_manager/transport_adapter/transport_adapter_controller.h" +#include "transport_manager/tcp/tcp_device.h" +#include "transport_manager/tcp/tcp_socket_connection.h" + +namespace transport_manager { +namespace transport_adapter { + +TcpClientListener::TcpClientListener(TransportAdapterController* controller, + const uint16_t port) + : port_(port), + controller_(controller), + thread_(), + socket_(-1), + thread_started_(false), + shutdown_requested_(false), + thread_stop_requested_(false) { +} + +void* tcpClientListenerThread(void* data) { + TcpClientListener* tcpClientListener = static_cast<TcpClientListener*>(data); + assert(tcpClientListener != 0); + tcpClientListener->Thread(); + return 0; +} + +TransportAdapter::Error TcpClientListener::Init() { + return TransportAdapter::OK; +} + +void TcpClientListener::Terminate() { + shutdown_requested_ = true; + if (TransportAdapter::OK != StopListening()) { + LOG4CXX_ERROR(logger_, "Cannot stop listening TCP"); + } +} + +bool TcpClientListener::IsInitialised() const { + return true; +} + +TcpClientListener::~TcpClientListener() { + LOG4CXX_INFO(logger_, "destructor"); +} + +void TcpClientListener::Thread() { + LOG4CXX_INFO(logger_, "Tcp client listener thread started"); + + while (false == thread_stop_requested_) { + sockaddr_in client_address; + socklen_t client_address_size = sizeof(client_address); + const int connection_fd = accept(socket_, + (struct sockaddr*) &client_address, + &client_address_size); + if (thread_stop_requested_) break; + + if (connection_fd < 0) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "accept() failed"); + continue; + } + + if (AF_INET != client_address.sin_family) { + LOG4CXX_ERROR(logger_, "Address of connected client is invalid"); + continue; + } + + char device_name[32]; + strncpy(device_name, inet_ntoa(client_address.sin_addr), + sizeof(device_name) / sizeof(device_name[0])); + LOG4CXX_INFO(logger_, "Connected client " << device_name); + + int yes = 1; + int keepidle = 3; // 3 seconds to disconnection detecting + int keepcnt = 5; + int keepintvl = 1; + int user_timeout = 7000; // milliseconds +#ifdef __linux__ + setsockopt(connection_fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)); + setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPIDLE, &keepidle, sizeof(keepidle)); + setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPCNT, &keepcnt, sizeof(keepcnt)); + setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPINTVL, &keepintvl, sizeof(keepintvl)); + setsockopt(connection_fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &user_timeout, sizeof(user_timeout)); +#elif __QNX__ // __linux__ + // TODO (KKolodiy): Out of order! + const int kMidLength = 4; + int mib[kMidLength]; + timeval tval; + + mib[0] = CTL_NET; + mib[1] = AF_INET; + mib[2] = IPPROTO_TCP; + mib[3] = TCPCTL_KEEPIDLE; + sysctl(mib, kMidLength, NULL, NULL, &keepidle, sizeof(keepidle)); + + mib[0] = CTL_NET; + mib[1] = AF_INET; + mib[2] = IPPROTO_TCP; + mib[3] = TCPCTL_KEEPCNT; + sysctl(mib, kMidLength, NULL, NULL, &keepcnt, sizeof(keepcnt)); + + mib[0] = CTL_NET; + mib[1] = AF_INET; + mib[2] = IPPROTO_TCP; + mib[3] = TCPCTL_KEEPINTVL; + sysctl(mib, kMidLength, NULL, NULL, &keepintvl, sizeof(keepintvl)); + + memset(&tval, sizeof(tval), 0); + tval.tv_sec = keepidle; + setsockopt(connection_fd, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(yes)); + setsockopt(connection_fd, IPPROTO_TCP, TCP_KEEPALIVE, &tval, sizeof(tval)); +#endif // __QNX__ + + TcpDevice* tcp_device = new TcpDevice(client_address.sin_addr.s_addr, device_name); + DeviceSptr device = controller_->AddDevice(tcp_device); + tcp_device = static_cast<TcpDevice*>(device.get()); + const ApplicationHandle app_handle = tcp_device->AddIncomingApplication( + connection_fd); + + TcpSocketConnection* connection( + new TcpSocketConnection(device->unique_device_id(), app_handle, + controller_)); + connection->set_socket(connection_fd); + const TransportAdapter::Error error = connection->Start(); + if (error != TransportAdapter::OK) { + delete connection; + } + } + + LOG4CXX_INFO(logger_, "Tcp client listener thread finished"); +} + +TransportAdapter::Error TcpClientListener::StartListening() { + if (thread_started_) + return TransportAdapter::BAD_STATE; + + socket_ = socket(AF_INET, SOCK_STREAM, 0); + + if (-1 == socket_) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "Failed to create socket"); + return TransportAdapter::FAIL; + } + + sockaddr_in server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port_); + server_address.sin_addr.s_addr = INADDR_ANY; + + int optval = 1; + setsockopt(socket_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); + + if (0 != bind(socket_, (sockaddr*) &server_address, sizeof(server_address))) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "bind() failed"); + return TransportAdapter::FAIL; + } + + if (0 != listen(socket_, 128)) { + LOG4CXX_ERROR_WITH_ERRNO(logger_, "listen() failed"); + return TransportAdapter::FAIL; + } + + const int thread_start_error = pthread_create(&thread_, 0, + &tcpClientListenerThread, this); + if (0 == thread_start_error) { + thread_started_ = true; + LOG4CXX_INFO(logger_, "Tcp client listener thread started"); + } else { + LOG4CXX_ERROR( + logger_, + "Tcp client listener thread start failed, error code " + << thread_start_error); + return TransportAdapter::FAIL; + } + return TransportAdapter::OK; +} + +TransportAdapter::Error TcpClientListener::StopListening() { + if (!thread_started_) + return TransportAdapter::BAD_STATE; + + thread_stop_requested_ = true; + int byebyesocket = socket(AF_INET, SOCK_STREAM, 0); + sockaddr_in server_address; + memset(&server_address, 0, sizeof(server_address)); + server_address.sin_family = AF_INET; + server_address.sin_port = htons(port_); + server_address.sin_addr.s_addr = INADDR_ANY; + connect(byebyesocket, (sockaddr*)&server_address, sizeof(server_address)); + shutdown(byebyesocket, SHUT_RDWR); + close(byebyesocket); + pthread_join(thread_, 0); + LOG4CXX_INFO(logger_, "Tcp client listener thread terminated"); + close(socket_); + socket_ = -1; + thread_started_ = false; + thread_stop_requested_ = false; + return TransportAdapter::OK; +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/SDL_Core/src/components/transport_manager/src/tcp/tcp_connection_factory.cc b/SDL_Core/src/components/transport_manager/src/tcp/tcp_connection_factory.cc new file mode 100644 index 000000000..cb6f4c5b6 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/tcp/tcp_connection_factory.cc @@ -0,0 +1,72 @@ +/** + * \file tcp_connection_factory.cc + * \brief TcpConnectionFactory class source file. + * + * Copyright (c) 2013, Ford Motor Company + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "transport_manager/tcp/tcp_connection_factory.h" +#include "transport_manager/tcp/tcp_socket_connection.h" + +namespace transport_manager { +namespace transport_adapter { + +TcpConnectionFactory::TcpConnectionFactory(TransportAdapterController* controller) + : controller_(controller) { +} + +TransportAdapter::Error TcpConnectionFactory::Init() { + return TransportAdapter::OK; +} + +TransportAdapter::Error TcpConnectionFactory::CreateConnection( + const DeviceUID& device_uid, const ApplicationHandle& app_handle) { + TcpServerOiginatedSocketConnection* connection( + new TcpServerOiginatedSocketConnection(device_uid, app_handle, + controller_)); + TransportAdapter::Error error = connection->Start(); + if (error != TransportAdapter::OK) + delete connection; + return error; +} + +void TcpConnectionFactory::Terminate() { +} + +bool TcpConnectionFactory::IsInitialised() const { + return true; +} + +TcpConnectionFactory::~TcpConnectionFactory() { +} + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/SDL_Core/src/components/transport_manager/src/tcp/tcp_device.cc b/SDL_Core/src/components/transport_manager/src/tcp/tcp_device.cc new file mode 100644 index 000000000..d8d714f44 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/tcp/tcp_device.cc @@ -0,0 +1,113 @@ +/** + * \file tcp_device.cc + * \brief TcpDevice class source file. + * + * Copyright (c) 2013, Ford Motor Company + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "transport_manager/tcp/tcp_device.h" + +namespace transport_manager { +namespace transport_adapter { + +TcpDevice::TcpDevice(const in_addr_t& in_addr, const std::string& name) + : Device(name, name), + in_addr_(in_addr), + last_handle_(0) { + pthread_mutex_init(&applications_mutex_, 0); +} + +bool TcpDevice::IsSameAs(const Device* other) const { + const TcpDevice* other_tcp_device = static_cast<const TcpDevice*>(other); + return other_tcp_device->in_addr_ == in_addr_; +} + +ApplicationList TcpDevice::GetApplicationList() const { + pthread_mutex_lock(&applications_mutex_); + ApplicationList app_list; + for (std::map<ApplicationHandle, Application>::const_iterator it = + applications_.begin(); it != applications_.end(); ++it) { + app_list.push_back(it->first); + } + pthread_mutex_unlock(&applications_mutex_); + return app_list; +} + +ApplicationHandle TcpDevice::AddIncomingApplication(int socket_fd) { + Application app; + app.incoming = true; + app.socket = socket_fd; + pthread_mutex_lock(&applications_mutex_); + const ApplicationHandle app_handle = ++last_handle_; + applications_[app_handle] = app; + pthread_mutex_unlock(&applications_mutex_); + return app_handle; +} + +ApplicationHandle TcpDevice::AddDiscoveredApplication(int port) { + Application app; + app.incoming = false; + app.port = port; + pthread_mutex_lock(&applications_mutex_); + const ApplicationHandle app_handle = ++last_handle_; + applications_[app_handle] = app; + pthread_mutex_unlock(&applications_mutex_); + return app_handle; +} + + +void TcpDevice::RemoveApplication(const ApplicationHandle app_handle) { + pthread_mutex_lock(&applications_mutex_); + applications_.erase(app_handle); + pthread_mutex_unlock(&applications_mutex_); +} + +TcpDevice::~TcpDevice() { + pthread_mutex_destroy(&applications_mutex_); +} + +int TcpDevice::GetApplicationSocket(const ApplicationHandle app_handle) const { + std::map<ApplicationHandle, Application>::const_iterator it = applications_.find(app_handle); + if(applications_.end() == it) return -1; + if(! it->second.incoming) return -1; + return it->second.socket; +} + +int TcpDevice::GetApplicationPort(const ApplicationHandle app_handle) const { + std::map<ApplicationHandle, Application>::const_iterator it = applications_.find(app_handle); + if(applications_.end() == it) return -1; + if(it->second.incoming) return -1; + return it->second.port; +} + + +} // namespace transport_adapter +} // namespace transport_manager diff --git a/SDL_Core/src/components/transport_manager/src/tcp/tcp_socket_connection.cc b/SDL_Core/src/components/transport_manager/src/tcp/tcp_socket_connection.cc new file mode 100644 index 000000000..321abb988 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/tcp/tcp_socket_connection.cc @@ -0,0 +1,117 @@ +/** + * \file tcp_socket_connection.cc + * \brief TcpSocketConnection class source file. + * + * Copyright (c) 2013, Ford Motor Company + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "transport_manager/transport_adapter/transport_adapter_controller.h" +#include "transport_manager/tcp/tcp_socket_connection.h" +#include "transport_manager/tcp/tcp_device.h" + +#include <memory.h> +#include <signal.h> +#include <errno.h> + +namespace transport_manager { +namespace transport_adapter { + +TcpSocketConnection::TcpSocketConnection(const DeviceUID& device_uid, + const ApplicationHandle& app_handle, + TransportAdapterController* controller) + : ThreadedSocketConnection(device_uid, app_handle, controller) { +} + +TcpSocketConnection::~TcpSocketConnection() { +} + +bool TcpSocketConnection::Establish(ConnectError** error) { + return true; +} + +TcpServerOiginatedSocketConnection::TcpServerOiginatedSocketConnection( + const DeviceUID& device_uid, const ApplicationHandle& app_handle, + TransportAdapterController* controller) + : ThreadedSocketConnection(device_uid, app_handle, controller) { +} + +TcpServerOiginatedSocketConnection::~TcpServerOiginatedSocketConnection() { +} + +bool TcpServerOiginatedSocketConnection::Establish(ConnectError** error) { + DeviceSptr device = controller()->FindDevice(device_handle()); + if (!device.valid()) { + LOG4CXX_ERROR( + logger_, + "Device " << device_handle() << " not found"); + *error = new ConnectError(); + return false; + } + TcpDevice* tcp_device = static_cast<TcpDevice*>(device.get()); + + int port; + if (-1 == (port = tcp_device->GetApplicationPort(application_handle()))) { + LOG4CXX_ERROR( + logger_, + "Application port for " << application_handle() << " not found"); + *error = new ConnectError(); + return false; + } + + const int socket = ::socket(AF_INET, SOCK_STREAM, 0); + if (socket < 0) { + LOG4CXX_ERROR(logger_, "Failed to create socket"); + *error = new ConnectError(); + return false; + } + + struct sockaddr_in addr; + memset((char*) &addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = tcp_device->in_addr(); + addr.sin_port = htons(port); + + LOG4CXX_INFO(logger_, "Connecting " << inet_ntoa(addr.sin_addr) << ":" + << port); + if (::connect(socket, (struct sockaddr*) &addr, sizeof(addr)) < 0) { + LOG4CXX_ERROR(logger_, "Failed to connect for application " + << application_handle() << ", error " << errno); + *error = new ConnectError(); + return false; + } + + set_socket(socket); + return true; +} + +} // namespace transport_adapter +} // namespace transport_manager + diff --git a/SDL_Core/src/components/transport_manager/src/tcp/tcp_transport_adapter.cc b/SDL_Core/src/components/transport_manager/src/tcp/tcp_transport_adapter.cc new file mode 100644 index 000000000..d5fde5d26 --- /dev/null +++ b/SDL_Core/src/components/transport_manager/src/tcp/tcp_transport_adapter.cc @@ -0,0 +1,146 @@ +/** + * \file tcp_transport_adapter.cc + * \brief TcpTransportAdapter class source file. + * + * Copyright (c) 2013, Ford Motor Company + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Ford Motor Company nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <memory.h> +#include <signal.h> +#include <errno.h> +#include <sstream> +#include <cstdlib> + +#include "resumption/last_state.h" + +#include "transport_manager/tcp/tcp_transport_adapter.h" +#include "transport_manager/tcp/tcp_client_listener.h" +#include "transport_manager/tcp/tcp_connection_factory.h" +#include "transport_manager/tcp/tcp_device.h" + +#ifdef AVAHI_SUPPORT +#include "transport_manager/tcp/dnssd_service_browser.h" +#endif + +namespace transport_manager { +namespace transport_adapter { + +TcpTransportAdapter::TcpTransportAdapter() + : TransportAdapterImpl( +#ifdef AVAHI_SUPPORT + new DnssdServiceBrowser(this) +#else + NULL +#endif + , new TcpConnectionFactory(this) + , new TcpClientListener(this, default_port)) { +} + +TcpTransportAdapter::~TcpTransportAdapter() { +} + +DeviceType TcpTransportAdapter::GetDeviceType() const { + return "sdl-tcp"; +} + +void TcpTransportAdapter::Store() const { + LOG4CXX_TRACE_ENTER(logger_); + resumption::LastState::Dictionary tcp_adapter_dictionary; + DeviceList device_ids = GetDeviceList(); + for (DeviceList::const_iterator i = device_ids.begin(); i != device_ids.end(); ++i) { + DeviceUID device_id = *i; + DeviceSptr device = FindDevice(device_id); + if (!device) { // device could have been disconnected + continue; + } + utils::SharedPtr<TcpDevice> tcp_device = + DeviceSptr::static_pointer_cast<TcpDevice>(device); + resumption::LastState::Dictionary device_dictionary; + struct in_addr address; + address.s_addr = tcp_device->in_addr(); + device_dictionary.AddItem("address", std::string(inet_ntoa(address))); + resumption::LastState::Dictionary applications_dictionary; + ApplicationList app_ids = tcp_device->GetApplicationList(); + for (ApplicationList::const_iterator j = app_ids.begin(); j != app_ids.end(); ++j) { + ApplicationHandle app_handle = *j; + const int port = tcp_device->GetApplicationPort(app_handle); + std::string port_string; + std::stringstream stream(port_string); + stream << port; + if (port != -1) { // don't want to store incoming applications + resumption::LastState::Dictionary application_dictionary; + application_dictionary.AddItem("port", port_string); + applications_dictionary.AddSubitem(port_string, application_dictionary); + } + } + device_dictionary.AddSubitem("applications", applications_dictionary); + tcp_adapter_dictionary.AddSubitem(tcp_device->name(), device_dictionary); + } + resumption::LastState::instance()->dictionary.AddSubitem( + "TcpAdapter", tcp_adapter_dictionary + ); + LOG4CXX_TRACE_EXIT(logger_); +} + +bool TcpTransportAdapter::Restore() { + LOG4CXX_TRACE_ENTER(logger_); + bool errors_occured = false; + resumption::LastState::Dictionary tcp_adapter_dictionary = + resumption::LastState::instance()->dictionary.SubitemAt("TcpAdapter"); + for (resumption::LastState::Dictionary::const_iterator i = + tcp_adapter_dictionary.begin(); i != tcp_adapter_dictionary.end(); ++i) { + std::string name = i->first; + resumption::LastState::Dictionary device_dictionary = i->second; + std::string address_record = device_dictionary.ItemAt("address"); + in_addr_t address = inet_addr(address_record.c_str()); + TcpDevice* tcp_device = new TcpDevice(address, name); + DeviceSptr device(tcp_device); + AddDevice(device); + resumption::LastState::Dictionary applications_dictionary = + device_dictionary.SubitemAt("applications"); + for (resumption::LastState::Dictionary::const_iterator j = + applications_dictionary.begin(); j != applications_dictionary.end(); ++j) { + resumption::LastState::Dictionary application_dictionary = j->second; + std::string port_record = application_dictionary.ItemAt("port"); + int port = atoi(port_record.c_str()); + ApplicationHandle app_handle = tcp_device->AddDiscoveredApplication(port); + if (Error::OK != Connect(device->unique_device_id(), app_handle)) { + errors_occured = true; + } + } + } + LOG4CXX_TRACE_EXIT(logger_); + return !errors_occured; +} + +} // namespace transport_adapter +} // namespace transport_manager + |