1/*  *********************************************************************
2    *  Broadcom Common Firmware Environment (CFE)
3    *
4    *  Domain Name System Resolver		File: net_dns.c
5    *
6    *  This module provides minimal support for looking up IP addresses
7    *  from DNS name servers (RFCxxxx)
8    *
9    *  Author:  Mitch Lichtenberg (mpl@broadcom.com)
10    *
11    *********************************************************************
12    *
13    *  Copyright 2000,2001,2002,2003
14    *  Broadcom Corporation. All rights reserved.
15    *
16    *  This software is furnished under license and may be used and
17    *  copied only in accordance with the following terms and
18    *  conditions.  Subject to these conditions, you may download,
19    *  copy, install, use, modify and distribute modified or unmodified
20    *  copies of this software in source and/or binary form.  No title
21    *  or ownership is transferred hereby.
22    *
23    *  1) Any source code used, modified or distributed must reproduce
24    *     and retain this copyright notice and list of conditions
25    *     as they appear in the source file.
26    *
27    *  2) No right is granted to use any trade name, trademark, or
28    *     logo of Broadcom Corporation.  The "Broadcom Corporation"
29    *     name may not be used to endorse or promote products derived
30    *     from this software without the prior written permission of
31    *     Broadcom Corporation.
32    *
33    *  3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR
34    *     IMPLIED WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED
35    *     WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
36    *     PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT
37    *     SHALL BROADCOM BE LIABLE FOR ANY DAMAGES WHATSOEVER, AND IN
38    *     PARTICULAR, BROADCOM SHALL NOT BE LIABLE FOR DIRECT, INDIRECT,
39    *     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40    *     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
41    *     GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
42    *     BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
43    *     OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
44    *     TORT (INCLUDING NEGLIGENCE OR OTHERWISE), EVEN IF ADVISED OF
45    *     THE POSSIBILITY OF SUCH DAMAGE.
46    ********************************************************************* */
47
48
49#include "lib_types.h"
50#include "lib_string.h"
51#include "lib_queue.h"
52#include "lib_malloc.h"
53#include "lib_printf.h"
54
55#include "cfe_timer.h"
56#include "cfe_error.h"
57
58#include "net_ebuf.h"
59#include "net_ether.h"
60
61#include "cfe.h"
62
63#include "net_api.h"
64
65/*  *********************************************************************
66    *  Macros
67    ********************************************************************* */
68
69#define ip_addriszero(a) (((a)[0]|(a)[1]|(a)[2]|(a)[3]) == 0)
70
71/*  *********************************************************************
72    *  Constants
73    ********************************************************************* */
74
75#define UDP_PORT_DNS	53
76
77#define DNS_FLG_QUERY	 0x0000
78#define DNS_FLG_RESPONSE 0x8000
79#define DNS_OPCODE_QUERY 0x0000
80#define DNS_FLG_AA       0x0400
81#define DNS_FLG_TC       0x0200
82#define DNS_FLG_RD       0x0100
83#define DNS_FLG_RA       0x0080
84#define DNS_RCODE_MASK   0x000F
85#define DNS_RCODE_OK	 0x0000
86#define DNS_RCODE_NAMEERR 0x0003
87
88
89#define QTYPE_HOSTADDR	1
90#define QCLASS_INTERNET	1
91
92#define DNS_QUERY_TIMEOUT  1 /* seconds */
93#define DNS_RETRY_COUNT    8
94
95
96/*  *********************************************************************
97    *  dns_dolookup(s,hostname,ipaddr)
98    *
99    *  Look up a host name and return its IP address.
100    *
101    *  Input parameters:
102    * 	   s - udp socket
103    *      server - name server to query
104    *  	   hostname - host name to find
105    *  	   ipaddr - buffer to place IP address
106    *
107    *  Return value:
108    *  	   0 if no responses found
109    *      >0 to indicate number of response records (usually 1)
110    *  	   else error code
111    ********************************************************************* */
112
113static int dns_dolookup(int s,uint8_t *server,char *hostname,uint8_t *ipaddr)
114{
115    ebuf_t *buf;
116    uint16_t id;
117    uint16_t tmp;
118    char *tok;
119    char *ptr;
120    int64_t timer;
121    int nres = 0;
122    uint16_t nqr,nar,nns,nxr;
123    uint8_t tmpb;
124    int expired;
125
126
127    /*
128     * Use the current time for our request ID
129     */
130
131    id = (uint16_t) cfe_ticks;
132
133    /*
134     * Get a buffer and fill it in.
135     */
136
137    buf = udp_alloc();
138
139    ebuf_append_u16_be(buf,id);
140    ebuf_append_u16_be(buf,(DNS_FLG_QUERY | DNS_OPCODE_QUERY | DNS_FLG_RD));
141    ebuf_append_u16_be(buf,1);		/* one question */
142    ebuf_append_u16_be(buf,0);		/* no answers */
143    ebuf_append_u16_be(buf,0);		/* no server resource records */
144    ebuf_append_u16_be(buf,0);		/* no additional records */
145
146    /*
147     * Chop up the hostname into pieces, basically replacing
148     * the dots with length indicators.
149     */
150
151    ptr = hostname;
152
153    while ((tok = strchr(ptr,'.'))) {
154	ebuf_append_u8(buf,(tok-ptr));
155	ebuf_append_bytes(buf,ptr,(tok-ptr));
156	ptr = tok + 1;
157	}
158
159    ebuf_append_u8(buf,strlen(ptr));
160    ebuf_append_bytes(buf,ptr,strlen(ptr));
161    ebuf_append_u8(buf,0);
162    ebuf_append_u16_be(buf,QTYPE_HOSTADDR);
163    ebuf_append_u16_be(buf,QCLASS_INTERNET);
164
165    /*
166     * Send the request to the name server
167     */
168
169    udp_send(s,buf,server);
170
171    /*
172     * Set a timer for the response
173     */
174
175    TIMER_SET(timer,DNS_QUERY_TIMEOUT*CFE_HZ);
176
177    /*
178     * Start waiting...
179     */
180
181    while (!(expired = TIMER_EXPIRED(timer))) {
182	POLL();
183
184	buf = udp_recv(s);
185	if (!buf) continue;
186
187	/* IDs must match */
188
189	ebuf_get_u16_be(buf,tmp);
190	if (id != tmp) goto drop;
191
192	/* It must be a response */
193
194	ebuf_get_u16_be(buf,tmp);
195
196	if ((tmp & DNS_FLG_RESPONSE) == 0)  goto drop;
197
198	if ((tmp & DNS_RCODE_MASK) != DNS_RCODE_OK) {
199	    udp_free(buf);
200	    /* name error */
201	    break;
202	    }
203
204	ebuf_get_u16_be(buf,nqr);
205	ebuf_get_u16_be(buf,nar);
206	ebuf_get_u16_be(buf,nns);
207	ebuf_get_u16_be(buf,nxr);
208
209	if (nar == 0) {
210	    goto drop;
211	    }
212
213	while (nqr > 0) {
214	    if (ebuf_length(buf) <= 0) {
215		goto drop;
216		}
217	    for (;;) {
218		ebuf_get_u8(buf,tmpb);
219		if (tmpb == 0) break;
220		ebuf_skip(buf,tmpb);
221		if (ebuf_length(buf) <= 0) {
222		    goto drop;
223		    }
224		}
225	    ebuf_skip(buf,2);	/* skip QTYPE */
226	    ebuf_skip(buf,2);	/* skip QCLASS */
227	    nqr--;		/* next question record */
228	    }
229
230	/*
231	 * Loop through the answer records to find
232	 * a HOSTADDR record.  Ignore any other records
233	 * we find.
234	 */
235
236	while (nar > 0) {
237	    uint16_t rname,rtype,rclass,dlen;
238
239	    ebuf_get_u16_be(buf,rname);	/* resource name */
240
241	    ebuf_get_u16_be(buf,rtype);	/* resource type */
242
243	    ebuf_get_u16_be(buf,rclass);	/* resource class */
244
245	    ebuf_skip(buf,4);		/* time to live */
246
247	    ebuf_get_u16_be(buf,dlen);	/* length of data */
248
249	    if (rtype != QTYPE_HOSTADDR) {
250		ebuf_skip(buf,dlen);
251		nar--;
252		continue;
253		}
254	    if (rclass != QCLASS_INTERNET) {
255		ebuf_skip(buf,dlen);
256		nar--;
257		continue;
258		}
259
260	    if (dlen != IP_ADDR_LEN) {
261		ebuf_skip(buf,dlen);
262		nar--;
263		continue;
264		}
265
266	    ebuf_get_bytes(buf,ipaddr,IP_ADDR_LEN);
267	    break;
268	    }
269
270	if (nar == 0) goto drop;
271
272	udp_free(buf);
273	nres++;
274	break;
275
276    drop:
277	udp_free(buf);
278	}
279
280    if (expired) return CFE_ERR_TIMEOUT;
281    if (nres == 0) return CFE_ERR_HOSTUNKNOWN;
282    return nres;
283}
284
285
286/*  *********************************************************************
287    *  dns_lookup(hostname,ipaddr)
288    *
289    *  Look up a host name and return its IP address.
290    *
291    *  Input parameters:
292    *  	   hostname - host name to find
293    *  	   ipaddr - buffer to place IP address
294    *
295    *  Return value:
296    *  	   0 if no responses found
297    *      >0 to indicate number of response records (usually 1)
298    *  	   else error code
299    ********************************************************************* */
300
301int dns_lookup(char *hostname,uint8_t *ipaddr)
302{
303    int s;
304    int nres = 0;
305    int retries;
306    char temphostname[100];
307    uint8_t *server;
308    char *tok;
309
310    /*
311     * If it's a valid IP address, don't look it up.
312     */
313
314    if (parseipaddr(hostname,ipaddr) == 0) return 1;
315
316    /*
317     * Add default domain if no domain was specified
318     */
319
320    if (strchr(hostname,'.') == NULL) {
321	tok = net_getparam(NET_DOMAIN);
322	if (tok) {
323	    xsprintf(temphostname,"%s.%s",hostname,tok);
324	    hostname = temphostname;
325	    }
326	}
327
328    /*
329     * Figure out who the name server is
330     */
331
332    server = net_getparam(NET_NAMESERVER);
333
334    if (!server) return CFE_ERR_NETDOWN;
335    if (ip_addriszero(server)) return CFE_ERR_NONAMESERVER;
336
337    /*
338     * Go do the name server lookup
339     */
340
341    s = udp_socket(UDP_PORT_DNS);
342    if (s < 0) return CFE_ERR_NOHANDLES;
343
344    for (retries = 0; retries < DNS_RETRY_COUNT; retries++) {
345	nres = dns_dolookup(s,server,hostname,ipaddr);
346	if (nres == CFE_ERR_TIMEOUT) continue;
347	if (nres >= 0) break;
348	}
349
350    udp_close(s);
351
352    return nres;
353
354}
355