SmartPtr<SSLSocket> SSLSocket::Create( int fd, int domain, const HostURL &hosturl, double timeout) { CryptoMethod method; const std::string scheme = hosturl.getScheme(); if (scheme == "ssl") { method = CryptoMethod::ClientSSLv23; } else if (scheme == "sslv2") { method = CryptoMethod::ClientSSLv2; } else if (scheme == "sslv3") { method = CryptoMethod::ClientSSLv3; } else if (scheme == "tls") { method = CryptoMethod::ClientTLS; } else { return nullptr; } auto sock = makeSmartPtr<SSLSocket>( fd, domain, hosturl.getHost().c_str(), hosturl.getPort()); sock->m_data->m_method = method; sock->m_data->m_connect_timeout = timeout; sock->m_data->m_enable_on_connect = true; return sock; }
static SmartPtr<Socket> create_new_socket( const HostURL &hosturl, Variant &errnum, Variant &errstr ) { int domain = hosturl.isIPv6() ? AF_INET6 : AF_INET; int type = SOCK_STREAM; const std::string scheme = hosturl.getScheme(); if (scheme == "udp" || scheme == "udg") { type = SOCK_DGRAM; } else if (scheme == "unix") { domain = AF_UNIX; } auto sock = makeSmartPtr<Socket>( socket(domain, type, 0), domain, hosturl.getHost().c_str(), hosturl.getPort() ); if (!sock->valid()) { SOCKET_ERROR(sock, "unable to create socket", errno); errnum = sock->getError(); errstr = HHVM_FN(socket_strerror)(sock->getError()); sock.reset(); } return sock; }
Variant socket_server_impl( const HostURL &hosturl, int flags, /* = STREAM_SERVER_BIND|STREAM_SERVER_LISTEN */ VRefParam errnum /* = null */, VRefParam errstr /* = null */ ) { errnum = 0; errstr = empty_string(); auto sock = create_new_socket(hosturl, errnum, errstr); if (!sock) { return false; } sockaddr_storage sa_storage; struct sockaddr *sa_ptr; size_t sa_size; if (!set_sockaddr(sa_storage, sock, hosturl.getHost().c_str(), hosturl.getPort(), sa_ptr, sa_size)) { return false; } int yes = 1; setsockopt(sock->fd(), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); if ((flags & k_STREAM_SERVER_BIND) != 0 && ::bind(sock->fd(), sa_ptr, sa_size) < 0) { SOCKET_ERROR(sock, "unable to bind to given address", errno); return false; } if ((flags & k_STREAM_SERVER_LISTEN) != 0 && listen(sock->fd(), 128) < 0) { SOCKET_ERROR(sock, "unable to listen on socket", errno); return false; } return Variant(std::move(sock)); }
req::ptr<SSLSocket> SSLSocket::Create( int fd, int domain, const HostURL &hosturl, double timeout, const req::ptr<StreamContext>& ctx) { CryptoMethod method; const std::string scheme = hosturl.getScheme(); if (scheme == "ssl") { method = CryptoMethod::ClientSSLv23; } else if (scheme == "sslv2") { method = CryptoMethod::ClientSSLv2; } else if (scheme == "sslv3") { method = CryptoMethod::ClientSSLv3; } else if (scheme == "tls") { method = CryptoMethod::ClientTLS; } else if ( scheme == "tcp" || (scheme.empty() && (domain == AF_INET || domain == AF_INET6)) ) { method = CryptoMethod::NoCrypto; } else { return nullptr; } auto sock = req::make<SSLSocket>( fd, domain, ctx, hosturl.getHost().c_str(), hosturl.getPort()); sock->m_data->m_method = method; sock->m_data->m_connect_timeout = timeout; sock->m_data->m_enable_on_connect = true; return sock; }
req::ptr<SSLSocket> SSLSocket::Create( int fd, int domain, const HostURL &hosturl, double timeout, const req::ptr<StreamContext>& ctx, bool nonblocking ) { CryptoMethod method; const std::string scheme = hosturl.getScheme(); if (scheme == "ssl") { method = CryptoMethod::ClientSSLv23; } else if (scheme == "sslv2") { method = CryptoMethod::ClientSSLv2; } else if (scheme == "sslv3") { method = CryptoMethod::ClientSSLv3; } else if (scheme == "tls") { method = CryptoMethod::ClientTLS; } else if ( scheme == "tcp" || (scheme.empty() && (domain == AF_INET || domain == AF_INET6)) ) { method = CryptoMethod::NoCrypto; } else { return nullptr; } return Create(fd, domain, method, hosturl.getHost(), hosturl.getPort(), timeout, ctx, nonblocking); }
static int connect_with_timeout(int fd, struct sockaddr *sa_ptr, size_t sa_size, double timeout, const HostURL &hosturl, std::string &errstr, int &errnum) { if (timeout <= 0) { int retval = connect(fd, sa_ptr, sa_size); if (retval < 0) { errstr = "unable to connect to " + hosturl.getHostURL(); errnum = errno; } return retval; } // set non-blocking so we can do timeouts long arg = fcntl(fd, F_GETFL, nullptr); fcntl(fd, F_SETFL, arg | O_NONBLOCK); int retval = connect(fd, sa_ptr, sa_size); if (retval < 0) { if (errno == EINPROGRESS) { struct pollfd fds[1]; fds[0].fd = fd; fds[0].events = POLLOUT; if (poll(fds, 1, (int)(timeout * 1000))) { int valopt; socklen_t lon = sizeof(int); getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon); if (valopt) { errstr = "failed to connect to " + hosturl.getHostURL(); errnum = valopt; } retval = valopt ? -1 : 0; } else { errstr = "timed out after "; errstr += folly::to<std::string>(timeout); errstr += " seconds when connecting to " + hosturl.getHostURL(); errnum = ETIMEDOUT; retval = -1; } } else { errstr = "unable to connect to " + hosturl.getHostURL(); errnum = errno; } } // set to blocking mode arg = fcntl(fd, F_GETFL, nullptr); fcntl(fd, F_SETFL, arg & ~O_NONBLOCK); return retval; }
Variant sockopen_impl(const HostURL &hosturl, VRefParam errnum, VRefParam errstr, double timeout, bool persistent) { errnum = 0; errstr = empty_string(); std::string key; if (persistent) { key = hosturl.getHostURL() + ":" + folly::to<std::string>(hosturl.getPort()); auto sockItr = s_sockets.find(key); if (sockItr != s_sockets.end()) { auto sock = makeSmartPtr<Socket>(sockItr->second); if (sock->getError() == 0 && sock->checkLiveness()) { return Variant(sock); } // socket had an error earlier, we need to close it, remove it from // persistent storage, and create a new one (in that order) sock->close(); s_sockets.erase(sockItr); } } if (timeout < 0) { timeout = ThreadInfo::s_threadInfo.getNoCheck()-> m_reqInjectionData.getSocketDefaultTimeout(); } auto socket = new_socket_connect(hosturl, timeout, errnum, errstr); if (!socket.isResource()) { return false; } if (persistent) { assert(!key.empty()); s_sockets[key] = cast<Socket>(socket)->getData(); assert(s_sockets[key]); } return socket; }
Variant sockopen_impl(const HostURL &hosturl, VRefParam errnum, VRefParam errstr, double timeout, bool persistent) { errnum = 0; errstr = empty_string(); std::string key; if (persistent) { key = hosturl.getHostURL() + ":" + folly::to<std::string>(hosturl.getPort()); Socket *sock = dynamic_cast<Socket*>(g_persistentResources->get("socket", key.c_str())); if (sock) { if (sock->getError() == 0 && sock->checkLiveness()) { return Resource(sock); } // socket had an error earlier, we need to close it, remove it from // persistent storage, and create a new one (in that order) sock->close(); g_persistentResources->remove("socket", key.c_str()); } } if (timeout < 0) { timeout = ThreadInfo::s_threadInfo.getNoCheck()-> m_reqInjectionData.getSocketDefaultTimeout(); } auto socket = new_socket_connect(hosturl, timeout, errnum, errstr); if (!socket.isResource()) { return false; } Resource ret = socket.toResource(); if (persistent) { assert(!key.empty()); g_persistentResources->set("socket", key.c_str(), ret.getTyped<Socket>()); } return ret; }
static bool create_new_socket(const HostURL &hosturl, Variant &errnum, Variant &errstr, Resource &ret, Socket *&sock) { int domain = hosturl.isIPv6() ? AF_INET6 : AF_INET; int type = SOCK_STREAM; const std::string scheme = hosturl.getScheme(); if (scheme == "udp" || scheme == "udg") { type = SOCK_DGRAM; } else if (scheme == "unix") { domain = AF_UNIX; } sock = new Socket(socket(domain, type, 0), domain, hosturl.getHost().c_str(), hosturl.getPort()); ret = Resource(sock); if (!sock->valid()) { SOCKET_ERROR(sock, "unable to create socket", errno); errnum = sock->getError(); errstr = HHVM_FN(socket_strerror)(sock->getError()); return false; } return true; }
static Variant new_socket_connect(const HostURL &hosturl, double timeout, Variant &errnum, Variant &errstr) { int domain = AF_UNSPEC; int type = SOCK_STREAM; auto const& scheme = hosturl.getScheme(); SmartPtr<Socket> sock; SmartPtr<SSLSocket> sslsock; std::string sockerr; int error; if (scheme == "udp" || scheme == "udg") { type = SOCK_DGRAM; } else if (scheme == "unix") { domain = AF_UNIX; } int fd = -1; if (domain == AF_UNIX) { sockaddr_storage sa_storage; struct sockaddr *sa_ptr; size_t sa_size; fd = socket(domain, type, 0); sock = makeSmartPtr<Socket>( fd, domain, hosturl.getHost().c_str(), hosturl.getPort()); if (!set_sockaddr(sa_storage, sock, hosturl.getHost().c_str(), hosturl.getPort(), sa_ptr, sa_size)) { // set_sockaddr raises its own warning on failure return false; } if (connect_with_timeout(fd, sa_ptr, sa_size, timeout, hosturl, sockerr, error) != 0) { SOCKET_ERROR(sock, sockerr.c_str(), error); errnum = sock->getLastError(); errstr = HHVM_FN(socket_strerror)(sock->getLastError()); return false; } } else { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = domain; hints.ai_socktype = type; auto port = folly::to<std::string>(hosturl.getPort()); auto host = hosturl.getHost(); struct addrinfo *aiHead; int errcode = getaddrinfo(host.c_str(), port.c_str(), &hints, &aiHead); if (errcode != 0) { errstr = String(gai_strerror(errcode), CopyString); return false; } SCOPE_EXIT { freeaddrinfo(aiHead); }; for (struct addrinfo *ai = aiHead; ai != nullptr; ai = ai->ai_next) { domain = ai->ai_family; fd = socket(domain, ai->ai_socktype, ai->ai_protocol); if (fd == -1) { continue; } if (connect_with_timeout(fd, ai->ai_addr, ai->ai_addrlen, timeout, hosturl, sockerr, error) == 0) { break; } close(fd); fd = -1; } sslsock = SSLSocket::Create(fd, domain, hosturl, timeout); if (sslsock) { sock = sslsock; } else { sock = makeSmartPtr<Socket>(fd, domain, hosturl.getHost().c_str(), hosturl.getPort()); } } if (!sock->valid()) { SOCKET_ERROR(sock, sockerr.empty() ? "unable to create socket" : sockerr.c_str(), error); errnum = sock->getLastError(); errstr = HHVM_FN(socket_strerror)(sock->getLastError()); return false; } if (sslsock && !sslsock->onConnect()) { raise_warning("Failed to enable crypto"); return false; } return Variant(std::move(sock)); }