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;
+}