예제 #1
0
/* fetch headers from disk, pass on to SwapInCBlock */
static void
peerDigestSwapInHeaders(void *data, char *buf, ssize_t size)
{
    DigestFetchState *fetch = data;
    size_t hdr_size;

    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestSwapInHeaders"))
	return;

    assert(!fetch->offset);
    if ((hdr_size = headersEnd(buf, size))) {
	assert(fetch->entry->mem_obj->reply);
	if (!fetch->entry->mem_obj->reply->sline.status)
	    httpReplyParse(fetch->entry->mem_obj->reply, buf, hdr_size);
	if (fetch->entry->mem_obj->reply->sline.status != HTTP_OK) {
	    debug(72, 1) ("peerDigestSwapInHeaders: %s status %d got cached!\n",
		strBuf(fetch->pd->host), fetch->entry->mem_obj->reply->sline.status);
	    peerDigestFetchAbort(fetch, buf, "internal status error");
	    return;
	}
	fetch->offset += hdr_size;
	storeClientCopy(fetch->entry, size, fetch->offset,
	    SM_PAGE_SIZE, buf,
	    peerDigestSwapInCBlock, fetch);
    } else {
	/* need more data, do we have space? */
	if (size >= SM_PAGE_SIZE)
	    peerDigestFetchAbort(fetch, buf, "stored header too big");
	else
	    storeClientCopy(fetch->entry, size, 0, SM_PAGE_SIZE, buf,
		peerDigestSwapInHeaders, fetch);
    }
}
예제 #2
0
/*
 * Try to parse the header.
 * return -1 on error, 0 on more required, +1 on completed.
 */
static int
storeClientParseHeader(store_client * sc, const char *b, int l)
{
    if (sc->copy_offset == 0 && l > 0 && memHaveHeaders(sc->entry->mem_obj) == 0)
	return httpReplyParse(sc->entry->mem_obj->reply, b, headersEnd(b, l));
    else
	return 1;
}
예제 #3
0
static void
storeClientReadBody(void *data, const char *buf, ssize_t len)
{
    store_client *sc = data;
    MemObject *mem = sc->entry->mem_obj;
    assert(sc->flags.disk_io_pending);
    sc->flags.disk_io_pending = 0;
    assert(sc->callback != NULL);
    debug(20, 3) ("storeClientReadBody: len %d\n", (int) len);
    if (sc->copy_offset == 0 && len > 0 && memHaveHeaders(mem) == 0)
	httpReplyParse(mem->reply, sc->copy_buf, headersEnd(sc->copy_buf, len));
    storeClientCallback(sc, len);
}
예제 #4
0
/* wait for full http headers to be received then parse them */
static void
peerDigestFetchReply(void *data, char *buf, ssize_t size)
{
    DigestFetchState *fetch = data;
    PeerDigest *pd = fetch->pd;
    size_t hdr_size;
    assert(pd && buf);
    assert(!fetch->offset);

    if (peerDigestFetchedEnough(fetch, buf, size, "peerDigestFetchReply"))
	return;

    if ((hdr_size = headersEnd(buf, size))) {
	http_status status;
	HttpReply *reply = fetch->entry->mem_obj->reply;
	assert(reply);
	httpReplyParse(reply, buf, hdr_size);
	status = reply->sline.status;
	debug(72, 3) ("peerDigestFetchReply: %s status: %d, expires: %d (%+d)\n",
	    strBuf(pd->host), status,
	    reply->expires, reply->expires - squid_curtime);

	/* this "if" is based on clientHandleIMSReply() */
	if (status == HTTP_NOT_MODIFIED) {
	    request_t *r = NULL;
	    /* our old entry is fine */
	    assert(fetch->old_entry);
	    if (!fetch->old_entry->mem_obj->request)
		fetch->old_entry->mem_obj->request = r =
		    requestLink(fetch->entry->mem_obj->request);
	    assert(fetch->old_entry->mem_obj->request);
	    httpReplyUpdateOnNotModified(fetch->old_entry->mem_obj->reply, reply);
	    storeTimestampsSet(fetch->old_entry);
	    /* get rid of 304 reply */
	    storeUnregister(fetch->entry, fetch);
	    storeUnlockObject(fetch->entry);
	    fetch->entry = fetch->old_entry;
	    fetch->old_entry = NULL;
	    /* preserve request -- we need its size to update counters */
	    /* requestUnlink(r); */
	    /* fetch->entry->mem_obj->request = NULL; */
	} else if (status == HTTP_OK) {
	    /* get rid of old entry if any */
	    if (fetch->old_entry) {
		debug(72, 3) ("peerDigestFetchReply: got new digest, releasing old one\n");
		storeUnregister(fetch->old_entry, fetch);
		storeReleaseRequest(fetch->old_entry);
		storeUnlockObject(fetch->old_entry);
		fetch->old_entry = NULL;
	    }
	} else {
	    /* some kind of a bug */
	    peerDigestFetchAbort(fetch, buf, httpStatusLineReason(&reply->sline));
	    return;
	}
	/* must have a ready-to-use store entry if we got here */
	/* can we stay with the old in-memory digest? */
	if (status == HTTP_NOT_MODIFIED && fetch->pd->cd)
	    peerDigestFetchStop(fetch, buf, "Not modified");
	else
	    storeClientCopy(fetch->entry,	/* have to swap in */
		0, 0, SM_PAGE_SIZE, buf, peerDigestSwapInHeaders, fetch);
    } else {
	/* need more data, do we have space? */
	if (size >= SM_PAGE_SIZE)
	    peerDigestFetchAbort(fetch, buf, "reply header too big");
	else
	    storeClientCopy(fetch->entry, size, 0, SM_PAGE_SIZE, buf,
		peerDigestFetchReply, fetch);
    }
}
예제 #5
0
static void
storeClientReadHeader(void *data, const char *buf, ssize_t len)
{
    static int md5_mismatches = 0;
    store_client *sc = data;
    StoreEntry *e = sc->entry;
    MemObject *mem = e->mem_obj;
    int swap_hdr_sz = 0;
    size_t body_sz;
    size_t copy_sz;
    tlv *tlv_list;
    tlv *t;
    int swap_object_ok = 1;
    char *new_url = NULL;
    char *new_store_url = NULL;
    assert(sc->flags.disk_io_pending);
    sc->flags.disk_io_pending = 0;
    assert(sc->callback != NULL);
    debug(20, 3) ("storeClientReadHeader: len %d\n", (int) len);
    if (len < 0) {
	debug(20, 3) ("storeClientReadHeader: %s\n", xstrerror());
	storeClientCallback(sc, len);
	return;
    }
    tlv_list = storeSwapMetaUnpack(buf, &swap_hdr_sz);
    if (swap_hdr_sz > len) {
	/* oops, bad disk file? */
	debug(20, 1) ("WARNING: swapfile header too small\n");
	storeClientCallback(sc, -1);
	return;
    }
    if (tlv_list == NULL) {
	debug(20, 1) ("WARNING: failed to unpack meta data\n");
	storeClientCallback(sc, -1);
	return;
    }
    /*
     * Check the meta data and make sure we got the right object.
     */
    for (t = tlv_list; t && swap_object_ok; t = t->next) {
	switch (t->type) {
	case STORE_META_KEY:
	    assert(t->length == SQUID_MD5_DIGEST_LENGTH);
	    if (!EBIT_TEST(e->flags, KEY_PRIVATE) &&
		memcmp(t->value, e->hash.key, SQUID_MD5_DIGEST_LENGTH)) {
		debug(20, 2) ("storeClientReadHeader: swapin MD5 mismatch\n");
		debug(20, 2) ("\t%s\n", storeKeyText(t->value));
		debug(20, 2) ("\t%s\n", storeKeyText(e->hash.key));
		if (isPowTen(++md5_mismatches))
		    debug(20, 1) ("WARNING: %d swapin MD5 mismatches\n",
			md5_mismatches);
		swap_object_ok = 0;
	    }
	    break;
	case STORE_META_URL:
	    new_url = xstrdup(t->value);
	    break;
	case STORE_META_STOREURL:
	    new_store_url = xstrdup(t->value);
	    break;
	case STORE_META_OBJSIZE:
	    break;
	case STORE_META_STD:
	case STORE_META_STD_LFS:
	    break;
	case STORE_META_VARY_HEADERS:
	    if (mem->vary_headers) {
		if (strcmp(mem->vary_headers, t->value) != 0)
		    swap_object_ok = 0;
	    } else {
		/* Assume the object is OK.. remember the vary request headers */
		mem->vary_headers = xstrdup(t->value);
	    }
	    break;
	default:
	    debug(20, 2) ("WARNING: got unused STORE_META type %d\n", t->type);
	    break;
	}
    }

    /* Check url / store_url */
    do {
	if (new_url == NULL) {
	    debug(20, 1) ("storeClientReadHeader: no URL!\n");
	    swap_object_ok = 0;
	    break;
	}
	/*
	 * If we have a store URL then it must match the requested object URL.
	 * The theory is that objects with a store URL have been normalised
	 * and thus a direct access which didn't go via the rewrite framework
	 * are illegal!
	 */
	if (new_store_url) {
	    if (NULL == mem->store_url)
		mem->store_url = new_store_url;
	    else if (0 == strcasecmp(mem->store_url, new_store_url))
		(void) 0;	/* a match! */
	    else {
		debug(20, 1) ("storeClientReadHeader: store URL mismatch\n");
		debug(20, 1) ("\t{%s} != {%s}\n", (char *) new_store_url, mem->store_url);
		swap_object_ok = 0;
		break;
	    }
	}
	/* If we have no store URL then the request and the memory URL must match */
	/*
	if ((!new_store_url) && mem->url && strcasecmp(mem->url, new_url) != 0) {
	    debug(20, 1) ("storeClientReadHeader: URL mismatch\n");
	    debug(20, 1) ("\t{%s} != {%s}\n", (char *) new_url, mem->url);
	    swap_object_ok = 0;
	    break;
	}
	*/
    } while (0);

    storeSwapTLVFree(tlv_list);
    xfree(new_url);
    /* don't free new_store_url if its owned by the mem object now */
    if (mem->store_url != new_store_url)
	xfree(new_store_url);

    if (!swap_object_ok) {
	storeClientCallback(sc, -1);
	return;
    }
    mem->swap_hdr_sz = swap_hdr_sz;
    mem->object_sz = e->swap_file_sz - swap_hdr_sz;
    /*
     * If our last read got some data the client wants, then give
     * it to them, otherwise schedule another read.
     */
    body_sz = len - swap_hdr_sz;
    if (sc->copy_offset < body_sz) {
	/*
	 * we have (part of) what they want
	 */
	copy_sz = XMIN(sc->copy_size, body_sz);
	debug(20, 3) ("storeClientReadHeader: copying %d bytes of body\n",
	    (int) copy_sz);
	xmemmove(sc->copy_buf, sc->copy_buf + swap_hdr_sz, copy_sz);
	if (sc->copy_offset == 0 && len > 0 && memHaveHeaders(mem) == 0)
	    httpReplyParse(mem->reply, sc->copy_buf,
		headersEnd(sc->copy_buf, copy_sz));
	storeClientCallback(sc, copy_sz);
	return;
    }
    /*
     * we don't have what the client wants, but at least we now
     * know the swap header size.
     */
    storeClientFileRead(sc);
}
예제 #6
0
파일: icap_respmod.c 프로젝트: OPSF/uClinux
static int
buildRespModHeader(MemBuf * mb, IcapStateData * icap, char *buf,
    ssize_t len, int theEnd)
{
    MemBuf mb_hdr;
    char *client_addr;
    int o2=0;
    int o3=0;
    int hlen;
    int consumed;
    icap_service *service;
    HttpReply *r;

    if (memBufIsNull(&icap->respmod.req_hdr_copy))
	memBufDefInit(&icap->respmod.req_hdr_copy);

    memBufAppend(&icap->respmod.req_hdr_copy, buf, len);

    if (icap->respmod.req_hdr_copy.size > 4 && strncmp(icap->respmod.req_hdr_copy.buf, "HTTP/", 5)) {
	debug(81, 3) ("buildRespModHeader: Non-HTTP-compliant header: '%s'\n", buf);
	/*
	 *Possible we can consider that we did not have http responce headers 
	 *(maybe HTTP 0.9 protocol), lets returning -1...
	 */
	consumed=-1;
	o2=-1;
	memBufDefInit(&mb_hdr);
    }
    else{

        hlen = headersEnd(icap->respmod.req_hdr_copy.buf,
			   icap->respmod.req_hdr_copy.size);
        debug(81, 3) ("buildRespModHeader: headersEnd = %d(%s)\n", hlen,buf);
        if (0 == hlen)
            return 0;

	/*
	 * calc how many bytes from this 'buf' went towards the
	 * reply header.
	 */
	consumed = hlen - (icap->respmod.req_hdr_copy.size - len);
	debug(81, 3) ("buildRespModHeader: consumed = %d\n", consumed);


	/*
	 * now, truncate our req_hdr_copy at the header end.
	 * this 'if' statement might be unncessary?
	 */
	if (hlen < icap->respmod.req_hdr_copy.size)
	     icap->respmod.req_hdr_copy.size = hlen;
	
	/* Copy request header */
	memBufDefInit(&mb_hdr);
	httpBuildRequestPrefix(icap->request, icap->request,
			       icap->respmod.entry, &mb_hdr, icap->http_flags);
	o2 = mb_hdr.size;
    }

    /* Copy response header - Append to request header mbuffer */
    memBufAppend(&mb_hdr,
	icap->respmod.req_hdr_copy.buf, icap->respmod.req_hdr_copy.size);
    o3 = mb_hdr.size;

    service = icap->current_service;
    assert(service);
    client_addr = inet_ntoa(icap->request->client_addr);

    r = httpReplyCreate();
    httpReplyParse(r, icap->respmod.req_hdr_copy.buf,
	icap->respmod.req_hdr_copy.size);
    icap->respmod.res_body_sz = httpReplyBodySize(icap->request->method, r);
    httpReplyDestroy(r);
    if (icap->respmod.res_body_sz)
	getICAPRespModString(mb, 0, o2, o3, client_addr, icap, service);
    else
	getICAPRespModString(mb, 0, o2, -o3, client_addr, icap, service);
    if (Config.icapcfg.preview_enable)
	if (icap->preview_size >= 0) {
	    memBufPrintf(mb, "Preview: %d\r\n", icap->preview_size);
	    icap->flags.preview_done = 0;
	}
    if(service->keep_alive){
	icap->flags.keep_alive = 1;
	memBufAppend(mb, "Connection: keep-alive\r\n", 24);
    }
    else{
	icap->flags.keep_alive = 0;
	memBufAppend(mb, "Connection: close\r\n", 19);
    }
    memBufAppend(mb, crlf, 2);
    memBufAppend(mb, mb_hdr.buf, mb_hdr.size);
    memBufClean(&mb_hdr);


    return consumed;
}
예제 #7
0
static void
storeClientReadHeader(void *data, const char *buf, ssize_t len)
{
    store_client *sc = data;
    StoreEntry *e = sc->entry;
    MemObject *mem = e->mem_obj;
    STCB *callback = sc->callback;
    int swap_hdr_sz = 0;
    size_t body_sz;
    size_t copy_sz;
    tlv *tlv_list;
    tlv *t;
    int swap_object_ok = 1;
    assert(sc->flags.disk_io_pending);
    sc->flags.disk_io_pending = 0;
    assert(sc->callback != NULL);
    debug(20, 3) ("storeClientReadHeader: len %d\n", len);
    if (len < 0) {
	debug(20, 3) ("storeClientReadHeader: %s\n", xstrerror());
	sc->callback = NULL;
	callback(sc->callback_data, sc->copy_buf, len);
	return;
    }
    tlv_list = storeSwapMetaUnpack(buf, &swap_hdr_sz);
    if (swap_hdr_sz > len) {
	/* oops, bad disk file? */
	debug(20, 1) ("WARNING: swapfile header too small\n");
	sc->callback = NULL;
	callback(sc->callback_data, sc->copy_buf, -1);
	return;
    }
    if (tlv_list == NULL) {
	debug(20, 1) ("WARNING: failed to unpack swapfile meta data\n");
	sc->callback = NULL;
	callback(sc->callback_data, sc->copy_buf, -1);
	return;
    }
    /*
     * Check the meta data and make sure we got the right object.
     */
    for (t = tlv_list; t; t = t->next) {
	switch (t->type) {
	case STORE_META_KEY:
	    assert(t->length == MD5_DIGEST_CHARS);
	    if (memcmp(t->value, e->key, MD5_DIGEST_CHARS))
		debug(20, 1) ("WARNING: swapin MD5 mismatch\n");
	    break;
	case STORE_META_URL:
	    if (NULL == mem->url)
		(void) 0;	/* can't check */
	    else if (0 == strcasecmp(mem->url, t->value))
		(void) 0;	/* a match! */
	    else {
		debug(20, 1) ("storeClientReadHeader: URL mismatch\n");
		debug(20, 1) ("\t{%s} != {%s}\n", t->value, mem->url);
		swap_object_ok = 0;
		break;
	    }
	    break;
	case STORE_META_STD:
	    break;
	default:
	    debug(20, 1) ("WARNING: got unused STORE_META type %d\n", t->type);
	    break;
	}
    }
    storeSwapTLVFree(tlv_list);
    if (!swap_object_ok) {
	sc->callback = NULL;
	callback(sc->callback_data, sc->copy_buf, -1);
	return;
    }
    mem->swap_hdr_sz = swap_hdr_sz;
    mem->object_sz = e->swap_file_sz - swap_hdr_sz;
    /*
     * If our last read got some data the client wants, then give
     * it to them, otherwise schedule another read.
     */
    body_sz = len - swap_hdr_sz;
    if (sc->copy_offset < body_sz) {
	/*
	 * we have (part of) what they want
	 */
	copy_sz = XMIN(sc->copy_size, body_sz);
	debug(20, 3) ("storeClientReadHeader: copying %d bytes of body\n",
	    copy_sz);
	xmemmove(sc->copy_buf, sc->copy_buf + swap_hdr_sz, copy_sz);
	if (sc->copy_offset == 0 && len > 0 && mem->reply->sline.status == 0)
	    httpReplyParse(mem->reply, sc->copy_buf,
		headersEnd(sc->copy_buf, copy_sz));
	sc->callback = NULL;
	callback(sc->callback_data, sc->copy_buf, copy_sz);
	return;
    }
    /*
     * we don't have what the client wants, but at least we now
     * know the swap header size.
     */
    storeClientFileRead(sc);
}
예제 #8
0
static void
urnHandleReply(void *data, char *buf, ssize_t size)
{
    UrnState *urnState = data;
    StoreEntry *e = urnState->entry;
    StoreEntry *urlres_e = urnState->urlres_e;
    char *s = NULL;
    size_t k;
    HttpReply *rep;
    url_entry *urls;
    url_entry *u;
    url_entry *min_u;
    MemBuf mb;
    ErrorState *err;
    int i;
    int urlcnt = 0;
    http_version_t version;

    debug(52, 3) ("urnHandleReply: Called with size=%d.\n", (int) size);
    if (EBIT_TEST(urlres_e->flags, ENTRY_ABORTED)) {
	memFree(buf, MEM_4K_BUF);
	return;
    }
    if (size == 0) {
	memFree(buf, MEM_4K_BUF);
	return;
    } else if (size < 0) {
	memFree(buf, MEM_4K_BUF);
	return;
    }
    if (urlres_e->store_status == STORE_PENDING && size < SM_PAGE_SIZE) {
	storeClientCopy(urnState->sc, urlres_e,
	    size,
	    0,
	    SM_PAGE_SIZE,
	    buf,
	    urnHandleReply,
	    urnState);
	return;
    }
    /* we know its STORE_OK */
    k = headersEnd(buf, size);
    if (0 == k) {
	debug(52, 1) ("urnHandleReply: didn't find end-of-headers for %s\n",
	    storeUrl(e));
	return;
    }
    s = buf + k;
    assert(urlres_e->mem_obj->reply);
    httpReplyParse(urlres_e->mem_obj->reply, buf, k);
    debug(52, 3) ("mem->reply exists, code=%d.\n",
	urlres_e->mem_obj->reply->sline.status);
    if (urlres_e->mem_obj->reply->sline.status != HTTP_OK) {
	debug(52, 3) ("urnHandleReply: failed.\n");
	err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND);
	err->request = requestLink(urnState->request);
	err->url = xstrdup(storeUrl(e));
	errorAppendEntry(e, err);
	return;
    }
    while (xisspace(*s))
	s++;
    urls = urnParseReply(s, urnState->request->method);
    for (i = 0; NULL != urls[i].url; i++)
	urlcnt++;
    debug(53, 3) ("urnFindMinRtt: Counted %d URLs\n", i);
    if (urls == NULL) {		/* unkown URN error */
	debug(52, 3) ("urnTranslateDone: unknown URN %s\n", storeUrl(e));
	err = errorCon(ERR_URN_RESOLVE, HTTP_NOT_FOUND);
	err->request = requestLink(urnState->request);
	err->url = xstrdup(storeUrl(e));
	errorAppendEntry(e, err);
	return;
    }
    min_u = urnFindMinRtt(urls, urnState->request->method, NULL);
    qsort(urls, urlcnt, sizeof(*urls), url_entry_sort);
    storeBuffer(e);
    memBufDefInit(&mb);
    memBufPrintf(&mb,
	"<TITLE>Select URL for %s</TITLE>\n"
	"<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"
	"<H2>Select URL for %s</H2>\n"
	"<TABLE BORDER=\"0\" WIDTH=\"100%%\">\n", storeUrl(e), storeUrl(e));
    for (i = 0; i < urlcnt; i++) {
	u = &urls[i];
	debug(52, 3) ("URL {%s}\n", u->url);
	memBufPrintf(&mb,
	    "<TR><TD><A HREF=\"%s\">%s</A></TD>", u->url, u->url);
	if (urls[i].rtt > 0)
	    memBufPrintf(&mb,
		"<TD align=\"right\">%4d <it>ms</it></TD>", u->rtt);
	else
	    memBufPrintf(&mb, "<TD align=\"right\">Unknown</TD>");
	memBufPrintf(&mb,
	    "<TD>%s</TD></TR>\n", u->flags.cached ? "    [cached]" : " ");
    }
    memBufPrintf(&mb,
	"</TABLE>"
	"<HR noshade size=\"1px\">\n"
	"<ADDRESS>\n"
	"Generated by %s@%s\n"
	"</ADDRESS>\n",
	full_appname_string, getMyHostname());
    rep = e->mem_obj->reply;
    httpReplyReset(rep);
    httpBuildVersion(&version, 1, 0);
    httpReplySetHeaders(rep, version, HTTP_MOVED_TEMPORARILY, NULL,
	"text/html", mb.size, 0, squid_curtime);
    if (urnState->flags.force_menu) {
	debug(51, 3) ("urnHandleReply: forcing menu\n");
    } else if (min_u) {
	httpHeaderPutStr(&rep->header, HDR_LOCATION, min_u->url);
    }
    httpBodySet(&rep->body, &mb);
    httpReplySwapOut(rep, e);
    storeComplete(e);
    memFree(buf, MEM_4K_BUF);
    for (i = 0; i < urlcnt; i++) {
	safe_free(urls[i].url);
	safe_free(urls[i].host);
    }
    safe_free(urls);
    /* mb was absorbed in httpBodySet call, so we must not clean it */
    storeUnregister(urnState->sc, urlres_e, urnState);
    storeUnlockObject(urlres_e);
    storeUnlockObject(urnState->entry);
    requestUnlink(urnState->request);
    requestUnlink(urnState->urlres_r);
    cbdataFree(urnState);
}
static void
netdbExchangeHandleReply(void *data, char *buf, ssize_t size)
{
    netdbExchangeState *ex = data;
    int rec_sz = 0;
    ssize_t o;
    struct in_addr addr;
    double rtt;
    double hops;
    char *p;
    int j;
    HttpReply *rep;
    size_t hdr_sz;
    int nused = 0;
    rec_sz = 0;
    rec_sz += 1 + sizeof(addr.s_addr);
    rec_sz += 1 + sizeof(int);
    rec_sz += 1 + sizeof(int);
    ex->seen = ex->used + size;
    debug(38, 3) ("netdbExchangeHandleReply: %d bytes\n", (int) size);
    if (!cbdataValid(ex->p)) {
	debug(38, 3) ("netdbExchangeHandleReply: Peer became invalid\n");
	netdbExchangeDone(ex);
	return;
    }
    debug(38, 3) ("netdbExchangeHandleReply: for '%s:%d'\n", ex->p->host, ex->p->http_port);
    p = buf;
    if (0 == ex->used) {
	/* skip reply headers */
	if ((hdr_sz = headersEnd(p, size))) {
	    debug(38, 5) ("netdbExchangeHandleReply: hdr_sz = %ld\n", (long int) hdr_sz);
	    rep = ex->e->mem_obj->reply;
	    if (0 == rep->sline.status)
		httpReplyParse(rep, buf, hdr_sz);
	    debug(38, 3) ("netdbExchangeHandleReply: reply status %d\n",
		rep->sline.status);
	    if (HTTP_OK != rep->sline.status) {
		netdbExchangeDone(ex);
		return;
	    }
	    assert(size >= hdr_sz);
	    ex->used += hdr_sz;
	    size -= hdr_sz;
	    p += hdr_sz;
	} else {
	    if (size >= ex->buf_sz) {
		debug(38, 3) ("netdbExchangeHandleReply: Too big HTTP header, aborting\n");
		netdbExchangeDone(ex);
		return;
	    } else {
		size = 0;
	    }
	}
    }
    debug(38, 5) ("netdbExchangeHandleReply: start parsing loop, size = %ld\n",
	(long int) size);
    while (size >= rec_sz) {
	debug(38, 5) ("netdbExchangeHandleReply: in parsing loop, size = %ld\n",
	    (long int) size);
	addr.s_addr = any_addr.s_addr;
	hops = rtt = 0.0;
	for (o = 0; o < rec_sz;) {
	    switch ((int) *(p + o)) {
	    case NETDB_EX_NETWORK:
		o++;
		xmemcpy(&addr.s_addr, p + o, sizeof(addr.s_addr));
		o += sizeof(addr.s_addr);
		break;
	    case NETDB_EX_RTT:
		o++;
		xmemcpy(&j, p + o, sizeof(int));
		o += sizeof(int);
		rtt = (double) ntohl(j) / 1000.0;
		break;
	    case NETDB_EX_HOPS:
		o++;
		xmemcpy(&j, p + o, sizeof(int));
		o += sizeof(int);
		hops = (double) ntohl(j) / 1000.0;
		break;
	    default:
		debug(38, 1) ("netdbExchangeHandleReply: corrupt data, aborting\n");
		netdbExchangeDone(ex);
		return;
	    }
	}
	if (addr.s_addr != any_addr.s_addr && rtt > 0)
	    netdbExchangeUpdatePeer(addr, ex->p, rtt, hops);
	assert(o == rec_sz);
	ex->used += rec_sz;
	size -= rec_sz;
	p += rec_sz;
	/*
	 * This is a fairly cpu-intensive loop, break after adding
	 * just a few
	 */
	if (++nused == 20)
	    break;
    }
    debug(38, 3) ("netdbExchangeHandleReply: used %d entries, (x %d bytes) == %d bytes total\n",
	nused, rec_sz, nused * rec_sz);
    debug(38, 3) ("netdbExchangeHandleReply: seen %ld, used %ld\n", (long int) ex->seen, (long int) ex->used);
    if (EBIT_TEST(ex->e->flags, ENTRY_ABORTED)) {
	debug(38, 3) ("netdbExchangeHandleReply: ENTRY_ABORTED\n");
	netdbExchangeDone(ex);
    } else if (ex->e->store_status == STORE_PENDING) {
	debug(38, 3) ("netdbExchangeHandleReply: STORE_PENDING\n");
	storeClientCopy(ex->sc, ex->e, ex->seen, ex->used, ex->buf_sz,
	    ex->buf, netdbExchangeHandleReply, ex);
    } else if (ex->seen < ex->e->mem_obj->inmem_hi) {
	debug(38, 3) ("netdbExchangeHandleReply: ex->e->mem_obj->inmem_hi\n");
	storeClientCopy(ex->sc, ex->e, ex->seen, ex->used, ex->buf_sz,
	    ex->buf, netdbExchangeHandleReply, ex);
    } else {
	debug(38, 3) ("netdbExchangeHandleReply: Done\n");
	netdbExchangeDone(ex);
    }
}