~vcs-imports/samba/main

« back to all changes in this revision

Viewing changes to source/libads/dns.c

  • Committer: jerry
  • Date: 2006-07-14 21:48:39 UTC
  • Revision ID: vcs-imports@canonical.com-20060714214839-586d8c489a8fcead
gutting trunk to move to svn:externals

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* 
2
 
   Unix SMB/CIFS implementation.
3
 
   DNS utility library
4
 
   Copyright (C) Gerald (Jerry) Carter           2006.
5
 
 
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.
10
 
   
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.
15
 
   
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.
19
 
*/
20
 
 
21
 
#include "includes.h"
22
 
 
23
 
/* AIX resolv.h uses 'class' in struct ns_rr */
24
 
 
25
 
#if defined(AIX)
26
 
#  if defined(class)
27
 
#    undef class
28
 
#  endif
29
 
#endif  /* AIX */
30
 
 
31
 
/* resolver headers */
32
 
 
33
 
#include <sys/types.h>
34
 
#include <netinet/in.h>
35
 
#include <arpa/nameser.h>
36
 
#include <resolv.h>
37
 
#include <netdb.h>
38
 
 
39
 
#define MAX_DNS_PACKET_SIZE 0xffff
40
 
 
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
62
 
 
63
 
/*********************************************************************
64
 
*********************************************************************/
65
 
 
66
 
static BOOL ads_dns_parse_query( TALLOC_CTX *ctx, uint8 *start, uint8 *end,
67
 
                          uint8 **ptr, struct dns_query *q )
68
 
{
69
 
        uint8 *p = *ptr;
70
 
        pstring hostname;
71
 
        int namelen;
72
 
 
73
 
        ZERO_STRUCTP( q );
74
 
        
75
 
        if ( !start || !end || !q || !*ptr)
76
 
                return False;
77
 
 
78
 
        /* See RFC 1035 for details. If this fails, then return. */
79
 
 
80
 
        namelen = dn_expand( start, end, p, hostname, sizeof(hostname) );
81
 
        if ( namelen < 0 ) {
82
 
                return False;
83
 
        }
84
 
        p += namelen;
85
 
        q->hostname = talloc_strdup( ctx, hostname );
86
 
 
87
 
        /* check that we have space remaining */
88
 
 
89
 
        if ( PTR_DIFF(p+4, end) > 0 )
90
 
                return False;
91
 
 
92
 
        q->type     = RSVAL( p, 0 );
93
 
        q->in_class = RSVAL( p, 2 );
94
 
        p += 4;
95
 
 
96
 
        *ptr = p;
97
 
 
98
 
        return True;
99
 
}
100
 
 
101
 
/*********************************************************************
102
 
*********************************************************************/
103
 
 
104
 
static BOOL ads_dns_parse_rr( TALLOC_CTX *ctx, uint8 *start, uint8 *end,
105
 
                       uint8 **ptr, struct dns_rr *rr )
106
 
{
107
 
        uint8 *p = *ptr;
108
 
        pstring hostname;
109
 
        int namelen;
110
 
 
111
 
        if ( !start || !end || !rr || !*ptr)
112
 
                return -1;
113
 
 
114
 
        ZERO_STRUCTP( rr );
115
 
        /* pull the name from the answer */
116
 
 
117
 
        namelen = dn_expand( start, end, p, hostname, sizeof(hostname) );
118
 
        if ( namelen < 0 ) {
119
 
                return -1;
120
 
        }
121
 
        p += namelen;
122
 
        rr->hostname = talloc_strdup( ctx, hostname );
123
 
 
124
 
        /* check that we have space remaining */
125
 
 
126
 
        if ( PTR_DIFF(p+10, end) > 0 )
127
 
                return False;
128
 
 
129
 
        /* pull some values and then skip onto the string */
130
 
 
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);
135
 
        
136
 
        p += 10;
137
 
 
138
 
        /* sanity check the available space */
139
 
 
140
 
        if ( PTR_DIFF(p+rr->rdatalen, end ) > 0 ) {
141
 
                return False;
142
 
 
143
 
        }
144
 
 
145
 
        /* save a point to the rdata for this section */
146
 
 
147
 
        rr->rdata = p;
148
 
        p += rr->rdatalen;
149
 
 
150
 
        *ptr = p;
151
 
 
152
 
        return True;
153
 
}
154
 
 
155
 
/*********************************************************************
156
 
*********************************************************************/
157
 
 
158
 
static BOOL ads_dns_parse_rr_srv( TALLOC_CTX *ctx, uint8 *start, uint8 *end,
159
 
                       uint8 **ptr, struct dns_rr_srv *srv )
160
 
{
161
 
        struct dns_rr rr;
162
 
        uint8 *p;
163
 
        pstring dcname;
164
 
        int namelen;
165
 
 
166
 
        if ( !start || !end || !srv || !*ptr)
167
 
                return -1;
168
 
 
169
 
        /* Parse the RR entry.  Coming out of the this, ptr is at the beginning 
170
 
           of the next record */
171
 
 
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;
175
 
        }
176
 
 
177
 
        if ( rr.type != T_SRV ) {
178
 
                DEBUG(1,("ads_dns_parse_rr_srv: Bad answer type (%d)\n", rr.type));
179
 
                return False;
180
 
        }
181
 
 
182
 
        p = rr.rdata;
183
 
 
184
 
        srv->priority = RSVAL(p, 0);
185
 
        srv->weight   = RSVAL(p, 2);
186
 
        srv->port     = RSVAL(p, 4);
187
 
 
188
 
        p += 6;
189
 
 
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;
194
 
        }
195
 
        srv->hostname = talloc_strdup( ctx, dcname );
196
 
 
197
 
        return True;
198
 
}
199
 
 
200
 
 
201
 
/*********************************************************************
202
 
 Sort SRV record list based on weight and priority.  See RFC 2782.
203
 
*********************************************************************/
204
 
 
205
 
static int dnssrvcmp( struct dns_rr_srv *a, struct dns_rr_srv *b )
206
 
{
207
 
        BOOL init = False;
208
 
 
209
 
        if ( !init ) {
210
 
                srand( (uint32)time(NULL) );
211
 
        }
212
 
 
213
 
        if ( a->priority == b->priority ) {
214
 
 
215
 
                /* randomize entries with an equal weight and priority */
216
 
                if ( a->weight == b->weight ) 
217
 
                        return rand() % 2 ? -1 : 1;
218
 
 
219
 
                /* higher weights should be sorted lower */ 
220
 
                if ( a->weight > b->weight )
221
 
                        return -1;
222
 
                else
223
 
                        return 1;
224
 
        }
225
 
                
226
 
        if ( a->priority < b->priority )
227
 
                return -1;
228
 
 
229
 
        return 1;
230
 
}
231
 
 
232
 
/*********************************************************************
233
 
 Simple wrapper for a DNS SRV query
234
 
*********************************************************************/
235
 
 
236
 
NTSTATUS ads_dns_lookup_srv( TALLOC_CTX *ctx, const char *name, struct dns_rr_srv **dclist, int *numdcs )
237
 
{
238
 
        uint8 *buffer = NULL;
239
 
        size_t buf_len;
240
 
        int resp_len = NS_PACKETSZ;
241
 
        struct dns_rr_srv *dcs = NULL;
242
 
        int query_count, answer_count, auth_count, additional_count;
243
 
        uint8 *p = buffer;
244
 
        int rrnum;
245
 
        int idx = 0;
246
 
 
247
 
        if ( !ctx || !name || !dclist ) {
248
 
                return NT_STATUS_INVALID_PARAMETER;
249
 
        }
250
 
        
251
 
        /* Send the request.  May have to loop several times in case 
252
 
           of large replies */
253
 
 
254
 
        do {
255
 
                if ( buffer )
256
 
                        TALLOC_FREE( buffer );
257
 
                
258
 
                buf_len = resp_len * sizeof(uint8);
259
 
 
260
 
                if ( (buffer = TALLOC_ARRAY(ctx, uint8, buf_len)) == NULL ) {
261
 
                        DEBUG(0,("ads_dns_lookup_srv: talloc() failed!\n"));
262
 
                        return NT_STATUS_NO_MEMORY;
263
 
                }
264
 
 
265
 
                if ( (resp_len = res_query(name, C_IN, T_SRV, buffer, buf_len)) < 0 ) {
266
 
                        DEBUG(1,("ads_dns_lookup_srv: Failed to resolve %s (%s)\n", name, strerror(errno)));
267
 
                        TALLOC_FREE( buffer );
268
 
                        return NT_STATUS_UNSUCCESSFUL;
269
 
                }
270
 
        } while ( buf_len < resp_len && resp_len < MAX_DNS_PACKET_SIZE );
271
 
 
272
 
        p = buffer;
273
 
 
274
 
        /* For some insane reason, the ns_initparse() et. al. routines are only
275
 
           available in libresolv.a, and not the shared lib.  Who knows why....
276
 
           So we have to parse the DNS reply ourselves */
277
 
 
278
 
        /* Pull the answer RR's count from the header.  Use the NMB ordering macros */
279
 
 
280
 
        query_count      = RSVAL( p, 4 );
281
 
        answer_count     = RSVAL( p, 6 );
282
 
        auth_count       = RSVAL( p, 8 );
283
 
        additional_count = RSVAL( p, 10 );
284
 
 
285
 
        DEBUG(4,("ads_dns_lookup_srv: %d records returned in the answer section.\n", 
286
 
                answer_count));
287
 
                
288
 
        if ( (dcs = TALLOC_ARRAY(ctx, struct dns_rr_srv, answer_count)) == NULL ) {
289
 
                DEBUG(0,("ads_dns_lookup_srv: talloc() failure for %d char*'s\n", 
290
 
                        answer_count));
291
 
                return NT_STATUS_NO_MEMORY;
292
 
        }
293
 
 
294
 
        /* now skip the header */
295
 
 
296
 
        p += NS_HFIXEDSZ;
297
 
 
298
 
        /* parse the query section */
299
 
 
300
 
        for ( rrnum=0; rrnum<query_count; rrnum++ ) {
301
 
                struct dns_query q;
302
 
 
303
 
                if ( !ads_dns_parse_query( ctx, buffer, buffer+resp_len, &p, &q ) ) {
304
 
                        DEBUG(1,("ads_dns_lookup_srv: Failed to parse query record!\n"));
305
 
                        return NT_STATUS_UNSUCCESSFUL;
306
 
                }
307
 
        }
308
 
 
309
 
        /* now we are at the answer section */
310
 
 
311
 
        for ( rrnum=0; rrnum<answer_count; rrnum++ ) {
312
 
                if ( !ads_dns_parse_rr_srv( ctx, buffer, buffer+resp_len, &p, &dcs[rrnum] ) ) {
313
 
                        DEBUG(1,("ads_dns_lookup_srv: Failed to parse answer record!\n"));
314
 
                        return NT_STATUS_UNSUCCESSFUL;
315
 
                }               
316
 
        }
317
 
        idx = rrnum;
318
 
 
319
 
        /* Parse the authority section */
320
 
        /* just skip these for now */
321
 
 
322
 
        for ( rrnum=0; rrnum<auth_count; rrnum++ ) {
323
 
                struct dns_rr rr;
324
 
 
325
 
                if ( !ads_dns_parse_rr( ctx, buffer, buffer+resp_len, &p, &rr ) ) {
326
 
                        DEBUG(1,("ads_dns_lookup_srv: Failed to parse authority record!\n"));
327
 
                        return NT_STATUS_UNSUCCESSFUL;
328
 
                }
329
 
        }
330
 
 
331
 
        /* Parse the additional records section */
332
 
 
333
 
        for ( rrnum=0; rrnum<additional_count; rrnum++ ) {
334
 
                struct dns_rr rr;
335
 
                int i;
336
 
 
337
 
                if ( !ads_dns_parse_rr( ctx, buffer, buffer+resp_len, &p, &rr ) ) {
338
 
                        DEBUG(1,("ads_dns_lookup_srv: Failed to parse additional records section!\n"));
339
 
                        return NT_STATUS_UNSUCCESSFUL;
340
 
                }
341
 
 
342
 
                /* only interested in A records as a shortcut for having to come 
343
 
                   back later and lookup the name */
344
 
 
345
 
                if ( (rr.type != T_A) || (rr.rdatalen != 4) ) 
346
 
                        continue;
347
 
 
348
 
                for ( i=0; i<idx; i++ ) {
349
 
                        if ( strcmp( rr.hostname, dcs[i].hostname ) == 0 ) {
350
 
                                uint8 *buf = (uint8*)&dcs[i].ip.s_addr;
351
 
                                memcpy( buf, rr.rdata, 4 );
352
 
                        }
353
 
                }
354
 
        }
355
 
 
356
 
        qsort( dcs, idx, sizeof(struct dns_rr_srv), QSORT_CAST dnssrvcmp );
357
 
        
358
 
        *dclist = dcs;
359
 
        *numdcs = idx;
360
 
        
361
 
        return NT_STATUS_OK;
362
 
}
363
 
 
364
 
/********************************************************************
365
 
********************************************************************/
366
 
 
367
 
NTSTATUS ads_dns_query_dcs( TALLOC_CTX *ctx, const char *domain, struct dns_rr_srv **dclist, int *numdcs )
368
 
{
369
 
        pstring name;
370
 
 
371
 
        snprintf( name, sizeof(name), "_ldap._tcp.dc._msdcs.%s", domain );
372
 
 
373
 
        return ads_dns_lookup_srv( ctx, name, dclist, numdcs );
374
 
}
375