Mercurial > ecos
diff packages/net/ns/dns/current/src/dns.c @ 1005:1f968b35cd30
* doc/dns.sgml: Updated to reflect changes for IPv6.
* include/dns_impl.inl (setdomainname): Append a . to the end of
the domainname if it does not have one.
* cdl/dns.cdl: CYGOPT_NS_DNS_FIRST_FAMILTY to control order of
results.
* src/dns.c: Order the results from cyg_dns_getaddrinfo.
* test/dns1.c: Re-written to perform better testing and for
getaddrinfo and getnameinfo.
* test/dns2.c: Removed. It does not add anything useful.
* src/dns.c (cyg_dns_getnameinfo): New. Interface between
getnameinfo and the DNS client.
* src/dns.c (cyg_dns_getaddrinfo): New. Interface between
getaddrinfo and the DNS client. This supports both IPv4 and IPv6
* test/dns1.c: Added tests for getnameinfo using both IPv4 and
IPv6 addresses.
| author | asl |
|---|---|
| date | Mon, 12 May 2003 10:06:05 +0000 |
| parents | 94b558c9fb67 |
| children | 1aaed612fe59 |
line wrap: on
line diff
--- a/packages/net/ns/dns/current/src/dns.c +++ b/packages/net/ns/dns/current/src/dns.c @@ -315,3 +315,389 @@ cyg_dns_res_init(struct in_addr *dns_ser CYG_REPORT_RETVAL( 0 ); return 0; } + +/* add_answer checks to see if we already have this answer and if not, + adds it to the answers. */ +static int +add_answer(char *rdata, short rr_type, int family, + struct sockaddr addrs[], int num, int used) { + int i; + int found = 0; + + for (i = 0; i < used ; i++) { + if ((addrs[i].sa_family == family) && + !memcmp(addrs[i].sa_data, rdata, addrs[i].sa_len)) { + found = 1; + break; + } + } + if (!found) { + memset(&addrs[used],0,sizeof(addrs[used])); + addrs[used].sa_family = family; + + switch(family) { + case AF_INET: { + struct sockaddr_in * addr = (struct sockaddr_in *) &addrs[used]; + addr->sin_len = sizeof(*addr); + memcpy(&addr->sin_addr, rdata, sizeof(struct in_addr)); + used++; + break; + } +#ifdef CYGPKG_NET_INET6 + case AF_INET6: { + struct sockaddr_in6 * addr = (struct sockaddr_in6 *) &addrs[used]; + addr->sin6_len = sizeof(*addr); + memcpy(&addr->sin6_addr, rdata, sizeof(struct in6_addr)); + used++; + break; + } +#endif + default: + used = -EAI_FAMILY; + } + } + return used; +} + +/* This decodes the answer and puts the results into the addrs + array. This function can deal with IPv6 AAAA records as well as A + records. Thus its more complex than the parse_answer function in + the inline code. This complexity is only needed by getaddrinfo, so + i decided to leave parse_anser alone. */ + +static int +decode(char *msg, short rr_type, int family, + struct sockaddr addrs[], int num, int used, char **canon) { + + struct dns_header *dns_hdr; + struct resource_record rr, *rr_p = NULL; + char *qname = NULL; + char *ptr; + + dns_hdr = (struct dns_header *)msg; + + if (DNS_REPLY_NAME_ERROR == dns_hdr->rcode) { + h_errno = HOST_NOT_FOUND; + return -EAI_NONAME; + } + + if ((dns_hdr->qr != 1) || + (dns_hdr->opcode != DNS_QUERY)) { + return -EAI_FAIL; + } + + if (dns_hdr->rcode != DNS_REPLY_NOERR) { + return -EAI_NONAME; + } + + dns_hdr->ancount = ntohs(dns_hdr->ancount); + dns_hdr->qdcount = ntohs(dns_hdr->qdcount); + ptr = (char *)&dns_hdr[1]; + + /* Skip over the query section */ + if (dns_hdr->qdcount > 0) { + while (dns_hdr->qdcount) { + ptr += qname_len(ptr); + ptr += 4; /* skip type & class */ + dns_hdr->qdcount--; + } + } + + /* Read the answers resource records to find an answer of the + correct type. */ + while (dns_hdr->ancount && (used >= 0) && (used < num)) { + qname = ptr; + ptr += qname_len(ptr); + rr_p = (struct resource_record *)ptr; + memcpy(&rr, ptr, sizeof(rr)); + if ((rr.rr_type == htons(rr_type)) && + (rr.class == htons(DNS_CLASS_IN))) { + used = add_answer(rr_p->rdata, rr_type, family, addrs, num, used); + if (canon && !*canon) { + *canon = real_name(msg,qname); + } + } + ptr += sizeof(struct resource_record) - + sizeof(rr.rdata) + ntohs(rr.rdlength); + dns_hdr->ancount--; + } + if (used == 0) { + return -EAI_NONAME; + } + return used; +} + +/* Do a lookup for a particular type of resource record. */ +static int +do_lookup (const char * hostname, + struct sockaddr addrs[], int num, int used, + short rr_type, int family, char **canon) { + + unsigned char msg[MAXDNSMSGSIZE]; + int error; + int len; + + /* First try the name as passed in */ + memset(msg, 0, sizeof(msg)); + len = build_query(msg, hostname, rr_type); + if (len < 0) { + return -EAI_FAIL; + } + + /* Send the query and wait for an answer */ + len = send_recv(msg, len, sizeof(msg)); + if (len < 0) { + return -EAI_FAIL; + } + + /* Decode the answer */ + error = decode(msg, rr_type, family, addrs, num, used, canon); + return error; +} + +static int do_lookups(const char * hostname, + struct sockaddr addrs[], int num, + int family, char ** canon) { + int error; +#ifdef CYGPKG_NET_INET6 + int error6; +#endif + + switch (family) { + case AF_INET: + error = do_lookup(hostname, addrs, num, 0, DNS_TYPE_A, AF_INET, canon); + break; +#ifdef CYGPKG_NET_INET6 + case AF_INET6: + error = do_lookup(hostname, addrs, num, 0, DNS_TYPE_AAAA, AF_INET6, canon); + break; +#endif + case PF_UNSPEC: +#ifndef CYGPKG_NET_INET6 + error = do_lookup(hostname, addrs, num, 0, DNS_TYPE_A, AF_INET, canon); +#else +#ifdef CYGOPT_NS_DNS_FIRST_FAMILTY_AF_INET + error = do_lookup(hostname, addrs, num, 0, DNS_TYPE_A, AF_INET, canon); + if (error > 0 ) { + error6 = do_lookup(hostname, addrs, num, error, DNS_TYPE_AAAA, + AF_INET6, canon); + } else { + error6 = do_lookup(hostname, addrs, num, 0, DNS_TYPE_AAAA, + AF_INET6, canon); + } + if (error6 > 0) { + error = error6; + } +#else // CYGOPT_NS_DNS_FIRST_FAMILY_AF_INET + error6 = do_lookup(hostname, addrs, num, 0, DNS_TYPE_AAAA, + AF_INET6, canon); + if (error6> 0 ) { + error = do_lookup(hostname, addrs, num, error6, DNS_TYPE_A, + AF_INET, canon); + } else { + error = do_lookup(hostname, addrs, num, 0, DNS_TYPE_A, + AF_INET, canon); + } +#endif // CYGOPT_NS_DNS_FIRST_FAMILY_AF_INET +#endif // CYGPKG_NET_INET6 + break; + default: + error = -EAI_FAMILY; + } + return error; +} + +/* This implements the interface between getaddrinfo and the dns + client. hostent is not used here since that only works with IPv4 + addresses, where as this function needs to be protocol + independent. */ +int +cyg_dns_getaddrinfo(const char * hostname, + struct sockaddr addrs[], int num, + int family, + char ** canon) +{ + int error; + char name[256]; + char * dot; + + CYG_REPORT_FUNCNAMETYPE( "cyg_dns_getaddrinfo", "returning %08x" ); + CYG_REPORT_FUNCARG3( "hostname=%08x, addrs=%08x, num=%2d", + hostname, addrs, num ); + + if ( !hostname || !addrs || !num ) { + CYG_REPORT_RETVAL( NULL ); + return -EAI_FAIL; + } + + CYG_CHECK_DATA_PTR( hostname, "hostname is not a valid pointer!" ); + CYG_CHECK_DATA_PTR( addrs, "addrs is not a valid pointer!"); + CYG_ASSERT( num > 0, "Invalid number of sockaddr stuctures"); + + if (!valid_hostname(hostname)) { + /* it could be a dot address */ + struct sockaddr_in * sa4 = (struct sockaddr_in *)&addrs[0]; + memset(&addrs[0],0,sizeof(struct sockaddr)); + if (inet_pton(AF_INET, hostname, (char *)&sa4->sin_addr.s_addr)) { + sa4->sin_family = AF_INET; + sa4->sin_len = sizeof(*sa4); + CYG_REPORT_RETVAL (1); + return 1; + } +#ifdef CYGPKG_NET_INET6 + { + /* it could be a colon address */ + struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *)&addrs[0]; + memset(&addrs[0],0,sizeof(struct sockaddr)); + if (inet_pton(AF_INET6, hostname, (char *)&sa6->sin6_addr.s6_addr)) { + sa6->sin6_family = AF_INET6; + sa6->sin6_len = sizeof(*sa6); + CYG_REPORT_RETVAL (1); + return 1; + } + } +#endif + CYG_REPORT_RETVAL (-EAI_NONAME); + return -EAI_NONAME; + } + + /* Has the socket to the DNS server been opened? */ + if (s < 0) { + CYG_REPORT_RETVAL( -EIA_FAIL ); + return -EAI_FAIL; + } + + if (domainname) { + if ((strlen(hostname) + strlen(domainname)) > 254) { + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( -EAI_FAIL ); + return -EAI_FAIL; + } + strcpy(name, hostname); + strcat(name, "."); + strcat(name, domainname); + } + cyg_drv_mutex_lock(&dns_mutex); + + /* If the hostname ends with . it a FQDN. Don't bother adding the + domainname. If it does not contain a . , try appending with the + domainname first. If it does have a . , try without a domain name + first. */ + + dot = rindex(hostname,'.'); + if (dot) { + if (*(dot+1) == '\0') { + /* FQDN */ + error = do_lookups(hostname, addrs, num, family, canon); + } else { + /* Dot somewhere */ + error = do_lookups(hostname, addrs, num, family, canon); + if (domainname && (error == -EAI_NONAME)) { + error = do_lookups(name, addrs, num, family, canon); + } + } + } else { + /* No Dot. Try adding domainname first */ + error = -EAI_NONAME; + if (domainname) { + error = do_lookups(name, addrs, num, family, canon); + } + if (error == -EAI_NONAME) { + error = do_lookups(hostname, addrs, num, family, canon); + } + } + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( error ); + return error; +} + +/* This implements the interface between getnameinfo and the dns + client. */ +externC int +cyg_dns_getnameinfo(const struct sockaddr * sa, char * host, size_t hostlen) +{ + char hostname[80]; + unsigned char msg[MAXDNSMSGSIZE]; + struct hostent * hent; + int len; + + CYG_REPORT_FUNCNAMETYPE( "cyg_dns_getnameinfo", "returning %08x" ); + CYG_REPORT_FUNCARG3( "sa=%08x, host=%08x, hostlen=%3d", + sa, host, hostlen ); + + CYG_CHECK_DATA_PTR( sa, "sa is not a valid pointer"); + CYG_CHECK_DATA_PTR( host, "host is not a valid data pointer"); + CYG_ASSERT(hostlen >0, "Invalid host length"); + + /* Has the socket to the DNS server been opened? */ + if (s < 0) { + CYG_REPORT_RETVAL( -EIA_FAIL ); + return -EAI_FAIL; + } + + cyg_drv_mutex_lock(&dns_mutex); + + switch (sa->sa_family) { + case AF_INET: { + struct sockaddr_in * sa4 = (struct sockaddr_in *)sa; + unsigned char * addr = (char *)&sa4->sin_addr.s_addr; + sprintf(hostname, "%d.%d.%d.%d.IN-ADDR.ARPA.", + addr[3],addr[2],addr[1],addr[0]); + break; + } +#ifdef CYGPKG_NET_INET6 + case AF_INET6: { + struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *)sa; + int i; + + for (i=15; i >= 0; i--) { + sprintf(&hostname[(15*2*2) - (i*2*2)], "%x.%x.", + sa6->sin6_addr.s6_addr[i] & 0x0f, + (sa6->sin6_addr.s6_addr[i] & 0xf0) >> 4); + } + sprintf(&hostname[16*2*2],"IP6.INT"); + break; + } +#endif + default: + cyg_drv_mutex_lock(&dns_mutex); + CYG_REPORT_RETVAL( -EAI_FAMILY); + return -EAI_FAMILY; + } + + memset(msg, 0, sizeof(msg)); + + /* Build a PTR type request using the hostname */ + len = build_query(msg, hostname, DNS_TYPE_PTR); + if (len < 0) { + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( -EAI_FAIL ); + return -EAI_FAIL; + } + + /* Send the request and wait for an answer */ + len = send_recv(msg, len, sizeof(msg)); + if (len < 0) { + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( -EAI_FAIL ); + return -EAI_FAIL; + } + + /* Parse the answer for the host name */ + hent = parse_answer(msg, DNS_TYPE_PTR); + + /* If no name is known return an error */ + if (!hent) { + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( -EAI_NONAME ); + return -EAI_NONAME; + } + + /* Otherwise copy it into our results buffer and tidy up */ + strncpy(host, hent->h_name,hostlen); + free_hent(hent); + + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( -EAI_NONE ); + return -EAI_NONE; +}
