2 * Domain search option for DHCP (RFC 3397)
4 * Copyright (c) 2012 Klaus Stengel
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 static const uint8_t RFC3397_OPT_DOMAIN_SEARCH
= 119;
32 static const uint8_t MAX_OPT_LEN
= 255;
33 static const uint8_t OPT_HEADER_LEN
= 2;
34 static const uint8_t REFERENCE_LEN
= 2;
36 struct compact_domain
;
38 typedef struct compact_domain
{
39 struct compact_domain
*self
;
40 struct compact_domain
*refdom
;
47 domain_suffix_diffoff(const CompactDomain
*a
, const CompactDomain
*b
)
49 size_t la
= a
->len
, lb
= b
->len
;
50 uint8_t *da
= a
->labels
+ la
, *db
= b
->labels
+ lb
;
51 size_t i
, lm
= (la
< lb
) ? la
: lb
;
53 for (i
= 0; i
< lm
; i
++) {
62 static int domain_suffix_ord(const void *cva
, const void *cvb
)
64 const CompactDomain
*a
= cva
, *b
= cvb
;
65 size_t la
= a
->len
, lb
= b
->len
;
66 size_t doff
= domain_suffix_diffoff(a
, b
);
67 uint8_t ca
= a
->labels
[la
- doff
];
68 uint8_t cb
= b
->labels
[lb
- doff
];
85 static size_t domain_common_label(CompactDomain
*a
, CompactDomain
*b
)
87 size_t res
, doff
= domain_suffix_diffoff(a
, b
);
88 uint8_t *first_eq_pos
= a
->labels
+ (a
->len
- doff
);
89 uint8_t *label
= a
->labels
;
91 while (*label
&& label
< first_eq_pos
) {
94 res
= a
->len
- (label
- a
->labels
);
95 /* only report if it can help to reduce the packet size */
96 return (res
> REFERENCE_LEN
) ? res
: 0;
99 static void domain_fixup_order(CompactDomain
*cd
, size_t n
)
103 for (i
= 0; i
< n
; i
++) {
104 CompactDomain
*cur
= cd
+ i
, *next
= cd
[i
].self
;
106 while (!cur
->common_octets
) {
107 CompactDomain
*tmp
= next
->self
; /* backup target value */
110 cur
->common_octets
++;
118 static void domain_mklabels(CompactDomain
*cd
, const char *input
)
120 uint8_t *len_marker
= cd
->labels
;
121 uint8_t *output
= len_marker
; /* pre-incremented */
122 const char *in
= input
;
133 if (cur_chr
== '.' || cur_chr
== '\0') {
134 len
= output
- len_marker
;
135 if ((len
== 0 && cur_chr
== '.') || len
>= 64) {
146 } while (cur_chr
!= '\0');
148 /* ensure proper zero-termination */
156 g_warning("failed to parse domain name '%s'\n", input
);
161 domain_mkxrefs(CompactDomain
*doms
, CompactDomain
*last
, size_t depth
)
163 CompactDomain
*i
= doms
, *target
= doms
;
166 if (i
->labels
< target
->labels
) {
169 } while (i
++ != last
);
171 for (i
= doms
; i
!= last
; i
++) {
172 CompactDomain
*group_last
;
175 if (i
->common_octets
== depth
) {
180 for (group_last
= i
; group_last
!= last
; group_last
++) {
181 size_t co
= group_last
->common_octets
;
185 if (co
< next_depth
) {
189 domain_mkxrefs(i
, group_last
, next_depth
);
203 if (i
!= target
&& i
->refdom
== NULL
) {
205 i
->common_octets
= depth
;
207 } while (i
++ != last
);
210 static size_t domain_compactify(CompactDomain
*domains
, size_t n
)
212 uint8_t *start
= domains
->self
->labels
, *outptr
= start
;
215 for (i
= 0; i
< n
; i
++) {
216 CompactDomain
*cd
= domains
[i
].self
;
217 CompactDomain
*rd
= cd
->refdom
;
220 size_t moff
= (rd
->labels
- start
)
221 + (rd
->len
- cd
->common_octets
);
222 if (moff
< 0x3FFFu
) {
223 cd
->len
-= cd
->common_octets
- 2;
224 cd
->labels
[cd
->len
- 1] = moff
& 0xFFu
;
225 cd
->labels
[cd
->len
- 2] = 0xC0u
| (moff
>> 8);
229 if (cd
->labels
!= outptr
) {
230 memmove(outptr
, cd
->labels
, cd
->len
);
235 return outptr
- start
;
238 int translate_dnssearch(Slirp
*s
, const char **names
)
240 size_t blocks
, bsrc_start
, bsrc_end
, bdst_start
;
241 size_t i
, num_domains
, memreq
= 0;
242 uint8_t *result
= NULL
, *outptr
;
243 CompactDomain
*domains
= NULL
;
244 const char **nameptr
= names
;
246 while (*nameptr
!= NULL
) {
250 num_domains
= nameptr
- names
;
251 if (num_domains
== 0) {
255 domains
= g_malloc(num_domains
* sizeof(*domains
));
257 for (i
= 0; i
< num_domains
; i
++) {
258 size_t nlen
= strlen(names
[i
]);
259 memreq
+= nlen
+ 2; /* 1 zero octet + 1 label length octet */
260 domains
[i
].self
= domains
+ i
;
261 domains
[i
].len
= nlen
;
262 domains
[i
].common_octets
= 0;
263 domains
[i
].refdom
= NULL
;
266 /* reserve extra 2 header bytes for each 255 bytes of output */
267 memreq
+= ((memreq
+ MAX_OPT_LEN
- 1) / MAX_OPT_LEN
) * OPT_HEADER_LEN
;
268 result
= g_malloc(memreq
* sizeof(*result
));
271 for (i
= 0; i
< num_domains
; i
++) {
272 domains
[i
].labels
= outptr
;
273 domain_mklabels(domains
+ i
, names
[i
]);
274 outptr
+= domains
[i
].len
;
277 if (outptr
== result
) {
283 qsort(domains
, num_domains
, sizeof(*domains
), domain_suffix_ord
);
284 domain_fixup_order(domains
, num_domains
);
286 for (i
= 1; i
< num_domains
; i
++) {
287 size_t cl
= domain_common_label(domains
+ i
- 1, domains
+ i
);
288 domains
[i
- 1].common_octets
= cl
;
291 domain_mkxrefs(domains
, domains
+ num_domains
- 1, 0);
292 memreq
= domain_compactify(domains
, num_domains
);
294 blocks
= (memreq
+ MAX_OPT_LEN
- 1) / MAX_OPT_LEN
;
296 bsrc_start
= (blocks
- 1) * MAX_OPT_LEN
;
297 bdst_start
= bsrc_start
+ blocks
* OPT_HEADER_LEN
;
298 memreq
+= blocks
* OPT_HEADER_LEN
;
301 size_t len
= bsrc_end
- bsrc_start
;
302 memmove(result
+ bdst_start
, result
+ bsrc_start
, len
);
303 result
[bdst_start
- 2] = RFC3397_OPT_DOMAIN_SEARCH
;
304 result
[bdst_start
- 1] = len
;
305 bsrc_end
= bsrc_start
;
306 bsrc_start
-= MAX_OPT_LEN
;
307 bdst_start
-= MAX_OPT_LEN
+ OPT_HEADER_LEN
;
311 s
->vdnssearch
= result
;
312 s
->vdnssearch_len
= memreq
;