static Variant sockopen_impl(CStrRef hostname, int port, Variant &errnum, Variant &errstr, double timeout, bool persistent) { string key; if (persistent) { key = hostname.data(); key += ":"; key += boost::lexical_cast<string>(port); Socket *sock = dynamic_cast<Socket*>(g_persistentObjects->get("socket", key.c_str())); if (sock) { if (sock->getError() == 0 && sock->checkLiveness()) { return Object(sock); } // socket had an error earlier, we need to remove it from persistent // storage, and create a new one g_persistentObjects->remove("socket", key.c_str()); } } Object ret; const char *name = hostname.data(); Socket *sock = NULL; if (timeout <= 0) timeout = RuntimeOption::SocketDefaultTimeout; // test if protocol is SSL SSLSocket *sslsock = SSLSocket::Create(name, port, timeout); if (sslsock) { sock = sslsock; ret = sock; } else if (!create_new_socket(name, port, errnum, errstr, ret, sock, timeout)) { return false; } assert(ret.get() && sock); sockaddr_storage sa_storage; struct sockaddr *sa_ptr; size_t sa_size; if (!set_sockaddr(sa_storage, sock, name, port, sa_ptr, sa_size)) { return false; } int retval; int fd = sock->fd(); IOStatusHelper io("socket::connect", name, port); if (timeout <= 0) { retval = connect(fd, sa_ptr, sa_size); } else { // set non-blocking so we can do timeouts long arg = fcntl(fd, F_GETFL, NULL); fcntl(fd, F_SETFL, arg | O_NONBLOCK); 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))) { socklen_t lon = sizeof(int); int valopt; getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*)(&valopt), &lon); if (valopt) { std::string msg = "failed to connect to "; msg += name; msg += ":"; msg += boost::lexical_cast<std::string>(port); SOCKET_ERROR(sock, msg.c_str(), valopt); errnum = sock->getError(); errstr = String(Util::safe_strerror(sock->getError())); return false; } else { retval = 0; // success } } else { std::string msg = "timed out after "; msg += boost::lexical_cast<std::string>(timeout); msg += " seconds when connecting to "; msg += name; msg += ":"; msg += boost::lexical_cast<std::string>(port); SOCKET_ERROR(sock, msg.c_str(), ETIMEDOUT); errnum = sock->getError(); errstr = String(Util::safe_strerror(sock->getError())); return false; } } } // set to blocking mode arg = fcntl(fd, F_GETFL, NULL); fcntl(fd, F_SETFL, arg & ~O_NONBLOCK); } if (retval != 0) { errnum = sock->getError(); errstr = String(Util::safe_strerror(sock->getError())); return false; } if (sslsock && !sslsock->onConnect()) { raise_warning("Failed to enable crypto"); return false; } if (persistent) { assert(!key.empty()); g_persistentObjects->set("socket", key.c_str(), sock); } return ret; }
static Variant new_socket_connect(const HostURL &hosturl, int timeout, Variant &errnum, Variant &errstr) { int domain = AF_UNSPEC; int type = SOCK_STREAM; auto const& scheme = hosturl.getScheme(); Socket* sock = nullptr; SSLSocket *sslsock = nullptr; 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 = new 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)) { 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()); delete sock; 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; } fd = -1; } sslsock = SSLSocket::Create(fd, domain, hosturl, timeout); if (sslsock) { sock = sslsock; } else { sock = new 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()); delete sock; return false; } if (sslsock && !sslsock->onConnect()) { raise_warning("Failed to enable crypto"); delete sslsock; return false; } return Resource(sock); }