Exemple #1
0
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;
}
Exemple #2
0
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);
}