ErrorCode ServerSocket::listen()

in util/ServerSocket.cpp [122:212]


ErrorCode ServerSocket::listen() {
  if (!listeningFds_.empty()) {
    return OK;
  }
  struct addrinfo sa;
  int port = socket_->getPort();

  memset(&sa, 0, sizeof(sa));
  const WdtOptions &options = threadCtx_.getOptions();
  if (options.ipv6) {
    sa.ai_family = AF_INET6;
  }
  if (options.ipv4) {
    sa.ai_family = AF_INET;
  }
  sa.ai_socktype = SOCK_STREAM;
  sa.ai_flags = AI_PASSIVE;
  // Dynamic port is the default on receiver (and setting the start_port flag
  // explictly automatically also sets static_ports to false)
  if (!options.static_ports) {
    WVLOG(1) << "Not using static_ports, changing port " << port << " to 0";
    port = 0;
  }
  // Lookup
  addrInfoList infoList = nullptr;
  std::string portStr = folly::to<std::string>(port);
  int res = getaddrinfo(nullptr, portStr.c_str(), &sa, &infoList);
  if (res) {
    // not errno, can't use WPLOG (perror)
    WLOG(ERROR) << "Failed getaddrinfo ai_passive on " << port << " : " << res
                << " : " << gai_strerror(res);
    return CONN_ERROR;
  }
  // if the port specified is 0, then a random port is selected for the first
  // address. We use that same port for other address types. Another addrinfo
  // list is created using the new port. This variable is used to ensure that we
  // do not try to bind again to the previous type.
  int addressTypeAlreadyBound = AF_UNSPEC;
  for (struct addrinfo *info = infoList; info != nullptr;) {
    if (info->ai_family == addressTypeAlreadyBound) {
      // we are already listening for this address type
      WVLOG(2) << "Ignoring address family " << info->ai_family
               << " since we are already listing on it " << port;
      info = info->ai_next;
      continue;
    }

    std::string host, portString;
    if (WdtSocket::getNameInfo(info->ai_addr, info->ai_addrlen, host,
                               portString)) {
      // even if getnameinfo fail, we can still continue. Error is logged inside
      // SocketUtils
      WDT_CHECK(port == folly::to<int32_t>(portString));
    }
    int listeningFd = listenInternal(info, host);
    if (listeningFd < 0) {
      info = info->ai_next;
      continue;
    }

    int addressFamily = info->ai_family;
    if (port == 0) {
      addrInfoList newInfoList = nullptr;
      int selectedPort =
          getSelectedPortAndNewAddress(listeningFd, sa, host, newInfoList);
      if (selectedPort < 0) {
        ::close(listeningFd);
        info = info->ai_next;
        continue;
      }
      port = selectedPort;
      socket_->setPort(port);
      addressTypeAlreadyBound = addressFamily;
      freeaddrinfo(infoList);
      infoList = newInfoList;
      info = infoList;
    } else {
      info = info->ai_next;
    }

    WVLOG(1) << "Successful listen on " << listeningFd << " host " << host
             << " port " << port << " ai_family " << addressFamily;
    listeningFds_.emplace_back(listeningFd);
  }
  freeaddrinfo(infoList);
  if (listeningFds_.empty()) {
    WLOG(ERROR) << "Unable to listen port " << port;
    return CONN_ERROR_RETRYABLE;
  }
  return OK;
}