int res_send (const u_char *buf, int buflen, u_char *ans, int anssiz) { hp = (HEADER *) buf; anhp = (HEADER *) ans; ns_buf = (u_char*)buf; ns_ans = ans; ns_buflen = buflen; ns_anssiz = anssiz; if ((_res.options & RES_INIT) == 0 && res_init() == -1) { /* errno should have been set by res_init() in this case */ return (-1); } DprintQ ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_QUERY), (";; res_send()\n"), buf, buflen); v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; gotsomewhere = 0; connreset = 0; terrno = ETIMEDOUT; badns = 0; /* Send request, RETRY times, or until successful */ for (Try = 0; Try < _res.retry; Try++) { for (ns = 0; ns < _res.nscount; ns++) { struct sockaddr_in *nsap = &_res.nsaddr_list[ns]; int rc; do rc = name_server_send (ns, nsap); while (rc == SAME_NS); if (rc != NEXT_NS) return (rc); } } resolve_close(); if (!v_circuit) { if (!gotsomewhere) SOCK_ERRNO (ECONNREFUSED); /* no nameservers found */ else SOCK_ERRNO (ETIMEDOUT); /* no answer obtained */ } else SOCK_ERRNO (terrno); return (-1); }
/*% res_nsendsigned */ int res_nsendsigned(res_state statp, const u_char *msg, int msglen, ns_tsig_key *key, u_char *answer, int anslen) { res_state nstatp; DST_KEY *dstkey; int usingTCP = 0; u_char *newmsg; int newmsglen, bufsize, siglen; u_char sig[64]; HEADER *hp; time_t tsig_time; int ret; int len; dst_init(); nstatp = (res_state) malloc(sizeof(*statp)); if (nstatp == NULL) { errno = ENOMEM; return (-1); } memcpy(nstatp, statp, sizeof(*statp)); bufsize = msglen + 1024; newmsg = (u_char *) malloc(bufsize); if (newmsg == NULL) { free(nstatp); errno = ENOMEM; return (-1); } memcpy(newmsg, msg, msglen); newmsglen = msglen; if (ns_samename(key->alg, NS_TSIG_ALG_HMAC_MD5) != 1) dstkey = NULL; else dstkey = dst_buffer_to_key(key->name, KEY_HMAC_MD5, NS_KEY_TYPE_AUTH_ONLY, NS_KEY_PROT_ANY, key->data, key->len); if (dstkey == NULL) { errno = EINVAL; free(nstatp); free(newmsg); return (-1); } nstatp->nscount = 1; siglen = sizeof(sig); ret = ns_sign(newmsg, &newmsglen, bufsize, NOERROR, dstkey, NULL, 0, sig, &siglen, 0); if (ret < 0) { free (nstatp); free (newmsg); dst_free_key(dstkey); if (ret == NS_TSIG_ERROR_NO_SPACE) errno = EMSGSIZE; else if (ret == -1) errno = EINVAL; return (ret); } if (newmsglen > PACKETSZ || nstatp->options & RES_USEVC) usingTCP = 1; if (usingTCP == 0) nstatp->options |= RES_IGNTC; else nstatp->options |= RES_USEVC; /* * Stop res_send printing the answer. */ nstatp->options &= ~RES_DEBUG; nstatp->pfcode &= ~RES_PRF_REPLY; retry: len = res_nsend(nstatp, newmsg, newmsglen, answer, anslen); if (len < 0) { free (nstatp); free (newmsg); dst_free_key(dstkey); return (len); } ret = ns_verify(answer, &len, dstkey, sig, siglen, NULL, NULL, &tsig_time, nstatp->options & RES_KEEPTSIG); if (ret != 0) { Dprint((statp->options & RES_DEBUG) || ((statp->pfcode & RES_PRF_REPLY) && (statp->pfcode & RES_PRF_HEAD1)), (stdout, ";; got answer:\n")); DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, "%s", ""), answer, (anslen > len) ? len : anslen); if (ret > 0) { Dprint(statp->pfcode & RES_PRF_REPLY, (stdout, ";; server rejected TSIG (%s)\n", p_rcode(ret))); } else { Dprint(statp->pfcode & RES_PRF_REPLY, (stdout, ";; TSIG invalid (%s)\n", p_rcode(-ret))); } free (nstatp); free (newmsg); dst_free_key(dstkey); if (ret == -1) errno = EINVAL; else errno = ENOTTY; return (-1); } hp = (HEADER *) answer; if (hp->tc && !usingTCP && (statp->options & RES_IGNTC) == 0U) { nstatp->options &= ~RES_IGNTC; usingTCP = 1; goto retry; } Dprint((statp->options & RES_DEBUG) || ((statp->pfcode & RES_PRF_REPLY) && (statp->pfcode & RES_PRF_HEAD1)), (stdout, ";; got answer:\n")); DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, "%s", ""), answer, (anslen > len) ? len : anslen); Dprint(statp->pfcode & RES_PRF_REPLY, (stdout, ";; TSIG ok\n")); free (nstatp); free (newmsg); dst_free_key(dstkey); return (len); }
static int name_server_send (int ns, struct sockaddr_in *nsap) { int resplen = 0; if (badns & (1 << ns)) /* this NameServer already marked bad */ { resolve_close(); return (NEXT_NS); } if (Qhook) { int done = 0; int loops = 0; do { res_sendhookact act = (*Qhook) (&nsap, (const u_char**)&ns_buf, &ns_buflen, ns_ans, ns_anssiz, &resplen); switch (act) { case res_goahead: done = 1; break; case res_nextns: resolve_close(); return (NEXT_NS); case res_done: return (resplen); case res_modified: /* give the hook another try */ if (++loops < 42) break; /* fallthrough */ case res_error: /* fallthrough */ default: return (-1); } } while (!done); } Dprint (_res.options & RES_DEBUG, (";; Querying server (# %d) address = %s\n", ns + 1, inet_ntoa(nsap->sin_addr))); if (v_circuit) /* i.e. TCP */ { int truncated; u_short len; u_char *cp; /* Use virtual circuit; at most one attempt per server. */ Try = _res.retry; truncated = 0; if (!sock || !vc) { DWORD his_ip = ntohl (nsap->sin_addr.s_addr); WORD his_port = ntohs (nsap->sin_port); if (sock) resolve_close(); sock = (sock_type*) calloc (sizeof(_tcp_Socket), 1); if (!sock) { Perror ("calloc(vc)", "no memory"); return (-1); } if (!tcp_open(&sock->tcp,0,his_ip,his_port,NULL) || !tcp_conn(&sock->tcp,&errno,dns_timeout)) { Aerror ("tcp_open/vc", "failed/timeout", *nsap); badns |= (1 << ns); resolve_close(); return (NEXT_NS); } vc = 1; } /* Send length & message */ { int send_len = INT16SZ + ns_buflen; BYTE *send_buf = (BYTE*) alloca (send_len); PUTSHORT (ns_buflen, send_buf); memcpy (send_buf + INT16SZ, ns_buf, ns_buflen); if (sock_write(sock,send_buf,send_len) != send_len) { Perror ("sock_write() failed", sockerr(sock)); badns |= (1 << ns); resolve_close(); return (NEXT_NS); } } /* Receive length & response */ cp = ns_ans; len = INT16SZ; while ((n = tcp_read(&sock->tcp,cp,len,&errno,dns_timeout)) > 0) { cp += n; len -= n; if ((signed)len <= 0) break; } if (n <= 0) { Perror ("tcp_read() failed", sockerr(sock)); resolve_close(); return (NEXT_NS); } resplen = _getshort (ns_ans); if (resplen > ns_anssiz) { Dprint (_res.options & RES_DEBUG,(";; response truncated\n")); truncated = 1; len = ns_anssiz; } else len = resplen; cp = ns_ans; while (len && (n = tcp_read(&sock->tcp,cp,len,&errno,dns_timeout)) > 0) { cp += n; len -= n; } if (n <= 0) { Perror ("tcp_read(vc)",sockerr(sock)); resolve_close(); return (NEXT_NS); } if (truncated) { /* Flush rest of answer so connection stays in synch. */ anhp->tc = 1; len = resplen - ns_anssiz; while (len) { u_char junk[PACKETSZ]; n = (len > sizeof(junk) ? sizeof(junk) : len); n = tcp_read (&sock->tcp,junk,n,&errno,dns_timeout); if (n > 0) len -= n; else break; } } } else /* !v_circuit, i.e. UDP */ { DWORD timeout; if (!sock || vc) { if (vc) resolve_close(); sock = (sock_type*) calloc (sizeof(_udp_Socket), 1); if (!sock) { Perror ("calloc(dg)", "no memory"); return (-1); } connected = 0; } /* Connect only if we are sure we won't * receive a response from another server. */ if (!connected) { DWORD his_ip = ntohl (nsap->sin_addr.s_addr); WORD his_port = ntohs (nsap->sin_port); if (!udp_open(&sock->udp,0,his_ip,his_port,NULL)) { Aerror ("connect/dg", "ARP failed", *nsap); badns |= (1 << ns); resolve_close(); return (NEXT_NS); } connected = 1; } if (sock_write(sock,(const BYTE*)ns_buf,ns_buflen) != ns_buflen) { Perror ("sock_write() failed", ""); badns |= (1 << ns); resolve_close(); return (NEXT_NS); } /* Wait for reply */ timeout = (unsigned)_res.retrans << Try; if (Try > 0) timeout /= _res.nscount; if ((long)timeout <= 0) timeout = 1; wait: n = udp_read (&sock->udp, ns_ans, ns_anssiz, &errno, timeout); if (n == 0) { Dprint (_res.options & RES_DEBUG, (";; timeout\n")); gotsomewhere = 1; resolve_close(); return (NEXT_NS); } gotsomewhere = 1; if (hp->id != anhp->id) { /* response from old query, ignore it. * XXX - potential security hazard could be detected here. */ DprintQ ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_REPLY), (";; old answer:\n"), ns_ans, resplen); goto wait; } if (!(_res.options & RES_INSECURE2) && !res_queriesmatch(ns_buf, ns_buf+ns_buflen, ns_ans, ns_ans+ns_anssiz)) { /* response contains wrong query? ignore it. * XXX - potential security hazard could be detected here. */ DprintQ ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_REPLY), (";; wrong query name:\n"), ns_ans, resplen); goto wait; } if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP || anhp->rcode == REFUSED) { DprintQ (_res.options & RES_DEBUG,("server rejected query:\n"), ns_ans,resplen); badns |= (1 << ns); resolve_close(); /* don't retry if called from dig */ if (!_res.pfcode) return (NEXT_NS); } if (!(_res.options & RES_IGNTC) && anhp->tc) { /* get rest of answer; use TCP with same server. */ Dprint (_res.options & RES_DEBUG, (";; truncated answer\n")); v_circuit = 1; resolve_close(); return (SAME_NS); } } /* if vcicuit / dg */ Dprint ((_res.options & RES_DEBUG) || ((_res.pfcode & RES_PRF_REPLY) && (_res.pfcode & RES_PRF_HEAD1)), (";; got answer:\n")); DprintQ ((_res.options & RES_DEBUG) || (_res.pfcode & RES_PRF_REPLY), (" \b"), ns_ans, resplen); /* * If using virtual circuits (TCP), we assume that the first server * is preferred over the rest (i.e. it is on the local machine) and * only keep that one open. If we have temporarily opened a virtual * circuit, or if we haven't been asked to keep a socket open, * close the socket. */ if ((v_circuit && (!(_res.options & RES_USEVC) || ns != 0)) || !(_res.options & RES_STAYOPEN)) resolve_close(); if (Rhook) { int done = 0, loops = 0; do { res_sendhookact act = (*Rhook) (nsap, ns_buf, ns_buflen, ns_ans, ns_anssiz, &resplen); switch (act) { case res_goahead: case res_done: done = 1; break; case res_nextns: resolve_close(); return (NEXT_NS); case res_modified: /* give the hook another try */ if (++loops < 42) break; /* fallthrough */ case res_error: /* fallthrough */ default: return (-1); } } while (!done); } return (resplen); }