-static bool upCheck(const shared_ptr<DownstreamState>& ds)
-try
-{
- DNSName checkName = ds->checkName;
- uint16_t checkType = ds->checkType.getCode();
- uint16_t checkClass = ds->checkClass;
- dnsheader checkHeader;
- memset(&checkHeader, 0, sizeof(checkHeader));
-
- checkHeader.qdcount = htons(1);
- checkHeader.id = getRandomDNSID();
-
- checkHeader.rd = true;
- if (ds->setCD) {
- checkHeader.cd = true;
- }
-
- if (ds->checkFunction) {
- std::lock_guard<std::mutex> lock(g_luamutex);
- auto ret = ds->checkFunction(checkName, checkType, checkClass, &checkHeader);
- checkName = std::get<0>(ret);
- checkType = std::get<1>(ret);
- checkClass = std::get<2>(ret);
- }
-
- vector<uint8_t> packet;
- DNSPacketWriter dpw(packet, checkName, checkType, checkClass);
- dnsheader * requestHeader = dpw.getHeader();
- *requestHeader = checkHeader;
-
- Socket sock(ds->remote.sin4.sin_family, SOCK_DGRAM);
- sock.setNonBlocking();
- if (!IsAnyAddress(ds->sourceAddr)) {
- sock.setReuseAddr();
- if (!ds->sourceItfName.empty()) {
-#ifdef SO_BINDTODEVICE
- int res = setsockopt(sock.getHandle(), SOL_SOCKET, SO_BINDTODEVICE, ds->sourceItfName.c_str(), ds->sourceItfName.length());
- if (res != 0 && g_verboseHealthChecks) {
- infolog("Error settting SO_BINDTODEVICE on the health check socket for backend '%s': %s", ds->getNameWithAddr(), stringerror());
- }
-#endif
- }
- sock.bind(ds->sourceAddr);
- }
- sock.connect(ds->remote);
- ssize_t sent = udpClientSendRequestToBackend(ds, sock.getHandle(), reinterpret_cast<char*>(&packet[0]), packet.size(), true);
- if (sent < 0) {
- int ret = errno;
- if (g_verboseHealthChecks)
- infolog("Error while sending a health check query to backend %s: %d", ds->getNameWithAddr(), ret);
- return false;
- }
-
- int ret = waitForRWData(sock.getHandle(), true, /* ms to seconds */ ds->checkTimeout / 1000, /* remaining ms to us */ (ds->checkTimeout % 1000) * 1000);
- if(ret < 0 || !ret) { // error, timeout, both are down!
- if (ret < 0) {
- ret = errno;
- if (g_verboseHealthChecks)
- infolog("Error while waiting for the health check response from backend %s: %d", ds->getNameWithAddr(), ret);
- }
- else {
- if (g_verboseHealthChecks)
- infolog("Timeout while waiting for the health check response from backend %s", ds->getNameWithAddr());
- }
- return false;
- }
-
- string reply;
- ComboAddress from;
- sock.recvFrom(reply, from);
-
- /* we are using a connected socket but hey.. */
- if (from != ds->remote) {
- if (g_verboseHealthChecks)
- infolog("Invalid health check response received from %s, expecting one from %s", from.toStringWithPort(), ds->remote.toStringWithPort());
- return false;
- }
-
- const dnsheader * responseHeader = reinterpret_cast<const dnsheader *>(reply.c_str());
-
- if (reply.size() < sizeof(*responseHeader)) {
- if (g_verboseHealthChecks)
- infolog("Invalid health check response of size %d from backend %s, expecting at least %d", reply.size(), ds->getNameWithAddr(), sizeof(*responseHeader));
- return false;
- }
-
- if (responseHeader->id != requestHeader->id) {
- if (g_verboseHealthChecks)
- infolog("Invalid health check response id %d from backend %s, expecting %d", responseHeader->id, ds->getNameWithAddr(), requestHeader->id);
- return false;
- }
-
- if (!responseHeader->qr) {
- if (g_verboseHealthChecks)
- infolog("Invalid health check response from backend %s, expecting QR to be set", ds->getNameWithAddr());
- return false;
- }
-
- if (responseHeader->rcode == RCode::ServFail) {
- if (g_verboseHealthChecks)
- infolog("Backend %s responded to health check with ServFail", ds->getNameWithAddr());
- return false;
- }
-
- if (ds->mustResolve && (responseHeader->rcode == RCode::NXDomain || responseHeader->rcode == RCode::Refused)) {
- if (g_verboseHealthChecks)
- infolog("Backend %s responded to health check with %s while mustResolve is set", ds->getNameWithAddr(), responseHeader->rcode == RCode::NXDomain ? "NXDomain" : "Refused");
- return false;
- }
-
- uint16_t receivedType;
- uint16_t receivedClass;
- DNSName receivedName(reply.c_str(), reply.size(), sizeof(dnsheader), false, &receivedType, &receivedClass);
-
- if (receivedName != checkName || receivedType != checkType || receivedClass != checkClass) {
- if (g_verboseHealthChecks)
- infolog("Backend %s responded to health check with an invalid qname (%s vs %s), qtype (%s vs %s) or qclass (%d vs %d)", ds->getNameWithAddr(), receivedName.toLogString(), checkName.toLogString(), QType(receivedType).getName(), QType(checkType).getName(), receivedClass, checkClass);
- return false;
- }
-
- return true;
-}
-catch(const std::exception& e)
-{
- if (g_verboseHealthChecks)
- infolog("Error checking the health of backend %s: %s", ds->getNameWithAddr(), e.what());
- return false;
-}
-catch(...)
-{
- if (g_verboseHealthChecks)
- infolog("Unknown exception while checking the health of backend %s", ds->getNameWithAddr());
- return false;
-}
-