]>
Commit | Line | Data |
---|---|---|
6ac0a1a3 MT |
1 | From cbe379ad6b52a538a4416a7cd992817e5637ccf9 Mon Sep 17 00:00:00 2001 |
2 | From: Simon Kelley <simon@thekelleys.org.uk> | |
3 | Date: Tue, 21 Apr 2015 22:57:06 +0100 | |
697b4f04 | 4 | Subject: [PATCH 081/113] Handle domain names with '.' or /000 within labels. |
6ac0a1a3 MT |
5 | |
6 | Only in DNSSEC mode, where we might need to validate or store | |
7 | such names. In none-DNSSEC mode, simply don't cache these, as before. | |
8 | --- | |
9 | src/dns-protocol.h | 4 ++++ | |
10 | src/dnsmasq.c | 15 +++++++++++++-- | |
11 | src/dnssec.c | 40 +++++++++++++++++++++++++++++++--------- | |
12 | src/rfc1035.c | 16 +++++++++++++++- | |
13 | src/util.c | 9 ++++++++- | |
14 | 5 files changed, 71 insertions(+), 13 deletions(-) | |
15 | ||
16 | diff --git a/src/dns-protocol.h b/src/dns-protocol.h | |
17 | index 16fade33d98c..7f5d686bb150 100644 | |
18 | --- a/src/dns-protocol.h | |
19 | +++ b/src/dns-protocol.h | |
20 | @@ -142,3 +142,7 @@ struct dns_header { | |
21 | ||
22 | #define ADD_RDLEN(header, pp, plen, len) \ | |
23 | (!CHECK_LEN(header, pp, plen, len) ? 0 : (((pp) += (len)), 1)) | |
24 | + | |
25 | +/* Escape character in our presentation format for names. | |
26 | + Cannot be '.' or /000 and must be !isprint() */ | |
27 | +#define NAME_ESCAPE 1 | |
28 | diff --git a/src/dnsmasq.c b/src/dnsmasq.c | |
29 | index 20b15c05103a..19a6428b09e8 100644 | |
30 | --- a/src/dnsmasq.c | |
31 | +++ b/src/dnsmasq.c | |
32 | @@ -102,8 +102,19 @@ int main (int argc, char **argv) | |
33 | #ifdef HAVE_DNSSEC | |
34 | if (option_bool(OPT_DNSSEC_VALID)) | |
35 | { | |
36 | - daemon->keyname = safe_malloc(MAXDNAME); | |
37 | - daemon->workspacename = safe_malloc(MAXDNAME); | |
38 | + /* Note that both /000 and '.' are allowed within labels. These get | |
39 | + represented in presentation format using NAME_ESCAPE as an escape | |
40 | + character when in DNSSEC mode. | |
41 | + In theory, if all the characters in a name were /000 or | |
42 | + '.' or NAME_ESCAPE then all would have to be escaped, so the | |
43 | + presentation format would be twice as long as the spec. | |
44 | + | |
45 | + daemon->namebuff was previously allocated by the option-reading | |
46 | + code before we knew if we're in DNSSEC mode, so reallocate here. */ | |
47 | + free(daemon->namebuff); | |
48 | + daemon->namebuff = safe_malloc(MAXDNAME * 2); | |
49 | + daemon->keyname = safe_malloc(MAXDNAME * 2); | |
50 | + daemon->workspacename = safe_malloc(MAXDNAME * 2); | |
51 | } | |
52 | #endif | |
53 | ||
54 | diff --git a/src/dnssec.c b/src/dnssec.c | |
55 | index 05e0983cb251..c116a7b5f6f4 100644 | |
56 | --- a/src/dnssec.c | |
57 | +++ b/src/dnssec.c | |
58 | @@ -321,10 +321,18 @@ static int verify(struct blockdata *key_data, unsigned int key_len, unsigned cha | |
59 | thus generating names in canonical form. | |
60 | Calling to_wire followed by from_wire is almost an identity, | |
61 | except that the UC remains mapped to LC. | |
62 | + | |
63 | + Note that both /000 and '.' are allowed within labels. These get | |
64 | + represented in presentation format using NAME_ESCAPE as an escape | |
65 | + character. In theory, if all the characters in a name were /000 or | |
66 | + '.' or NAME_ESCAPE then all would have to be escaped, so the | |
67 | + presentation format would be twice as long as the spec (1024). | |
68 | + The buffers are all delcared as 2049 (allowing for the trailing zero) | |
69 | + for this reason. | |
70 | */ | |
71 | static int to_wire(char *name) | |
72 | { | |
73 | - unsigned char *l, *p, term; | |
74 | + unsigned char *l, *p, *q, term; | |
75 | int len; | |
76 | ||
77 | for (l = (unsigned char*)name; *l != 0; l = p) | |
78 | @@ -332,7 +340,10 @@ static int to_wire(char *name) | |
79 | for (p = l; *p != '.' && *p != 0; p++) | |
80 | if (*p >= 'A' && *p <= 'Z') | |
81 | *p = *p - 'A' + 'a'; | |
82 | - | |
83 | + else if (*p == NAME_ESCAPE) | |
84 | + for (q = p; *q; q++) | |
85 | + *q = *(q+1); | |
86 | + | |
87 | term = *p; | |
88 | ||
89 | if ((len = p - l) != 0) | |
90 | @@ -351,13 +362,23 @@ static int to_wire(char *name) | |
91 | /* Note: no compression allowed in input. */ | |
92 | static void from_wire(char *name) | |
93 | { | |
94 | - unsigned char *l; | |
95 | + unsigned char *l, *p, *last; | |
96 | int len; | |
97 | - | |
98 | + | |
99 | + for (last = (unsigned char *)name; *last != 0; last += *last+1); | |
100 | + | |
101 | for (l = (unsigned char *)name; *l != 0; l += len+1) | |
102 | { | |
103 | len = *l; | |
104 | memmove(l, l+1, len); | |
105 | + for (p = l; p < l + len; p++) | |
106 | + if (*p == '.' || *p == 0 || *p == NAME_ESCAPE) | |
107 | + { | |
108 | + memmove(p+1, p, 1 + last - p); | |
109 | + len++; | |
110 | + *p++ = NAME_ESCAPE; | |
111 | + } | |
112 | + | |
113 | l[len] = '.'; | |
114 | } | |
115 | ||
116 | @@ -645,7 +666,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int | |
117 | if (left1 != 0) | |
118 | memmove(buff1, buff1 + len1 - left1, left1); | |
119 | ||
120 | - if ((len1 = get_rdata(header, plen, end1, buff1 + left1, MAXDNAME - left1, &p1, &dp1)) == 0) | |
121 | + if ((len1 = get_rdata(header, plen, end1, buff1 + left1, (MAXDNAME * 2) - left1, &p1, &dp1)) == 0) | |
122 | { | |
123 | quit = 1; | |
124 | len1 = end1 - p1; | |
125 | @@ -656,7 +677,7 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int | |
126 | if (left2 != 0) | |
127 | memmove(buff2, buff2 + len2 - left2, left2); | |
128 | ||
129 | - if ((len2 = get_rdata(header, plen, end2, buff2 + left2, MAXDNAME - left2, &p2, &dp2)) == 0) | |
130 | + if ((len2 = get_rdata(header, plen, end2, buff2 + left2, (MAXDNAME *2) - left2, &p2, &dp2)) == 0) | |
131 | { | |
132 | quit = 1; | |
133 | len2 = end2 - p2; | |
134 | @@ -902,10 +923,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
135 | ||
136 | end = p + rdlen; | |
137 | ||
138 | - /* canonicalise rdata and calculate length of same, use name buffer as workspace */ | |
139 | + /* canonicalise rdata and calculate length of same, use name buffer as workspace. | |
140 | + Note that name buffer is twice MAXDNAME long in DNSSEC mode. */ | |
141 | cp = p; | |
142 | dp = rr_desc; | |
143 | - for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME, &cp, &dp)) != 0; len += seg); | |
144 | + for (len = 0; (seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp)) != 0; len += seg); | |
145 | len += end - cp; | |
146 | len = htons(len); | |
147 | hash->update(ctx, 2, (unsigned char *)&len); | |
148 | @@ -913,7 +935,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in | |
149 | /* Now canonicalise again and digest. */ | |
150 | cp = p; | |
151 | dp = rr_desc; | |
152 | - while ((seg = get_rdata(header, plen, end, name, MAXDNAME, &cp, &dp))) | |
153 | + while ((seg = get_rdata(header, plen, end, name, MAXDNAME * 2, &cp, &dp))) | |
154 | hash->update(ctx, seg, (unsigned char *)name); | |
155 | if (cp != end) | |
156 | hash->update(ctx, end - cp, cp); | |
157 | diff --git a/src/rfc1035.c b/src/rfc1035.c | |
158 | index a995ab50d74a..19fecc818c06 100644 | |
159 | --- a/src/rfc1035.c | |
160 | +++ b/src/rfc1035.c | |
161 | @@ -128,6 +128,15 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, | |
162 | if (isExtract) | |
163 | { | |
164 | unsigned char c = *p; | |
165 | +#ifdef HAVE_DNSSEC | |
166 | + if (option_bool(OPT_DNSSEC_VALID)) | |
167 | + { | |
168 | + if (c == 0 || c == '.' || c == NAME_ESCAPE) | |
169 | + *cp++ = NAME_ESCAPE; | |
170 | + *cp++ = c; | |
171 | + } | |
172 | + else | |
173 | +#endif | |
174 | if (c != 0 && c != '.') | |
175 | *cp++ = c; | |
176 | else | |
177 | @@ -144,9 +153,14 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, | |
178 | cp++; | |
179 | if (c1 >= 'A' && c1 <= 'Z') | |
180 | c1 += 'a' - 'A'; | |
181 | +#ifdef HAVE_DNSSEC | |
182 | + if (option_bool(OPT_DNSSEC_VALID) && c1 == NAME_ESCAPE) | |
183 | + c1 = *cp++; | |
184 | +#endif | |
185 | + | |
186 | if (c2 >= 'A' && c2 <= 'Z') | |
187 | c2 += 'a' - 'A'; | |
188 | - | |
189 | + | |
190 | if (c1 != c2) | |
191 | retvalue = 2; | |
192 | } | |
193 | diff --git a/src/util.c b/src/util.c | |
194 | index 648bc4d4b428..0c1a48b4700a 100644 | |
195 | --- a/src/util.c | |
196 | +++ b/src/util.c | |
197 | @@ -226,7 +226,14 @@ unsigned char *do_rfc1035_name(unsigned char *p, char *sval) | |
198 | { | |
199 | unsigned char *cp = p++; | |
200 | for (j = 0; *sval && (*sval != '.'); sval++, j++) | |
201 | - *p++ = *sval; | |
202 | + { | |
203 | +#ifdef HAVE_DNSSEC | |
204 | + if (option_bool(OPT_DNSSEC_VALID) && *sval == NAME_ESCAPE) | |
205 | + *p++ = *(++sval); | |
206 | + else | |
207 | +#endif | |
208 | + *p++ = *sval; | |
209 | + } | |
210 | *cp = j; | |
211 | if (*sval) | |
212 | sval++; | |
213 | -- | |
214 | 2.1.0 | |
215 |