| /* |
| * Copyright 2014, The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "Log.h" |
| #include "SocketConnection.h" |
| #include "Target.h" |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| |
| #define _WSPIAPI_EMIT_LEGACY |
| #include <winsock2.h> |
| #include <ws2tcpip.h> |
| |
| #else // TARGET_OS == CAZE_OS_WINDOWS |
| |
| #include <errno.h> |
| #include <netdb.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| |
| namespace android { |
| namespace caze { |
| namespace { |
| |
| void close(int fd) { |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| ::closesocket(fd); |
| #else // TARGET_OS == CAZE_OS_WINDOWS |
| ::close(fd); |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| } |
| |
| size_t recv(int sockfd, void *buf, size_t len, int flags) { |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| return ::recv(sockfd, static_cast<char*>(buf), static_cast<int>(len), flags); |
| #else // TARGET_OS == CAZE_OS_WINDOWS |
| return ::recv(sockfd, buf, len, flags); |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| } |
| |
| size_t send(int sockfd, const void *buf, size_t len, int flags) { |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| return ::send(sockfd, static_cast<const char*>(buf), static_cast<int>(len), flags); |
| #else // TARGET_OS == CAZE_OS_WINDOWS |
| return ::send(sockfd, buf, len, flags); |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| } |
| |
| int accept(int sockfd, struct sockaddr *addr, size_t *addrlen) { |
| if (addrlen == nullptr) { |
| return static_cast<int>(::accept(sockfd, addr, nullptr)); |
| } |
| |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| int addrlenTmp = static_cast<int>(*addrlen); |
| #else // TARGET_OS == CAZE_OS_WINDOWS |
| socklen_t addrlenTmp = *addrlen; |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| |
| int ret = static_cast<int>(::accept(sockfd, addr, &addrlenTmp)); |
| *addrlen = addrlenTmp; |
| return ret; |
| } |
| |
| int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, |
| struct addrinfo **res) { |
| return ::getaddrinfo(node, service, hints, res); |
| } |
| |
| void freeaddrinfo(struct addrinfo *res) { |
| ::freeaddrinfo(res); |
| } |
| |
| int setsockopt(int sockfd, int level, int optname, const void *optval, size_t optlen) { |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| return ::setsockopt(sockfd, level, optname, static_cast<const char*>(optval), |
| static_cast<int>(optlen)); |
| #else // TARGET_OS == CAZE_OS_WINDOWS |
| return ::setsockopt(sockfd, level, optname, optval, optlen); |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| } |
| |
| int socket(int domain, int type, int protocol) { |
| return static_cast<int>(::socket(domain, type, protocol)); |
| } |
| |
| int listen(int sockfd, int backlog) { |
| return ::listen(sockfd, backlog); |
| } |
| |
| int bind(int sockfd, const struct sockaddr *addr, size_t addrlen) { |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| return ::bind(sockfd, addr, static_cast<int>(addrlen)); |
| #else // TARGET_OS == CAZE_OS_WINDOWS |
| return ::bind(sockfd, addr, addrlen); |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| } |
| |
| int error() { |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| return ::WSAGetLastError(); |
| #else // TARGET_OS == CAZE_OS_WINDOWS |
| return errno; |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| } |
| |
| } // end of anonymous namespace |
| |
| SocketConnection::SocketConnection(int socket) : mSocket(socket) { |
| } |
| |
| SocketConnection::~SocketConnection() { |
| caze::close(mSocket); |
| } |
| |
| size_t SocketConnection::send(const void* data, size_t size) { |
| return caze::send(mSocket, data, size, 0); |
| } |
| |
| size_t SocketConnection::recv(void* data, size_t size) { |
| return caze::recv(mSocket, data, size, MSG_WAITALL); |
| } |
| |
| const char* SocketConnection::error() { |
| return strerror(caze::error()); |
| } |
| |
| std::unique_ptr<Connection> SocketConnection::accept() { |
| int clientSocket = caze::accept(mSocket, nullptr, nullptr); |
| if (-1 == clientSocket) { |
| CAZE_WARNING("Failed to accept incoming connection: %s\n", strerror(caze::error())); |
| return nullptr; |
| } |
| return std::unique_ptr<Connection>(new SocketConnection(clientSocket)); |
| } |
| |
| std::unique_ptr<Connection> SocketConnection::create(const char* hostname, const char* port) { |
| // Network initializer to ensure that the network driver is initialized during the lifetime of |
| // the create function. If the connection created successfully then the new connection will |
| // hold a reference to a networkInitializer struct to ensure that the network is initialized |
| NetworkInitializer networkInitializer; |
| |
| struct addrinfo* addr; |
| struct addrinfo hints; |
| |
| memset(&hints, 0, sizeof(addrinfo)); |
| hints.ai_family = AF_INET; |
| hints.ai_socktype = SOCK_STREAM; |
| |
| const int getaddrinfoRes = caze::getaddrinfo(hostname, port, &hints, &addr); |
| if (0 != getaddrinfoRes) { |
| CAZE_WARNING("getaddrinfo() failed: %d - %s.\n", getaddrinfoRes, strerror(caze::error())); |
| return nullptr; |
| } |
| auto addrDeleter = [](struct addrinfo* ptr) { caze::freeaddrinfo(ptr); }; // deferred. |
| std::unique_ptr<struct addrinfo, decltype(addrDeleter)> addrScopeGuard(addr, addrDeleter); |
| |
| const int sock = caze::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); |
| if (-1 == sock) { |
| CAZE_WARNING("socket() failed: %s.\n", strerror(caze::error())); |
| return nullptr; |
| } |
| auto socketCloser = [](const int* ptr) { caze::close(*ptr); }; // deferred. |
| std::unique_ptr<const int, decltype(socketCloser)> sockScopeGuard(&sock, socketCloser); |
| |
| const int one = 1; |
| if (-1 == caze::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(int))) { |
| CAZE_WARNING("setsockopt() failed: %s\n", strerror(caze::error())); |
| return nullptr; |
| } |
| |
| if (-1 == caze::bind(sock, addr->ai_addr, addr->ai_addrlen)) { |
| CAZE_WARNING("bind() failed: %s.\n", strerror(caze::error())); |
| return nullptr; |
| } |
| |
| if (-1 == caze::listen(sock, 10)) { |
| CAZE_WARNING("listen() failed: %s.\n", strerror(caze::error())); |
| return nullptr; |
| } |
| |
| sockScopeGuard.release(); |
| return std::unique_ptr<Connection>(new SocketConnection(sock)); |
| } |
| |
| SocketConnection::NetworkInitializer::NetworkInitializer() { |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| WSADATA wsaData; |
| int wsaInitRes = ::WSAStartup(MAKEWORD(2, 2), &wsaData); |
| if (wsaInitRes != 0) { |
| CAZE_FATAL("WSAStartup failed with error code: %d\n", wsaInitRes); |
| } |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| } |
| |
| SocketConnection::NetworkInitializer::~NetworkInitializer() { |
| #if TARGET_OS == CAZE_OS_WINDOWS |
| ::WSACleanup(); |
| #endif // TARGET_OS == CAZE_OS_WINDOWS |
| } |
| |
| } // end of namespace caze |
| } // end of namespace android |