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;
}
Esempio n. 2
0
File: send.c Progetto: aosm/bind
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;
}