📄 resolv.c
字号:
j = __encode_question(&q, packet + i, maxlen - i); if (j < 0) return j; return i + j;}#endif#ifdef L_dnslookup#ifdef __UCLIBC_HAS_THREADS__static pthread_mutex_t mylock = PTHREAD_MUTEX_INITIALIZER;# define LOCK __pthread_mutex_lock(&mylock)# define UNLOCK __pthread_mutex_unlock(&mylock);#else# define LOCK# define UNLOCK#endif/* Just for the record, having to lock __dns_lookup() just for these two globals * is pretty lame. I think these two variables can probably be de-global-ized, * which should eliminate the need for doing locking here... Needs a closer * look anyways. */static int ns=0, id=1;int __dns_lookup(const char *name, int type, int nscount, char **nsip, unsigned char **outpacket, struct resolv_answer *a){ int i, j, len, fd, pos, rc; struct timeval tv; fd_set fds; struct resolv_header h; struct resolv_question q; int retries = 0; unsigned char * packet = malloc(PACKETSZ); char *dns, *lookup = malloc(MAXDNAME); int variant = -1; struct sockaddr_in sa;#ifdef __UCLIBC_HAS_IPV6__ int v6; struct sockaddr_in6 sa6;#endif fd = -1; if (!packet || !lookup || !nscount) goto fail; DPRINTF("Looking up type %d answer for '%s'\n", type, name); LOCK; ns %= nscount; UNLOCK; while (retries < MAX_RETRIES) { if (fd != -1) close(fd); memset(packet, 0, PACKETSZ); memset(&h, 0, sizeof(h)); /* Mess with globals while under lock */ LOCK; ++id; id &= 0xffff; h.id = id; dns = nsip[ns]; UNLOCK; h.qdcount = 1; h.rd = 1; DPRINTF("encoding header\n", h.rd); i = __encode_header(&h, packet, PACKETSZ); if (i < 0) goto fail; strncpy(lookup,name,MAXDNAME); if (variant >= 0) { BIGLOCK; if (variant < __searchdomains) { strncat(lookup,".", MAXDNAME); strncat(lookup,__searchdomain[variant], MAXDNAME); } BIGUNLOCK; } DPRINTF("lookup name: %s\n", lookup); q.dotted = (char *)lookup; q.qtype = type; q.qclass = C_IN; /* CLASS_IN */ j = __encode_question(&q, packet+i, PACKETSZ-i); if (j < 0) goto fail; len = i + j; DPRINTF("On try %d, sending query to port %d of machine %s\n", retries+1, NAMESERVER_PORT, dns);#ifdef __UCLIBC_HAS_IPV6__ v6 = inet_pton(AF_INET6, dns, &sa6.sin6_addr) > 0; fd = socket(v6 ? AF_INET6 : AF_INET, SOCK_DGRAM, IPPROTO_UDP);#else fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);#endif if (fd < 0) { retries++; continue; } /* Connect to the UDP socket so that asyncronous errors are returned */ #ifdef __UCLIBC_HAS_IPV6__ if (v6) { sa6.sin6_family = AF_INET6; sa6.sin6_port = htons(NAMESERVER_PORT); /* sa6.sin6_addr is already here */ rc = connect(fd, (struct sockaddr *) &sa6, sizeof(sa6)); } else {#endif sa.sin_family = AF_INET; sa.sin_port = htons(NAMESERVER_PORT); sa.sin_addr.s_addr = inet_addr(dns); rc = connect(fd, (struct sockaddr *) &sa, sizeof(sa));#ifdef __UCLIBC_HAS_IPV6__ }#endif if (rc < 0) { if (errno == ENETUNREACH) { /* routing error, presume not transient */ goto tryall; } else /* retry */ retries++; continue; } DPRINTF("Transmitting packet of length %d, id=%d, qr=%d\n", len, h.id, h.qr); send(fd, packet, len, 0); FD_ZERO(&fds); FD_SET(fd, &fds); tv.tv_sec = REPLY_TIMEOUT; tv.tv_usec = 0; if (select(fd + 1, &fds, NULL, NULL, &tv) <= 0) { DPRINTF("Timeout\n"); /* timed out, so retry send and receive, * to next nameserver on queue */ goto tryall; } len = recv(fd, packet, 512, 0); if (len < HFIXEDSZ) { /* too short ! */ goto again; } __decode_header(packet, &h); DPRINTF("id = %d, qr = %d\n", h.id, h.qr); LOCK; if ((h.id != id) || (!h.qr)) { UNLOCK; /* unsolicited */ goto again; } UNLOCK; DPRINTF("Got response %s\n", "(i think)!"); DPRINTF("qrcount=%d,ancount=%d,nscount=%d,arcount=%d\n", h.qdcount, h.ancount, h.nscount, h.arcount); DPRINTF("opcode=%d,aa=%d,tc=%d,rd=%d,ra=%d,rcode=%d\n", h.opcode, h.aa, h.tc, h.rd, h.ra, h.rcode); if ((h.rcode) || (h.ancount < 1)) { /* negative result, not present */ goto again; } pos = HFIXEDSZ; for (j = 0; j < h.qdcount; j++) { DPRINTF("Skipping question %d at %d\n", j, pos); i = __length_question(packet, pos); DPRINTF("Length of question %d is %d\n", j, i); if (i < 0) goto again; pos += i; } DPRINTF("Decoding answer at pos %d\n", pos); for (j=0;j<h.ancount;j++) { i = __decode_answer(packet, pos, a); if (i<0) { DPRINTF("failed decode %d\n", i); goto again; } /* For all but T_SIG, accept first answer */ if (a->atype != T_SIG) break; DPRINTF("skipping T_SIG %d\n", i); free(a->dotted); pos += i; } DPRINTF("Answer name = |%s|\n", a->dotted); DPRINTF("Answer type = |%d|\n", a->atype); close(fd); if (outpacket) *outpacket = packet; else free(packet); free(lookup); return (len); /* success! */ tryall: /* if there are other nameservers, give them a go, otherwise return with error */ { variant = -1; LOCK; ns = (ns + 1) % nscount; if (ns == 0) retries++; UNLOCK; continue; } again: /* if there are searchdomains, try them or fallback as passed */ { int sdomains; BIGLOCK; sdomains=__searchdomains; BIGUNLOCK; if (variant < sdomains - 1) { /* next search */ variant++; } else { /* next server, first search */ LOCK; ns = (ns + 1) % nscount; if (ns == 0) retries++; UNLOCK; variant = -1; } } }fail: if (fd != -1) close(fd); if (lookup) free(lookup); if (packet) free(packet); h_errno = NETDB_INTERNAL; return -1;}#endif#ifdef L_opennameserversint __nameservers;char * __nameserver[MAX_SERVERS];int __searchdomains;char * __searchdomain[MAX_SEARCH];#ifdef __UCLIBC_HAS_THREADS__pthread_mutex_t __resolv_lock = PTHREAD_MUTEX_INITIALIZER;#endif/* * we currently read formats not quite the same as that on normal * unix systems, we can have a list of nameservers after the keyword. */int __open_nameservers(){ FILE *fp; int i;#define RESOLV_ARGS 5 char szBuffer[128], *p, *argv[RESOLV_ARGS]; int argc; BIGLOCK; if (__nameservers > 0) { BIGUNLOCK; return 0; } if ((fp = fopen("/etc/resolv.conf", "r")) || (fp = fopen("/etc/config/resolv.conf", "r"))) { while (fgets(szBuffer, sizeof(szBuffer), fp) != NULL) { for (p = szBuffer; *p && isspace(*p); p++) /* skip white space */; if (*p == '\0' || *p == '\n' || *p == '#') /* skip comments etc */ continue; argc = 0; while (*p && argc < RESOLV_ARGS) { argv[argc++] = p; while (*p && !isspace(*p) && *p != '\n') p++; while (*p && (isspace(*p) || *p == '\n')) /* remove spaces */ *p++ = '\0'; } if (strcmp(argv[0], "nameserver") == 0) { for (i = 1; i < argc && __nameservers < MAX_SERVERS; i++) { __nameserver[__nameservers++] = strdup(argv[i]); DPRINTF("adding nameserver %s\n", argv[i]); } } /* domain and search are mutually exclusive, the last one wins */ if (strcmp(argv[0],"domain")==0 || strcmp(argv[0],"search")==0) { while (__searchdomains > 0) { free(__searchdomain[--__searchdomains]); __searchdomain[__searchdomains] = NULL; } for (i=1; i < argc && __searchdomains < MAX_SEARCH; i++) { __searchdomain[__searchdomains++] = strdup(argv[i]); DPRINTF("adding search %s\n", argv[i]); } } } fclose(fp); DPRINTF("nameservers = %d\n", __nameservers); BIGUNLOCK; return 0; } DPRINTF("failed to open %s\n", "resolv.conf"); h_errno = NO_RECOVERY; BIGUNLOCK; return -1;}#endif#ifdef L_closenameserversvoid __close_nameservers(void){ BIGLOCK; while (__nameservers > 0) { free(__nameserver[--__nameservers]); __nameserver[__nameservers] = NULL; } while (__searchdomains > 0) { free(__searchdomain[--__searchdomains]); __searchdomain[__searchdomains] = NULL; } BIGUNLOCK;}#endif#ifdef L_gethostbynamestruct hostent *gethostbyname(const char *name){ static struct hostent h; static char buf[sizeof(struct in_addr) + sizeof(struct in_addr *)*2 + sizeof(char *)*(ALIAS_DIM) + 256/*namebuffer*/ + 32/* margin */]; struct hostent *hp; gethostbyname_r(name, &h, buf, sizeof(buf), &hp, &h_errno); return hp;}#endif#ifdef L_gethostbyname2struct hostent *gethostbyname2(const char *name, int family){#ifndef __UCLIBC_HAS_IPV6__ return family == AF_INET ? gethostbyname(name) : (struct hostent*)0;#else /* __UCLIBC_HAS_IPV6__ */ static struct hostent h; static char buf[sizeof(struct in6_addr) + sizeof(struct in6_addr *)*2 + sizeof(char *)*(ALIAS_DIM) + 256/*namebuffer*/ + 32/* margin */]; struct hostent *hp; gethostbyname2_r(name, family, &h, buf, sizeof(buf), &hp, &h_errno); return hp;#endif /* __UCLIBC_HAS_IPV6__ */}#endif#ifdef L_res_initstruct __res_state _res;int res_init(void){ struct __res_state *rp = &(_res); __close_nameservers(); __open_nameservers(); rp->retrans = RES_TIMEOUT; rp->retry = 4; rp->options = RES_INIT; rp->id = (u_int) random(); rp->nsaddr.sin_addr.s_addr = INADDR_ANY; rp->nsaddr.sin_family = AF_INET; rp->nsaddr.sin_port = htons(NAMESERVER_PORT); rp->ndots = 1; /** rp->pfcode = 0; **/ rp->_vcsock = -1; /** rp->_flags = 0; **/ /** rp->qhook = NULL; **/ /** rp->rhook = NULL; **/ /** rp->_u._ext.nsinit = 0; **/ BIGLOCK; if(__searchdomains) { int i; for(i=0; i<__searchdomains; i++) { rp->dnsrch[i] = __searchdomain[i]; } } if(__nameservers) { int i; struct in_addr a; for(i=0; i<__nameservers; i++) { if (inet_aton(__nameserver[i], &a)) { rp->nsaddr_list[i].sin_addr = a; rp->nsaddr_list[i].sin_family = AF_INET; rp->nsaddr_list[i].sin_port = htons(NAMESERVER_PORT); } } } rp->nscount = __nameservers; BIGUNLOCK; return(0);}void res_close( void ){ return;}#endif#ifdef L_res_query#ifndef MIN#define MIN(x, y) ((x) < (y) ? (x) : (y))#endifint res_query(const char *dname, int class, int type, unsigned char *answer, int anslen){ int i; unsigned char * packet = 0; struct resolv_answer a; int __nameserversXX; char ** __nameserverXX; __open_nameservers(); if (!dname || class != 1 /* CLASS_IN */) { h_errno = NO_RECOVERY; return(-1); } memset((char *) &a, '\0', sizeof(a)); BIGLOCK; __nameserversXX=__nameservers; __nameserverXX=__nameserver; BIGUNLOCK; i = __dns_lookup(dname, type, __nameserversXX, __nameserverXX, &packet, &a); if (i < 0) { h_errno = TRY_AGAIN; return(-1); } free(a.dotted); if (a.atype == type) { /* CNAME*/ int len = MIN(anslen, i); memcpy(answer, packet, len); if (packet) free(packet); return(len); } if (packet) free(packet); return i;}/* * Formulate a normal query, send, and retrieve answer in supplied buffer. * Return the size of the response on success, -1 on error. * If enabled, implement search rules until answer or unrecoverable failure * is detected. Error code, if any, is left in h_errno. */int res_search(name, class, type, answer, anslen) const char *name; /* domain name */ int class, type; /* class and type of query */ u_char *answer; /* buffer to put answer */ int anslen; /* size of answer */{ const char *cp, * const *domain; HEADER *hp = (HEADER *)(void *)answer; u_int dots; int trailing_dot, ret, saved_herrno; int got_nodata = 0, got_servfail = 0, tried_as_is = 0; if ((!name || !answer) || ((_res.options & RES_INIT) == 0 && res_init() == -1)) { h_errno = NETDB_INTERNAL; return (-1); } errno = 0; h_errno = HOST_NOT_FOUND; /* default, if we never query */ dots = 0; for (cp = name; *cp; cp++) dots += (*cp == '.'); trailing_dot = 0; if (cp > name && *--cp == '.') trailing_dot++; /* * If there are dots in the name already, let's just give it a try * 'as is'. The threshold can be set with the "ndots" option. */ saved_herrno = -1; if (dots >= _res.ndots) { ret = res_querydomain(name, NULL, class, type, answer, anslen); if (ret > 0) return (ret); saved_herrno = h_errno; tried_as_is++; } /* * We do at least one level of search if * - there is no dot and RES_DEFNAME is set, or * - there is at least one dot, there is no trailing dot, * and RES_DNSRCH is set. */ if ((!dots && (_res.options & RES_DEFNAMES)) || (dots && !trailing_dot && (_res.options & RES_DNSRCH))) { int done = 0; for (domain = (const char * const *)_res.dnsrch; *domain && !done; domain++) { ret = res_querydomain(name, *domain, class, type, answer, anslen); if (ret > 0) return (ret); /* * If no server present, give up. * If name isn't found in this domain, * keep trying higher domains in the search list * (if that's enabled). * On a NO_DATA error, keep trying, otherwise * a wildcard entry of another type could keep us * from finding this entry higher in the domain. * If we get some other error (negative answer or * server failure), then stop searching up, * but try the input name below in case it's * fully-qualified. */ if (errno == ECONNREFUSED) { h_errno = TRY_AGAIN; return (-1); } switch (h_errno) { case NO_DATA: got_nodata++; /* FALLTHROUGH */ case HOST_NOT_FOUND: /* keep trying */ break; case TRY_AGAIN: if (hp->rcode == SERVFAIL) { /* try next search element, if any */ got_servfail++; break; } /* FALLTHROUGH */ default: /* anything else implies that we're done */ done++; } /* * if we got here for some reason other than DNSRCH, * we only wanted one iteration of the loop, so stop. */ if (!(_res.options & RES_DNSRCH)) done++; } } /* * if we have not already tried the name "as is", do that now. * note that we do this regardless of how many dots were in the * name or whether it ends with a dot. */ if (!tried_as_is) { ret = res_querydomain(name, NULL, class, type, answer, anslen); if (ret > 0) return (ret); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -