// Copyright (C) 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 "emugl/common/sockets.h" #include #ifdef _WIN32 #include #include #else #include #include #include #include #include #endif #include #include #include #include namespace emugl { namespace { static void socketSetDontLinger(int s) { #ifdef _WIN32 // TODO: Verify default behavior on WINDOWS #else // Ungraceful shutdown, no reason to linger at all struct linger so_linger; so_linger.l_onoff = 1; so_linger.l_linger = 0; if(setsockopt(s, SOL_SOCKET, SO_LINGER, &so_linger, sizeof so_linger) < 0) perror("Setting socket option SO_LINGER={on, 0} failed"); #endif } static void socketSetReuseAddress(int s) { #ifdef _WIN32 // The default behaviour on Windows is equivalent to SO_REUSEADDR // so we don't need to set this option. Moreover, one should never // set this option with WinSock because it's badly implemented and // generates a huge security issue. See: // http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx #else int val = 1; if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0) perror("Setting socket option SO_REUSEADDR failed"); #endif } // Helper union to store a socket address. struct SockAddr { socklen_t len; union { sockaddr generic; sockaddr_in inet; #ifndef _WIN32 sockaddr_un local; #endif }; int getFamily() const { return generic.sa_family; } void initEmpty() { ::memset(this, 0, sizeof(*this)); this->len = static_cast(sizeof(*this)); } int initFromInet(uint32_t ip_address, int port) { if (port < 0 || port >= 65536) return -EINVAL; ::memset(this, 0, sizeof(*this)); this->inet.sin_family = AF_INET; this->inet.sin_port = htons(port); this->inet.sin_addr.s_addr = htonl(ip_address); this->len = sizeof(this->inet); return 0; } int initFromLocalhost(int port) { return initFromInet(0x7f000001, port); } #ifndef _WIN32 // Initialize the SockAddr from a Unix path. Returns 0 on success, // or -errno code on failure. int initFromUnixPath(const char* path) { if (!path || !path[0]) return -EINVAL; size_t pathLen = ::strlen(path); if (pathLen >= sizeof(local.sun_path)) return -E2BIG; ::memset(this, 0, sizeof(*this)); this->local.sun_family = AF_LOCAL; ::memcpy(this->local.sun_path, path, pathLen + 1U); this->len = pathLen + offsetof(sockaddr_un, sun_path); return 0; } #endif }; int socketBindInternal(const SockAddr* addr, int socketType) { int s = ::socket(addr->getFamily(), socketType, 0); if (s < 0) { perror("Could not create socket to bind"); return -errno; } socketSetDontLinger(s); socketSetReuseAddress(s); // Bind to the socket. if (::bind(s, &addr->generic, addr->len) < 0 || ::listen(s, 5) < 0) { int ret = -errno; perror("Could not bind or listen to socket"); ::close(s); return ret; } return s; } int socketConnectInternal(const SockAddr* addr, int socketType) { int s = ::socket(addr->getFamily(), socketType, 0); if (s < 0) { perror("Could not create socket to connect"); return -errno; } socketSetDontLinger(s); socketSetReuseAddress(s); int ret; do { ret = ::connect(s, &addr->generic, addr->len); } while (ret < 0 && errno == EINTR); if (ret < 0) { ret = -errno; ::close(s); return ret; } return s; } } // namespace void socketTcpDisableNagle(int s) { // disable Nagle algorithm to improve bandwidth of small // packets which are quite common in our implementation. #ifdef _WIN32 DWORD flag; #else int flag; #endif flag = 1; setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (const char*)&flag, sizeof(flag)); } int socketGetPort(int s) { SockAddr addr; addr.initEmpty(); if (getsockname(s, &addr.generic, &addr.len) < 0) { return -errno; } switch (addr.generic.sa_family) { case AF_INET: return ntohs(addr.inet.sin_port); default: ; } return -EINVAL; } #ifndef _WIN32 int socketLocalServer(const char* path, int socketType) { SockAddr addr; int ret = addr.initFromUnixPath(path); if (ret < 0) { return ret; } return socketBindInternal(&addr, socketType); } int socketLocalClient(const char* path, int socketType) { SockAddr addr; int ret = addr.initFromUnixPath(path); if (ret < 0) { return ret; } return socketConnectInternal(&addr, socketType); } #endif // !_WIN32 int socketTcpLoopbackServer(int port, int socketType) { SockAddr addr; int ret = addr.initFromLocalhost(port); if (ret < 0) { return ret; } return socketBindInternal(&addr, socketType); } int socketTcpLoopbackClient(int port, int socketType) { SockAddr addr; int ret = addr.initFromLocalhost(port); if (ret < 0) { return ret; } return socketConnectInternal(&addr, socketType); } int socketTcpClient(const char* hostname, int port, int socketType) { // TODO(digit): Implement this. return -ENOSYS; } int socketAccept(int serverSocket) { int ret; do { ret = ::accept(serverSocket, NULL, NULL); } while (ret < 0 && errno == EINTR); return ret; } } // namespace emugl