📄 res_send.c
字号:
#ifdef HAVE_SA_LEN /* There are people do not set sa_len. Be forgiving to them. */ if (sa->sa_len) return (sa->sa_len);#endif if (sa->sa_family == AF_INET) return (sizeof(struct sockaddr_in)); else if (sa->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6)); else return (0); /* unknown, die on connect */}/* * pick appropriate nsaddr_list for use. see res_init() for initialization. */static struct sockaddr *get_nsaddr(statp, n) res_state statp; size_t n;{ if (!statp->nsaddr_list[n].sin_family && EXT(statp).ext) { /* * - EXT(statp).ext->nsaddrs[n] holds an address that is larger * than struct sockaddr, and * - user code did not update statp->nsaddr_list[n]. */ return (struct sockaddr *)(void *)&EXT(statp).ext->nsaddrs[n]; } else { /* * - user code updated statp->nsaddr_list[n], or * - statp->nsaddr_list[n] has the same content as * EXT(statp).ext->nsaddrs[n]. */ return (struct sockaddr *)(void *)&statp->nsaddr_list[n]; }}static intsend_vc(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz, int *terrno, int ns){ const HEADER *hp = (const HEADER *) buf; HEADER *anhp = (HEADER *) ans; struct sockaddr *nsap; int nsaplen; int truncating, connreset, resplen, n; struct iovec iov[2]; u_short len; u_char *cp; void *tmp; nsap = get_nsaddr(statp, ns); nsaplen = get_salen(nsap); connreset = 0; same_ns: truncating = 0; /* Are we still talking to whom we want to talk to? */ if (statp->_vcsock >= 0 && (statp->_flags & RES_F_VC) != 0) { struct sockaddr_storage peer; ISC_SOCKLEN_T size = sizeof peer; if (getpeername(statp->_vcsock, (struct sockaddr *)&peer, &size) < 0 || !sock_eq((struct sockaddr *)&peer, nsap)) { res_nclose(statp); statp->_flags &= ~RES_F_VC; } } if (statp->_vcsock < 0 || (statp->_flags & RES_F_VC) == 0) { if (statp->_vcsock >= 0) res_nclose(statp); statp->_vcsock = socket(nsap->sa_family, SOCK_STREAM, 0); if (statp->_vcsock > highestFD) { res_nclose(statp); errno = ENOTSOCK; } if (statp->_vcsock < 0) { switch (errno) { case EPROTONOSUPPORT:#ifdef EPFNOSUPPORT case EPFNOSUPPORT:#endif case EAFNOSUPPORT: Perror(statp, stderr, "socket(vc)", errno); return (0); default: *terrno = errno; Perror(statp, stderr, "socket(vc)", errno); return (-1); } } errno = 0; if (connect(statp->_vcsock, nsap, nsaplen) < 0) { *terrno = errno; Aerror(statp, stderr, "connect/vc", errno, nsap, nsaplen); res_nclose(statp); return (0); } statp->_flags |= RES_F_VC; } /* * Send length & message */ ns_put16((u_short)buflen, (u_char*)&len); iov[0] = evConsIovec(&len, INT16SZ); DE_CONST(buf, tmp); iov[1] = evConsIovec(tmp, buflen); if (writev(statp->_vcsock, iov, 2) != (INT16SZ + buflen)) { *terrno = errno; Perror(statp, stderr, "write failed", errno); res_nclose(statp); return (0); } /* * Receive length & response */ read_len: cp = ans; len = INT16SZ; while ((n = read(statp->_vcsock, (char *)cp, (int)len)) > 0) { cp += n; if ((len -= n) == 0) break; } if (n <= 0) { *terrno = errno; Perror(statp, stderr, "read failed", errno); res_nclose(statp); /* * 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; res_nclose(statp); goto same_ns; } res_nclose(statp); return (0); } resplen = ns_get16(ans); if (resplen > anssiz) { Dprint(statp->options & RES_DEBUG, (stdout, ";; response truncated\n") ); truncating = 1; len = anssiz; } else len = resplen; if (len < HFIXEDSZ) { /* * Undersized message. */ Dprint(statp->options & RES_DEBUG, (stdout, ";; undersized: %d\n", len)); *terrno = EMSGSIZE; res_nclose(statp); return (0); } cp = ans; while (len != 0 && (n = read(statp->_vcsock, (char *)cp, (int)len)) > 0){ cp += n; len -= n; } if (n <= 0) { *terrno = errno; Perror(statp, stderr, "read(vc)", errno); res_nclose(statp); return (0); } if (truncating) { /* * Flush rest of answer so connection stays in synch. */ anhp->tc = 1; len = resplen - anssiz; while (len != 0) { char junk[PACKETSZ]; n = read(statp->_vcsock, junk, (len > sizeof junk) ? sizeof junk : len); if (n > 0) len -= n; else break; } } /* * If the calling applicating has bailed out of * a previous call and failed to arrange to have * the circuit closed or the server has got * itself confused, then drop the packet and * wait for the correct one. */ if (hp->id != anhp->id) { DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer (unexpected):\n"), ans, (resplen > anssiz) ? anssiz: resplen); goto read_len; } /* * All is well, or the error is fatal. Signal that the * next nameserver ought not be tried. */ return (resplen);}static intsend_dg(res_state statp, const u_char *buf, int buflen, u_char *ans, int anssiz, int *terrno, int ns, int *v_circuit, int *gotsomewhere){ const HEADER *hp = (const HEADER *) buf; HEADER *anhp = (HEADER *) ans; const struct sockaddr *nsap; int nsaplen; struct timespec now, timeout, finish; fd_set dsmask; struct sockaddr_storage from; ISC_SOCKLEN_T fromlen; int resplen, seconds, n, s; nsap = get_nsaddr(statp, ns); nsaplen = get_salen(nsap); if (EXT(statp).nssocks[ns] == -1) { EXT(statp).nssocks[ns] = socket(nsap->sa_family, SOCK_DGRAM, 0); if (EXT(statp).nssocks[ns] > highestFD) { res_nclose(statp); errno = ENOTSOCK; } if (EXT(statp).nssocks[ns] < 0) { switch (errno) { case EPROTONOSUPPORT:#ifdef EPFNOSUPPORT case EPFNOSUPPORT:#endif case EAFNOSUPPORT: Perror(statp, stderr, "socket(dg)", errno); return (0); default: *terrno = errno; Perror(statp, stderr, "socket(dg)", errno); return (-1); } }#ifndef CANNOT_CONNECT_DGRAM /* * On a 4.3BSD+ machine (client and server, * actually), sending to a nameserver datagram * port with no nameserver will cause an * ICMP port unreachable message to be returned. * If our datagram socket is "connected" to the * server, we get an ECONNREFUSED error on the next * socket operation, and select returns if the * error message is received. We can thus detect * the absence of a nameserver without timing out. */ if (connect(EXT(statp).nssocks[ns], nsap, nsaplen) < 0) { Aerror(statp, stderr, "connect(dg)", errno, nsap, nsaplen); res_nclose(statp); return (0); }#endif /* !CANNOT_CONNECT_DGRAM */ Dprint(statp->options & RES_DEBUG, (stdout, ";; new DG socket\n")) } s = EXT(statp).nssocks[ns];#ifndef CANNOT_CONNECT_DGRAM if (send(s, (const char*)buf, buflen, 0) != buflen) { Perror(statp, stderr, "send", errno); res_nclose(statp); return (0); }#else /* !CANNOT_CONNECT_DGRAM */ if (sendto(s, (const char*)buf, buflen, 0, nsap, nsaplen) != buflen) { Aerror(statp, stderr, "sendto", errno, nsap, nsaplen); res_nclose(statp); return (0); }#endif /* !CANNOT_CONNECT_DGRAM */ /* * Wait for reply. */ seconds = (statp->retrans << ns); if (ns > 0) seconds /= statp->nscount; if (seconds <= 0) seconds = 1; now = evNowTime(); timeout = evConsTime(seconds, 0); finish = evAddTime(now, timeout); goto nonow; wait: now = evNowTime(); nonow: FD_ZERO(&dsmask); FD_SET(s, &dsmask); if (evCmpTime(finish, now) > 0) timeout = evSubTime(finish, now); else timeout = evConsTime(0, 0); n = pselect(s + 1, &dsmask, NULL, NULL, &timeout, NULL); if (n == 0) { Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n")); *gotsomewhere = 1; return (0); } if (n < 0) { if (errno == EINTR) goto wait; Perror(statp, stderr, "select", errno); res_nclose(statp); return (0); } errno = 0; fromlen = sizeof(from); resplen = recvfrom(s, (char*)ans, anssiz,0, (struct sockaddr *)&from, &fromlen); if (resplen <= 0) { Perror(statp, stderr, "recvfrom", errno); res_nclose(statp); return (0); } *gotsomewhere = 1; if (resplen < HFIXEDSZ) { /* * Undersized message. */ Dprint(statp->options & RES_DEBUG, (stdout, ";; undersized: %d\n", resplen)); *terrno = EMSGSIZE; res_nclose(statp); return (0); } if (hp->id != anhp->id) { /* * response from old query, ignore it. * XXX - potential security hazard could * be detected here. */ DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; old answer:\n"), ans, (resplen > anssiz) ? anssiz : resplen); goto wait; } if (!(statp->options & RES_INSECURE1) && !res_ourserver_p(statp, (struct sockaddr *)&from)) { /* * response from wrong server? ignore it. * XXX - potential security hazard could * be detected here. */ DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; not our server:\n"), ans, (resplen > anssiz) ? anssiz : resplen); goto wait; }#ifdef RES_USE_EDNS0 if (anhp->rcode == FORMERR && (statp->options & RES_USE_EDNS0) != 0U) { /* * Do not retry if the server do not understand EDNS0. * The case has to be captured here, as FORMERR packet do not * carry query section, hence res_queriesmatch() returns 0. */ DprintQ(statp->options & RES_DEBUG, (stdout, "server rejected query with EDNS0:\n"), ans, (resplen > anssiz) ? anssiz : resplen); /* record the error */ statp->_flags |= RES_F_EDNS0ERR; res_nclose(statp); return (0); }#endif if (!(statp->options & RES_INSECURE2) && !res_queriesmatch(buf, buf + buflen, ans, ans + anssiz)) { /* * response contains wrong query? ignore it. * XXX - potential security hazard could * be detected here. */ DprintQ((statp->options & RES_DEBUG) || (statp->pfcode & RES_PRF_REPLY), (stdout, ";; wrong query name:\n"), ans, (resplen > anssiz) ? anssiz : resplen); goto wait; } if (anhp->rcode == SERVFAIL || anhp->rcode == NOTIMP || anhp->rcode == REFUSED) { DprintQ(statp->options & RES_DEBUG, (stdout, "server rejected query:\n"), ans, (resplen > anssiz) ? anssiz : resplen); res_nclose(statp); /* don't retry if called from dig */ if (!statp->pfcode) return (0); } if (!(statp->options & RES_IGNTC) && anhp->tc) { /* * To get the rest of answer, * use TCP with same server. */ Dprint(statp->options & RES_DEBUG, (stdout, ";; truncated answer\n")); *v_circuit = 1; res_nclose(statp); return (1); } /* * All is well, or the error is fatal. Signal that the * next nameserver ought not be tried. */ return (resplen);}static voidAerror(const res_state statp, FILE *file, const char *string, int error, const struct sockaddr *address, int alen){ int save = errno; char hbuf[NI_MAXHOST]; char sbuf[NI_MAXSERV]; alen = alen; if ((statp->options & RES_DEBUG) != 0U) { if (getnameinfo(address, alen, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), niflags)) { strncpy(hbuf, "?", sizeof(hbuf) - 1); hbuf[sizeof(hbuf) - 1] = '\0'; strncpy(sbuf, "?", sizeof(sbuf) - 1); sbuf[sizeof(sbuf) - 1] = '\0'; } fprintf(file, "res_send: %s ([%s].%s): %s\n", string, hbuf, sbuf, strerror(error)); } errno = save;}static voidPerror(const res_state statp, FILE *file, const char *string, int error) { int save = errno; if ((statp->options & RES_DEBUG) != 0U) fprintf(file, "res_send: %s: %s\n", string, strerror(error)); errno = save;}static intsock_eq(struct sockaddr *a, struct sockaddr *b) { struct sockaddr_in *a4, *b4; struct sockaddr_in6 *a6, *b6; if (a->sa_family != b->sa_family) return 0; switch (a->sa_family) { case AF_INET: a4 = (struct sockaddr_in *)a; b4 = (struct sockaddr_in *)b; return a4->sin_port == b4->sin_port && a4->sin_addr.s_addr == b4->sin_addr.s_addr; case AF_INET6: a6 = (struct sockaddr_in6 *)a; b6 = (struct sockaddr_in6 *)b; return a6->sin6_port == b6->sin6_port &&#ifdef HAVE_SIN6_SCOPE_ID a6->sin6_scope_id == b6->sin6_scope_id &&#endif IN6_ARE_ADDR_EQUAL(&a6->sin6_addr, &b6->sin6_addr); default: return 0; }}#ifdef NEED_PSELECT/* XXX needs to move to the porting library. */static intpselect(int nfds, void *rfds, void *wfds, void *efds, struct timespec *tsp, const sigset_t *sigmask){ struct timeval tv, *tvp; sigset_t sigs; int n; if (tsp) { tvp = &tv; tv = evTimeVal(*tsp); } else tvp = NULL; if (sigmask) sigprocmask(SIG_SETMASK, sigmask, &sigs); n = select(nfds, rfds, wfds, efds, tvp); if (sigmask) sigprocmask(SIG_SETMASK, &sigs, NULL); if (tsp) *tsp = evTimeSpec(tv); return (n);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -