| /* |
| * Copyright (C) 2016 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 "socket_connection.h" |
| |
| #include <gapic/log.h> |
| #include <gapic/target.h> |
| |
| #include <stdint.h> |
| #include <string.h> |
| |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| |
| #define _WSPIAPI_EMIT_LEGACY |
| #include <winsock2.h> |
| #include <ws2tcpip.h> |
| |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| |
| #include <errno.h> |
| #include <netdb.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <sys/types.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| |
| namespace gapic { |
| namespace { |
| |
| static const int ACCEPT_ERROR = -1; |
| static const int ACCEPT_TIMEOUT = -2; |
| |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| int gWinsockUsageCount = 0; |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| |
| int error() { |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| return ::WSAGetLastError(); |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| return errno; |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| void close(int fd) { |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| ::closesocket(fd); |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| ::close(fd); |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| size_t recv(int sockfd, void *buf, size_t len, int flags) { |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| return ::recv(sockfd, static_cast<char*>(buf), static_cast<int>(len), flags); |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| return ::recv(sockfd, buf, len, flags); |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| size_t send(int sockfd, const void *buf, size_t len, int flags) { |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| return ::send(sockfd, static_cast<const char*>(buf), static_cast<int>(len), flags); |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| return ::send(sockfd, buf, len, flags); |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| int accept(int sockfd, int timeoutMs) { |
| if (timeoutMs != Connection::NO_TIMEOUT) { |
| fd_set set; |
| FD_ZERO(&set); |
| FD_SET(sockfd, &set); |
| |
| timeval timeout; |
| timeout.tv_sec = timeoutMs / 1000; |
| timeout.tv_usec = (timeoutMs - timeout.tv_sec * 1000) * 1000; |
| |
| switch (select(sockfd + 1, &set, NULL, NULL, &timeout)) { |
| case -1: |
| GAPID_WARNING("Error from select(): %s", strerror(gapic::error())); |
| return ACCEPT_ERROR; |
| case 0: |
| return ACCEPT_TIMEOUT; |
| default: |
| break; |
| } |
| } |
| |
| return static_cast<int>(::accept(sockfd, nullptr, nullptr)); |
| } |
| |
| 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 == GAPID_OS_WINDOWS |
| return ::setsockopt(sockfd, level, optname, static_cast<const char*>(optval), |
| static_cast<int>(optlen)); |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| return ::setsockopt(sockfd, level, optname, optval, optlen); |
| #endif // TARGET_OS == GAPID_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 == GAPID_OS_WINDOWS |
| return ::bind(sockfd, addr, static_cast<int>(addrlen)); |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| return ::bind(sockfd, addr, addrlen); |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| int getsockname(int sockfd, struct sockaddr *addr, socklen_t* addrlen) { |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| return ::getsockname(sockfd, addr, static_cast<int*>(addrlen)); |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| return ::getsockname(sockfd, addr, addrlen); |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| } // anonymous namespace |
| |
| SocketConnection::SocketConnection(int socket) : mSocket(socket) { |
| } |
| |
| SocketConnection::~SocketConnection() { |
| gapic::close(mSocket); |
| } |
| |
| size_t SocketConnection::send(const void* data, size_t size) { |
| return gapic::send(mSocket, data, size, 0); |
| } |
| |
| size_t SocketConnection::recv(void* data, size_t size) { |
| return gapic::recv(mSocket, data, size, MSG_WAITALL); |
| } |
| |
| const char* SocketConnection::error() { |
| return strerror(gapic::error()); |
| } |
| |
| std::unique_ptr<Connection> SocketConnection::accept(int timeoutMs /* = NO_TIMEOUT */) { |
| int clientSocket = gapic::accept(mSocket, timeoutMs); |
| switch (clientSocket) { |
| case ACCEPT_ERROR: |
| GAPID_WARNING("Failed to accept incoming connection: %s", strerror(gapic::error())); |
| return nullptr; |
| case ACCEPT_TIMEOUT: |
| GAPID_INFO("Timeout accepting incoming connection"); |
| return nullptr; |
| default: |
| return std::unique_ptr<Connection>(new SocketConnection(clientSocket)); |
| } |
| } |
| |
| std::unique_ptr<Connection> SocketConnection::createSocket( |
| 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{}; |
| hints.ai_family = AF_INET; |
| hints.ai_socktype = SOCK_STREAM; |
| |
| const int getaddrinfoRes = gapic::getaddrinfo(hostname, port, &hints, &addr); |
| if (0 != getaddrinfoRes) { |
| GAPID_WARNING("getaddrinfo() failed: %d - %s.", getaddrinfoRes, strerror(gapic::error())); |
| return nullptr; |
| } |
| auto addrDeleter = [](struct addrinfo* ptr) { gapic::freeaddrinfo(ptr); }; // deferred. |
| std::unique_ptr<struct addrinfo, decltype(addrDeleter)> addrScopeGuard(addr, addrDeleter); |
| |
| const int sock = gapic::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); |
| if (-1 == sock) { |
| GAPID_WARNING("socket() failed: %s.", strerror(gapic::error())); |
| return nullptr; |
| } |
| auto socketCloser = [](const int* ptr) { gapic::close(*ptr); }; // deferred. |
| std::unique_ptr<const int, decltype(socketCloser)> sockScopeGuard(&sock, socketCloser); |
| |
| const int one = 1; |
| if (-1 == gapic::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&one, sizeof(int))) { |
| GAPID_WARNING("setsockopt() failed: %s", strerror(gapic::error())); |
| return nullptr; |
| } |
| |
| if (-1 == gapic::bind(sock, addr->ai_addr, addr->ai_addrlen)) { |
| GAPID_WARNING("bind() failed: %s.", strerror(gapic::error())); |
| return nullptr; |
| } |
| struct sockaddr_in sin; |
| socklen_t len = sizeof(sin); |
| if (-1 == gapic::getsockname(sock, (struct sockaddr *)&sin, &len)) { |
| GAPID_WARNING("getsockname() failed: %s.", strerror(gapic::error())); |
| return nullptr; |
| } |
| |
| if (-1 == gapic::listen(sock, 10)) { |
| GAPID_WARNING("listen() failed: %s.", strerror(gapic::error())); |
| return nullptr; |
| } |
| |
| // NOTE This approach does not work on android, because you need to |
| // forward stdout through adb, which is not done automatically for APK |
| // applications. We think the right approach on Android is to use a |
| // named port and side-step the whole port picking problem. |
| // See b/23519541 |
| |
| // The following message is parsed by launchers to detect the selected port. DO NOT CHANGE! |
| printf("Bound on port '%d'\n", ntohs(sin.sin_port)); |
| fflush(stdout); // Force the message for piped readers |
| |
| sockScopeGuard.release(); |
| return std::unique_ptr<Connection>(new SocketConnection(sock)); |
| } |
| |
| std::unique_ptr<Connection> SocketConnection::createPipe(const char* pipename, bool abstract) { |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| // AF_UNIX is not supported on Windows. |
| return nullptr; |
| #else // TARGET_OS == GAPID_OS_WINDOWS |
| const int sock = gapic::socket(AF_UNIX, SOCK_STREAM, 0); |
| if (-1 == sock) { |
| GAPID_WARNING("socket() failed: %s.", strerror(gapic::error())); |
| return nullptr; |
| } |
| auto socketCloser = [](const int* ptr) { gapic::close(*ptr); }; // deferred. |
| std::unique_ptr<const int, decltype(socketCloser)> sockScopeGuard(&sock, socketCloser); |
| |
| // In order to create a socket in the abstract namespace (no filesystem link), sun_path must |
| // start with a null byte followed by the abstract socket name. Abstract sockets/pipes are a |
| // non-portable Linux extension available on Android, described in Linux's man unix(7). |
| |
| #if (TARGET_OS != GAPID_OS_LINUX) && (TARGET_OS != GAPID_OS_ANDROID) |
| if (abstract) { |
| GAPID_WARNING("Abstract pipe '%s' creation unsupported for this platform. " |
| "Falling back to non-abstract.", pipename); |
| abstract = false; |
| } |
| #endif |
| |
| struct sockaddr_un pipe{}; |
| pipe.sun_family = AF_UNIX; |
| strncpy(pipe.sun_path + (abstract ? 1 : 0), pipename, sizeof(pipe.sun_path)-2); |
| const size_t pipelen = sizeof(pipe.sun_family) + strlen(pipename) + (abstract ? 1 : 0); |
| |
| if (-1 == gapic::bind(sock, (struct sockaddr*)&pipe, pipelen)) { |
| GAPID_WARNING("bind() failed: %s.", strerror(gapic::error())); |
| return nullptr; |
| } |
| |
| if (-1 == gapic::listen(sock, 10)) { |
| GAPID_WARNING("listen() failed: %s.", strerror(gapic::error())); |
| return nullptr; |
| } |
| |
| sockScopeGuard.release(); |
| return std::unique_ptr<Connection>(new SocketConnection(sock)); |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| SocketConnection::NetworkInitializer::NetworkInitializer() { |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| if (gWinsockUsageCount++ == 0) { |
| WSADATA wsaData; |
| int wsaInitRes = ::WSAStartup(MAKEWORD(2, 2), &wsaData); |
| if (wsaInitRes != 0) { |
| GAPID_FATAL("WSAStartup failed with error code: %d", wsaInitRes); |
| } |
| } |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| SocketConnection::NetworkInitializer::~NetworkInitializer() { |
| #if TARGET_OS == GAPID_OS_WINDOWS |
| if (--gWinsockUsageCount == 0) { |
| ::WSACleanup(); |
| } |
| #endif // TARGET_OS == GAPID_OS_WINDOWS |
| } |
| |
| } // namespace gapic |