Exemplo n.º 1
0
static int
dnsTimeoutHandler(TimeEventHandlerPtr event)
{
    DnsQueryPtr query = *(DnsQueryPtr*)event->data;
    ObjectPtr object = query->object;
    int rc;

    /* People are reporting that this does happen.  And I have no idea why. */
    if(!queryInFlight(query)) {
        do_log(L_ERROR, "BUG: timing out martian query (%s, flags: 0x%x).\n",
               scrub(query->name->string), (unsigned)object->flags);
        return 1;
    }

    query->timeout = MAX(10, query->timeout * 2);

    if(query->timeout > dnsMaxTimeout) {
        abortObject(object, 501, internAtom("Timeout"));
        goto fail;
    } else {
        rc = sendQuery(query);
        if(rc < 0) {
            if(rc != -EWOULDBLOCK && rc != -EAGAIN && rc != -ENOBUFS) {
                abortObject(object, 501,
                            internAtomError(-rc,
                                            "Couldn't send DNS query"));
                goto fail;
            }
            /* else let it timeout */
        }
        query->timeout_handler =
            scheduleTimeEvent(query->timeout, dnsTimeoutHandler,
                              sizeof(query), &query);
        if(query->timeout_handler == NULL) {
            do_log(L_ERROR, "Couldn't schedule DNS timeout handler.\n");
            abortObject(object, 501,
                        internAtom("Couldn't schedule DNS timeout handler"));
            goto fail;
        }
        return 1;
    }

 fail:
    removeQuery(query);
    object->flags &= ~OBJECT_INPROGRESS;
    if(query->inet4) releaseAtom(query->inet4);
    if(query->inet6) releaseAtom(query->inet6);
    free(query);
    releaseNotifyObject(object);
    return 1;
}
Exemplo n.º 2
0
int
specialRequestHandler(int status,
		      FdEventHandlerPtr event, StreamRequestPtr srequest)
{
	SpecialRequestPtr request = srequest->data;
	int rc;
	int killed = 0;

	(void)event;
	if (status < 0) {
		kill(request->pid, SIGTERM);
		killed = 1;
		request->object->flags &= ~OBJECT_INPROGRESS;
		abortObject(request->object, 502,
			    internAtomError(-status,
					    "Couldn't read from client"));
		goto done;
	}

	if (srequest->offset > 0) {
		rc = objectAddData(request->object, request->buf,
				   request->offset, srequest->offset);
		if (rc < 0) {
			kill(request->pid, SIGTERM);
			killed = 1;
			request->object->flags &= ~OBJECT_INPROGRESS;
			abortObject(request->object, 503,
				    internAtom
				    ("Couldn't add data to connection"));
			goto done;
		}
		request->offset += srequest->offset;
	}
	if (status) {
		request->object->flags &= ~OBJECT_INPROGRESS;
		request->object->length = request->object->size;
		goto done;
	}

	if (request->object->refcount <= 1) {
		kill(request->pid, SIGTERM);
		killed = 1;
		request->object->flags &= ~OBJECT_INPROGRESS;
		abortObject(request->object, 500, internAtom("Aborted"));
		goto done;
	}
	notifyObject(request->object);
	do_stream(IO_READ | IO_NOTNOW, request->fd, 0, request->buf, CHUNK_SIZE,
		  specialRequestHandler, request);
	return 1;

      done:
	close(request->fd);
	dispose_chunk(request->buf);
	releaseNotifyObject(request->object);
	do {
		rc = waitpid(request->pid, &status, 0);
	} while (rc < 0 && errno == EINTR);
	if (rc < 0) {
		do_log(L_ERROR, "Wait for %d: %d\n", (int)request->pid, errno);
	} else {
		int normal =
		    (WIFEXITED(status) && WEXITSTATUS(status) == 0) ||
		    (killed && WIFSIGNALED(status)
		     && WTERMSIG(status) == SIGTERM);
		char *reason =
		    WIFEXITED(status) ? "with status" : WIFSIGNALED(status) ?
		    "on signal" : "with unknown status";
		int value =
		    WIFEXITED(status) ? WEXITSTATUS(status) :
		    WIFSIGNALED(status) ? WTERMSIG(status) : status;
		do_log(normal ? D_CHILD : L_ERROR, "Child %d exited %s %d.\n",
		       (int)request->pid, reason, value);
	}
	free(request);
	return 1;
}
Exemplo n.º 3
0
static int
dnsGethostbynameFallback(int id, AtomPtr message)
{
    DnsQueryPtr query, previous;
    ObjectPtr object;

    if(inFlightDnsQueries == NULL) {
        releaseAtom(message);
        return 1;
    }

    query = NULL;
    if(id < 0 || inFlightDnsQueries->id == id) {
        previous = NULL;
        query = inFlightDnsQueries;
    } else {
        previous = inFlightDnsQueries;
        while(previous->next) {
            if(previous->next->id == id) {
                query = previous->next;
                break;
            }
            previous = previous->next;
        }
        if(!query) {
            previous = NULL;
            query = inFlightDnsQueries;
        }
    }

    if(previous == NULL) {
        inFlightDnsQueries = query->next;
        if(inFlightDnsQueries == NULL)
            inFlightDnsQueriesLast = NULL;
    } else {
        previous->next = query->next;
        if(query->next == NULL)
            inFlightDnsQueriesLast = NULL;
    }

    object = makeObject(OBJECT_DNS, query->name->string, query->name->length,
                        1, 0, NULL, NULL);
    if(!object) {
        do_log(L_ERROR, "Couldn't make DNS object.\n");
        releaseAtom(query->name);
        releaseAtom(message);
        releaseObject(query->object);
        cancelTimeEvent(query->timeout_handler);
        free(query);
        return -1;
    }
    if(dnsUseGethostbyname >= 1) {
        releaseAtom(message);
        do_log(L_WARN, "Falling back to using system resolver.\n");
        really_do_gethostbyname(retainAtom(query->name), object);
    } else {
        releaseAtom(object->message);
        object->message = message;
        object->flags &= ~OBJECT_INPROGRESS;
        releaseNotifyObject(object);
    }
    cancelTimeEvent(query->timeout_handler);
    releaseAtom(query->name);
    if(query->inet4) releaseAtom(query->inet4);
    if(query->inet6) releaseAtom(query->inet6);
    releaseObject(query->object);
    free(query);
    return 1;
}
Exemplo n.º 4
0
int
do_gethostbyname(char *origname,
                 int count,
                 int (*handler)(int, GethostbynameRequestPtr),
                 void *data)
{
    ObjectPtr object;
    int n = strlen(origname);
    AtomPtr name;
    GethostbynameRequestRec request;
    int done, rc;

    memset(&request, 0, sizeof(request));
    request.name = NULL;
    request.addr = NULL;
    request.error_message = NULL;
    request.count = count;
    request.handler = handler;
    request.data = data;

    if(n <= 0 || n > 131) {
        if(n <= 0) {
            request.error_message = internAtom("empty name");
            do_log(L_ERROR, "Empty DNS name.\n");
            done = handler(-EINVAL, &request);
        } else {
            request.error_message = internAtom("name too long");
            do_log(L_ERROR, "DNS name too long.\n");
            done = handler(-ENAMETOOLONG, &request);
        }
        assert(done);
        releaseAtom(request.error_message);
        return 1;
    }

    if(origname[n - 1] == '.')
        n--;

    name = internAtomLowerN(origname, n);

    if(name == NULL) {
        request.error_message = internAtom("couldn't allocate name");
        do_log(L_ERROR, "Couldn't allocate DNS name.\n");
        done = handler(-ENOMEM, &request);
        assert(done);
        releaseAtom(request.error_message);
        return 1;
    }

    request.name = name;
    request.addr = NULL;
    request.error_message = NULL;
    request.count = count;
    request.object = NULL;
    request.handler = handler;
    request.data = data;

    object = findObject(OBJECT_DNS, name->string, name->length);
    if(object == NULL || objectMustRevalidate(object, NULL)) {
        if(object) {
            privatiseObject(object, 0);
            releaseObject(object);
        }
        object = makeObject(OBJECT_DNS, name->string, name->length, 1, 0,
                            NULL, NULL);
        if(object == NULL) {
            request.error_message = internAtom("Couldn't allocate object");
            do_log(L_ERROR, "Couldn't allocate DNS object.\n");
            done = handler(-ENOMEM, &request);
            assert(done);
            releaseAtom(name);
            releaseAtom(request.error_message);
            return 1;
        }
    }

    if((object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)) ==
       OBJECT_INITIAL) {
        if(dnsUseGethostbyname >= 3)
            rc = really_do_gethostbyname(name, object);
        else
            rc = really_do_dns(name, object);
        if(rc < 0) {
            assert(!(object->flags & (OBJECT_INITIAL | OBJECT_INPROGRESS)));
            goto fail;
        }
    }

    if(dnsUseGethostbyname >= 3)
        assert(!(object->flags & OBJECT_INITIAL));

#ifndef NO_FANCY_RESOLVER    
    if(object->flags & OBJECT_INITIAL) {
        ConditionHandlerPtr chandler;
        assert(object->flags & OBJECT_INPROGRESS);
        request.object = object;
        chandler = conditionWait(&object->condition, dnsHandler,
                                 sizeof(request), &request);
        if(chandler == NULL)
            goto fail;
        return 1;
    }
#endif

    if(object->headers && object->headers->length > 0) {
        if(object->headers->string[0] == DNS_A)
            assert(((object->headers->length - 1) % 
                    sizeof(HostAddressRec)) == 0);
        else
            assert(object->headers->string[0] == DNS_CNAME);
        request.addr = retainAtom(object->headers);
    } else if(object->message) {
        request.error_message = retainAtom(object->message);
    }

    releaseObject(object);

    if(request.addr && request.addr->length > 0)
        done = handler(1, &request);
    else
        done = handler(-EDNS_HOST_NOT_FOUND, &request);
    assert(done);

    releaseAtom(request.addr); request.addr = NULL;
    releaseAtom(request.name); request.name = NULL;
    releaseAtom(request.error_message); request.error_message = NULL;
    return 1;

 fail:
    releaseNotifyObject(object);
    done = handler(-errno, &request);
    assert(done);
    releaseAtom(name);
    return 1;
}
Exemplo n.º 5
0
static int
dnsReplyHandler(int abort, FdEventHandlerPtr event)
{
    int fd = event->fd;
    char buf[2048];
    int len, rc;
    ObjectPtr object;
    unsigned ttl = 0;
    AtomPtr name, value, message = NULL;
    int id;
    int af;
    DnsQueryPtr query;
    AtomPtr cname = NULL;

    if(abort) {
        dnsSocketHandler = NULL;
        rc = establishDnsSocket();
        if(rc < 0) {
            do_log(L_ERROR, "Couldn't reestablish DNS socket.\n");
            /* At this point, we should abort all in-flight
               DNS requests.  Oh, well, they'll timeout anyway. */
        }
        return 1;
    }

    len = recv(fd, buf, 2048, 0);
    if(len <= 0) {
        if(errno == EINTR || errno == EAGAIN) return 0;
        /* This is where we get ECONNREFUSED for an ICMP port unreachable */
        do_log_error(L_ERROR, errno, "DNS: recv failed");
        dnsGethostbynameFallback(-1, message);
        return 0;
    }

    /* This could be a late reply to a query that timed out and was
       resent, a reply to a query that timed out, or a reply to an
       AAAA query when we already got a CNAME reply to the associated
       A.  We filter such replies straight away, without trying to
       parse them. */
    rc = dnsReplyId(buf, 0, len, &id);
    if(rc < 0) {
        do_log(L_WARN, "Short DNS reply.\n");
        return 0;
    }
    if(!findQuery(id, NULL)) {
        return 0;
    }

    rc = dnsDecodeReply(buf, 0, len, &id, &name, &value, &af, &ttl);
    if(rc < 0) {
        assert(value == NULL);
        /* We only want to fallback on gethostbyname if we received a
           reply that we could not understand.  What about truncated
           replies? */
        if(rc < 0) {
            do_log_error(L_WARN, -rc, "DNS");
            if(dnsUseGethostbyname >= 2 ||
               (dnsUseGethostbyname && 
                (rc != -EDNS_HOST_NOT_FOUND && rc != -EDNS_NO_RECOVERY &&
                 rc != -EDNS_FORMAT))) {
                dnsGethostbynameFallback(id, message);
                return 0;
            } else {
                message = internAtom(pstrerror(-rc));
            }
        } else {
            assert(name != NULL && id >= 0 && af >= 0);
        }
    }

    query = findQuery(id, name);
    if(query == NULL) {
        /* Duplicate id ? */
        releaseAtom(value);
        releaseAtom(name);
        return 0;
    }

    /* We're going to use the information in this reply.  If it was an
       error, construct an empty atom to distinguish it from information
       we're still waiting for. */
    if(value == NULL)
        value = internAtom("");

 again:
    if(af == 4) {
        if(query->inet4 == NULL) {
            query->inet4 = value;
            query->ttl4 = current_time.tv_sec + ttl;
        } else
            releaseAtom(value);
    } else if(af == 6) {
        if(query->inet6 == NULL) {
            query->inet6 = value;
            query->ttl6 = current_time.tv_sec + ttl;
        } else
            releaseAtom(value);
    } else if(af == 0) {
        /* Ignore errors in this case. */
        if(query->inet4 && query->inet4->length == 0) {
            releaseAtom(query->inet4);
            query->inet4 = NULL;
        }
        if(query->inet6 && query->inet6->length == 0) {
            releaseAtom(query->inet6);
            query->inet6 = NULL;
        }
        if(query->inet4 || query->inet6) {
            do_log(L_WARN, "Host %s has both %s and CNAME -- "
                   "ignoring CNAME.\n", scrub(query->name->string),
                   query->inet4 ? "A" : "AAAA");
            releaseAtom(value);
            value = internAtom("");
            af = query->inet4 ? 4 : 6;
            goto again;
        } else {
            cname = value;
        }
    }

    if(rc >= 0 && !cname &&
       ((dnsQueryIPv6 < 3 && query->inet4 == NULL) ||
        (dnsQueryIPv6 > 0 && query->inet6 == NULL)))
        return 0;

    /* This query is complete */

    cancelTimeEvent(query->timeout_handler);
    object = query->object;

    if(object->flags & OBJECT_INITIAL) {
        assert(!object->headers);
        if(cname) {
            assert(query->inet4 == NULL && query->inet6 == NULL);
            object->headers = cname;
            object->expires = current_time.tv_sec + ttl;
        } else if((!query->inet4 || query->inet4->length == 0) &&
                  (!query->inet6 || query->inet6->length == 0)) {
            releaseAtom(query->inet4);
            releaseAtom(query->inet6);
            object->expires = current_time.tv_sec + dnsNegativeTtl;
            abortObject(object, 500, retainAtom(message));
        } else if(!query->inet4 || query->inet4->length == 0) {
            object->headers = query->inet6;
            object->expires = query->ttl6;
            releaseAtom(query->inet4);
        } else if(!query->inet6 || query->inet6->length == 0) {
            object->headers = query->inet4;
            object->expires = query->ttl4;
            releaseAtom(query->inet6);
        } else {
            /* need to merge results */
            char buf[1024];
            if(query->inet4->length + query->inet6->length > 1024) {
                releaseAtom(query->inet4);
                releaseAtom(query->inet6);
                abortObject(object, 500, internAtom("DNS reply too long"));
            } else {
                if(dnsQueryIPv6 <= 1) {
                    memcpy(buf, query->inet4->string, query->inet4->length);
                    memcpy(buf + query->inet4->length,
                           query->inet6->string + 1, query->inet6->length - 1);
                } else {
                    memcpy(buf, query->inet6->string, query->inet6->length);
                    memcpy(buf + query->inet6->length,
                           query->inet4->string + 1, query->inet4->length - 1);
                }
                object->headers =
                    internAtomN(buf, 
                                query->inet4->length + 
                                query->inet6->length - 1);
                if(object->headers == NULL)
                    abortObject(object, 500, 
                                internAtom("Couldn't allocate DNS atom"));
            }
            object->expires = MIN(query->ttl4, query->ttl6);
        }
        object->age = current_time.tv_sec;
        object->flags &= ~(OBJECT_INITIAL | OBJECT_INPROGRESS);
    } else {
        do_log(L_WARN, "DNS object ex nihilo for %s.\n",
               scrub(query->name->string));
    }
    
    removeQuery(query);
    free(query);

    releaseAtom(name);
    releaseAtom(message);
    releaseNotifyObject(object);
    return 0;
}