/*
 *  Copyright (c) 1996-2007 Digi International Inc., All Rights Reserved
 *
 *  This software contains proprietary and confidential information of Digi
 *  International Inc.  By accepting transfer of this copy, Recipient agrees
 *  to retain this software in confidence, to prevent disclosure to others,
 *  and to make no use of this software other than that for which it was
 *  delivered.  This is an unpublished copyrighted work of Digi International
 *  Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
 *  prohibited.
 *
 *  Restricted Rights Legend
 *
 *  Use, duplication, or disclosure by the Government is subject to
 *  restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
 *  Technical Data and Computer Software clause at DFARS 252.227-7031 or
 *  subparagraphs (c)(1) and (2) of the Commercial Computer Software -
 *  Restricted Rights at 48 CFR 52.227-19, as applicable.
 *
 *  Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
 *
 * Description.
 * =======================================================================
 *  applicationStart() is the entry point for user applications.  This
 *  This function will be called after the kernel has started and the
 *  TCP/IP stack has loaded.  applicationTcpDown() is called periodically
 *  after the kernel has started while the system is waiting for the
 *  TCP/IP stack to start.  
 *
 *
 *
 *
 * Edit Date/Ver   Edit Description
 * ==============  =======================================================
 * V1.1  10/27/00	 jzw "beefed up" the example application with the following:
 *			 1) Two name servers are added to the system instead of one
 *			 2) After the call to DNSGetServers, the servers found are
 *			    displayed
 *			 3) After one of the DNS servers is removed, a second
 *			    call to DNSGetServers is performed and the resultant servers
 *			    are displayed to show that a DNS server was actually removed
 *			 4) Added function DisplayServers to have one function fpr repetitive
 *			   operations
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <tx_api.h>
#include <bsp_api.h>
#include <dnsc_api.h>
#include <sockapi.h>
#include "appconf.h"
#include "narmapi.h"

#define HOST_NAME_STR_LEN       256
#define MAX_ADDR_STR_LEN        46

#define FIRST_DNS_SERVER        0
#define SECOND_DNS_SERVER       1

/* Change these to your liking. */
#define DNS_SERVER1_IPV4_ADDR   "10.52.1.15" 
#define DNS_SERVER2_IPV4_ADDR   "10.10.8.62"
#define DNS_SERVER3_IPV4_ADDR   "10.52.1.16" 
#define DNS_SERVER4_IPV4_ADDR   "10.52.1.17" 
#define DNS_SERVER1_IPV6_ADDR   "FE80::208:74FF:FEF2:BBD0"
#define DNS_DOMAIN_NAME1        "digi.com"
#define DNS_DOMAIN_NAME2        "yahoo.com"
#define DNS_DOMAIN_NAME3        "google.com"
#define DNS_DOMAIN_NAME4        "microsoft.com"

//#define TEST_IPV6_DNS

static void DisplayServer(struct sockaddr_storage *server)
{
    char buffer[128];
    char *addr;

    /* retrieve the servers */
    if (server)
    {
        addr = NULL;
        if (server->ss_family == AF_INET6)
        {
            addr = (char *)&server->addr.ipv6.sin6_addr;
        }
        else if (server->ss_family == AF_INET)
        {
            addr = (char *)&server->addr.ipv4.sin_addr;
        }       

        if (addr != NULL)
        {
            inet_ntop(server->ss_family, (void *)addr, buffer, 250); 
            
            printf("%s\n", buffer);
        }
    }
}

static void DisplayServers(struct sockaddr_storage *servers, int NumServers)
{
    int indx = 0;
    
    /* retrieve the servers */
    if (NumServers == 0)
    {
        printf("No DNS servers found\n");
    }
    else
    {
        /* list the servers found */
        printf("The following servers were found:\n");
        for (indx = 0; indx < NumServers; indx++)
        {
            DisplayServer(servers++);
        }
    }
}

/*
 *
 *  Function: void applicationTcpDown (void)
 *
 *  Description:
 *
 *      This routine will be called by the NET+OS root thread once every 
 *      clock tick while it is waiting for the TCP/IP stack to come up.  
 *      This function can increment a counter everytime it's called to 
 *      keep track of how long we've been waiting for the stack to start.
 *      If we've been waiting too long, then this function can do something
 *      to handle the error.  
 *
 *      This function will not be called once the stack has started.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */

void applicationTcpDown (void)

{
    static int ticksPassed = 0;

    ticksPassed++;
/*
 * Code to handle error condition if the stack doesn't come up goes here.
 */
}




/*
 *
 *  Function: void applicationStart (void)
 *
 *  Description:
 *
 *      This routine is responsible for starting the user application.  It should 
 *      create any threads or other resources the application needs.
 *
 *      ThreadX, the NET+OS device drivers, and the TCP/IP stack will be running
 *      when this function is called.
 *
 *  Parameters:
 *
 *      none
 *
 *  Return Values:
 *
 *      none
 *
 */
void applicationStart (void)

{
    char addr_string[MAX_ADDR_STR_LEN];
    int  addr_len[TM_DNS_MAX_SERVERS];
    struct addrinfo addr_hint, * addr_info;
    struct sockaddr_storage dns_servers[TM_DNS_MAX_SERVERS];
    struct sockaddr_storage servers_list[TM_DNS_MAX_SERVERS];
    struct sockaddr_storage addr[TM_DNS_MAX_SERVERS]; 
#if defined IPV6_ENABLED && defined TEST_IPV6_DNS
    char*  dns_addrs[TM_DNS_MAX_SERVERS] = {DNS_SERVER1_IPV6_ADDR,
                                         DNS_SERVER1_IPV4_ADDR,
                                         DNS_SERVER2_IPV4_ADDR,
                                         DNS_SERVER3_IPV4_ADDR};
#else
    char*  dns_addrs[TM_DNS_MAX_SERVERS] = {DNS_SERVER1_IPV4_ADDR,
                                         DNS_SERVER2_IPV4_ADDR,
                                         DNS_SERVER3_IPV4_ADDR,
                                         DNS_SERVER4_IPV4_ADDR};
#endif
    char*  dns_names[TM_DNS_MAX_SERVERS] = {DNS_DOMAIN_NAME1,
                                         DNS_DOMAIN_NAME2,
                                         DNS_DOMAIN_NAME3,
                                         DNS_DOMAIN_NAME4};

    int LoopIdx = 0;
    int rc;
    char hostname[HOST_NAME_STR_LEN];
 
    printf("Start DNS Client application\n");

    /* Display servers already configured. */
    if ((rc = DNSGetServers(servers_list, TM_DNS_MAX_SERVERS)) < 0)
    {
        printf("Cannot retrieve DNS servers. \n");
        return;
    }

    DisplayServers(servers_list, rc);

    /* Remove the configured servers before adding new ones. */
    for ( LoopIdx = 0; LoopIdx < rc; LoopIdx++)
    { 
        if (DNSRemoveServer(&servers_list[LoopIdx]) < 0)
        {
            printf("Call to DNSRemoveServer() failed \n");
        }
        else
        {
            printf("Removed DNS server ");
            DisplayServer(&servers_list[LoopIdx]);
        }
    }

    memset(dns_servers, 0, sizeof(struct sockaddr_storage) * TM_DNS_MAX_SERVERS);

    for ( LoopIdx = 0; LoopIdx < TM_DNS_MAX_SERVERS; LoopIdx++)
    {
#if defined IPV6_ENABLED && defined TEST_IPV6_DNS

        dns_servers[LoopIdx].ss_family = LoopIdx ? AF_INET : AF_INET6;

        if (!LoopIdx)
        {
            if (!inet_pton(AF_INET6, dns_addrs[LoopIdx],
                           &(dns_servers[LoopIdx].addr.ipv6.sin6_addr)))
            {
                printf("Bad IPV6 DNS server address string - Exiting\n");
                return;
            }
        }
        else
        {
            if (!inet_pton(AF_INET, dns_addrs[LoopIdx],
                           &(dns_servers[LoopIdx].addr.ipv4.sin_addr)))
            {
                printf("Bad IPV4 DNS server address string - Exiting\n");
                return;
            }
        } 
#else
        dns_servers[LoopIdx].ss_family = AF_INET;
        if (!inet_pton(AF_INET, dns_addrs[LoopIdx],
                       &(dns_servers[LoopIdx].addr.ipv4.sin_addr)))
        {
            printf("Bad DNS server address - Exiting\n");
            return;
        }  
#endif          

        if (DNSAddServer(&dns_servers[LoopIdx]) < 0)
        {
            printf("DNSAddServer() for %s failed. \n", dns_addrs[LoopIdx]);
        }
        else
        {
            printf("DNSAddServer() for %s succeeded.\n", dns_addrs[LoopIdx]);
        }

    } /* end for loop */

    /* how many DNS servers do we have */
    if ((rc = DNSGetServers(servers_list, TM_DNS_MAX_SERVERS)) < 0)
    {
        printf("Cannot retrieve DNS servers. \n");
        return;
    }

    DisplayServers(servers_list, rc);


    for (LoopIdx = 0; LoopIdx < rc; LoopIdx++)
    {
        addr_hint.ai_flags  = 0;
#if defined IPV6_ENABLED && defined TEST_IPV6_DNS
        addr_hint.ai_family = LoopIdx ? PF_INET : PF_INET6;
#else
        addr_hint.ai_family = PF_INET;
#endif    

        /* Get IP address of domain name.*/
        if (getaddrinfo(dns_names[LoopIdx], NULL ,&addr_hint,
                                        &addr_info) != TM_ENOERROR) 
        {
            printf("Cannot resolve %s with getaddrinfo\n", dns_names[LoopIdx]);
            #ifdef DNS_SERVER_SUPPORTS_IPV6
            /* Try getnameinfo because it may be an ip address */ 
            if (na_pton(dns_names[LoopIdx], &addr[LoopIdx]) != 1)
            {
                continue;
            }            
            addr_len[LoopIdx] = addr[LoopIdx].ss_len;
            #else
            addr[LoopIdx].addr.ipv4.sin_addr.s_addr = NAInet_addr(dns_names[LoopIdx]);
            if (!addr[LoopIdx].addr.ipv4.sin_addr.s_addr)
            {
                continue;
            }
            addr[LoopIdx].ss_family = AF_INET;
            addr_len[LoopIdx] = sizeof(struct sockaddr);
            #endif
            printf ("This is an IP address\n");
        }
        else
        {
            do
            {
                addr_len[LoopIdx] =  addr_info->ai_addrlen;
                memcpy(&addr[LoopIdx], addr_info->ai_addr, addr_len[LoopIdx]);
            #if defined IPV6_ENABLED && defined TEST_IPV6_DNS
                if (addr_info->ai_family == AF_INET6)
                {
                    inet_ntop(AF_INET6,
                              (void *)&addr[LoopIdx].addr.ipv6.sin6_addr,
                              addr_string,
                              MAX_ADDR_STR_LEN);
                }
                else
                {
                    inet_ntop(AF_INET,
                              (void *)&addr[LoopIdx].addr.ipv4.sin_addr.s_addr,
                              addr_string,
                              MAX_ADDR_STR_LEN);
                }
            #else
                inet_ntop(addr_info->ai_family,
                          (void *)&addr[LoopIdx].addr.ipv4.sin_addr.s_addr,
                          addr_string,
                          MAX_ADDR_STR_LEN);
            #endif

                printf("Domain name: %s has IP address: %s\n", dns_names[LoopIdx], addr_string);
              
                /* Get domain name of IP address.*/
                if(getnameinfo((struct sockaddr *)&addr[LoopIdx],
                                    addr_len[LoopIdx],
                                    hostname,
                                    HOST_NAME_STR_LEN,
                                    NULL, /* This and the following param. are not used */
                                    0,
                                    NI_NAMEREQD) != TM_ENOERROR)
                {
                    printf("Cannot resolve IP address for domain name: %s\n", dns_names[LoopIdx]);
                }
                else
                {
                    printf("IP address: %s has domain name: %s\n", addr_string, hostname);
                }

                addr_info = addr_info->ai_next;

            } while (addr_info != NULL);        
        }
    } 

    printf("All done\n");
    tx_thread_suspend(tx_thread_identify());
}



