Mercurial > ecos
diff packages/net/ns/dns/current/src/dns.c @ 208:e0c0827131d1 ecos
Merge from eCos master repository on 2002-05-20-20:11:54-BST
| author | jlarmour |
|---|---|
| date | Mon, 20 May 2002 22:19:26 +0000 |
| parents | |
| children | d2c90368aeef |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/net/ns/dns/current/src/dns.c @@ -0,0 +1,276 @@ +//============================================================================= +// +// dns.c +// +// DNS client code +// +//============================================================================= +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// +// 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 +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//============================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): andrew.lunn +// Contributors:andrew.lunn, jskov +// Date: 2001-09-18 +// Description: Provides DNS lookup as per RFC 1034/1035. +// +// Note: Does only support A and PTR lookups. Maybe add for other +// types as well? +// +// parse_answer() only returns the first found record. +// +// Add tracing and assertions. +// +//####DESCRIPTIONEND#### +// +//============================================================================= + +#include <cyg/hal/drv_api.h> +#include <cyg/infra/cyg_type.h> +#include <cyg/infra/cyg_trac.h> /* Tracing support */ + +#include <netdb.h> + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <cyg/ns/dns/dns_priv.h> + +static short id = 0; /* ID of the last query */ +static int s = -1; /* Socket to the DNS server */ +static cyg_drv_mutex_t dns_mutex; /* Mutex to stop multiple queries as once */ +static cyg_ucount32 ptdindex; /* Index for the per thread data */ +static char * domainname=NULL; /* Domain name used for queries */ + +/* Allocate space for string of length len. Return NULL on failure. */ +static inline char* +alloc_string(int len) +{ + return malloc(len); +} + +static inline void +free_string(char* s) +{ + free(s); +} + +/* Deallocate the memory taken to hold a hent structure */ +static void +free_hent(struct hostent * hent) +{ + if (hent->h_name) { + free_string(hent->h_name); + } + + if (hent->h_addr_list) { + int i = 0; + while (hent->h_addr_list[i]) { + free(hent->h_addr_list[i]); + i++; + } + free(hent->h_addr_list); + } +} + +/* Allocate hent structure with room for one in_addr. Returns NULL on + failure. */ +static struct hostent* +alloc_hent(void) +{ + struct hostent *hent; + + hent = malloc(sizeof(struct hostent)); + if (hent) { + memset(hent, 0, sizeof(struct hostent)); + hent->h_addr_list = malloc(sizeof(char *)*2); + if (!hent->h_addr_list) { + free_hent(hent); + return NULL; + } + hent->h_addr_list[0] = malloc(sizeof(struct in_addr)); + if (!hent->h_addr_list[0]) { + free_hent(hent); + return NULL; + } + hent->h_addr_list[1] = NULL; + } + + return hent; +} + +/* Send the query to the server and read the response back. Return -1 + if it fails, otherwise put the response back in msg and return the + length of the response. */ +static int +send_recv(char * msg, int len, int msglen) +{ + struct dns_header *dns_hdr; + struct timeval timeout; + int finished = false; + int backoff = 1; + fd_set readfds; + int written; + int ret; + + CYG_REPORT_FUNCNAMETYPE( "send_recv", "returning %d" ); + CYG_REPORT_FUNCARG3( "msg=%08x, len=%d, msglen", msg, len, msglen ); + + CYG_CHECK_DATA_PTR( msg, "msg is not a valid pointer!" ); + + dns_hdr = (struct dns_header *) msg; + + do { + written = write(s, msg, len); + if (written < 0) { + ret = -1; + break; + } + + FD_ZERO(&readfds); + FD_SET(s, &readfds); + + timeout.tv_sec = backoff; + timeout.tv_usec = 0; + backoff = backoff << 1; + + ret = select(s+1, &readfds, NULL, NULL, &timeout); + if (ret < 0) { + ret = -1; + break; + } + /* Timeout */ + if (ret == 0) { + if (backoff > 16) { + h_errno = TRY_AGAIN; + ret = -1; + break; + } + } + if (ret == 1) { + ret = read(s, msg, msglen); + if (ret < 0) { + ret = -1; + break; + } + + /* Reply to an old query. Ignore it */ + if (ntohs(dns_hdr->id) != (id-1)) { + continue; + } + finished = true; + } + } while (!finished); + + CYG_REPORT_RETVAL( ret ); + + return ret; +} + +/* Include the DNS client implementation code */ +#include <cyg/ns/dns/dns_impl.inl> + +/* Initialise the resolver. Open a socket and bind it to the address + of the server. return -1 if something goes wrong, otherwise 0. If + we are being called a second time we have to be careful to allow + any ongoing lookups to finish before we close the socket and + connect to a different DNS server. The danger here is that we may + have to wait for upto 32 seconds if the DNS server is down. + */ +int +cyg_dns_res_init(struct in_addr *dns_server) +{ + struct sockaddr_in server; + struct servent *sent; + static int init =0; + + CYG_REPORT_FUNCNAMETYPE( "cyg_dns_res_init", "returning %d" ); + CYG_REPORT_FUNCARG1( "dns_server=%08x", dns_server ); + + CYG_CHECK_DATA_PTR( dns_server, "dns_server is not a valid pointer!" ); + + if (init) { + cyg_drv_mutex_lock(&dns_mutex); + cyg_thread_free_data_index(ptdindex); + if (s >= 0) { + close(s); + } + } else { + init = 1; + cyg_drv_mutex_init(&dns_mutex); + cyg_drv_mutex_lock(&dns_mutex); + } + + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( -1 ); + return -1; + } + + sent = getservbyname("domain", "udp"); + if (sent == (struct servent *)0) { + s = -1; + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( -1 ); + return -1; + } + + memcpy((char *)&server.sin_addr, dns_server, sizeof(server.sin_addr)); + server.sin_port = sent->s_port; + server.sin_family = AF_INET; + server.sin_len = sizeof(server); + + if (connect(s, (struct sockaddr *)&server, sizeof(server)) < 0) { + s = -1; + cyg_drv_mutex_unlock(&dns_mutex); + CYG_REPORT_RETVAL( -1 ); + return -1; + } + ptdindex = cyg_thread_new_data_index(); + + cyg_drv_mutex_unlock(&dns_mutex); + + CYG_REPORT_RETVAL( 0 ); + return 0; +}
