Mercurial > flash_v2
changeset 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 | c1abcb7d0cf8 |
| children | 766a12db7c5b |
| files | packages/net/ns/dns/current/ChangeLog packages/net/ns/dns/current/cdl/dns.cdl packages/net/ns/dns/current/doc/dns.sgml packages/net/ns/dns/current/include/dns.h packages/net/ns/dns/current/include/dns_impl.inl packages/net/ns/dns/current/include/dns_priv.h packages/net/ns/dns/current/src/dns.c packages/net/ns/dns/current/tests/dns1.c |
| diffstat | 8 files changed, 1432 insertions(+), 161 deletions(-) [+] |
line wrap: on
line diff
--- a/packages/net/ns/dns/current/ChangeLog +++ b/packages/net/ns/dns/current/ChangeLog @@ -1,3 +1,30 @@ +2003-04-25 Andrew Lunn <andrew.lunn@ascom.ch> + + * doc/dns.sgml: Updated to reflect changes for IPv6. + +2003-04-24 Andrew Lunn <andrew.lunn@ascom.ch> + + * 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. + +2003-04-21 Andrew Lunn <andrew.lunn@ascom.ch> + + * src/dns.c (cyg_dns_getnameinfo): New. Interface between + getnameinfo and the DNS client. + +2003-04-19 Andrew Lunn <andrew.lunn@ascom.ch> + + * 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. + 2003-03-03 Jonathan Larmour <jifl@eCosCentric.com> * tests/dns1.c: Use BOOTP info where possible in preference to
--- a/packages/net/ns/dns/current/cdl/dns.cdl +++ b/packages/net/ns/dns/current/cdl/dns.cdl @@ -108,6 +108,31 @@ cdl_package CYGPKG_NS_DNS { default_value { "default.domain.com" } } } + + cdl_option CYGNUM_NS_DNS_GETADDRINFO_ADDRESSES { + display "Max number of results for getaddrinfo" + flavor data + default_value 5 + description " + This option controls the number of addresses the DNS client + can return to getaddrinfo and hence the size of the buffer + passed to the DNS client." + } + + cdl_option CYGOPT_NS_DNS_FIRST_FAMILY { + display "AF_INET or AF_INET6 first in the getaddrinfo list" + active_if CYGPKG_NET_INET6 + flavor data + default_value {"AF_INET6"} + legal_values {"AF_INET4" "AF_INET6"} + description " + This option controls the order DNS results will appear in the + information returned by getaddrinfo. This will in turn control + the order in which network clients try talking to servers, + ie does it try IPv6 or IPv4 addresses first when it has both + types of addresses." + } + cdl_component CYGPKG_NS_DNS_OPTIONS { display "DNS support build options" flavor none @@ -142,7 +167,7 @@ cdl_package CYGPKG_NS_DNS { flavor data active_if CYGPKG_NS_DNS_BUILD no_define - calculated { "tests/dns1 tests/dns2" } + calculated { "tests/dns1" } description " This option specifies the set of tests for the DNS package."
--- a/packages/net/ns/dns/current/doc/dns.sgml +++ b/packages/net/ns/dns/current/doc/dns.sgml @@ -11,6 +11,7 @@ <!-- --> <!-- =============================================================== --> <!-- Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. --> +<!-- Copyright (c) 2003 Andrew Lunn --> <!-- This material may be distributed only subject to the terms --> <!-- and conditions set forth in the Open Publication License, v1.0 --> <!-- or later (the latest version is presently available at --> @@ -43,19 +44,18 @@ can both use the DNS package to perform <SECT1 id="net-ns-dns-api1"> <TITLE>DNS API</TITLE> <PARA>The DNS client uses the normal BSD API for performing lookups: -<function>gethostbyname()</function> and -<FUNCTION>gethostbyaddr()</FUNCTION>. +<function>gethostbyname(),</function> +<FUNCTION>gethostbyaddr()</FUNCTION>, +<FUNCTION>getaddrinfo()</function>, +<FUNCTION>getnameinfo()</function>. </PARA> <PARA>There are a few restrictions:</PARA> <ITEMIZEDLIST> <LISTITEM> -<PARA>Only IPv4 is supported, ie IPv6 addresses cannot be looked -up.</PARA> -</LISTITEM> -<LISTITEM> <PARA>If the DNS server returns multiple authoritive records -for a host name, the hostent will only contain a record for the -first entry.</PARA> +for a host name to <function>gethostbyname</function>, the hostent will only contain a record for the +first entry. If multiple records are desired, use +<function>getaddrinfo</function>, which will return multiple results.</PARA> </LISTITEM> <LISTITEM> <PARA>The code has been made thread safe. ie multiple threads @@ -67,7 +67,15 @@ is not safe is one thread using both and <FUNCTION>gethostbyaddr()</FUNCTION>. A call to one will destroy the results from the previous call -to the other function.</PARA> +to the other function. <FUNCTION>getaddrinfo()</FUNCTION> is thread +safe and so is the preferred interface.</PARA> +</LISTITEM> +<LISTITEM> +<PARA>The client only uses IPv4 to access the DNS server.</PARA> +</LISTITEM> +<LISTITEM><PARA>The DNS client will only return IPv4 addresses to +RedBoot. At the moment this is not really a limitation, +since RedBoot only supports IPv4 and not IPv6.</PARA> </LISTITEM> </ITEMIZEDLIST> <PARA>To initialise the DNS client the following function must be @@ -77,12 +85,14 @@ int cyg_dns_res_init(struct in_addr *dns <PARA>where dns_server is the address of the DNS server the client should query. On Error this function returns -1, otherwise 0 for success. If lookups are attemped before this function has -been called, they will fail and return NULL.</PARA> +been called, they will fail and return NULL, unless numeric host addresses +are passed. In this cause, the address will be converted and returned +without the need for a lookup.</PARA> <PARA>A default, hard coded, server may be specified in the CDL option <literal>CYGDAT_NS_DNS_DEFAULT_SERVER</literal>. The use of this is controlled by <literal>CYGPKG_NS_DNS_DEFAULT</literal>. If this is -enabled, <literal>init_all_network_interfaces</literal> will +enabled, <literal>init_all_network_interfaces()</literal> will initialize the resolver with the hard coded address. The DHCP client or user code my override this address by calling <literal>cyg_dns_res_init</literal> again. </PARA> @@ -110,9 +120,95 @@ The boolean <literal>CYGPKG_NS_DNS_DOMAI and the domain name is taken from <literal>CYGPKG_NS_DNS_DOMAINNAME_NAME</literal>.</PARA> -<PARA>Once set, the DNS client will first perform a lookup with the domain -name appended. If this fails it will then perform a second lookup -without the appended domain name. </PARA> -</SECT1> -</CHAPTER> +<PARA>Once set, the DNS client will use some simple heuristics when + deciding how to use the domainname. If the name given to the + client ends with a "." it is assumed to be a FQDN and the domain + name will not be used. If the name contains a "." somewhere + within it, first a lookup will be performed without the + domainname. If that fails the domainname will be appended and + looked up. If the name does not contain a ".", the domainname is + appended and used for the first query. If that fails, the + unadorned name is lookup. +</PARA> + <PARA>The <FUNCTION>getaddrinfo</FUNCTION> will return both IPv4 and + IPv6 addresses for a given host name, when IPv6 is enabled in + the eCos configuration. The CDL option + <LITERAL>CYGOPT_NS_DNS_FIRST_FAMILY</LITERAL> controls the order + IPv6 and IPv4 addresses are returned in the linked list of + <LITERAL>addrinfo</LITERAL> structures. If the value + <LITERAL>AF_INET</LITERAL> is used, the IPv4 addresses will be + first. If the value <LITERAL>AF_INET6</LITERAL>, which is the + default, is used, IPv6 address will be first. This ordering will + control how clients attempt to connect to servers, ie using IPv6 + or IPv4 first. + </PARA> + </SECT1> + <SECT1 id="net-ns-dns-testing"> + <TITLE>DNS Client Testing</TITLE> + + <PARA>The DNS client has a test program, dns1.c, which tests many of + the features of the DNS client and the functions + <FUNCTION>gethostbyname(),</FUNCTION> + <FUNCTION>gethostbyaddr()</FUNCTION>, + <FUNCTION>getaddrinfo()</FUNCTION>, + <FUNCTION>getnameinfo()</FUNCTION>. + </PARA> + <PARA>In order for this test to work, a DNS server must be configured + with a number of names and addresses. The following is an example + forward address resolution database for bind v9, which explains the + requirements.</para> + + <PROGRAMLISTING> + $TTL 680400 + @ IN SOA lunn.org. andrew.lunn.lunn.org ( + 2003041801 ; serial + 10800 ; refresh + 1800 ; retry + 3600000 ; expire + 259200) ; mimimum + IN NS londo.lunn.org. + + hostnamev4 IN A 192.168.88.1 + cnamev4 IN CNAME hostnamev4 + hostnamev6 IN AAAA fec0::88:4:3:2:1 + cnamev6 IN CNAME hostnamev6 + hostnamev46 IN A 192.168.88.2 + hostnamev46 IN AAAA fec0::88:4:3:2:2 + cnamev46 IN CNAME hostnamev46 + </PROGRAMLISTING> + <PARA>The actual names and addresses do not matter, since they are + configurable in the test. What is important is the relationship + between the names and the addresses and there family. ie + hostnamev4 should map to one IPv4 address. hostnamev46 should + map to both an IPv4 and an IPv6 address. cnamev4 should be a + CNAME record for hostname4. Reverse lookup information is also + needed by the test. + </PARA> + <PARA>The information placed into the DNS server is also need in the + test case. A structure is defined to hold this + information:</PARA> + <PROGRAMLISTING> + struct test_info_s { + char * dns_server; + char * domain_name; + char * hostname_v4; + char * cname_v4; + char * ip_addr_v4; + char * hostname_v6; + char * cname_v6; + char * ip_addr_v6; + char * hostname_v46; + char * cname_v46; + char * ip_addr_v46_v4; + char * ip_addr_v46_v6; + }; + </PROGRAMLISTING> + <PARA>The test program may hold a number of such structures for + different DNS server. The test will use each structure in turn + to perform the tests. If IPv6 is not enabled in the eCos + configuration, the entries which use IPv6 may be assigned to + NULL. + </PARA> + </SECT1> + </CHAPTER> </PART>
--- a/packages/net/ns/dns/current/include/dns.h +++ b/packages/net/ns/dns/current/include/dns.h @@ -83,6 +83,15 @@ externC const char* hstrerror(int err); #define NO_RECOVERY 3 #define NO_DATA 4 +// Interface between the DNS client and getaddrinfo +externC int +cyg_dns_getaddrinfo(const char * hostname, + struct sockaddr addrs[], int num, + int family, char **canon); +// Interface between the DNS client and getnameinfo +externC int +cyg_dns_getnameinfo(const struct sockaddr * sa, char * host, size_t hostlen); + //----------------------------------------------------------------------------- #endif // CYGONCE_NS_DNS_DNS_H // End of dns.h
--- a/packages/net/ns/dns/current/include/dns_impl.inl +++ b/packages/net/ns/dns/current/include/dns_impl.inl @@ -191,7 +191,7 @@ real_name(char *msg, unsigned char *qnam /* Build a query message which can be sent to the server. If something goes wrong return -1, otherwise the length of the query message */ static int -build_query(char * msg, const char * hostname, short rr_type) +build_query(const char * msg, const char * hostname, short rr_type) { struct dns_header *dns_hdr; char *ptr; @@ -251,6 +251,33 @@ dot_hostname(const char *hostname) return hent; } +#ifdef CYGPKG_NET_INET6 +/* Check if the hostname is actually colon format of IPv6. If so convert it + and return host entity structure. If not, return NULL. */ +static struct hostent * +colon_hostname(const char *hostname) +{ + struct sockaddr_in6 addr; + struct hostent *hent = NULL; + + if (inet_pton(AF_INET6, hostname, (void *)&addr)) { + hent = alloc_hent(); + if (hent) { + memcpy(hent->h_addr_list[0], &addr, sizeof(addr)); + hent->h_addrtype = AF_INET6; + hent->h_length = sizeof(addr); + hent->h_name = alloc_string(strlen(hostname)+1); + if (!hent->h_name) { + free_hent(hent); + return NULL; + } + strcpy(hent->h_name, hostname); + } + } + return hent; +} +#endif + /* Decode the answer from the server. Returns NULL if failed, or a hostent structure containing different data depending on the query type: @@ -418,14 +445,37 @@ gethostbyaddr(const char *addr, int len, return hent; } +/* Build message, send, receive and decode */ +static struct hostent * +do_query(const char * hostname) +{ + unsigned char msg[MAXDNSMSGSIZE]; + int len; + + memset(msg, 0, sizeof(msg)); + len = build_query(msg, hostname, DNS_TYPE_A); + if (len < 0) { + return NULL; + } + + /* Send the query and wait for an answer */ + len = send_recv(msg, len, sizeof(msg)); + if (len < 0) { + return NULL; + } + + /* Decode the answer */ + return parse_answer(msg, DNS_TYPE_A); +} + + /* Given a hostname find out the IP address */ struct hostent * gethostbyname(const char * hostname) { - unsigned char msg[MAXDNSMSGSIZE]; char name[256]; + char * dot; struct hostent *hent; - int len; CYG_REPORT_FUNCNAMETYPE( "gethostbyname", "returning %08x" ); CYG_REPORT_FUNCARG1( "hostname=%08x", hostname ); @@ -448,42 +498,25 @@ gethostbyname(const char * hostname) free_stored_hent(); if (!valid_hostname(hostname)) { - /* It could be a dot address */ - hent = dot_hostname(hostname); - store_hent(hent); - CYG_REPORT_RETVAL( hent ); - return hent; + /* It could be a dot address */ + if ((hent = dot_hostname(hostname)) != NULL) { + store_hent(hent); + CYG_REPORT_RETVAL( hent ); + return hent; + } +#ifdef CYGPKG_NET_INET6 + /* It could be a colon seperated IPv6 address */ + if ((hent = colon_hostname(hostname)) != NULL) { + store_hent(hent); + CYG_REPORT_RETVAL( hent ); + return hent; + } +#endif + CYG_REPORT_RETVAL( hent ); + return hent; } - cyg_drv_mutex_lock(&dns_mutex); - /* First try the name as passed in */ - memset(msg, 0, sizeof(msg)); - len = build_query(msg, hostname, DNS_TYPE_A); - if (len < 0) { - cyg_drv_mutex_unlock(&dns_mutex); - CYG_REPORT_RETVAL( NULL ); - return NULL; - } - - /* Send the query and wait for an answer */ - len = send_recv(msg, len, sizeof(msg)); - if (len < 0) { - cyg_drv_mutex_unlock(&dns_mutex); - CYG_REPORT_RETVAL( NULL ); - return NULL; - } - - /* Decode the answer */ - hent = parse_answer(msg, DNS_TYPE_A); - if (hent) { - cyg_drv_mutex_unlock(&dns_mutex); - store_hent(hent); - CYG_REPORT_RETVAL( hent ); - return hent; - } - - /* If no match, try appending the domainname if we have one */ if (domainname) { if ((strlen(hostname) + strlen(domainname)) > 254) { h_errno = NO_RECOVERY; @@ -494,27 +527,36 @@ gethostbyname(const char * hostname) strcpy(name, hostname); strcat(name, "."); strcat(name, domainname); - - memset(msg, 0, sizeof(msg)); - len = build_query(msg, name, DNS_TYPE_A); - if (len < 0) { - cyg_drv_mutex_unlock(&dns_mutex); - CYG_REPORT_RETVAL( NULL ); - return NULL; - } - - /* Send the query and wait for an answer */ - len = send_recv(msg, len, sizeof(msg)); - if (len < 0) { - cyg_drv_mutex_unlock(&dns_mutex); - CYG_REPORT_RETVAL( NULL ); - return NULL; - } - - /* Decode the answer */ - hent = parse_answer(msg, DNS_TYPE_A); } + /* 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 */ + hent = do_query(hostname); + } else { + /* Dot somewhere */ + hent = do_query(hostname); + if (domainname && (hent == NULL)) { + hent = do_query(name); + } + } + } else { + /* No Dot. Try adding domainname first */ + hent = NULL; + if (domainname) { + hent = do_query(name); + } + if (hent == NULL) { + hent = do_query(hostname); + } + } + cyg_drv_mutex_unlock(&dns_mutex); store_hent(hent); CYG_REPORT_RETVAL( hent ); @@ -526,6 +568,7 @@ int setdomainname(const char *name, size_t len) { char * ptr; + int length; CYG_REPORT_FUNCNAMETYPE( "setdomainname", "returning %d" ); CYG_REPORT_FUNCARG2( "name=%08x, len=%d", name, len ); @@ -537,13 +580,28 @@ setdomainname(const char *name, size_t l } if (len != 0) { CYG_CHECK_DATA_PTR( name, "name is not a valid pointer!" ); - ptr = alloc_string(len+1); + length = strlen(name); + if (length > len) { + h_errno = NO_RECOVERY; + CYG_REPORT_RETVAL( -1 ); + return -1; + } + if (name[length-1] != '.') { + length++; + } + ptr = alloc_string(length+1); if (!ptr) { + h_errno = NO_RECOVERY; CYG_REPORT_RETVAL( -1 ); return -1; } memcpy(ptr, name, len); - ptr[len]=0; + if (name[length-1] != '.') { + ptr[length-1] = '.'; + ptr[length] = '\0'; + } else { + ptr[len]=0; + } } else { ptr = NULL; }
--- a/packages/net/ns/dns/current/include/dns_priv.h +++ b/packages/net/ns/dns/current/include/dns_priv.h @@ -112,6 +112,7 @@ struct resource_record { #define DNS_TYPE_NS 2 /* Authoritative name server */ #define DNS_TYPE_CNAME 5 /* Canonical name for an alias */ #define DNS_TYPE_PTR 12 /* Domain name pointer */ +#define DNS_TYPE_AAAA 28 /* IPv6 host address */ /* DNS CLASSs */ #define DNS_CLASS_IN 1 /* Internet */
--- 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; +}
--- a/packages/net/ns/dns/current/tests/dns1.c +++ b/packages/net/ns/dns/current/tests/dns1.c @@ -9,6 +9,7 @@ // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2003 Andrew Lunn // // eCos is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free @@ -44,7 +45,7 @@ // Contributors: andrew.lunn, jskov // Date: 2001-09-18 // Purpose: -// Description: Test DNS functions. Note that the _XXX defines below +// Description: Test DNS functions. Note that the structure below that // control what addresses the test uses. These must be // changed to match the particular testing network in which // the test is to be run. @@ -52,6 +53,7 @@ //####DESCRIPTIONEND#### // //========================================================================== +#include <pkgconf/ns_dns.h> #include <network.h> #include <netdb.h> @@ -65,23 +67,760 @@ static char stack[STACK_SIZE]; static cyg_thread thread_data; static cyg_handle_t thread_handle; -#define __string(_x) #_x -#define __xstring(_x) __string(_x) +#define NELEM(x) (sizeof(x) / sizeof(x[0])) + +struct test_info_s { + char * dns_server; + char * domain_name; + char * hostname_v4; + char * cname_v4; + char * ip_addr_v4; + char * hostname_v6; + char * cname_v6; + char * ip_addr_v6; + char * hostname_v46; + char * cname_v46; + char * ip_addr_v46_v4; + char * ip_addr_v46_v6; +}; -// Change the following if you aren't using BOOTP! It's almost certainly not right for you. -#define _DNS_IP __xstring(172.16.1.254) // test farm host addr -#define _LOOKUP_FQDN __xstring(b.root-servers.net.) // should stay the same? -#define _LOOKUP_DOMAINNAME __xstring(root-servers.net.) -#define _LOOKUP_HOSTNAME __xstring(b) -#define _LOOKUP_IP __xstring(128.9.0.107) // must be same as _LOOKUP_FQDN -#define _LOOKUP_IP_BAD __xstring(10.0.0.0) +struct test_info_s test_info[] = { + { + "192.168.10.1", + "lunn.org.", + "hostnamev4", + "cnamev4", + "192.168.88.1", + "hostnamev6", + "cnamev6", + "fec0::88:4:3:2:1", + "hostnamev46", + "cnamev46", + "192.168.88.2", + "fec0::88:4:3:2:2" + } +}; -void -dns_test(cyg_addrword_t p) -{ +char * familytoa(int family) { + + switch (family) { + case AF_INET: + return "AF_INET"; +#ifdef CYGPKG_NET_INET6 + case AF_INET6: + return "AF_INET6"; +#endif + default: + return "Unknown"; + } +} + +void dns_test(struct test_info_s *info) { struct in_addr addr; struct hostent *hent; char dn[256]; + char name[256]; + char cname[256]; + char buffer[256]; + char buff[64]; + size_t hostlen = 128; + char host[hostlen]; + struct addrinfo * res; + struct addrinfo hints; + struct sockaddr_in sa4; +#ifdef CYGPKG_NET_INET6 + struct sockaddr_in6 sa6; + struct addrinfo *ai4, *ai6; +#endif + int error; + + if (inet_pton(AF_INET, info->dns_server, (void *)&addr) < 0) { + CYG_TEST_FAIL_FINISH("Error with DNS server address"); + } + cyg_dns_res_init(&addr); + + setdomainname(NULL,0); + + inet_aton(info->ip_addr_v4, &addr); + strcpy(name,info->hostname_v4); + strcat(name,"."); + strcat(name,info->domain_name); + strcpy(cname,info->cname_v4); + strcat(cname,"."); + strcat(cname,info->domain_name); + + // Lookup the IPv4 FQDN hostname + hent = gethostbyname(name); + if (hent != NULL) { + diag_sprintf(buffer,"Lookup %s: Result <%s is %s>", + name, + hent->h_name, + inet_ntoa(*(struct in_addr *)hent->h_addr)); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void*)&addr, + (void*)(hent->h_addr), + sizeof(struct in_addr))), + "IPv4 FQDN hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, hent->h_name)), + "IPv4 FQDN hostname name"); + } else { + diag_sprintf(buffer,"IPv4 FQDN hostname: error %s", + hstrerror(h_errno)); + CYG_TEST_FAIL(buffer); + } + + // Lookup the IPv4 FQDN cname + hent = gethostbyname(cname); + if (hent != NULL) { + diag_sprintf(buffer,"Lookup %s: Result <%s is %s>", + cname, + hent->h_name, + inet_ntoa(*(struct in_addr *)hent->h_addr)); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void*)&addr, + (void*)(hent->h_addr), + sizeof(struct in_addr))), + "IPv4 FQDN cname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, hent->h_name)), + "IPv4 FQDN hostname name"); + } else { + diag_sprintf(buffer,"IPv4 FQDN cname: error %s", hstrerror(h_errno)); + CYG_TEST_FAIL(buffer); + } + + // Lookup the IP address as a string. This does not require a DNS + // lookup. Just turn the value into binary + hent = gethostbyname(info->ip_addr_v4); + if (hent != NULL) { + diag_sprintf(buffer,"Lookup %s: Result <%s is %s>", + info->ip_addr_v4, + hent->h_name, + inet_ntoa(*(struct in_addr *)hent->h_addr)); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void*)&addr, + (void*)(hent->h_addr), + sizeof(struct in_addr))), + "IPv4 IP address"); + CYG_TEST_PASS_FAIL((0 == strcmp(info->ip_addr_v4, hent->h_name)), + "IPv4 IP address name"); + + } else { + diag_sprintf(buffer,"IPv4 IP address: error %s", hstrerror(h_errno)); + CYG_TEST_FAIL(buffer); + } + + // Reverse lookup the IPv4 address, expect the FQDN hostname + hent = gethostbyaddr((char *)&addr, sizeof(struct in_addr), AF_INET); + if (hent != NULL) { + inet_ntop(AF_INET,(void *)&addr, buff, sizeof(buff)); + diag_sprintf(buffer,"Reverse lookup %s: Result <%s is %s>", + buff, + hent->h_name, + inet_ntoa(*(struct in_addr *)hent->h_addr)); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void*)&addr, + (void*)(hent->h_addr), + sizeof(struct in_addr))), + "Reverse lookup IPv4 IP address"); + + CYG_TEST_PASS_FAIL((0 == strcmp(name, hent->h_name)), + "Reverse lookup IPv4 IP address name"); + } else { + diag_sprintf(buffer,"Reverse lookup IPv4 IP address: error %s", + hstrerror(h_errno)); + CYG_TEST_FAIL(buffer); + } + + // Setup a domainname. We now don't have to use fully qualified + // domain names + setdomainname(info->domain_name, strlen(info->domain_name)); + getdomainname(dn, sizeof(dn)); + CYG_TEST_PASS_FAIL(0 == strcmp(info->domain_name, dn), + "{get|set}domainname"); + + // Make sure FQDN still work + strcpy(name,info->hostname_v4); + strcat(name,"."); + strcat(name,info->domain_name); + + hent = gethostbyname(name); + if (hent != NULL) { + diag_sprintf(buffer,"Lookup %s: Result <%s is %s>", + name, + hent->h_name, + inet_ntoa(*(struct in_addr *)hent->h_addr)); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void*)&addr, + (void*)(hent->h_addr), + sizeof(struct in_addr))), + "IPv4 FQDN hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, hent->h_name)), + "IPv4 FQDN hostname name"); + } else { + diag_sprintf(buffer,"IPv4 FQDN hostname: error %s", + hstrerror(h_errno)); + CYG_TEST_FAIL(buffer); + } + + // Now just the hostname + hent = gethostbyname(info->hostname_v4); + if (hent != NULL) { + diag_sprintf(buffer,"Lookup %s: Result <%s is %s>", + info->hostname_v4, + hent->h_name, + inet_ntoa(*(struct in_addr *)hent->h_addr)); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void*)&addr, + (void*)(hent->h_addr), + sizeof(struct in_addr))), + "IPv4 hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, hent->h_name)), + "IPv4 hostname name"); + } else { + diag_sprintf(buffer,"IPv4 hostname: error %s", hstrerror(h_errno)); + CYG_TEST_FAIL(buffer); + } + + // Run the same tests as above, but this time use getaddrinfo and + // getnameinfo. + setdomainname(NULL,0); + + memset(&hints,0,sizeof(hints)); + hints.ai_flags = AI_CANONNAME; + memset(&sa4,0,sizeof(sa4)); + memcpy(&sa4.sin_addr, &addr, sizeof(addr)); + sa4.sin_family=AF_INET; + sa4.sin_len = sizeof(sa4); + + // Lookup the IPv4 FQDN hostname + error = getaddrinfo(name, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + name, + res->ai_canonname, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa4, + (void*)res->ai_addr, + sizeof(sa4))) && + (res->ai_family == AF_INET), + "IPv4 FQDN hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, res->ai_canonname)), + "IPv4 FQDN hostname name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv4 FQDN hostname one result"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv4 FQDN hostname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Lookup the IPv4 FQDN cname + error = getaddrinfo(cname, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + cname, + res->ai_canonname, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa4, + (void*)res->ai_addr, + sizeof(sa4))) && + (res->ai_family == AF_INET), + "IPv4 FQDN cname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, res->ai_canonname)), + "IPv4 FQDN cname name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv4 FQDN cname one result"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv4 FQDN cname: error %s", gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Lookup the IP address as a string. This does not require a DNS + // lookup. Just turn the value into binary + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(info->ip_addr_v4, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s %s>", + info->ip_addr_v4, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa4, + (void*)res->ai_addr, + sizeof(sa4))) && + (res->ai_family == AF_INET), + "IPv4 IP address"); + CYG_TEST_PASS_FAIL((NULL == res->ai_canonname), + "IPv4 IP address name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv4 IP address one result"); + // Don't free results - use for next test. + } else { + diag_sprintf(buffer,"IPv4 IP address: error %s", gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Reverse lookup the IPv4 address, expect the FQDN hostname + error = getnameinfo(res->ai_addr,res->ai_addrlen, + host, hostlen, NULL, 0, 0); + if (error == EAI_NONE) { + diag_sprintf(buffer, "IPv4 Reverse lookup FDQN %s: Result %s", + info->ip_addr_v4, + host); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == strcmp(name, host)), + "IPv4 Reverse lookup FQDN"); + } else { + diag_sprintf(buffer,"IPv4 Reverse lookup FQDN: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + // Reverse lookup the IPv4 address, expect just the hostname part + error = getnameinfo(res->ai_addr,res->ai_addrlen, + host, hostlen, NULL, 0, NI_NOFQDN); + if (error == EAI_NONE) { + diag_sprintf(buffer, "IPv4 Reverse lookup hostname part %s: Result %s", + info->ip_addr_v4, + host); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == strcmp(info->hostname_v4, host)), + "IPv4 Reverse lookup hostname part"); + } else { + diag_sprintf(buffer,"IPv4 Reverse lookup hostname part: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + freeaddrinfo(res); + + // Setup a domainname. We now don't have to use fully qualified + // domain names + setdomainname(info->domain_name, strlen(info->domain_name)); + getdomainname(dn, sizeof(dn)); + CYG_TEST_PASS_FAIL(0 == strcmp(info->domain_name, dn), + "{get|set}domainname"); + + // Lookup the IPv4 FQDN hostname to make sure it still works + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(name, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + name, + res->ai_canonname, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa4, + (void*)res->ai_addr, + sizeof(sa4))) && + (res->ai_family == AF_INET), + "IPv4 FQDN hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, res->ai_canonname)), + "IPv4 FQDN hostname name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv4 FQDN hostname one result"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv4 FQDN hostname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Now the host name without the domain name + error = getaddrinfo(info->hostname_v4, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + info->hostname_v4, + res->ai_canonname, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa4, + (void*)res->ai_addr, + sizeof(sa4))) && + (res->ai_family == AF_INET), + "IPv4 hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, res->ai_canonname)), + "IPv4 hostname name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv4 hostname one result"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv4 hostname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } +#ifdef CYGPKG_NET_INET6 + // Check we have the needed information for the IPv6 tests + if (!info->hostname_v6 || !info->cname_v6 || !info->ip_addr_v6) { + return; + } + + setdomainname(NULL,0); + + strcpy(name,info->hostname_v6); + strcat(name,"."); + strcat(name,info->domain_name); + strcpy(cname,info->cname_v6); + strcat(cname,"."); + strcat(cname,info->domain_name); + + memset(&sa6,0,sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_len = sizeof(sa6); + inet_pton(AF_INET6, info->ip_addr_v6, (void *)&sa6.sin6_addr); + + // Lookup the IPv6 FQDN hostname + error = getaddrinfo(name, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + name, + res->ai_canonname, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa6, + (void*)res->ai_addr, + sizeof(sa6))) && + (res->ai_family == AF_INET6), + "IPv6 FQDN hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, res->ai_canonname)), + "IPv6 FQDN hostname name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv6 FQDN hostname one result"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv6 FQDN hostname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Lookup the IPv6 FQDN cname + error = getaddrinfo(cname, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + cname, + res->ai_canonname, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa6, + (void*)res->ai_addr, + sizeof(sa6))) && + (res->ai_family == AF_INET6), + "IPv6 FQDN cname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, res->ai_canonname)), + "IPv6 FQDN cname name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv6 FQDN cname one result"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv6 FQDN cname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Lookup the IP address as a string. This does not require a DNS + // lookup. Just turn the value into binary + hints.ai_flags = AI_NUMERICHOST; + error = getaddrinfo(info->ip_addr_v6, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s %s>", + info->ip_addr_v6, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa6, + (void*)res->ai_addr, + sizeof(sa6))) && + (res->ai_family == AF_INET6), + "IPv6 IP address"); + CYG_TEST_PASS_FAIL((NULL == res->ai_canonname), + "IPv6 IP address name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv6 IP address one result"); + // Don't free the result, use it in the next tests + } else { + diag_sprintf(buffer,"IPv6 IP address: error %s", gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Reverse lookup the IPv6 address, expect the FQDN hostname + error = getnameinfo(res->ai_addr,res->ai_addrlen, + host, hostlen, NULL, 0, 0); + if (error == EAI_NONE) { + diag_sprintf(buffer, "IPv6 Reverse lookup FDQN %s: Result %s", + info->ip_addr_v6, + host); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == strcmp(name, host)), + "IPv6 Reverse lookup FQDN"); + } else { + diag_sprintf(buffer,"IPv6 Reverse lookup FQDN: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + // Reverse lookup the IPv6 address, expect just the hostname part + error = getnameinfo(res->ai_addr,res->ai_addrlen, + host, hostlen, NULL, 0, NI_NOFQDN); + if (error == EAI_NONE) { + diag_sprintf(buffer, "IPv6 Reverse lookup hostname part %s: Result %s", + info->ip_addr_v6, + host); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == strcmp(info->hostname_v6, host)), + "IPv6 Reverse lookup hostname part"); + } else { + diag_sprintf(buffer,"IPv6 Reverse lookup hostname part: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + freeaddrinfo(res); + + // Setup a domainname. We now don't have to use fully qualified + // domain names + setdomainname(info->domain_name, strlen(info->domain_name)); + getdomainname(dn, sizeof(dn)); + CYG_TEST_PASS_FAIL(0 == strcmp(info->domain_name, dn), + "{get|set}domainname"); + + // Lookup the IPv6 FQDN hostname to make sure it still works + hints.ai_flags = AI_CANONNAME; + error = getaddrinfo(name, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + name, + res->ai_canonname, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa6, + (void*)res->ai_addr, + sizeof(sa6))) && + (res->ai_family == AF_INET6), + "IPv6 FQDN hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, res->ai_canonname)), + "IPv6 FQDN hostname name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv6 FQDN hostname one result"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv6 FQDN hostname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Now the host name without the domain name + error = getaddrinfo(info->hostname_v6, NULL, &hints, &res); + + if (error == EAI_NONE) { + getnameinfo(res->ai_addr, res->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + info->hostname_v6, + res->ai_canonname, + familytoa(res->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa6, + (void*)res->ai_addr, + sizeof(sa6))) && + (res->ai_family == AF_INET6), + "IPv6 hostname address"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, res->ai_canonname)), + "IPv6 hostname name"); + CYG_TEST_PASS_FAIL((NULL == res->ai_next), + "IPv6 hostname one result"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv6 hostname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Check if we have the information to do tests which result in two + // answers, an AF_INET and an AF_INET6 + if (!info->hostname_v46 || !info->cname_v46 || + !info->ip_addr_v46_v4 || !info->ip_addr_v46_v6 ) { + return; + } + setdomainname(NULL,0); + + strcpy(name,info->hostname_v46); + strcat(name,"."); + strcat(name,info->domain_name); + strcpy(cname,info->cname_v46); + strcat(cname,"."); + strcat(cname,info->domain_name); + + inet_aton(info->ip_addr_v46_v4, &addr); + memset(&sa4,0,sizeof(sa4)); + memcpy(&sa4.sin_addr, &addr, sizeof(addr)); + sa4.sin_family=AF_INET; + sa4.sin_len = sizeof(sa4); + + memset(&sa6,0,sizeof(sa6)); + sa6.sin6_family = AF_INET6; + sa6.sin6_len = sizeof(sa6); + inet_pton(AF_INET6, info->ip_addr_v46_v6, (void *)&sa6.sin6_addr); + + // Lookup the IPv4 and IPv6 FQDN hostname + error = getaddrinfo(name, NULL, &hints, &res); + + if (error == EAI_NONE) { +#ifdef CYGOPT_NS_DNS_FIRST_FAMILY_AF_INET6 + ai6 = res; + ai4 = res->ai_next; + CYG_TEST_PASS_FAIL((NULL != ai6->ai_next), + "IPv6 FQDN hostname not one result"); + CYG_TEST_PASS_FAIL((NULL == ai4->ai_next), + "IPv4 & IPv6 FQDN hostname two results"); +#else + ai4 = res; + ai6 = res->ai_next; + CYG_TEST_PASS_FAIL((NULL != ai4->ai_next), + "IPv6 FQDN hostname not one result"); + CYG_TEST_PASS_FAIL((NULL == ai6->ai_next), + "IPv4 & IPv6 FQDN hostname two results"); +#endif + getnameinfo(ai4->ai_addr, ai4->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + name, + ai4->ai_canonname, + familytoa(ai4->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa4, + (void*)ai4->ai_addr, + sizeof(sa4))) && + (ai4->ai_family == AF_INET), + "IPv4 & IPv6 FQDN hostname address IPv4"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, ai4->ai_canonname)), + "IPv4 & IPv6 FQDN hostname name"); + getnameinfo(ai6->ai_addr, ai6->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s %s>", + name, + familytoa(ai6->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa6, + (void*)ai6->ai_addr, + sizeof(sa6))) && + (ai6->ai_family == AF_INET6), + "IPv4 & IPv6 FQDN hostname address IPv6"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv4 & IPv6 FQDN hostname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } + + // Lookup the IPv4 and IPv6 FQDN cname + error = getaddrinfo(cname, NULL, &hints, &res); + + if (error == EAI_NONE) { +#ifdef CYGOPT_NS_DNS_FIRST_FAMILY_AF_INET6 + ai6 = res; + ai4 = res->ai_next; + CYG_TEST_PASS_FAIL((NULL != ai6->ai_next), + "IPv6 FQDN hostname not one result"); + CYG_TEST_PASS_FAIL((NULL == ai4->ai_next), + "IPv4 & IPv6 FQDN hostname two results"); +#else + ai4 = res; + ai6 = res->ai_next; + CYG_TEST_PASS_FAIL((NULL != ai4->ai_next), + "IPv6 FQDN hostname not one result"); + CYG_TEST_PASS_FAIL((NULL == ai6->ai_next), + "IPv4 & IPv6 FQDN hostname two results"); +#endif + getnameinfo(ai4->ai_addr, ai4->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s is %s %s>", + cname, + ai4->ai_canonname, + familytoa(ai4->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa4, + (void*)ai4->ai_addr, + sizeof(sa4))) && + (ai4->ai_family == AF_INET), + "IPv4 & IPv6 FQDN cname address IPv4"); + CYG_TEST_PASS_FAIL((0 == strcmp(name, ai4->ai_canonname)), + "IPv4 & IPv6 FQDN cname name"); + getnameinfo(ai6->ai_addr, ai6->ai_addrlen, + buff, sizeof(buff), + NULL,0,NI_NUMERICHOST); + diag_sprintf(buffer,"Lookup %s: Result <%s %s>", + cname, + familytoa(ai6->ai_family), + buff); + CYG_TEST_INFO(buffer); + CYG_TEST_PASS_FAIL((0 == memcmp((void *)&sa6, + (void*)ai6->ai_addr, + sizeof(sa6))) && + (ai6->ai_family == AF_INET6), + "IPv4 & IPv6 FQDN cname address IPv6"); + freeaddrinfo(res); + } else { + diag_sprintf(buffer,"IPv4 & IPv6 FQDN cname: error %s", + gai_strerror(error)); + CYG_TEST_FAIL(buffer); + } +#endif +} + +void +dns_test_thread(cyg_addrword_t p) +{ int i; CYG_TEST_INIT(); @@ -89,81 +828,11 @@ dns_test(cyg_addrword_t p) init_all_network_interfaces(); CYG_TEST_INFO("Starting dns1 test"); - - setdomainname(NULL,0); - for (i=0; i<2; i++) { - /* Expect _LOOKUP_IP as the answer. This is a CNAME lookup */ - inet_aton(_LOOKUP_IP, &addr); - hent = gethostbyname(_LOOKUP_FQDN); - if (hent != NULL) { - diag_printf("PASS:<%s is %s>\n", hent->h_name, inet_ntoa(*(struct in_addr *)hent->h_addr)); - if (0 != memcmp((void*)&addr, (void*)(hent->h_addr), sizeof(struct in_addr))) { - diag_printf("FAIL:<expected " _LOOKUP_FQDN " to be " _LOOKUP_IP ">\n"); - } - break; - } else { - diag_printf("FAIL:<Asked for " _LOOKUP_FQDN ". No answer: %s>\n", hstrerror(h_errno)); - CYG_TEST_INFO("Retrying with explicit DNS server"); - CYG_TEST_INFO("Connecting to DNS at " _DNS_IP); - inet_aton(_DNS_IP, &addr); - CYG_TEST_CHECK(cyg_dns_res_init(&addr) == 0, "Failed to initialize resolver"); - } - } - - /* Reverse lookup the _LOOKUP_IP addres, expect _LOOKUP_FQDN - as the answer. */ - hent = gethostbyaddr((char *)&addr, sizeof(struct in_addr), AF_INET); - if (hent != NULL) { - diag_printf("PASS:<%s is %s>\n", hent->h_name, inet_ntoa(*(struct in_addr *)hent->h_addr)); - if (0 != strcmp(_LOOKUP_FQDN, hent->h_name)) { - diag_printf("FAIL:<expected " _LOOKUP_IP " to be " _LOOKUP_FQDN ">\n"); - } - } else { - diag_printf("FAIL:<Asked for " _LOOKUP_IP ". No answer: %s>\n", hstrerror(h_errno)); + for (i = 0; i < NELEM(test_info); i++) { + dns_test(&test_info[i]); } - - /* This does not require a DNS lookup. Just turn the value into - binary */ - hent = gethostbyname(_LOOKUP_IP); - if (hent != NULL) { - diag_printf("PASS:<%s is %s>\n", hent->h_name, inet_ntoa(*(struct in_addr *)hent->h_addr)); - } else { - diag_printf("FAIL:<Asked for " _LOOKUP_IP ". No answer: %s>\n", hstrerror(h_errno)); - } - - /* Reverse lookup an address this is not in the server. Expect a - NULL back */ - inet_aton(_LOOKUP_IP_BAD, &addr); - hent = gethostbyaddr((char *)&addr, sizeof(struct in_addr), AF_INET); - if (hent != NULL) { - diag_printf("FAIL:<%s is %s>\n", hent->h_name, inet_ntoa(*(struct in_addr *)hent->h_addr)); - } else { - diag_printf("PASS:<Asked for bad IP " _LOOKUP_IP_BAD ". No answer: %s>\n", hstrerror(h_errno)); - } - - /* Setup a domainname. We now don't have to use fully qualified - domain names */ - setdomainname(_LOOKUP_DOMAINNAME, sizeof(_LOOKUP_DOMAINNAME)); - getdomainname(dn, sizeof(dn)); - diag_printf("INFO:<Domainname is now %s>\n", dn); - - /* Make sure FQDN still work */ - hent = gethostbyname(_LOOKUP_FQDN); - if (hent != NULL) { - diag_printf("PASS:<%s is %s>\n", hent->h_name, inet_ntoa(*(struct in_addr *)hent->h_addr)); - } else { - diag_printf("FAIL:<Asked for " _LOOKUP_FQDN ". No answer: %s>\n", hstrerror(h_errno)); - } - - /* Now just the hostname */ - hent = gethostbyname(_LOOKUP_HOSTNAME); - if (hent != NULL) { - diag_printf("PASS:<%s is %s>\n", _LOOKUP_HOSTNAME, inet_ntoa(*(struct in_addr *)hent->h_addr)); - } else { - diag_printf("FAIL:<Asked for " _LOOKUP_HOSTNAME ". No answer: %s>\n", hstrerror(h_errno)); - } - + CYG_TEST_FINISH("dns1 test completed"); } @@ -172,7 +841,7 @@ cyg_start(void) { // Create a main thread, so we can run the scheduler and have time 'pass' cyg_thread_create(10, // Priority - just a number - dns_test, // entry + dns_test_thread, // entry 0, // entry parameter "DNS test", // Name &stack[0], // Stack
