/* 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); } }
/* * 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; }
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); }
/* 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); } }
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); }
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; }
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); }
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); } }