int tcp_connect(nsp_state *N, TCP_SOCKET *sock, char *host, unsigned short port, short int use_ssl) { #define __FN__ __FILE__ ":tcp_connect()" struct hostent *hp; struct sockaddr_in serv; if ((hp = gethostbyname(host)) == NULL) { n_warn(N, __FN__, "Host lookup error for %s", host); return -1; } nc_memset((char *)&serv, 0, sizeof(serv)); nc_memcpy((char *)&serv.sin_addr, hp->h_addr, hp->h_length); serv.sin_family = hp->h_addrtype; serv.sin_port = htons(port); if ((sock->socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) return -2; /* setsockopt(sock->socket, SOL_SOCKET, SO_KEEPALIVE, 0, 0); */ if (tcp_conn(N, sock, &serv, sizeof(serv), use_ssl) < 0) { /* n_warn(N, __FN__, "Error connecting to %s:%d", host, port); */ return -2; } return 0; #undef __FN__ }
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); }