1
/* $Id: dns.c 4712 2014-01-23 08:09:29Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjlib-util/dns.h>
21
#include <pjlib-util/errno.h>
22
#include <pj/assert.h>
26
#include <pj/string.h>
29
PJ_DEF(const char *) pj_dns_get_type_name(int type)
32
case PJ_DNS_TYPE_A: return "A";
33
case PJ_DNS_TYPE_AAAA: return "AAAA";
34
case PJ_DNS_TYPE_SRV: return "SRV";
35
case PJ_DNS_TYPE_NS: return "NS";
36
case PJ_DNS_TYPE_CNAME: return "CNAME";
37
case PJ_DNS_TYPE_PTR: return "PTR";
38
case PJ_DNS_TYPE_MX: return "MX";
39
case PJ_DNS_TYPE_TXT: return "TXT";
40
case PJ_DNS_TYPE_NAPTR: return "NAPTR";
46
static void write16(pj_uint8_t *p, pj_uint16_t val)
48
p[0] = (pj_uint8_t)(val >> 8);
49
p[1] = (pj_uint8_t)(val & 0xFF);
54
* Initialize a DNS query transaction.
56
PJ_DEF(pj_status_t) pj_dns_make_query( void *packet,
62
pj_uint8_t *p = (pj_uint8_t*)packet;
63
const char *startlabel, *endlabel, *endname;
67
PJ_ASSERT_RETURN(packet && size && qtype && name, PJ_EINVAL);
69
/* Calculate total number of bytes required. */
70
d = sizeof(pj_dns_hdr) + name->slen + 4;
72
/* Check that size is sufficient. */
73
PJ_ASSERT_RETURN(*size >= d, PJLIB_UTIL_EDNSQRYTOOSMALL);
75
/* Initialize header */
76
pj_assert(sizeof(pj_dns_hdr)==12);
77
pj_bzero(p, sizeof(struct pj_dns_hdr));
79
write16(p+2, (pj_uint16_t)PJ_DNS_SET_RD(1));
80
write16(p+4, (pj_uint16_t)1);
82
/* Initialize query */
83
p = ((pj_uint8_t*)packet)+sizeof(pj_dns_hdr);
86
startlabel = endlabel = name->ptr;
87
endname = name->ptr + name->slen;
88
while (endlabel != endname) {
89
while (endlabel != endname && *endlabel != '.')
91
*p++ = (pj_uint8_t)(endlabel - startlabel);
92
pj_memcpy(p, startlabel, endlabel-startlabel);
93
p += (endlabel-startlabel);
94
if (endlabel != endname && *endlabel == '.')
96
startlabel = endlabel;
101
write16(p, (pj_uint16_t)qtype);
104
/* Set class (IN=1) */
108
/* Done, calculate length */
109
*size = (unsigned)(p - (pj_uint8_t*)packet);
115
/* Get a name length (note: name consists of multiple labels and
116
* it may contain pointers when name compression is applied)
118
static pj_status_t get_name_len(int rec_counter, const pj_uint8_t *pkt,
119
const pj_uint8_t *start, const pj_uint8_t *max,
120
int *parsed_len, int *name_len)
125
/* Limit the number of recursion */
126
if (rec_counter > 10) {
127
/* Too many name recursion */
128
return PJLIB_UTIL_EDNSINNAMEPTR;
131
*name_len = *parsed_len = 0;
134
if ((*p & 0xc0) == 0xc0) {
135
/* Compression is found! */
140
/* Get the 14bit offset */
141
pj_memcpy(&offset, p, 2);
142
offset ^= pj_htons((pj_uint16_t)(0xc0 << 8));
143
offset = pj_ntohs(offset);
145
/* Check that offset is valid */
146
if (offset >= max - pkt)
147
return PJLIB_UTIL_EDNSINNAMEPTR;
149
/* Get the name length from that offset. */
150
status = get_name_len(rec_counter+1, pkt, pkt + offset, max,
152
if (status != PJ_SUCCESS)
156
*name_len += ptr_len;
160
unsigned label_len = *p;
162
/* Check that label length is valid */
163
if (pkt+label_len > max)
164
return PJLIB_UTIL_EDNSINNAMEPTR;
166
p += (label_len + 1);
167
*parsed_len += (label_len + 1);
172
*name_len += label_len;
175
return PJLIB_UTIL_EDNSINSIZE;
185
/* Parse and copy name (note: name consists of multiple labels and
186
* it may contain pointers when compression is applied).
188
static pj_status_t get_name(int rec_counter, const pj_uint8_t *pkt,
189
const pj_uint8_t *start, const pj_uint8_t *max,
195
/* Limit the number of recursion */
196
if (rec_counter > 10) {
197
/* Too many name recursion */
198
return PJLIB_UTIL_EDNSINNAMEPTR;
203
if ((*p & 0xc0) == 0xc0) {
204
/* Compression is found! */
207
/* Get the 14bit offset */
208
pj_memcpy(&offset, p, 2);
209
offset ^= pj_htons((pj_uint16_t)(0xc0 << 8));
210
offset = pj_ntohs(offset);
212
/* Check that offset is valid */
213
if (offset >= max - pkt)
214
return PJLIB_UTIL_EDNSINNAMEPTR;
216
/* Retrieve the name from that offset. */
217
status = get_name(rec_counter+1, pkt, pkt + offset, max, name);
218
if (status != PJ_SUCCESS)
223
unsigned label_len = *p;
225
/* Check that label length is valid */
226
if (pkt+label_len > max)
227
return PJLIB_UTIL_EDNSINNAMEPTR;
229
pj_memcpy(name->ptr + name->slen, p+1, label_len);
230
name->slen += label_len;
234
*(name->ptr + name->slen) = '.';
239
return PJLIB_UTIL_EDNSINSIZE;
247
/* Parse query records. */
248
static pj_status_t parse_query(pj_dns_parsed_query *q, pj_pool_t *pool,
249
const pj_uint8_t *pkt, const pj_uint8_t *start,
250
const pj_uint8_t *max, int *parsed_len)
252
const pj_uint8_t *p = start;
253
int name_len, name_part_len;
256
/* Get the length of the name */
257
status = get_name_len(0, pkt, start, max, &name_part_len, &name_len);
258
if (status != PJ_SUCCESS)
261
/* Allocate memory for the name */
262
q->name.ptr = (char*) pj_pool_alloc(pool, name_len+4);
266
status = get_name(0, pkt, start, max, &q->name);
267
if (status != PJ_SUCCESS)
270
p = (start + name_part_len);
273
pj_memcpy(&q->type, p, 2);
274
q->type = pj_ntohs(q->type);
278
pj_memcpy(&q->dnsclass, p, 2);
279
q->dnsclass = pj_ntohs(q->dnsclass);
282
*parsed_len = (int)(p - start);
288
/* Parse RR records */
289
static pj_status_t parse_rr(pj_dns_parsed_rr *rr, pj_pool_t *pool,
290
const pj_uint8_t *pkt,
291
const pj_uint8_t *start, const pj_uint8_t *max,
294
const pj_uint8_t *p = start;
295
int name_len, name_part_len;
298
/* Get the length of the name */
299
status = get_name_len(0, pkt, start, max, &name_part_len, &name_len);
300
if (status != PJ_SUCCESS)
303
/* Allocate memory for the name */
304
rr->name.ptr = (char*) pj_pool_alloc(pool, name_len+4);
308
status = get_name(0, pkt, start, max, &rr->name);
309
if (status != PJ_SUCCESS)
312
p = (start + name_part_len);
314
/* Check the size can accomodate next few fields. */
316
return PJLIB_UTIL_EDNSINSIZE;
319
pj_memcpy(&rr->type, p, 2);
320
rr->type = pj_ntohs(rr->type);
324
pj_memcpy(&rr->dnsclass, p, 2);
325
rr->dnsclass = pj_ntohs(rr->dnsclass);
328
/* Class MUST be IN */
329
if (rr->dnsclass != 1)
330
return PJLIB_UTIL_EDNSINCLASS;
333
pj_memcpy(&rr->ttl, p, 4);
334
rr->ttl = pj_ntohl(rr->ttl);
338
pj_memcpy(&rr->rdlength, p, 2);
339
rr->rdlength = pj_ntohs(rr->rdlength);
342
/* Check that length is valid */
343
if (p + rr->rdlength > max)
344
return PJLIB_UTIL_EDNSINSIZE;
346
/* Parse some well known records */
347
if (rr->type == PJ_DNS_TYPE_A) {
348
pj_memcpy(&rr->rdata.a.ip_addr, p, 4);
351
} else if (rr->type == PJ_DNS_TYPE_AAAA) {
352
pj_memcpy(&rr->rdata.aaaa.ip_addr, p, 16);
355
} else if (rr->type == PJ_DNS_TYPE_CNAME ||
356
rr->type == PJ_DNS_TYPE_NS ||
357
rr->type == PJ_DNS_TYPE_PTR)
360
/* Get the length of the target name */
361
status = get_name_len(0, pkt, p, max, &name_part_len, &name_len);
362
if (status != PJ_SUCCESS)
365
/* Allocate memory for the name */
366
rr->rdata.cname.name.ptr = (char*) pj_pool_alloc(pool, name_len);
367
rr->rdata.cname.name.slen = 0;
370
status = get_name(0, pkt, p, max, &rr->rdata.cname.name);
371
if (status != PJ_SUCCESS)
376
} else if (rr->type == PJ_DNS_TYPE_SRV) {
379
pj_memcpy(&rr->rdata.srv.prio, p, 2);
380
rr->rdata.srv.prio = pj_ntohs(rr->rdata.srv.prio);
384
pj_memcpy(&rr->rdata.srv.weight, p, 2);
385
rr->rdata.srv.weight = pj_ntohs(rr->rdata.srv.weight);
389
pj_memcpy(&rr->rdata.srv.port, p, 2);
390
rr->rdata.srv.port = pj_ntohs(rr->rdata.srv.port);
393
/* Get the length of the target name */
394
status = get_name_len(0, pkt, p, max, &name_part_len, &name_len);
395
if (status != PJ_SUCCESS)
398
/* Allocate memory for the name */
399
rr->rdata.srv.target.ptr = (char*) pj_pool_alloc(pool, name_len);
400
rr->rdata.srv.target.slen = 0;
403
status = get_name(0, pkt, p, max, &rr->rdata.srv.target);
404
if (status != PJ_SUCCESS)
409
/* Copy the raw data */
410
rr->data = pj_pool_alloc(pool, rr->rdlength);
411
pj_memcpy(rr->data, p, rr->rdlength);
416
*parsed_len = (int)(p - start);
422
* Parse raw DNS packet into DNS packet structure.
424
PJ_DEF(pj_status_t) pj_dns_parse_packet( pj_pool_t *pool,
427
pj_dns_parsed_packet **p_res)
429
pj_dns_parsed_packet *res;
430
const pj_uint8_t *start, *end;
435
PJ_ASSERT_RETURN(pool && packet && size && p_res, PJ_EINVAL);
437
/* Packet size must be at least as big as the header */
438
if (size < sizeof(pj_dns_hdr))
439
return PJLIB_UTIL_EDNSINSIZE;
441
/* Create the structure */
442
res = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet);
444
/* Copy the DNS header, and convert endianness to host byte order */
445
pj_memcpy(&res->hdr, packet, sizeof(pj_dns_hdr));
446
res->hdr.id = pj_ntohs(res->hdr.id);
447
res->hdr.flags = pj_ntohs(res->hdr.flags);
448
res->hdr.qdcount = pj_ntohs(res->hdr.qdcount);
449
res->hdr.anscount = pj_ntohs(res->hdr.anscount);
450
res->hdr.nscount = pj_ntohs(res->hdr.nscount);
451
res->hdr.arcount = pj_ntohs(res->hdr.arcount);
453
/* Mark start and end of payload */
454
start = ((const pj_uint8_t*)packet) + sizeof(pj_dns_hdr);
455
end = ((const pj_uint8_t*)packet) + size;
457
/* Parse query records (if any).
459
if (res->hdr.qdcount) {
460
res->q = (pj_dns_parsed_query*)
461
pj_pool_zalloc(pool, res->hdr.qdcount *
462
sizeof(pj_dns_parsed_query));
463
for (i=0; i<res->hdr.qdcount; ++i) {
466
status = parse_query(&res->q[i], pool, (const pj_uint8_t*)packet,
467
start, end, &parsed_len);
468
if (status != PJ_SUCCESS)
475
/* Parse answer, if any */
476
if (res->hdr.anscount) {
477
res->ans = (pj_dns_parsed_rr*)
478
pj_pool_zalloc(pool, res->hdr.anscount *
479
sizeof(pj_dns_parsed_rr));
481
for (i=0; i<res->hdr.anscount; ++i) {
484
status = parse_rr(&res->ans[i], pool, (const pj_uint8_t*)packet,
485
start, end, &parsed_len);
486
if (status != PJ_SUCCESS)
493
/* Parse authoritative NS records, if any */
494
if (res->hdr.nscount) {
495
res->ns = (pj_dns_parsed_rr*)
496
pj_pool_zalloc(pool, res->hdr.nscount *
497
sizeof(pj_dns_parsed_rr));
499
for (i=0; i<res->hdr.nscount; ++i) {
502
status = parse_rr(&res->ns[i], pool, (const pj_uint8_t*)packet,
503
start, end, &parsed_len);
504
if (status != PJ_SUCCESS)
511
/* Parse additional RR answer, if any */
512
if (res->hdr.arcount) {
513
res->arr = (pj_dns_parsed_rr*)
514
pj_pool_zalloc(pool, res->hdr.arcount *
515
sizeof(pj_dns_parsed_rr));
517
for (i=0; i<res->hdr.arcount; ++i) {
520
status = parse_rr(&res->arr[i], pool, (const pj_uint8_t*)packet,
521
start, end, &parsed_len);
522
if (status != PJ_SUCCESS)
529
/* Looks like everything is okay */
536
/* Perform name compression scheme.
537
* If a name is already in the nametable, when no need to duplicate
538
* the string with the pool, but rather just use the pointer there.
540
static void apply_name_table( unsigned *count,
541
pj_str_t nametable[],
548
/* Scan strings in nametable */
549
for (i=0; i<*count; ++i) {
550
if (pj_stricmp(&nametable[i], src) == 0)
554
/* If name is found in nametable, use the pointer in the nametable */
556
dst->ptr = nametable[i].ptr;
557
dst->slen = nametable[i].slen;
561
/* Otherwise duplicate the string, and insert new name in nametable */
562
pj_strdup(pool, dst, src);
564
if (*count < PJ_DNS_MAX_NAMES_IN_NAMETABLE) {
565
nametable[*count].ptr = dst->ptr;
566
nametable[*count].slen = dst->slen;
572
static void copy_query(pj_pool_t *pool, pj_dns_parsed_query *dst,
573
const pj_dns_parsed_query *src,
574
unsigned *nametable_count,
575
pj_str_t nametable[])
577
pj_memcpy(dst, src, sizeof(*src));
578
apply_name_table(nametable_count, nametable, &src->name, pool, &dst->name);
582
static void copy_rr(pj_pool_t *pool, pj_dns_parsed_rr *dst,
583
const pj_dns_parsed_rr *src,
584
unsigned *nametable_count,
585
pj_str_t nametable[])
587
pj_memcpy(dst, src, sizeof(*src));
588
apply_name_table(nametable_count, nametable, &src->name, pool, &dst->name);
591
dst->data = pj_pool_alloc(pool, src->rdlength);
592
pj_memcpy(dst->data, src->data, src->rdlength);
595
if (src->type == PJ_DNS_TYPE_SRV) {
596
apply_name_table(nametable_count, nametable, &src->rdata.srv.target,
597
pool, &dst->rdata.srv.target);
598
} else if (src->type == PJ_DNS_TYPE_A) {
599
dst->rdata.a.ip_addr.s_addr = src->rdata.a.ip_addr.s_addr;
600
} else if (src->type == PJ_DNS_TYPE_AAAA) {
601
pj_memcpy(&dst->rdata.aaaa.ip_addr, &src->rdata.aaaa.ip_addr,
602
sizeof(pj_in6_addr));
603
} else if (src->type == PJ_DNS_TYPE_CNAME) {
604
pj_strdup(pool, &dst->rdata.cname.name, &src->rdata.cname.name);
605
} else if (src->type == PJ_DNS_TYPE_NS) {
606
pj_strdup(pool, &dst->rdata.ns.name, &src->rdata.ns.name);
607
} else if (src->type == PJ_DNS_TYPE_PTR) {
608
pj_strdup(pool, &dst->rdata.ptr.name, &src->rdata.ptr.name);
613
* Duplicate DNS packet.
615
PJ_DEF(void) pj_dns_packet_dup(pj_pool_t *pool,
616
const pj_dns_parsed_packet*p,
618
pj_dns_parsed_packet **p_dst)
620
pj_dns_parsed_packet *dst;
621
unsigned nametable_count = 0;
622
#if PJ_DNS_MAX_NAMES_IN_NAMETABLE
623
pj_str_t nametable[PJ_DNS_MAX_NAMES_IN_NAMETABLE];
625
pj_str_t *nametable = NULL;
629
PJ_ASSERT_ON_FAIL(pool && p && p_dst, return);
631
/* Create packet and copy header */
632
*p_dst = dst = PJ_POOL_ZALLOC_T(pool, pj_dns_parsed_packet);
633
pj_memcpy(&dst->hdr, &p->hdr, sizeof(p->hdr));
635
/* Initialize section counts in the target packet to zero.
636
* If memory allocation fails during copying process, the target packet
637
* should have a correct section counts.
639
dst->hdr.qdcount = 0;
640
dst->hdr.anscount = 0;
641
dst->hdr.nscount = 0;
642
dst->hdr.arcount = 0;
645
/* Copy query section */
646
if (p->hdr.qdcount && (options & PJ_DNS_NO_QD)==0) {
647
dst->q = (pj_dns_parsed_query*)
648
pj_pool_alloc(pool, p->hdr.qdcount *
649
sizeof(pj_dns_parsed_query));
650
for (i=0; i<p->hdr.qdcount; ++i) {
651
copy_query(pool, &dst->q[i], &p->q[i],
652
&nametable_count, nametable);
657
/* Copy answer section */
658
if (p->hdr.anscount && (options & PJ_DNS_NO_ANS)==0) {
659
dst->ans = (pj_dns_parsed_rr*)
660
pj_pool_alloc(pool, p->hdr.anscount *
661
sizeof(pj_dns_parsed_rr));
662
for (i=0; i<p->hdr.anscount; ++i) {
663
copy_rr(pool, &dst->ans[i], &p->ans[i],
664
&nametable_count, nametable);
669
/* Copy NS section */
670
if (p->hdr.nscount && (options & PJ_DNS_NO_NS)==0) {
671
dst->ns = (pj_dns_parsed_rr*)
672
pj_pool_alloc(pool, p->hdr.nscount *
673
sizeof(pj_dns_parsed_rr));
674
for (i=0; i<p->hdr.nscount; ++i) {
675
copy_rr(pool, &dst->ns[i], &p->ns[i],
676
&nametable_count, nametable);
681
/* Copy additional info section */
682
if (p->hdr.arcount && (options & PJ_DNS_NO_AR)==0) {
683
dst->arr = (pj_dns_parsed_rr*)
684
pj_pool_alloc(pool, p->hdr.arcount *
685
sizeof(pj_dns_parsed_rr));
686
for (i=0; i<p->hdr.arcount; ++i) {
687
copy_rr(pool, &dst->arr[i], &p->arr[i],
688
&nametable_count, nametable);
695
PJ_DEF(void) pj_dns_init_srv_rr( pj_dns_parsed_rr *rec,
696
const pj_str_t *res_name,
702
const pj_str_t *target)
704
pj_bzero(rec, sizeof(*rec));
705
rec->name = *res_name;
706
rec->type = PJ_DNS_TYPE_SRV;
707
rec->dnsclass = (pj_uint16_t) dnsclass;
709
rec->rdata.srv.prio = (pj_uint16_t) prio;
710
rec->rdata.srv.weight = (pj_uint16_t) weight;
711
rec->rdata.srv.port = (pj_uint16_t) port;
712
rec->rdata.srv.target = *target;
716
PJ_DEF(void) pj_dns_init_cname_rr( pj_dns_parsed_rr *rec,
717
const pj_str_t *res_name,
720
const pj_str_t *name)
722
pj_bzero(rec, sizeof(*rec));
723
rec->name = *res_name;
724
rec->type = PJ_DNS_TYPE_CNAME;
725
rec->dnsclass = (pj_uint16_t) dnsclass;
727
rec->rdata.cname.name = *name;
731
PJ_DEF(void) pj_dns_init_a_rr( pj_dns_parsed_rr *rec,
732
const pj_str_t *res_name,
735
const pj_in_addr *ip_addr)
737
pj_bzero(rec, sizeof(*rec));
738
rec->name = *res_name;
739
rec->type = PJ_DNS_TYPE_A;
740
rec->dnsclass = (pj_uint16_t) dnsclass;
742
rec->rdata.a.ip_addr = *ip_addr;