Samba 3: added Samba 3.0.24 sources
[tomato.git] / release / src / router / samba3 / source / libads / dns.c
blob23c76213cf45528db4f64f205f9fd9d29b7899e2
1 /*
2 Unix SMB/CIFS implementation.
3 DNS utility library
4 Copyright (C) Gerald (Jerry) Carter 2006.
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "includes.h"
23 /* AIX resolv.h uses 'class' in struct ns_rr */
25 #if defined(AIX)
26 # if defined(class)
27 # undef class
28 # endif
29 #endif /* AIX */
31 /* resolver headers */
33 #include <sys/types.h>
34 #include <netinet/in.h>
35 #include <arpa/nameser.h>
36 #include <resolv.h>
37 #include <netdb.h>
39 #define MAX_DNS_PACKET_SIZE 0xffff
41 #ifdef NS_HFIXEDSZ /* Bind 8/9 interface */
42 #if !defined(C_IN) /* AIX 5.3 already defines C_IN */
43 # define C_IN ns_c_in
44 #endif
45 #if !defined(T_A) /* AIX 5.3 already defines T_A */
46 # define T_A ns_t_a
47 #endif
48 # define T_SRV ns_t_srv
49 #else
50 # ifdef HFIXEDSZ
51 # define NS_HFIXEDSZ HFIXEDSZ
52 # else
53 # define NS_HFIXEDSZ sizeof(HEADER)
54 # endif /* HFIXEDSZ */
55 # ifdef PACKETSZ
56 # define NS_PACKETSZ PACKETSZ
57 # else /* 512 is usually the default */
58 # define NS_PACKETSZ 512
59 # endif /* PACKETSZ */
60 # define T_SRV 33
61 #endif
63 /*********************************************************************
64 *********************************************************************/
66 static BOOL ads_dns_parse_query( TALLOC_CTX *ctx, uint8 *start, uint8 *end,
67 uint8 **ptr, struct dns_query *q )
69 uint8 *p = *ptr;
70 pstring hostname;
71 int namelen;
73 ZERO_STRUCTP( q );
75 if ( !start || !end || !q || !*ptr)
76 return False;
78 /* See RFC 1035 for details. If this fails, then return. */
80 namelen = dn_expand( start, end, p, hostname, sizeof(hostname) );
81 if ( namelen < 0 ) {
82 return False;
84 p += namelen;
85 q->hostname = talloc_strdup( ctx, hostname );
87 /* check that we have space remaining */
89 if ( PTR_DIFF(p+4, end) > 0 )
90 return False;
92 q->type = RSVAL( p, 0 );
93 q->in_class = RSVAL( p, 2 );
94 p += 4;
96 *ptr = p;
98 return True;
101 /*********************************************************************
102 *********************************************************************/
104 static BOOL ads_dns_parse_rr( TALLOC_CTX *ctx, uint8 *start, uint8 *end,
105 uint8 **ptr, struct dns_rr *rr )
107 uint8 *p = *ptr;
108 pstring hostname;
109 int namelen;
111 if ( !start || !end || !rr || !*ptr)
112 return -1;
114 ZERO_STRUCTP( rr );
115 /* pull the name from the answer */
117 namelen = dn_expand( start, end, p, hostname, sizeof(hostname) );
118 if ( namelen < 0 ) {
119 return -1;
121 p += namelen;
122 rr->hostname = talloc_strdup( ctx, hostname );
124 /* check that we have space remaining */
126 if ( PTR_DIFF(p+10, end) > 0 )
127 return False;
129 /* pull some values and then skip onto the string */
131 rr->type = RSVAL(p, 0);
132 rr->in_class = RSVAL(p, 2);
133 rr->ttl = RIVAL(p, 4);
134 rr->rdatalen = RSVAL(p, 8);
136 p += 10;
138 /* sanity check the available space */
140 if ( PTR_DIFF(p+rr->rdatalen, end ) > 0 ) {
141 return False;
145 /* save a point to the rdata for this section */
147 rr->rdata = p;
148 p += rr->rdatalen;
150 *ptr = p;
152 return True;
155 /*********************************************************************
156 *********************************************************************/
158 static BOOL ads_dns_parse_rr_srv( TALLOC_CTX *ctx, uint8 *start, uint8 *end,
159 uint8 **ptr, struct dns_rr_srv *srv )
161 struct dns_rr rr;
162 uint8 *p;
163 pstring dcname;
164 int namelen;
166 if ( !start || !end || !srv || !*ptr)
167 return -1;
169 /* Parse the RR entry. Coming out of the this, ptr is at the beginning
170 of the next record */
172 if ( !ads_dns_parse_rr( ctx, start, end, ptr, &rr ) ) {
173 DEBUG(1,("ads_dns_parse_rr_srv: Failed to parse RR record\n"));
174 return False;
177 if ( rr.type != T_SRV ) {
178 DEBUG(1,("ads_dns_parse_rr_srv: Bad answer type (%d)\n", rr.type));
179 return False;
182 p = rr.rdata;
184 srv->priority = RSVAL(p, 0);
185 srv->weight = RSVAL(p, 2);
186 srv->port = RSVAL(p, 4);
188 p += 6;
190 namelen = dn_expand( start, end, p, dcname, sizeof(dcname) );
191 if ( namelen < 0 ) {
192 DEBUG(1,("ads_dns_parse_rr_srv: Failed to uncompress name!\n"));
193 return False;
195 srv->hostname = talloc_strdup( ctx, dcname );
197 return True;
201 /*********************************************************************
202 Sort SRV record list based on weight and priority. See RFC 2782.
203 *********************************************************************/
205 static int dnssrvcmp( struct dns_rr_srv *a, struct dns_rr_srv *b )
207 if ( a->priority == b->priority ) {
209 /* randomize entries with an equal weight and priority */
210 if ( a->weight == b->weight )
211 return 0;
213 /* higher weights should be sorted lower */
214 if ( a->weight > b->weight )
215 return -1;
216 else
217 return 1;
220 if ( a->priority < b->priority )
221 return -1;
223 return 1;
226 /*********************************************************************
227 Simple wrapper for a DNS SRV query
228 *********************************************************************/
230 NTSTATUS ads_dns_lookup_srv( TALLOC_CTX *ctx, const char *name, struct dns_rr_srv **dclist, int *numdcs )
232 uint8 *buffer = NULL;
233 size_t buf_len;
234 int resp_len = NS_PACKETSZ;
235 struct dns_rr_srv *dcs = NULL;
236 int query_count, answer_count, auth_count, additional_count;
237 uint8 *p = buffer;
238 int rrnum;
239 int idx = 0;
241 if ( !ctx || !name || !dclist ) {
242 return NT_STATUS_INVALID_PARAMETER;
245 /* Send the request. May have to loop several times in case
246 of large replies */
248 do {
249 if ( buffer )
250 TALLOC_FREE( buffer );
252 buf_len = resp_len * sizeof(uint8);
254 if ( (buffer = TALLOC_ARRAY(ctx, uint8, buf_len)) == NULL ) {
255 DEBUG(0,("ads_dns_lookup_srv: talloc() failed!\n"));
256 return NT_STATUS_NO_MEMORY;
259 if ( (resp_len = res_query(name, C_IN, T_SRV, buffer, buf_len)) < 0 ) {
260 DEBUG(1,("ads_dns_lookup_srv: Failed to resolve %s (%s)\n", name, strerror(errno)));
261 TALLOC_FREE( buffer );
262 return NT_STATUS_UNSUCCESSFUL;
264 } while ( buf_len < resp_len && resp_len < MAX_DNS_PACKET_SIZE );
266 p = buffer;
268 /* For some insane reason, the ns_initparse() et. al. routines are only
269 available in libresolv.a, and not the shared lib. Who knows why....
270 So we have to parse the DNS reply ourselves */
272 /* Pull the answer RR's count from the header. Use the NMB ordering macros */
274 query_count = RSVAL( p, 4 );
275 answer_count = RSVAL( p, 6 );
276 auth_count = RSVAL( p, 8 );
277 additional_count = RSVAL( p, 10 );
279 DEBUG(4,("ads_dns_lookup_srv: %d records returned in the answer section.\n",
280 answer_count));
282 if ( (dcs = TALLOC_ZERO_ARRAY(ctx, struct dns_rr_srv, answer_count)) == NULL ) {
283 DEBUG(0,("ads_dns_lookup_srv: talloc() failure for %d char*'s\n",
284 answer_count));
285 return NT_STATUS_NO_MEMORY;
288 /* now skip the header */
290 p += NS_HFIXEDSZ;
292 /* parse the query section */
294 for ( rrnum=0; rrnum<query_count; rrnum++ ) {
295 struct dns_query q;
297 if ( !ads_dns_parse_query( ctx, buffer, buffer+resp_len, &p, &q ) ) {
298 DEBUG(1,("ads_dns_lookup_srv: Failed to parse query record!\n"));
299 return NT_STATUS_UNSUCCESSFUL;
303 /* now we are at the answer section */
305 for ( rrnum=0; rrnum<answer_count; rrnum++ ) {
306 if ( !ads_dns_parse_rr_srv( ctx, buffer, buffer+resp_len, &p, &dcs[rrnum] ) ) {
307 DEBUG(1,("ads_dns_lookup_srv: Failed to parse answer record!\n"));
308 return NT_STATUS_UNSUCCESSFUL;
311 idx = rrnum;
313 /* Parse the authority section */
314 /* just skip these for now */
316 for ( rrnum=0; rrnum<auth_count; rrnum++ ) {
317 struct dns_rr rr;
319 if ( !ads_dns_parse_rr( ctx, buffer, buffer+resp_len, &p, &rr ) ) {
320 DEBUG(1,("ads_dns_lookup_srv: Failed to parse authority record!\n"));
321 return NT_STATUS_UNSUCCESSFUL;
325 /* Parse the additional records section */
327 for ( rrnum=0; rrnum<additional_count; rrnum++ ) {
328 struct dns_rr rr;
329 int i;
331 if ( !ads_dns_parse_rr( ctx, buffer, buffer+resp_len, &p, &rr ) ) {
332 DEBUG(1,("ads_dns_lookup_srv: Failed to parse additional records section!\n"));
333 return NT_STATUS_UNSUCCESSFUL;
336 /* only interested in A records as a shortcut for having to come
337 back later and lookup the name. For multi-homed hosts, the
338 number of additional records and exceed the number of answer
339 records. */
342 if ( (rr.type != T_A) || (rr.rdatalen != 4) )
343 continue;
345 for ( i=0; i<idx; i++ ) {
346 if ( strcmp( rr.hostname, dcs[i].hostname ) == 0 ) {
347 int num_ips = dcs[i].num_ips;
348 uint8 *buf;
349 struct in_addr *tmp_ips;
351 /* allocate new memory */
353 if ( dcs[i].num_ips == 0 ) {
354 if ( (dcs[i].ips = TALLOC_ARRAY( dcs,
355 struct in_addr, 1 )) == NULL )
357 return NT_STATUS_NO_MEMORY;
359 } else {
360 if ( (tmp_ips = TALLOC_REALLOC_ARRAY( dcs, dcs[i].ips,
361 struct in_addr, dcs[i].num_ips+1)) == NULL )
363 return NT_STATUS_NO_MEMORY;
366 dcs[i].ips = tmp_ips;
368 dcs[i].num_ips++;
370 /* copy the new IP address */
372 buf = (uint8*)&dcs[i].ips[num_ips].s_addr;
373 memcpy( buf, rr.rdata, 4 );
378 qsort( dcs, idx, sizeof(struct dns_rr_srv), QSORT_CAST dnssrvcmp );
380 *dclist = dcs;
381 *numdcs = idx;
383 return NT_STATUS_OK;
386 /********************************************************************
387 ********************************************************************/
389 NTSTATUS ads_dns_query_dcs( TALLOC_CTX *ctx, const char *domain, struct dns_rr_srv **dclist, int *numdcs )
391 pstring name;
393 snprintf( name, sizeof(name), "_ldap._tcp.dc._msdcs.%s", domain );
395 return ads_dns_lookup_srv( ctx, name, dclist, numdcs );