| 1 |
/* $Id: udns_parse.c,v 1.14 2005/09/12 10:55:21 mjt Exp $ |
|---|
| 2 |
raw DNS packet parsing routines |
|---|
| 3 |
|
|---|
| 4 |
Copyright (C) 2005 Michael Tokarev <mjt@corpit.ru> |
|---|
| 5 |
This file is part of UDNS library, an async DNS stub resolver. |
|---|
| 6 |
|
|---|
| 7 |
This library is free software; you can redistribute it and/or |
|---|
| 8 |
modify it under the terms of the GNU Lesser General Public |
|---|
| 9 |
License as published by the Free Software Foundation; either |
|---|
| 10 |
version 2.1 of the License, or (at your option) any later version. |
|---|
| 11 |
|
|---|
| 12 |
This library is distributed in the hope that it will be useful, |
|---|
| 13 |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 15 |
Lesser General Public License for more details. |
|---|
| 16 |
|
|---|
| 17 |
You should have received a copy of the GNU Lesser General Public |
|---|
| 18 |
License along with this library, in file named COPYING.LGPL; if not, |
|---|
| 19 |
write to the Free Software Foundation, Inc., 59 Temple Place, |
|---|
| 20 |
Suite 330, Boston, MA 02111-1307 USA |
|---|
| 21 |
|
|---|
| 22 |
*/ |
|---|
| 23 |
|
|---|
| 24 |
#include <string.h> |
|---|
| 25 |
#include <assert.h> |
|---|
| 26 |
#include "udns.h" |
|---|
| 27 |
|
|---|
| 28 |
dnscc_t *dns_skipdn(dnscc_t *cur, dnscc_t *end) { |
|---|
| 29 |
unsigned c; |
|---|
| 30 |
for(;;) { |
|---|
| 31 |
if (cur >= end) |
|---|
| 32 |
return NULL; |
|---|
| 33 |
c = *cur++; |
|---|
| 34 |
if (!c) |
|---|
| 35 |
return cur; |
|---|
| 36 |
if (c & 192) /* jump */ |
|---|
| 37 |
return cur + 1 >= end ? NULL : cur + 1; |
|---|
| 38 |
cur += c; |
|---|
| 39 |
} |
|---|
| 40 |
} |
|---|
| 41 |
|
|---|
| 42 |
int |
|---|
| 43 |
dns_getdn(dnscc_t *pkt, dnscc_t **cur, dnscc_t *end, |
|---|
| 44 |
register dnsc_t *dn, unsigned dnsiz) { |
|---|
| 45 |
unsigned c; |
|---|
| 46 |
dnscc_t *pp = *cur; /* current packet pointer */ |
|---|
| 47 |
dnsc_t *dp = dn; /* current dn pointer */ |
|---|
| 48 |
dnsc_t *const de /* end of the DN dest */ |
|---|
| 49 |
= dn + (dnsiz < DNS_MAXDN ? dnsiz : DNS_MAXDN); |
|---|
| 50 |
dnscc_t *jump = NULL; /* ptr after first jump if any */ |
|---|
| 51 |
unsigned loop = 100; /* jump loop counter */ |
|---|
| 52 |
|
|---|
| 53 |
for(;;) { /* loop by labels */ |
|---|
| 54 |
if (pp >= end) /* reached end of packet? */ |
|---|
| 55 |
return -1; |
|---|
| 56 |
c = *pp++; /* length of the label */ |
|---|
| 57 |
if (!c) { /* empty label: terminate */ |
|---|
| 58 |
if (dn >= de) /* can't fit terminator */ |
|---|
| 59 |
goto noroom; |
|---|
| 60 |
*dp++ = 0; |
|---|
| 61 |
/* return next pos: either after the first jump or current */ |
|---|
| 62 |
*cur = jump ? jump : pp; |
|---|
| 63 |
return dp - dn; |
|---|
| 64 |
} |
|---|
| 65 |
if (c & 192) { /* jump */ |
|---|
| 66 |
if (pp >= end) /* eop instead of jump pos */ |
|---|
| 67 |
return -1; |
|---|
| 68 |
if (!jump) jump = pp + 1; /* remember first jump */ |
|---|
| 69 |
else if (!--loop) return -1; /* too many jumps */ |
|---|
| 70 |
c = ((c & ~192) << 8) | *pp; /* new pos */ |
|---|
| 71 |
if (c < DNS_HSIZE) /* don't allow jump into the header */ |
|---|
| 72 |
return -1; |
|---|
| 73 |
pp = pkt + c; |
|---|
| 74 |
continue; |
|---|
| 75 |
} |
|---|
| 76 |
if (c > DNS_MAXLABEL) /* too long label? */ |
|---|
| 77 |
return -1; |
|---|
| 78 |
if (pp + c > end) /* label does not fit in packet? */ |
|---|
| 79 |
return -1; |
|---|
| 80 |
if (dp + c + 1 > de) /* if enouth room for the label */ |
|---|
| 81 |
goto noroom; |
|---|
| 82 |
*dp++ = c; /* label length */ |
|---|
| 83 |
memcpy(dp, pp, c); /* and the label itself */ |
|---|
| 84 |
dp += c; |
|---|
| 85 |
pp += c; /* advance to the next label */ |
|---|
| 86 |
} |
|---|
| 87 |
noroom: |
|---|
| 88 |
return dnsiz < DNS_MAXDN ? 0 : -1; |
|---|
| 89 |
} |
|---|
| 90 |
|
|---|
| 91 |
void dns_rewind(struct dns_parse *p, dnscc_t *qdn) { |
|---|
| 92 |
p->dnsp_qdn = qdn; |
|---|
| 93 |
p->dnsp_cur = p->dnsp_ans; |
|---|
| 94 |
p->dnsp_rrl = dns_numan(p->dnsp_pkt); |
|---|
| 95 |
p->dnsp_ttl = 0xffffffffu; |
|---|
| 96 |
p->dnsp_nrr = 0; |
|---|
| 97 |
} |
|---|
| 98 |
|
|---|
| 99 |
void |
|---|
| 100 |
dns_initparse(struct dns_parse *p, dnscc_t *qdn, |
|---|
| 101 |
dnscc_t *pkt, dnscc_t *cur, dnscc_t *end) { |
|---|
| 102 |
p->dnsp_pkt = pkt; |
|---|
| 103 |
p->dnsp_end = end; |
|---|
| 104 |
p->dnsp_rrl = dns_numan(pkt); |
|---|
| 105 |
p->dnsp_qdn = qdn; |
|---|
| 106 |
assert(cur + 4 <= end); |
|---|
| 107 |
if ((p->dnsp_qtyp = dns_get16(cur+0)) == DNS_T_ANY) p->dnsp_qtyp = 0; |
|---|
| 108 |
if ((p->dnsp_qcls = dns_get16(cur+2)) == DNS_C_ANY) p->dnsp_qcls = 0; |
|---|
| 109 |
p->dnsp_cur = p->dnsp_ans = cur + 4; |
|---|
| 110 |
p->dnsp_ttl = 0xffffffffu; |
|---|
| 111 |
p->dnsp_nrr = 0; |
|---|
| 112 |
} |
|---|
| 113 |
|
|---|
| 114 |
int dns_nextrr(struct dns_parse *p, struct dns_rr *rr) { |
|---|
| 115 |
dnscc_t *cur = p->dnsp_cur; |
|---|
| 116 |
while(p->dnsp_rrl > 0) { |
|---|
| 117 |
--p->dnsp_rrl; |
|---|
| 118 |
if (dns_getdn(p->dnsp_pkt, &cur, p->dnsp_end, |
|---|
| 119 |
rr->dnsrr_dn, sizeof(rr->dnsrr_dn)) <= 0) |
|---|
| 120 |
return -1; |
|---|
| 121 |
if (cur + 10 > p->dnsp_end) |
|---|
| 122 |
return -1; |
|---|
| 123 |
rr->dnsrr_typ = dns_get16(cur); |
|---|
| 124 |
rr->dnsrr_cls = dns_get16(cur+2); |
|---|
| 125 |
rr->dnsrr_ttl = dns_get32(cur+4); |
|---|
| 126 |
rr->dnsrr_dsz = dns_get16(cur+8); |
|---|
| 127 |
rr->dnsrr_dptr = cur = cur + 10; |
|---|
| 128 |
rr->dnsrr_dend = cur = cur + rr->dnsrr_dsz; |
|---|
| 129 |
if (cur > p->dnsp_end) |
|---|
| 130 |
return -1; |
|---|
| 131 |
if (p->dnsp_qdn && !dns_dnequal(p->dnsp_qdn, rr->dnsrr_dn)) |
|---|
| 132 |
continue; |
|---|
| 133 |
if ((!p->dnsp_qcls || p->dnsp_qcls == rr->dnsrr_cls) && |
|---|
| 134 |
(!p->dnsp_qtyp || p->dnsp_qtyp == rr->dnsrr_typ)) { |
|---|
| 135 |
p->dnsp_cur = cur; |
|---|
| 136 |
++p->dnsp_nrr; |
|---|
| 137 |
if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl; |
|---|
| 138 |
return 1; |
|---|
| 139 |
} |
|---|
| 140 |
if (p->dnsp_qdn && rr->dnsrr_typ == DNS_T_CNAME && !p->dnsp_nrr) { |
|---|
| 141 |
if (dns_getdn(p->dnsp_pkt, &rr->dnsrr_dptr, p->dnsp_end, |
|---|
| 142 |
p->dnsp_dnbuf, sizeof(p->dnsp_dnbuf)) <= 0 || |
|---|
| 143 |
rr->dnsrr_dptr != rr->dnsrr_dend) |
|---|
| 144 |
return -1; |
|---|
| 145 |
p->dnsp_qdn = p->dnsp_dnbuf; |
|---|
| 146 |
if (p->dnsp_ttl > rr->dnsrr_ttl) p->dnsp_ttl = rr->dnsrr_ttl; |
|---|
| 147 |
} |
|---|
| 148 |
} |
|---|
| 149 |
p->dnsp_cur = cur; |
|---|
| 150 |
return 0; |
|---|
| 151 |
} |
|---|
| 152 |
|
|---|
| 153 |
int dns_stdrr_size(const struct dns_parse *p) { |
|---|
| 154 |
return |
|---|
| 155 |
dns_dntop_size(p->dnsp_qdn) + |
|---|
| 156 |
(p->dnsp_qdn == dns_payload(p->dnsp_pkt) ? 0 : |
|---|
| 157 |
dns_dntop_size(dns_payload(p->dnsp_pkt))); |
|---|
| 158 |
} |
|---|
| 159 |
|
|---|
| 160 |
void *dns_stdrr_finish(struct dns_rr_null *ret, char *cp, |
|---|
| 161 |
const struct dns_parse *p) { |
|---|
| 162 |
cp += dns_dntop(p->dnsp_qdn, (ret->dnsn_cname = cp), DNS_MAXNAME); |
|---|
| 163 |
if (p->dnsp_qdn == dns_payload(p->dnsp_pkt)) |
|---|
| 164 |
ret->dnsn_qname = ret->dnsn_cname; |
|---|
| 165 |
else |
|---|
| 166 |
dns_dntop(dns_payload(p->dnsp_pkt), (ret->dnsn_qname = cp), DNS_MAXNAME); |
|---|
| 167 |
ret->dnsn_ttl = p->dnsp_ttl; |
|---|
| 168 |
return ret; |
|---|
| 169 |
} |
|---|