int Resolver :: ar_procanswer(DNSSession* session, HEADER* hptr, u_char* buf, u_char* eob) { u_char* cp = NULL; char** alias = NULL; int xclass, type = 0, dlen, len, ans = 0, n = 0; char** adr = NULL; char ar_hostbuf[MAXDNAME]; /* Must be on stack */ unsigned int ancount = 0 , arcount = 0 , nscount = 0, qdcount = 0; int host_cnt = 0; /* * convert things to be in the right order. */ ancount = ntohs(hptr->ancount); arcount = ntohs(hptr->arcount); nscount = ntohs(hptr->nscount); qdcount = ntohs(hptr->qdcount); cp = buf + HFIXEDSZ; hent& re_he = session->getHent(); adr = re_he.h_addr_list; #ifdef DNS_DEBUG Print_query(buf, eob, 1); #endif while (*adr) { adr++; host_cnt++; } alias = re_he.h_aliases; while (*alias) { alias++; } /* * Skip over the original question. */ while (qdcount > 0) { qdcount--; cp += dn_skipname((const unsigned char*)cp, (const unsigned char*)eob) + QFIXEDSZ; } /* * process each answer sent to us. blech. */ while ((ancount > 0) && (cp < eob)) { ancount--; PR_Lock(dnslock); n = dn_expand((const unsigned char*)buf, (const unsigned char*)eob, (const unsigned char*)cp, (char*)ar_hostbuf, sizeof(ar_hostbuf)); PR_Unlock(dnslock); cp += n; if (n <= 0) return ans; ans++; /* * 'skip' past the general dns crap (ttl, xclass, etc) to get * the pointer to the right spot. Some of thse are actually * useful so its not a good idea to skip past in one big jump. */ type = (int)GetShort(cp); cp += sizeof(short); xclass = (int)GetShort(cp); cp += sizeof(short); /* ttl = */ (void)(PRUint32)GetLong(cp); cp += INT32SZ; dlen = (int)GetShort(cp); cp += sizeof(short); session->setType(type); switch(type) { case T_A : { re_he.h_addrtype = PR_AF_INET; re_he.h_length = INADDRSZ; re_he.h_addr_list[host_cnt] = (char *)malloc(INADDRSZ); memcpy(re_he.h_addr_list[host_cnt], cp, dlen); re_he.h_addr_list[++host_cnt] = NULL; cp += dlen; len = strlen(ar_hostbuf); if (!re_he.h_name) { re_he.h_name = (char *)malloc(len+1); (void)strcpy(re_he.h_name, ar_hostbuf); } break; } case T_AAAA : { char buf[1024]; re_he.h_addrtype = PR_AF_INET6; re_he.h_length = IN6ADDRSZ; re_he.h_addr_list[host_cnt] = (char *)malloc(16); memcpy(re_he.h_addr_list[host_cnt], cp, dlen); re_he.h_addr_list[++host_cnt] = NULL; cp += dlen; len = strlen(ar_hostbuf); if (!re_he.h_name) { re_he.h_name = (char *)malloc(len+1); (void)strcpy(re_he.h_name, ar_hostbuf); } break; } case T_PTR : { PR_Lock(dnslock); n = dn_expand((const unsigned char*)buf, (const unsigned char*)eob, (const unsigned char*)cp, (char*)ar_hostbuf, sizeof(ar_hostbuf)); PR_Unlock(dnslock); if (n < 0) { cp += n; continue; } cp += n; len = strlen(ar_hostbuf)+1; /* * copy the returned hostname into the host name * or alias field if there is a known hostname * already. */ if (!re_he.h_name) { re_he.h_name = (char *)malloc(len); (void)strcpy(re_he.h_name, ar_hostbuf); } else { *alias = (char*)malloc(len); if (!*alias) return -1; (void)strcpy(*alias++, ar_hostbuf); *alias = NULL; } break; } case T_CNAME : { cp += dlen; if (alias >= &(re_he.h_aliases[MAXALIASES-1])) continue; n = strlen(ar_hostbuf)+1; *alias = (char*)malloc(n); if (!*alias) return -1; (void)strcpy(*alias++, ar_hostbuf); *alias = NULL; break; } default : { break; } } } return ans; }
int SendRequest(union res_sockaddr_union *nsAddrPtr, const u_char *buf, int buflen, u_char *answer, u_int anslen, int *trueLenPtr) { int n, try, v_circuit, resplen; ISC_SOCKLEN_T salen; int gotsomewhere = 0, connected = 0; int connreset = 0; u_short id, len; u_char *cp; fd_set dsmask; struct timeval timeout; const HEADER *hp = (const HEADER *) buf; HEADER *anhp = (HEADER *) answer; struct iovec iov[2]; int terrno = ETIMEDOUT; char junk[512]; struct sockaddr_storage sa; int family = nsAddrPtr->sin.sin_family; int clen = (family == AF_INET) ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6); if (res.options & RES_DEBUG2) { printf("------------\nSendRequest(), len %d\n", buflen); Print_query(buf, buf + buflen, 1); } v_circuit = (res.options & RES_USEVC) || buflen > PACKETSZ; id = hp->id; /* * Send request, RETRY times, or until successful */ for (try = 0; try < res.retry; try++) { usevc: if (v_circuit) { int truncated = 0; /* * Use virtual circuit; * at most one attempt per server. */ try = res.retry; if (s < 0) { s = socket(family, SOCK_STREAM, 0); if (s < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("socket (vc) failed"); continue; } if (connect(s, (struct sockaddr *)nsAddrPtr, clen) < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("connect failed"); (void) close(s); s = -1; continue; } } /* * Send length & message */ __putshort(buflen, (u_char *)&len); iov[0].iov_base = (caddr_t)&len; iov[0].iov_len = INT16SZ; DE_CONST(buf, iov[1].iov_base); iov[1].iov_len = buflen; if (writev(s, iov, 2) != INT16SZ + buflen) { terrno = errno; if (res.options & RES_DEBUG) perror("write failed"); (void) close(s); s = -1; continue; } /* * Receive length & response */ cp = answer; len = INT16SZ; while ((n = read(s, (char *)cp, (int)len)) > 0) { cp += n; if ((len -= n) <= 0) break; } if (n <= 0) { terrno = errno; if (res.options & RES_DEBUG) perror("read failed"); (void) close(s); s = -1; /* * A long running process might get its TCP * connection reset if the remote server was * restarted. Requery the server instead of * trying a new one. When there is only one * server, this means that a query might work * instead of failing. We only allow one reset * per query to prevent looping. */ if (terrno == ECONNRESET && !connreset) { connreset = 1; } continue; } cp = answer; if ((resplen = ns_get16((u_char*)cp)) > (int)anslen) { if (res.options & RES_DEBUG) fprintf(stderr, "response truncated\n"); len = anslen; truncated = 1; } else len = resplen; while (len != 0 && (n = read(s, (char *)cp, (int)len)) > 0) { cp += n; len -= n; } if (n <= 0) { terrno = errno; if (res.options & RES_DEBUG) perror("read failed"); (void) close(s); s = -1; continue; } if (truncated) { /* * Flush rest of answer * so connection stays in synch. */ anhp->tc = 1; len = resplen - anslen; while (len != 0) { n = (len > sizeof(junk) ? sizeof(junk) : len); if ((n = read(s, junk, n)) > 0) len -= n; else break; } } } else { /* * Use datagrams. */ if (s < 0) { s = socket(family, SOCK_DGRAM, 0); if (s < 0) { terrno = errno; if (res.options & RES_DEBUG) perror("socket (dg) failed"); continue; } } #if BSD >= 43 if (connected == 0) { if (connect(s, (struct sockaddr *)nsAddrPtr, clen) < 0) { if (res.options & RES_DEBUG) perror("connect"); continue; } connected = 1; } if (send(s, buf, buflen, 0) != buflen) { if (res.options & RES_DEBUG) perror("send"); continue; } #else /* BSD */ if (sendto(s, (const char *)buf, buflen, 0, (struct sockaddr *) nsAddrPtr, clen) != buflen) { if (res.options & RES_DEBUG) perror("sendto"); continue; } #endif /* * Wait for reply */ timeout.tv_sec = (res.retrans << try); if (timeout.tv_sec <= 0) timeout.tv_sec = 1; timeout.tv_usec = 0; wait: FD_ZERO(&dsmask); FD_SET(s, &dsmask); n = select(s+1, &dsmask, (fd_set *)NULL, (fd_set *)NULL, &timeout); if (n < 0) { if (res.options & RES_DEBUG) perror("select"); continue; } if (n == 0) { /* * timeout */ if (res.options & RES_DEBUG) printf("timeout\n"); #if BSD >= 43 gotsomewhere = 1; #endif continue; } salen = sizeof sa; resplen = recvfrom(s, (char *)answer, anslen, 0, (struct sockaddr *)&sa, &salen); if (resplen <= 0) { if (res.options & RES_DEBUG) perror("recvfrom"); continue; } gotsomewhere = 1; if (id != anhp->id) { /* * response from old query, ignore it */ if (res.options & RES_DEBUG2) { printf("------------\nOld answer:\n"); Print_query(answer, answer+resplen, 1); } goto wait; } if (!(res.options & RES_IGNTC) && anhp->tc) { /* * get rest of answer; * use TCP with same server. */ if (res.options & RES_DEBUG) printf("truncated answer\n"); (void) close(s); s = -1; v_circuit = 1; goto usevc; } } if (res.options & RES_DEBUG) { if (res.options & RES_DEBUG2) printf("------------\nGot answer (%d bytes):\n", resplen); else printf("------------\nGot answer:\n"); Print_query(answer, answer+resplen, 1); } (void) close(s); s = -1; *trueLenPtr = resplen; return (SUCCESS); } if (s >= 0) { (void) close(s); s = -1; } if (v_circuit == 0) if (gotsomewhere == 0) return NO_RESPONSE; /* no nameservers found */ else return TIME_OUT; /* no answer obtained */ else if (errno == ECONNREFUSED) return NO_RESPONSE; else return ERROR; }